{"version":3,"file":"mass-tools.js","sources":["../packages/ms-spectrum/node_modules/is-any-array/lib-esm/index.js","../packages/ms-spectrum/node_modules/ml-spectra-processing/lib-esm/x/xMedian.js","../packages/ms-spectrum/node_modules/ml-spectra-processing/lib-esm/x/utils/getOutputArray.js","../packages/ms-spectrum/node_modules/ml-spectra-processing/lib-esm/x/xCheck.js","../packages/ms-spectrum/node_modules/ml-spectra-processing/lib-esm/x/xFindClosestIndex.js","../packages/ms-spectrum/node_modules/ml-spectra-processing/lib-esm/x/xGetFromToIndex.js","../packages/ms-spectrum/node_modules/ml-spectra-processing/lib-esm/x/xMaxValue.js","../packages/ms-spectrum/node_modules/ml-spectra-processing/lib-esm/x/xMinValue.js","../packages/ms-spectrum/node_modules/ml-spectra-processing/lib-esm/x/xIsEquallySpaced.js","../packages/ms-spectrum/node_modules/ml-spectra-processing/lib-esm/x/xIsMonotone.js","../packages/ms-spectrum/node_modules/ml-spectra-processing/lib-esm/x/xIsMonotoneIncreasing.js","../packages/ms-spectrum/node_modules/ml-spectra-processing/lib-esm/x/xMedianAbsoluteDeviation.js","../packages/ms-spectrum/node_modules/ml-spectra-processing/lib-esm/x/xMinMaxValues.js","../packages/ms-spectrum/node_modules/ml-spectra-processing/lib-esm/x/xNoiseStandardDeviation.js","../packages/ms-spectrum/node_modules/ml-spectra-processing/lib-esm/x/xSum.js","../packages/ms-spectrum/node_modules/ml-spectra-processing/lib-esm/x/xNormed.js","../packages/ms-spectrum/node_modules/ml-spectra-processing/lib-esm/xy/xyCheck.js","../packages/ms-spectrum/node_modules/ml-spectra-processing/lib-esm/xy/xyEnsureGrowingX.js","../packages/ms-spectrum/node_modules/ml-spectra-processing/lib-esm/xy/xyFilterMinYValue.js","../packages/ms-spectrum/node_modules/ml-spectra-processing/lib-esm/x/xSort.js","../packages/ms-spectrum/node_modules/ml-spectra-processing/lib-esm/xy/xyFilterTopYValues.js","../packages/ms-spectrum/node_modules/ml-spectra-processing/lib-esm/xy/xyJoinX.js","../packages/ms-spectrum/node_modules/ml-spectra-processing/lib-esm/xy/xySortX.js","../packages/ms-spectrum/node_modules/ml-spectra-processing/lib-esm/xyArray/utils/getSlots.js","../packages/ms-spectrum/node_modules/ml-spectra-processing/lib-esm/xyArray/xyArrayAlign.js","../packages/ms-spectrum/node_modules/ml-spectra-processing/lib-esm/xyObject/xyObjectCheck.js","../packages/ms-spectrum/node_modules/ml-spectra-processing/lib-esm/xyObject/xyObjectMaxXPoint.js","../packages/ms-spectrum/node_modules/ml-spectra-processing/lib-esm/xyObject/xyObjectMinXPoint.js","../packages/ms-spectrum/node_modules/ml-spectra-processing/lib-esm/xyObject/xyObjectSumY.js","../packages/ms-spectrum/src/getBestPeaks.js","../packages/mf-parser/src/Kind.js","../packages/mf-parser/src/util/parseCharge.js","../packages/mf-parser/src/parse.js","../packages/mf-parser/src/util/subSuperscript.js","../packages/mf-parser/src/Format.js","../packages/mf-parser/src/util/formatCharge.js","../packages/mf-parser/src/util/toDisplay.js","../packages/mf-parser/src/Style.js","../packages/mf-parser/src/util/toHtml.js","../packages/chemical-elements/src/constants.js","../packages/chemical-elements/src/elementsAndIsotopes.js","../packages/chemical-elements/src/elements.js","../packages/chemical-elements/src/elementsAndIsotopesObject.js","../packages/chemical-elements/src/elementsAndStableIsotopes.js","../packages/chemical-elements/src/elementsAndStableIsotopesObject.js","../packages/chemical-elements/src/elementsObject.js","../packages/chemical-elements/src/stableIsotopesObject.js","../packages/chemical-elements/src/isotopesObject.js","../packages/chemical-elements/src/unsaturationsObject.js","../packages/mf-parser/src/ensureCase.js","../packages/chemical-groups/src/groups.js","../packages/chemical-groups/src/groupsObject.js","../packages/chemical-groups/src/groupsToSequence.js","../packages/mf-parser/src/util/getIsotopeRatioInfo.js","../packages/mf-parser/src/util/getEA.js","../packages/mf-parser/src/util/getElements.js","../packages/mf-parser/src/util/partToAtoms.js","../packages/mf-parser/src/util/partToMF.js","../packages/mf-parser/src/util/getInfo.js","../packages/mf-parser/src/util/getIsotopesInfo.js","../packages/mf-parser/src/util/partsToDisplay.js","../packages/mf-parser/src/util/partsToMF.js","../packages/atom-sorter/src/index.js","../packages/mf-parser/src/util/toParts.js","../packages/mf-parser/src/util/toText.js","../packages/mf-parser/src/MF.js","../packages/ms-spectrum/src/getPeaks.js","../packages/ms-spectrum/src/getFragmentPeaks.js","../packages/ms-spectrum/src/getMassRemainder.js","../packages/ms-spectrum/node_modules/peaks-similarity/lib-esm/calculateOverlapFromDiff.js","../packages/ms-spectrum/node_modules/peaks-similarity/lib-esm/checkPeaks.js","../packages/ms-spectrum/node_modules/peaks-similarity/lib-esm/extract.js","../packages/ms-spectrum/node_modules/peaks-similarity/lib-esm/getCommonArray.js","../packages/ms-spectrum/node_modules/ml-stat/array.js","../packages/ms-spectrum/node_modules/ml-stat/matrix.js","../packages/ms-spectrum/node_modules/ml-stat/index.js","../packages/ms-spectrum/node_modules/peaks-similarity/lib-esm/normalize.js","../packages/ms-spectrum/node_modules/peaks-similarity/lib-esm/commonExtractAndNormalize.js","../packages/ms-spectrum/node_modules/peaks-similarity/lib-esm/extractAndNormalize.js","../packages/ms-spectrum/node_modules/peaks-similarity/lib-esm/getIntersection.js","../packages/ms-spectrum/node_modules/peaks-similarity/lib-esm/index.js","../packages/ms-spectrum/src/getPeakChargeBySimilarity.js","../packages/ms-spectrum/src/isContinuous.js","../packages/ms-spectrum/node_modules/@lukeed/uuid/dist/index.mjs","../packages/ms-spectrum/node_modules/ml-savitzky-golay-generalized/lib-esm/index.js","../packages/ms-spectrum/node_modules/ml-gsd/lib-esm/utils/optimizeTop.js","../packages/ms-spectrum/node_modules/ml-gsd/lib-esm/gsd.js","../packages/ms-spectrum/src/appendPeaksCharge.js","../packages/ms-spectrum/src/peakPicking.js","../packages/ms-spectrum/node_modules/ml-regression-base/src/maybeToPrecision.js","../packages/ms-spectrum/node_modules/ml-regression-base/src/checkArrayLength.js","../packages/ms-spectrum/node_modules/ml-regression-base/src/index.js","../packages/ms-spectrum/node_modules/ml-regression-simple-linear/src/index.js","../packages/ms-spectrum/node_modules/ml-regression-power/src/index.js","../packages/ms-spectrum/src/peaksWidth.js","../packages/ms-spectrum/src/Spectrum.js","../packages/ms-spectrum/node_modules/ml-distance/src/similarities/cosine.js","../packages/ms-spectrum/src/MSComparator.js","../packages/ms-spectrum/src/jsgraph/getPeaksAnnotation.js","../packages/ms-spectrum/src/jsgraph/index.js","../packages/mf-matcher/src/unsaturationMatcher.js","../packages/mf-matcher/src/generalMatcher.js","../packages/mf-utilities/src/getMsem.js","../packages/mf-utilities/src/getMsInfo.js","../packages/mf-utilities/src/mfDiff.js","../packages/mf-utilities/src/processRange.js","../packages/mf-utilities/src/preprocessIonizations.js","../packages/mf-utilities/src/preprocessRanges.js","../packages/mf-utilities/src/getRangesForFragment.js","../packages/mf-matcher/node_modules/ml-spectra-processing/lib-esm/x/xFindClosestIndex.js","../packages/mf-matcher/src/msemMatcher.js","../packages/mf-finder/src/TargetMassCache.js","../packages/mf-finder/src/findMFsSync.js","../packages/mf-finder/src/findMFs.js","../packages/emdb/src/append/appendFragmentsInfo.js","../packages/mf-generator/node_modules/sum-object-keys/lib/index.js","../packages/mf-generator/src/index.js","../packages/emdb/src/from/fromArray.js","../packages/mass-fragmentation/node_modules/openchemlib-utils/src/util/tagAtom.js","../packages/mass-fragmentation/node_modules/openchemlib-utils/src/util/makeRacemic.js","../packages/mass-fragmentation/node_modules/openchemlib-utils/src/util/isCsp3.js","../packages/mass-fragmentation/node_modules/openchemlib-utils/src/hose/getHoseCodesForAtoms.js","../packages/mass-fragmentation/node_modules/atom-sorter/src/index.js","../packages/mass-fragmentation/node_modules/openchemlib-utils/src/util/getMF.js","../packages/mass-fragmentation/src/fragmentAcyclicBonds.js","../packages/mass-fragmentation/src/utils/getRingsInfo.js","../packages/mass-fragmentation/src/utils/getFragmentableRings.js","../packages/mass-fragmentation/src/fragmentRings.js","../packages/mass-fragmentation/src/fragment.js","../packages/emdb/src/from/fromMolecules.js","../packages/emdb/src/from/fromMonoisotopicMass.js","../packages/nucleotide/src/ensureUppercaseSequence.js","../packages/nucleotide/src/sequenceToMF.js","../packages/nucleotide/src/furanThreeTerm.js","../packages/nucleotide/src/addFiveTerm.js","../packages/nucleotide/src/baseLoss.js","../packages/nucleotide/src/addFiveTermBaseLoss.js","../packages/nucleotide/src/addInternalTerm.js","../packages/nucleotide/src/addThreeTerm.js","../packages/nucleotide/src/addThreeTermBaseLoss.js","../packages/nucleotide/src/generateFragments.js","../packages/emdb/src/from/fromNucleicSequence.js","../packages/peptide/src/aminoAcids.js","../packages/peptide/src/isoElectricPoint.js","../packages/peptide/src/splitPeptide.js","../packages/peptide/src/allowNeutralLoss.js","../packages/peptide/src/getAA.js","../packages/peptide/src/chargePeptide.js","../packages/peptide/src/sequenceToMF.js","../packages/peptide/src/generatePeptideFragments.js","../packages/peptide/src/digestPeptide.js","../packages/peptide/src/index.js","../packages/emdb/src/from/util/fragmentPeptide.js","../packages/emdb/src/from/fromPeptidicSequence.js","../packages/emdb/src/from/fromRange.js","../packages/emdb/node_modules/jszip/dist/jszip.min.js","../packages/emdb/node_modules/cross-fetch/dist/browser-ponyfill.js","../packages/emdb/src/util/fetchArrayBuffer.js","../packages/emdb/src/loadCommercials.js","../packages/mf-from-google-sheet/node_modules/papaparse/papaparse.min.js","../packages/mf-from-google-sheet/node_modules/cross-fetch/dist/browser-ponyfill.js","../packages/mf-from-google-sheet/src/util/fetchText.js","../packages/mf-from-google-sheet/src/index.js","../packages/emdb/src/loadGoogleSheet.js","../packages/emdb/src/loadKnapSack.js","../packages/emdb/src/search.js","../packages/emdb/src/searchMSEM.js","../packages/isotopic-distribution/node_modules/ml-peak-shape-generator/lib-esm/util/constants.js","../packages/isotopic-distribution/node_modules/ml-peak-shape-generator/lib-esm/util/erfinv.js","../packages/isotopic-distribution/node_modules/ml-peak-shape-generator/lib-esm/shapes/1d/gaussian/Gaussian.js","../packages/isotopic-distribution/node_modules/ml-peak-shape-generator/lib-esm/shapes/1d/lorentzian/Lorentzian.js","../packages/isotopic-distribution/node_modules/ml-peak-shape-generator/lib-esm/shapes/1d/pseudoVoigt/PseudoVoigt.js","../packages/isotopic-distribution/node_modules/ml-peak-shape-generator/lib-esm/shapes/1d/getShape1D.js","../packages/isotopic-distribution/node_modules/spectrum-generator/lib-esm/util/addBaseline.js","../packages/isotopic-distribution/node_modules/is-any-array/lib-esm/index.js","../packages/isotopic-distribution/node_modules/ml-spectra-processing/lib-esm/x/xAdd.js","../packages/isotopic-distribution/node_modules/ml-spectra-processing/lib-esm/x/xCheck.js","../packages/isotopic-distribution/node_modules/ml-spectra-processing/lib-esm/x/xFindClosestIndex.js","../packages/isotopic-distribution/node_modules/ml-spectra-processing/lib-esm/x/xGetFromToIndex.js","../packages/isotopic-distribution/node_modules/ml-spectra-processing/lib-esm/x/xMaxValue.js","../packages/isotopic-distribution/node_modules/ml-xsadd/lib-es6/xsadd.js","../packages/isotopic-distribution/node_modules/ml-spectra-processing/lib-esm/utils/createRandomArray.js","../packages/isotopic-distribution/node_modules/spectrum-generator/lib-esm/util/addNoise.js","../packages/isotopic-distribution/node_modules/spectrum-generator/lib-esm/SpectrumGenerator.js","../packages/isotopic-distribution/src/utils/closestPointX.js","../packages/isotopic-distribution/src/utils/joinX.js","../packages/isotopic-distribution/src/utils/multiply.js","../packages/isotopic-distribution/src/utils/power.js","../packages/isotopic-distribution/src/Distribution.js","../packages/isotopic-distribution/src/utils/getDerivedCompositionInfo.js","../packages/isotopic-distribution/src/IsotopicDistribution.js","../packages/emdb/node_modules/peaks-similarity/lib-esm/calculateOverlapFromDiff.js","../packages/emdb/node_modules/peaks-similarity/lib-esm/checkPeaks.js","../packages/emdb/node_modules/peaks-similarity/lib-esm/extract.js","../packages/emdb/node_modules/peaks-similarity/lib-esm/getCommonArray.js","../packages/emdb/node_modules/ml-stat/array.js","../packages/emdb/node_modules/ml-stat/matrix.js","../packages/emdb/node_modules/ml-stat/index.js","../packages/emdb/node_modules/peaks-similarity/lib-esm/normalize.js","../packages/emdb/node_modules/peaks-similarity/lib-esm/commonExtractAndNormalize.js","../packages/emdb/node_modules/peaks-similarity/lib-esm/extractAndNormalize.js","../packages/emdb/node_modules/peaks-similarity/lib-esm/getIntersection.js","../packages/emdb/node_modules/peaks-similarity/lib-esm/index.js","../packages/emdb/src/searchSimilarity.js","../packages/emdb/src/util/fetchJSON.js","../packages/emdb/src/searchPubchem.js","../packages/emdb/src/searchActivesOrNaturals.js","../packages/emdb/node_modules/is-any-array/lib-esm/index.js","../packages/emdb/node_modules/ml-regression-base/src/maybeToPrecision.js","../packages/emdb/node_modules/ml-regression-base/src/checkArrayLength.js","../packages/emdb/node_modules/ml-regression-base/src/index.js","../packages/emdb/node_modules/median-quickselect/lib/median-quickselect.min.js","../packages/emdb/node_modules/ml-array-median/lib-es6/index.js","../packages/emdb/node_modules/ml-regression-theil-sen/src/index.js","../packages/emdb/node_modules/ml-spectra-processing/lib-esm/x/xCheck.js","../packages/emdb/node_modules/ml-spectra-processing/lib-esm/x/xFindClosestIndex.js","../packages/emdb/node_modules/ml-spectra-processing/lib-esm/x/xGetFromToIndex.js","../packages/emdb/node_modules/ml-spectra-processing/lib-esm/x/xMaxValue.js","../packages/emdb/node_modules/ml-spectra-processing/lib-esm/x/xMinValue.js","../packages/emdb/src/massShifts.js","../packages/emdb/src/index.js","../packages/ms-report/src/appendInternals.js","../packages/ms-report/src/appendResidues.js","../packages/ms-report/node_modules/@svgdotjs/svg.js/dist/svg.esm.js","../packages/ms-report/src/getPaper.browser.js","../packages/ms-report/src/appendResiduesPosition.js","../packages/ms-report/src/appendResults.js","../packages/ms-report/src/appendRows.js","../packages/ms-report/src/appendRowsInformation.js","../packages/ms-report/src/draw/addCSS.js","../packages/ms-report/src/draw/addScript.js","../packages/ms-report/src/draw/drawLabel.js","../packages/ms-report/src/draw/drawInternals.js","../packages/ms-report/src/draw/drawReplacements.js","../packages/ms-report/src/draw/drawTerminals.js","../packages/ms-report/src/draw/drawSequence.js","../packages/ms-report/src/sequenceSVG.js","../packages/mf-from-ea/src/preprocessEARanges.js","../packages/mf-from-ea/src/index.js","../packages/mass-tools/src/index.js"],"sourcesContent":["const toString = Object.prototype.toString;\n/**\n * Checks if an object is an instance of an Array (array or typed array).\n *\n * @param {any} value - Object to check.\n * @returns {boolean} True if the object is an array.\n */\nexport function isAnyArray(value) {\n    return toString.call(value).endsWith('Array]');\n}\n//# sourceMappingURL=index.js.map","import { isAnyArray } from 'is-any-array';\n/**\n * Calculates the median of an array\n *\n * @param input - Array containing values\n * @returns - median\n */\nexport function xMedian(input) {\n    if (!isAnyArray(input)) {\n        throw new TypeError('input must be an array');\n    }\n    if (input.length === 0) {\n        throw new TypeError('input must not be empty');\n    }\n    const array = input.slice();\n    let low = 0;\n    let high = array.length - 1;\n    let middle, ll, hh;\n    let median = calcMiddle(low, high);\n    while (true) {\n        if (high <= low) {\n            return array[median];\n        }\n        if (high === low + 1) {\n            if (array[low] > array[high]) {\n                swap(array, low, high);\n            }\n            return array[median];\n        }\n        // Find median of low, middle and high items; swap into position low\n        middle = calcMiddle(low, high);\n        if (array[middle] > array[high])\n            swap(array, middle, high);\n        if (array[low] > array[high])\n            swap(array, low, high);\n        if (array[middle] > array[low])\n            swap(array, middle, low);\n        // Swap low item (now in position middle) into position (low+1)\n        swap(array, middle, low + 1);\n        // Nibble from each end towards middle, swapping items when stuck\n        ll = low + 1;\n        hh = high;\n        while (true) {\n            do\n                ll++;\n            while (array[low] > array[ll]);\n            do\n                hh--;\n            while (array[hh] > array[low]);\n            if (hh < ll) {\n                break;\n            }\n            swap(array, ll, hh);\n        }\n        // Swap middle item (in position low) back into correct position\n        swap(array, low, hh);\n        // Re-set active partition\n        if (hh <= median) {\n            low = ll;\n        }\n        if (hh >= median) {\n            high = hh - 1;\n        }\n    }\n}\nfunction swap(array, i, j) {\n    const temp = array[j];\n    array[j] = array[i];\n    array[i] = temp;\n}\nfunction calcMiddle(i, j) {\n    return ~~((i + j) / 2);\n}\n//# sourceMappingURL=xMedian.js.map","import { isAnyArray } from 'is-any-array';\n/**\n * This function\n * @param output - undefined or a new array\n * @param length - length of the output array\n * @returns\n */\nexport function getOutputArray(output, length) {\n    if (output !== undefined) {\n        if (!isAnyArray(output)) {\n            throw new TypeError('output option must be an array if specified');\n        }\n        if (output.length !== length) {\n            throw new TypeError('the output array does not have the correct length');\n        }\n        return output;\n    }\n    else {\n        return new Float64Array(length);\n    }\n}\n//# sourceMappingURL=getOutputArray.js.map","import { isAnyArray } from 'is-any-array';\n/**\n * Checks if input is of type array\n *\n * @param input - input\n */\nexport function xCheck(input) {\n    if (!isAnyArray(input)) {\n        throw new TypeError('input must be an array');\n    }\n    if (input.length === 0) {\n        throw new TypeError('input must not be empty');\n    }\n}\n//# sourceMappingURL=xCheck.js.map","/**\n * Returns the closest index of a `target`\n *\n * @param array - array of numbers\n * @param target - target\n * @returns - closest index\n */\nexport function xFindClosestIndex(array, target, options = {}) {\n    const { sorted = true } = options;\n    if (sorted) {\n        let low = 0;\n        let high = array.length - 1;\n        let middle = 0;\n        while (high - low > 1) {\n            middle = low + ((high - low) >> 1);\n            if (array[middle] < target) {\n                low = middle;\n            }\n            else if (array[middle] > target) {\n                high = middle;\n            }\n            else {\n                return middle;\n            }\n        }\n        if (low < array.length - 1) {\n            if (Math.abs(target - array[low]) < Math.abs(array[low + 1] - target)) {\n                return low;\n            }\n            else {\n                return low + 1;\n            }\n        }\n        else {\n            return low;\n        }\n    }\n    else {\n        let index = 0;\n        let diff = Number.POSITIVE_INFINITY;\n        for (let i = 0; i < array.length; i++) {\n            const currentDiff = Math.abs(array[i] - target);\n            if (currentDiff < diff) {\n                diff = currentDiff;\n                index = i;\n            }\n        }\n        return index;\n    }\n}\n//# sourceMappingURL=xFindClosestIndex.js.map","import { xFindClosestIndex } from './xFindClosestIndex';\n/**\n * Returns an object with {fromIndex, toIndex} for a specific from / to\n *\n * @param x - array of numbers\n * @param options - Options\n */\nexport function xGetFromToIndex(x, options = {}) {\n    let { fromIndex, toIndex, from, to } = options;\n    if (fromIndex === undefined) {\n        if (from !== undefined) {\n            fromIndex = xFindClosestIndex(x, from);\n        }\n        else {\n            fromIndex = 0;\n        }\n    }\n    if (toIndex === undefined) {\n        if (to !== undefined) {\n            toIndex = xFindClosestIndex(x, to);\n        }\n        else {\n            toIndex = x.length - 1;\n        }\n    }\n    if (fromIndex < 0)\n        fromIndex = 0;\n    if (toIndex < 0)\n        toIndex = 0;\n    if (fromIndex >= x.length)\n        fromIndex = x.length - 1;\n    if (toIndex >= x.length)\n        toIndex = x.length - 1;\n    if (fromIndex > toIndex)\n        [fromIndex, toIndex] = [toIndex, fromIndex];\n    return { fromIndex, toIndex };\n}\n//# sourceMappingURL=xGetFromToIndex.js.map","import { xCheck } from './xCheck';\nimport { xGetFromToIndex } from './xGetFromToIndex';\n/**\n * Computes the maximal value of an array of values\n *\n * @param array - array of numbers\n * @param options - options\n */\nexport function xMaxValue(array, options = {}) {\n    xCheck(array);\n    const { fromIndex, toIndex } = xGetFromToIndex(array, options);\n    let maxValue = array[fromIndex];\n    for (let i = fromIndex + 1; i <= toIndex; i++) {\n        if (array[i] > maxValue) {\n            maxValue = array[i];\n        }\n    }\n    return maxValue;\n}\n//# sourceMappingURL=xMaxValue.js.map","import { xCheck } from './xCheck';\nimport { xGetFromToIndex } from './xGetFromToIndex';\n/**\n * Computes the minimal value of an array of values\n *\n * @param array - array of numbers\n * @param options - options\n */\nexport function xMinValue(array, options = {}) {\n    xCheck(array);\n    const { fromIndex, toIndex } = xGetFromToIndex(array, options);\n    let minValue = array[fromIndex];\n    for (let i = fromIndex + 1; i <= toIndex; i++) {\n        if (array[i] < minValue) {\n            minValue = array[i];\n        }\n    }\n    return minValue;\n}\n//# sourceMappingURL=xMinValue.js.map","/**\n * Check if the values are separated always by the same difference\n *\n * @param array - Monotone growing array of number\n */\nexport function xIsEquallySpaced(array, options = {}) {\n    if (array.length < 3)\n        return true;\n    const { tolerance = 0.05 } = options;\n    let maxDx = 0;\n    let minDx = Number.MAX_SAFE_INTEGER;\n    for (let i = 0; i < array.length - 1; ++i) {\n        let absoluteDifference = array[i + 1] - array[i];\n        if (absoluteDifference < minDx) {\n            minDx = absoluteDifference;\n        }\n        if (absoluteDifference > maxDx) {\n            maxDx = absoluteDifference;\n        }\n    }\n    return (maxDx - minDx) / maxDx < tolerance;\n}\n//# sourceMappingURL=xIsEquallySpaced.js.map","/**\n * Returns true if x is monotone\n *\n * @param array - array of numbers\n */\nexport function xIsMonotone(array) {\n    if (array.length <= 2) {\n        return true;\n    }\n    if (array[0] === array[1]) {\n        // maybe a constant series\n        for (let i = 1; i < array.length - 1; i++) {\n            if (array[i] !== array[i + 1])\n                return false;\n        }\n        return true;\n    }\n    if (array[0] < array[array.length - 1]) {\n        for (let i = 0; i < array.length - 1; i++) {\n            if (array[i] >= array[i + 1])\n                return false;\n        }\n    }\n    else {\n        for (let i = 0; i < array.length - 1; i++) {\n            if (array[i] <= array[i + 1])\n                return false;\n        }\n    }\n    return true;\n}\n//# sourceMappingURL=xIsMonotone.js.map","/**\n * Returns true if x is monotone\n *\n * @param array - array of numbers\n */\nexport function xIsMonotoneIncreasing(array) {\n    if (array.length < 2) {\n        return true;\n    }\n    for (let i = 0; i < array.length - 1; i++) {\n        if (array[i] >= array[i + 1])\n            return false;\n    }\n    return true;\n}\n//# sourceMappingURL=xIsMonotoneIncreasing.js.map","import { xMedian } from './xMedian';\n/**\n * This function calculates the median absolute deviation (MAD)\n * https://en.wikipedia.org/wiki/Median_absolute_deviation\n * @param array\n */\nexport function xMedianAbsoluteDeviation(array) {\n    const median = xMedian(array);\n    const averageDeviations = new Float64Array(array.length);\n    for (let i = 0; i < array.length; i++) {\n        averageDeviations[i] = Math.abs(array[i] - median);\n    }\n    return {\n        median,\n        mad: xMedian(averageDeviations),\n    };\n}\n//# sourceMappingURL=xMedianAbsoluteDeviation.js.map","import { xCheck } from './xCheck';\n/**\n * Return min and max values of an array\n *\n * @param array - array of number\n * @returns - Object with 2 properties, min and max\n */\nexport function xMinMaxValues(array) {\n    xCheck(array);\n    let min = array[0];\n    let max = array[0];\n    for (let value of array) {\n        if (value < min)\n            min = value;\n        if (value > max)\n            max = value;\n    }\n    return { min, max };\n}\n//# sourceMappingURL=xMinMaxValues.js.map","import { xMedianAbsoluteDeviation } from '..';\n/**\n * Determine noise level using MAD https://en.wikipedia.org/wiki/Median_absolute_deviation\n * Constant to convert mad to sd calculated using https://www.wolframalpha.com/input?i=sqrt%282%29+inverse+erf%280.5%29\n * This assumes a gaussian distribution of the noise\n * @param array\n * @returns noise level corresponding to one standard deviation\n */\nexport function xNoiseStandardDeviation(array) {\n    const { mad, median } = xMedianAbsoluteDeviation(array);\n    return { sd: mad / 0.6744897501960817, mad, median };\n}\n//# sourceMappingURL=xNoiseStandardDeviation.js.map","import { xCheck } from './xCheck';\nimport { xGetFromToIndex } from './xGetFromToIndex';\n/**\n * Calculate the sum of the values\n *\n * @param array - Object that contains property x (an ordered increasing array) and y (an array).\n * @param options - Options.\n * @returns XSum value on the specified range.\n */\nexport function xSum(array, options = {}) {\n    xCheck(array);\n    const { fromIndex, toIndex } = xGetFromToIndex(array, options);\n    let sumValue = array[fromIndex];\n    for (let i = fromIndex + 1; i <= toIndex; i++) {\n        sumValue += array[i];\n    }\n    return sumValue;\n}\n//# sourceMappingURL=xSum.js.map","import { getOutputArray } from './utils/getOutputArray';\nimport { xCheck } from './xCheck';\nimport { xMaxValue } from './xMaxValue';\nimport { xSum } from './xSum';\n/**\n * Divides the data with either the sum, the absolute sum or the maximum of the data\n * @param array - Array containing values\n * @param options - options\n * @returns - normalized data\n */\nexport function xNormed(input, options = {}) {\n    const { algorithm = 'absolute', value = 1 } = options;\n    xCheck(input);\n    const output = getOutputArray(options.output, input.length);\n    if (input.length === 0) {\n        throw new Error('input must not be empty');\n    }\n    switch (algorithm.toLowerCase()) {\n        case 'absolute': {\n            let absoluteSumValue = absoluteSum(input) / value;\n            if (absoluteSumValue === 0) {\n                throw new Error('xNormed: trying to divide by 0');\n            }\n            for (let i = 0; i < input.length; i++) {\n                output[i] = input[i] / absoluteSumValue;\n            }\n            return output;\n        }\n        case 'max': {\n            let currentMaxValue = xMaxValue(input);\n            if (currentMaxValue === 0) {\n                throw new Error('xNormed: trying to divide by 0');\n            }\n            const factor = value / currentMaxValue;\n            for (let i = 0; i < input.length; i++) {\n                output[i] = input[i] * factor;\n            }\n            return output;\n        }\n        case 'sum': {\n            let sumFactor = xSum(input) / value;\n            if (sumFactor === 0) {\n                throw new Error('xNormed: trying to divide by 0');\n            }\n            for (let i = 0; i < input.length; i++) {\n                output[i] = input[i] / sumFactor;\n            }\n            return output;\n        }\n        default:\n            throw new Error(`norm: unknown algorithm: ${algorithm}`);\n    }\n}\nfunction absoluteSum(input) {\n    let sumValue = 0;\n    for (let i = 0; i < input.length; i++) {\n        sumValue += Math.abs(input[i]);\n    }\n    return sumValue;\n}\n//# sourceMappingURL=xNormed.js.map","import { isAnyArray } from 'is-any-array';\n/**\n * Throw an error in no an object of x,y arrays\n *\n * @param data - array of points {x,y,z}\n */\nexport function xyCheck(data, options = {}) {\n    const { minLength } = options;\n    if (typeof data !== 'object' || !isAnyArray(data.x) || !isAnyArray(data.y)) {\n        throw new Error('Data must be an object of x and y arrays');\n    }\n    if (data.x.length !== data.y.length) {\n        throw new Error('The x and y arrays must have the same length');\n    }\n    if (minLength && data.x.length < minLength) {\n        throw new Error(`data.x must have a length of at least ${minLength}`);\n    }\n}\n//# sourceMappingURL=xyCheck.js.map","import { xIsMonotoneIncreasing } from '../x/xIsMonotoneIncreasing';\nimport { xyCheck } from './xyCheck';\n/**\n * Filters x,y values to allow strictly growing values in x axis.\n *\n * @param data - Object that contains property x (an ordered increasing array) and y (an array).\n */\nexport function xyEnsureGrowingX(data) {\n    xyCheck(data);\n    if (xIsMonotoneIncreasing(data.x))\n        return data;\n    const x = Array.from(data.x);\n    const y = Array.from(data.y);\n    let prevX = Number.NEGATIVE_INFINITY;\n    let currentIndex = 0;\n    for (let index = 0; index < x.length; index++) {\n        if (prevX < x[index]) {\n            if (currentIndex < index) {\n                x[currentIndex] = x[index];\n                y[currentIndex] = y[index];\n            }\n            currentIndex++;\n            prevX = x[index];\n        }\n    }\n    x.length = currentIndex;\n    y.length = currentIndex;\n    return { x, y };\n}\n//# sourceMappingURL=xyEnsureGrowingX.js.map","import { xMinMaxValues } from '../x/xMinMaxValues';\n/** Filter an array x/y based on various criteria x points are expected to be sorted\n *\n * @param data - object containing 2 properties x and y\n * @param minRelativeYValue - the minimum relative value compare to the Y max value\n * @return filtered data\n */\nexport function xyFilterMinYValue(data, minRelativeYValue) {\n    if (minRelativeYValue === undefined)\n        return data;\n    const { x, y } = data;\n    const { min, max } = xMinMaxValues(y);\n    const threshold = max * minRelativeYValue;\n    if (min >= threshold)\n        return data;\n    const newX = [];\n    const newY = [];\n    for (let i = 0; i < x.length; i++) {\n        if (y[i] >= threshold) {\n            newX.push(x[i]);\n            newY.push(y[i]);\n        }\n    }\n    return {\n        x: newX,\n        y: newY,\n    };\n}\n//# sourceMappingURL=xyFilterMinYValue.js.map","/** Function that sorts arrays or Float64Arrays in ascending order\n *\n * @param array - array to sort\n * @returns sorted array\n */\nexport function xSortAscending(array) {\n    if (array instanceof Float64Array) {\n        return array.sort();\n    }\n    else if (Array.isArray(array)) {\n        return array.sort((a, b) => a - b);\n    }\n    throw new Error('Trying to sort non aray');\n}\n/** Function that sorts arrays or Float64Arrays in descending order\n *\n * @param array - array to sort\n * @returns sorted array\n */\nexport function xSortDescending(array) {\n    if (array instanceof Float64Array) {\n        return array.sort().reverse();\n    }\n    else if (Array.isArray(array)) {\n        return array.sort((a, b) => b - a);\n    }\n    throw new Error('Trying to sort non aray');\n}\n//# sourceMappingURL=xSort.js.map","import { xSortDescending } from '../x/xSort';\n/** Filter an array x/y based on various criteria x points are expected to be sorted\n *\n * @param data - object containing 2 properties x and y\n * @param options - options\n * @return filtered data\n */\nexport function xyFilterTopYValues(data, nbPeaks) {\n    if (nbPeaks === undefined)\n        return data;\n    if (nbPeaks > data.x.length)\n        return data;\n    const { x, y } = data;\n    const newX = [];\n    const newY = [];\n    const descending = xSortDescending(y.slice());\n    const threshold = descending[nbPeaks - 1];\n    let nbThreshold = 0;\n    for (let i = 0; i < nbPeaks; i++) {\n        if (descending[i] === threshold) {\n            nbThreshold++;\n        }\n    }\n    for (let i = 0; i < x.length; i++) {\n        if (y[i] > threshold) {\n            newX.push(x[i]);\n            newY.push(y[i]);\n        }\n        else if (y[i] === threshold) {\n            nbThreshold--;\n            if (nbThreshold >= 0) {\n                newX.push(x[i]);\n                newY.push(y[i]);\n            }\n        }\n    }\n    return {\n        x: newX,\n        y: newY,\n    };\n}\n//# sourceMappingURL=xyFilterTopYValues.js.map","import { xyCheck } from './xyCheck';\n/**\n * Join x / y values when difference in X is closer than delta.\n * When joining, y values are summed and x values are weighted average\n *\n * @param data - Object that contains property x (an ordered increasing array) and y (an array)\n * @param options - Options\n * @returns - An object with the xyIntegration function\n */\nexport function xyJoinX(data, options = {}) {\n    xyCheck(data);\n    const { delta = 1 } = options;\n    const deltaIsFunction = typeof delta === 'function';\n    const x = Array.from(data.x);\n    const y = Array.from(data.y);\n    if (x.length < 2) {\n        return { x, y };\n    }\n    let position = 0;\n    for (let i = 1; i < x.length; i++) {\n        let difference = x[i] - x[i - 1];\n        let currentDelta = deltaIsFunction ? delta((x[i] + x[i - 1]) / 2) : delta;\n        if (difference <= currentDelta) {\n            // we join\n            if (y[position] !== 0 || y[i] !== 0) {\n                x[position] =\n                    (x[position] * y[position] + x[i] * y[i]) / (y[position] + y[i]);\n                y[position] += y[i];\n            }\n        }\n        else {\n            position++;\n            x[position] = x[i];\n            y[position] = y[i];\n        }\n    }\n    x.length = position + 1;\n    y.length = position + 1;\n    return { x, y };\n}\n//# sourceMappingURL=xyJoinX.js.map","import { xIsMonotone } from '../x/xIsMonotone';\n/**\n * This function performs a quick sort of the x array while transforming the y array to preserve the coordinates.\n *\n * @param data - Object that contains property x (Array) and y (Array)\n */\nexport function xySortX(data) {\n    const { x, y } = data;\n    if (xIsMonotone(x) && x.length > 1) {\n        if (x[0] < x[1]) {\n            return {\n                x: Float64Array.from(x),\n                y: Float64Array.from(y),\n            };\n        }\n        else {\n            return {\n                x: Float64Array.from(x).reverse(),\n                y: Float64Array.from(y).reverse(),\n            };\n        }\n    }\n    let xyObject = x\n        .map((val, index) => ({\n        x: val,\n        y: y[index],\n    }))\n        .sort((a, b) => a.x - b.x);\n    let response = {\n        x: new Float64Array(x.length),\n        y: new Float64Array(y.length),\n    };\n    for (let i = 0; i < x.length; i++) {\n        response.x[i] = xyObject[i].x;\n        response.y[i] = xyObject[i].y;\n    }\n    return response;\n}\n//# sourceMappingURL=xySortX.js.map","/**\n * GetSlots.\n *\n * @param data - data.\n * @param options - Options.\n */\nexport function getSlots(data, options = {}) {\n    const { delta = 1 } = options;\n    const deltaIsFunction = typeof delta === 'function';\n    let possibleXs = Float64Array.from([].concat(...data.map((spectrum) => spectrum.x))).sort();\n    if (possibleXs.length === 0) {\n        throw new Error('xyArrayMerge can not process empty arrays');\n    }\n    let currentSlot = {\n        from: possibleXs[0],\n        to: possibleXs[0],\n        average: possibleXs[0],\n        sum: possibleXs[0],\n        number: 1,\n    };\n    let slots = [currentSlot];\n    for (let i = 1; i < possibleXs.length; i++) {\n        let currentDelta = deltaIsFunction ? delta(possibleXs[i]) : delta;\n        if (possibleXs[i] - currentSlot.to <= currentDelta) {\n            currentSlot.to = possibleXs[i];\n            currentSlot.number++;\n            currentSlot.sum += possibleXs[i];\n            currentSlot.average = currentSlot.sum / currentSlot.number;\n        }\n        else {\n            currentSlot = {\n                from: possibleXs[i],\n                to: possibleXs[i],\n                average: possibleXs[i],\n                sum: possibleXs[i],\n                number: 1,\n            };\n            slots.push(currentSlot);\n        }\n    }\n    return slots;\n}\n//# sourceMappingURL=getSlots.js.map","import { xyJoinX } from '../xy/xyJoinX';\nimport { getSlots } from './utils/getSlots';\n/**\n * Aligns data, can be used for spectra\n *\n * @param data - data\n * @param options - Options\n */\nexport function xyArrayAlign(data, options = {}) {\n    const { delta = 1 } = options;\n    data = data.map((spectrum) => xyJoinX(spectrum, { delta }));\n    const slots = getSlots(data, options);\n    let x = Float64Array.from(slots.map((slot) => slot.average));\n    let ys = new Array(data.length).fill(0).map(() => new Float64Array(x.length));\n    let positions = new Uint32Array(data.length);\n    for (let i = 0; i < slots.length; i++) {\n        let slot = slots[i];\n        for (let j = 0; j < data.length; j++) {\n            let spectrum = data[j];\n            while (positions[j] < spectrum.x.length &&\n                spectrum.x[positions[j]] <= slot.to) {\n                ys[j][i] += spectrum.y[positions[j]];\n                positions[j]++;\n            }\n        }\n    }\n    return { x, ys };\n}\n//# sourceMappingURL=xyArrayAlign.js.map","/**\n * Throw an error in no an object of x,y arrays\n *\n * @param points - list of points\n */\nexport function xyObjectCheck(points = []) {\n    if (!Array.isArray(points)) {\n        throw new Error('ArrayPoints must be an array of {x,y} object');\n    }\n    if (points.length > 0 &&\n        (points[0].x === undefined || points[0].y === undefined)) {\n        throw new Error('ArrayPoints must be an array of {x,y} object');\n    }\n}\n//# sourceMappingURL=xyObjectCheck.js.map","import { xyObjectCheck } from './xyObjectCheck';\n/**\n * Finds the max x value and return a {x,y,index} point\n *\n * @param points - Object that contains property x (an ordered increasing array) and y (an array)\n */\nexport function xyObjectMaxXPoint(points = []) {\n    xyObjectCheck(points);\n    if (points.length === 0)\n        return { x: 0, y: 0 };\n    let current = {\n        x: points[0].x,\n        y: points[0].y,\n        index: 0,\n    };\n    for (let i = 1; i < points.length; i++) {\n        if (points[i].x > current.x) {\n            current = {\n                x: points[i].x,\n                y: points[i].y,\n                index: i,\n            };\n        }\n    }\n    return current;\n}\n//# sourceMappingURL=xyObjectMaxXPoint.js.map","import { xyObjectCheck } from './xyObjectCheck';\n/**\n * Finds the min x value and return a {x,y,index} point\n *\n * @param points - Object that contains property x (an ordered increasing array) and y (an array)\n */\nexport function xyObjectMinXPoint(points = []) {\n    xyObjectCheck(points);\n    if (points.length === 0)\n        return { x: 0, y: 0 };\n    let current = {\n        x: points[0].x,\n        y: points[0].y,\n        index: 0,\n    };\n    for (let i = 1; i < points.length; i++) {\n        if (points[i].x < current.x) {\n            current = {\n                x: points[i].x,\n                y: points[i].y,\n                index: i,\n            };\n        }\n    }\n    return current;\n}\n//# sourceMappingURL=xyObjectMinXPoint.js.map","import { xyObjectCheck } from './xyObjectCheck';\n/**\n * Calculate the sum of Y values\n *\n * @param points - Object that contains property x and y (an array)\n */\nexport function xyObjectSumY(points = []) {\n    xyObjectCheck(points);\n    let sum = 0;\n    for (let point of points) {\n        sum += point.y;\n    }\n    return sum;\n}\n//# sourceMappingURL=xyObjectSumY.js.map","import { xyObjectMaxXPoint, xyObjectMinXPoint } from 'ml-spectra-processing';\n\n/**\n * Filter the array by taking the higher peaks and only\n * keep one per slot.\n * There are 2 different slots, the smallest one will have the\n * extra annotation `close` to true\n * @param {array} peaks - array of all the peaks\n * @param {object} [options={}]\n * @param {number} [options.from] - min X value of the window to consider\n * @param {number} [options.to] - max X value of the window to consider\n * @param {number} [options.searchMonoisotopicRatio=0] - search previous peaks with at least ratio height\n * @param {number} [options.limit=20] - max number of peaks\n * @param {number} [options.threshold=0.01] - minimal intensity compare to base peak\n * @param {number} [options.numberSlots=10] - define the number of slots and indirectly the slot width\n * @param {number} [options.numberCloseSlots=50]\n * @returns {array} - copy of peaks with 'close' annotation\n */\n\nexport function getBestPeaks(peaks, options = {}) {\n  const {\n    searchMonoisotopicRatio = 0,\n    from = xyObjectMinXPoint(peaks).x,\n    to = xyObjectMaxXPoint(peaks).x,\n    limit = 20,\n    threshold = 0.01,\n    numberCloseSlots = 50,\n    numberSlots = 10,\n  } = options;\n  let slot = (to - from) / numberSlots;\n  let closeSlot = (to - from) / numberCloseSlots;\n  let selected = peaks\n    .filter((peak) => peak.x >= from && peak.x <= to)\n    .map((peak) => {\n      return {\n        peak,\n        monoisotopic: false,\n      };\n    });\n\n  if (searchMonoisotopicRatio) {\n    selected = selected.sort((a, b) => b.peak.x - a.peak.x);\n\n    for (let i = 0; i < selected.length; i++) {\n      let item = selected[i];\n      for (let j = i + 1; j < selected.length; j++) {\n        let nextItem = selected[j];\n        if (item.peak.x - nextItem.peak.x < 0.09) continue;\n        if (item.peak.x - nextItem.peak.x > 1.1) break;\n        if (nextItem.peak.y > item.peak.y * searchMonoisotopicRatio) {\n          item.monoisotopic = false;\n          nextItem.monoisotopic = true;\n          break;\n        }\n      }\n    }\n  }\n\n  selected = selected.sort((a, b) => {\n    if (a.monoisotopic && !b.monoisotopic) return -1;\n    if (b.monoisotopic && !a.monoisotopic) return 1;\n    return b.peak.y - a.peak.y;\n  });\n\n  let toReturn = [];\n  if (selected.length === 0) return [];\n  let minY = selected[0].peak.y * threshold;\n  peakLoop: for (let item of selected) {\n    if (item.peak.y < minY) {\n      if (item.monoisotopic) {\n        continue;\n      } else {\n        break;\n      }\n    }\n    let close = false;\n    for (let existing of toReturn) {\n      if (Math.abs(existing.x - item.peak.x) < closeSlot) {\n        continue peakLoop;\n      }\n      if (Math.abs(existing.x - item.peak.x) < slot) {\n        close = true;\n      }\n    }\n    let newPeak = JSON.parse(JSON.stringify(item.peak));\n    newPeak.close = close;\n    toReturn.push(newPeak);\n    if (toReturn.length === limit) break;\n  }\n  return toReturn.sort((a, b) => a.x - b.x);\n}\n","/**\n * Define static variable corresponding to the various Kinds of a molecular formula part.\n */\n\nexport const Kind = {\n  BEGIN: 'begin',\n  ATOM: 'atom',\n  MULTIPLIER_RANGE: 'multiplierRange',\n  ISOTOPE: 'isotope',\n  ISOTOPE_RATIO: 'isotopeRatio',\n  CHARGE: 'charge',\n  SALT: 'salt',\n  OPENING_PARENTHESIS: 'openingParenthesis',\n  CLOSING_PARENTHESIS: 'closingParenthesis',\n  PRE_MULTIPLIER: 'preMultiplier',\n  MULTIPLIER: 'multiplier',\n  TEXT: 'text',\n  ANCHOR: 'anchor',\n  COMMENT: 'comment',\n};\n","/**\n * Parse a string to extract the charge\n * The charge may be in the form --, +++, +3, -2, 4+, 2-\n * @param {*} charge\n */\n\nexport function parseCharge(charge) {\n  charge = charge.replace(/[()]/g, '');\n  let chargeNumber = 0;\n  if (charge.match(/^[+-]+$/)) {\n    for (let i = 0; i < charge.length; i++) {\n      if (charge.charAt(i) === '+') chargeNumber++;\n      else chargeNumber--;\n    }\n  } else if (charge.match(/^[0-9]+[+-]$/)) {\n    chargeNumber = Number(\n      charge.charAt(charge.length - 1) + charge.substring(0, charge.length - 1),\n    );\n  } else {\n    chargeNumber = Number(charge);\n  }\n  return chargeNumber;\n}\n","import { Kind } from './Kind';\nimport { parseCharge } from './util/parseCharge';\n\n/**\n * Parse a mf to an array of kind / value\n * @param {String} mf\n */\n\nexport function parse(mf) {\n  return new MFParser().parse(mf);\n}\n\nclass MFParser {\n  parse(mf = '') {\n    this.mf = mf;\n    this.i = 0;\n    this.result = [];\n\n    let lastKind = Kind.BEGIN;\n    while (this.i < mf.length) {\n      if (\n        this.result.length > 0 &&\n        this.result[this.result.length - 1].kind !== Kind.TEXT\n      ) {\n        lastKind = this.result[this.result.length - 1].kind;\n      }\n      let char = mf.charAt(this.i);\n      let ascii = mf.charCodeAt(this.i);\n      let nextAscii = 0;\n      if (this.i + 1 < mf.length) nextAscii = mf.charCodeAt(this.i + 1);\n\n      if (\n        (ascii > 47 && ascii < 58) ||\n        (char === '-' && nextAscii > 47 && nextAscii < 58)\n      ) {\n        // a number\n        let value = this.getNumber(ascii);\n        if (\n          lastKind === Kind.SALT ||\n          lastKind === Kind.BEGIN ||\n          lastKind === Kind.OPENING_PARENTHESIS\n        ) {\n          if (value.to) {\n            throw new MFError(\n              this.mf,\n              this.i,\n              'Premultiplier may not contain a -',\n            );\n          }\n          this.result.push({ kind: Kind.PRE_MULTIPLIER, value: value.from });\n        } else if (lastKind === Kind.ANCHOR) {\n          if (value.to) {\n            throw new MFError(this.mf, this.i, 'Anchor ID may not contain -');\n          }\n          this.result[this.result.length - 1].value = value.from;\n        } else if (value.to) {\n          this.result.push({\n            kind: Kind.MULTIPLIER_RANGE,\n            value: {\n              from: Math.min(value.from, value.to),\n              to: Math.max(value.from, value.to),\n            },\n          });\n        } else {\n          this.result.push({ kind: Kind.MULTIPLIER, value: value.from });\n        }\n        continue;\n      } else if (char === '.') {\n        // a point\n        this.result.push({ kind: Kind.SALT, value: char });\n        // it is not in a number otherwise it would have been taken before\n        // it must be in a salt\n      } else if (char === '#') {\n        // an anchor\n        this.result.push({ kind: Kind.ANCHOR, value: 0 });\n        // it is not in a number otherwise it would have been taken before\n        // it must be in a salt\n      } else if (ascii > 64 && ascii < 91) {\n        // an uppercase = new atom\n        let value = this.getAtom(ascii);\n        this.result.push({ kind: Kind.ATOM, value });\n        continue;\n      } else if (ascii > 96 && ascii < 123) {\n        // a lowercase\n        throw new MFError(\n          this.mf,\n          this.i,\n          'found a lowercase not following an uppercase',\n        );\n      } else if (char === '(') {\n        let charge = this.getParenthesisCharge(ascii);\n        if (charge) {\n          this.result.push({ kind: Kind.CHARGE, value: charge });\n        } else {\n          this.result.push({ kind: Kind.OPENING_PARENTHESIS, value: '(' });\n        }\n      } else if (char === ')') {\n        this.result.push({ kind: Kind.CLOSING_PARENTHESIS, value: ')' });\n      } else if (char === '[') {\n        // defines an isotope\n        let isotope = this.getIsotope(ascii);\n        this.result.push({ kind: Kind.ISOTOPE, value: isotope });\n      } else if (char === ']') {\n        throw new MFError(\n          this.mf,\n          this.i,\n          'should never meet an closing bracket not in isotopes',\n        );\n      } else if (char === '{') {\n        // can define an exotic isotopic ratio or mixtures of groups\n        let isotopeRatio = this.getCurlyBracketIsotopeRatio(ascii);\n        if (lastKind === Kind.ATOM) {\n          let lastResult = this.result[this.result.length - 1];\n          lastResult.kind = Kind.ISOTOPE_RATIO;\n          lastResult.value = {\n            atom: lastResult.value,\n            ratio: isotopeRatio,\n          };\n        } else {\n          throw new MFError(\n            this.mf,\n            this.i,\n            'isotopic composition has to follow an atom',\n          );\n        }\n      } else if (char === '}') {\n        throw new MFError(\n          this.mf,\n          this.i,\n          'found a unexpected closing curly bracket',\n        );\n      } else if (char === '+') {\n        // charge not in parenthesis\n        let charge = this.getNonParenthesisCharge(ascii);\n        this.result.push({ kind: Kind.CHARGE, value: charge });\n      } else if (char === '-') {\n        // charge not in parenthesis\n        let charge = this.getNonParenthesisCharge(ascii);\n        this.result.push({ kind: Kind.CHARGE, value: charge });\n      } else if (char === '$') {\n        // it is a comment after\n        this.result.push({\n          kind: Kind.COMMENT,\n          value: this.mf.substring(this.i + 1),\n        });\n        break;\n      } else {\n        this.result.push({ kind: Kind.TEXT, value: char });\n      }\n      this.i++;\n    }\n\n    this.checkParenthesis();\n    return this.result;\n  }\n\n  checkParenthesis() {\n    let counter = 0;\n    for (let line of this.result) {\n      if (line.kind === Kind.OPENING_PARENTHESIS) counter++;\n      if (line.kind === Kind.CLOSING_PARENTHESIS) counter--;\n    }\n    if (counter !== 0) {\n      throw new MFError(\n        this.mf,\n        this.i,\n        'number of opening and closing parenthesis not equal',\n      );\n    }\n  }\n\n  getNumber(ascii) {\n    let number = '';\n    let previous;\n    do {\n      previous = ascii;\n      number += String.fromCharCode(ascii);\n      this.i++;\n      ascii = this.mf.charCodeAt(this.i);\n    } while (\n      (ascii > 47 && ascii < 58) ||\n      ascii === 46 ||\n      ascii === 45 ||\n      ascii === 47\n    ); // number . - /\n    // we need to deal with the case there is a from / to\n    if (previous === 46) this.i--;\n    let indexOfDash = number.indexOf('-', 1);\n\n    if (indexOfDash > -1) {\n      return {\n        from: parseNumberWithDivision(number.substr(0, indexOfDash)),\n        to: parseNumberWithDivision(number.substr(indexOfDash + 1)),\n      };\n    }\n    return { from: parseNumberWithDivision(number) };\n  }\n\n  getAtom(ascii) {\n    let atom = '';\n    do {\n      atom += String.fromCharCode(ascii);\n      this.i++;\n      ascii = this.mf.charCodeAt(this.i);\n    } while (ascii > 96 && ascii < 123);\n    return atom;\n  }\n\n  getIsotope(ascii) {\n    // [13C]\n    let substring = '';\n    do {\n      substring += String.fromCharCode(ascii);\n      this.i++;\n      ascii = this.mf.charCodeAt(this.i);\n    } while (ascii !== 93 && this.i <= this.mf.length);\n\n    let atom = substring.replace(/[^a-zA-Z]/g, '');\n    let isotope = Number(substring.replace(/[^0-9]/g, ''));\n    return { atom, isotope };\n  }\n\n  getCurlyBracketIsotopeRatio(ascii) {\n    let substring = '';\n    let first = true;\n    do {\n      if (!first) {\n        substring += String.fromCharCode(ascii);\n      } else {\n        first = false;\n      }\n      this.i++;\n      ascii = this.mf.charCodeAt(this.i);\n    } while (ascii !== 125 && this.i <= this.mf.length); // closing curly bracket\n    if (substring.match(/^[0-9,]+$/)) {\n      return substring.split(',').map((a) => Number(a));\n    }\n    throw new MFError(\n      this.mf,\n      this.i,\n      'Curly brackets should contain only number and comma',\n    );\n  }\n\n  getParenthesisCharge(ascii) {\n    let substring = '';\n    let begin = this.i;\n    do {\n      substring += String.fromCharCode(ascii);\n      this.i++;\n      ascii = this.mf.charCodeAt(this.i);\n    } while (ascii !== 41 && this.i <= this.mf.length); // closing parenthesis\n    if (substring.match(/^\\([0-9+-]+$/)) {\n      return parseCharge(substring.substring(1));\n    } else {\n      this.i = begin;\n      return undefined;\n    }\n  }\n\n  getNonParenthesisCharge(ascii) {\n    let substring = '';\n    do {\n      substring += String.fromCharCode(ascii);\n      this.i++;\n      ascii = this.mf.charCodeAt(this.i);\n    } while (ascii === 43 || ascii === 45 || (ascii > 47 && ascii < 58));\n    this.i--;\n    return parseCharge(substring);\n  }\n}\nclass MFError extends SyntaxError {\n  constructor(mf, i, message) {\n    let text = `${message}\\n\\n${mf}\\n${' '.repeat(i)}^`;\n    super(text);\n  }\n}\n\nfunction parseNumberWithDivision(string) {\n  if (string.includes('/')) {\n    let parts = string.split('/');\n    if (parts.length !== 2) {\n      throw new TypeError('Can not parse MF with number like: ', string);\n    }\n    return Number(parts[0]) / Number(parts[1]);\n  } else {\n    return Number(string);\n  }\n}\n","export const superscript = {\n  0: '⁰',\n  1: '¹',\n  2: '²',\n  3: '³',\n  4: '⁴',\n  5: '⁵',\n  6: '⁶',\n  7: '⁷',\n  8: '⁸',\n  9: '⁹',\n  '+': '⁺',\n  '-': '⁻',\n  '(': '⁽',\n  ')': '⁾',\n  '{': '⁽',\n  '}': '⁾',\n  '.': '˙',\n  ',': '˙',\n};\n\nexport const subscript = {\n  0: '₀',\n  1: '₁',\n  2: '₂',\n  3: '₃',\n  4: '₄',\n  5: '₅',\n  6: '₆',\n  7: '₇',\n  8: '₈',\n  9: '₉',\n  '(': '₍',\n  ')': '₎',\n  '{': '₍',\n  '}': '₎',\n  '.': ' ',\n  ',': ' ',\n};\n","/**\n * Defines static variables corresponding to the various formatting possibilities\n */\n\nexport const Format = {\n  SUBSCRIPT: 'subscript',\n  SUPERSCRIPT: 'superscript',\n  SUPERIMPOSE: 'superimpose',\n  TEXT: 'text',\n};\n","export function formatCharge(charge) {\n  if (charge === 1) return '+';\n  if (charge > 1) return `+${charge}`;\n  if (charge < 0) return String(charge);\n  return '';\n}\n","import { Format } from '../Format';\nimport { Kind } from '../Kind';\n\nimport { formatCharge } from './formatCharge.js';\n\n/**\n * Converts an array of mf elements to an array of formatting information\n * @param {object[]} lines of the parse method\n */\n\nexport function toDisplay(lines) {\n  let results = [];\n  let result = {};\n  for (let line of lines) {\n    switch (line.kind) {\n      case Kind.MULTIPLIER:\n        if (line.value !== 1) {\n          result = {\n            kind: Format.SUBSCRIPT,\n            value: String(line.value),\n          };\n          results.push(result);\n        }\n        break;\n      case Kind.MULTIPLIER_RANGE:\n        result = {\n          kind: Format.SUBSCRIPT,\n          value: `${String(line.value.from)}-${line.value.to}`,\n        };\n        results.push(result);\n        break;\n      case Kind.CHARGE:\n        if (result.kind === Format.SUBSCRIPT) {\n          result.kind = Format.SUPERIMPOSE;\n          result.over = formatCharge(line.value);\n          result.under = result.value;\n          result.value = undefined;\n        } else {\n          result = {\n            kind: Format.SUPERSCRIPT,\n            value: formatCharge(line.value),\n          };\n          results.push(result);\n        }\n\n        break;\n\n      case Kind.ISOTOPE:\n        result = {\n          kind: Format.SUPERSCRIPT,\n          value: line.value.isotope,\n        };\n        results.push(result);\n        result = {\n          kind: Format.TEXT,\n          value: line.value.atom,\n        };\n        results.push(result);\n        break;\n\n      case Kind.ISOTOPE_RATIO:\n        if (result.kind === Format.TEXT) {\n          result.value += line.value.atom;\n        } else {\n          result = {\n            kind: Format.TEXT,\n            value: line.value.atom,\n          };\n          results.push(result);\n        }\n        result = {\n          kind: Format.SUPERSCRIPT,\n          value: `{${line.value.ratio.join(',')}}`,\n        };\n        results.push(result);\n        break;\n      case Kind.SALT:\n        if (result.kind === Format.TEXT) {\n          result.value += ' • ';\n        } else {\n          result = {\n            kind: Format.TEXT,\n            value: ' • ',\n          };\n          results.push(result);\n        }\n        break;\n      default:\n        if (result.kind === Format.TEXT) {\n          result.value += line.value;\n        } else {\n          result = {\n            kind: Format.TEXT,\n            value: line.value,\n          };\n          results.push(result);\n        }\n    }\n  }\n  return results;\n}\n","export const Style = {\n  SUPERIMPOSE:\n    'flex-direction: column;display: inline-flex;justify-content: center;text-align: left;vertical-align: middle;',\n  SUPERIMPOSE_SUP_SUB: 'line-height: 1; font-size: 70%',\n};\n","import { Format } from '../Format';\nimport { Style } from '../Style';\n\nexport function toHtml(lines) {\n  let html = [];\n  for (let line of lines) {\n    switch (line.kind) {\n      case Format.SUBSCRIPT:\n        html.push(`<sub>${line.value}</sub>`);\n        break;\n      case Format.SUPERSCRIPT:\n        html.push(`<sup>${line.value}</sup>`);\n        break;\n      case Format.SUPERIMPOSE:\n        html.push(`<span style=\"${Style.SUPERIMPOSE}\">`);\n        html.push(\n          `<sup style=\"${Style.SUPERIMPOSE_SUP_SUB}\">${line.over}</sup>`,\n        );\n        html.push(\n          `<sub style=\"${Style.SUPERIMPOSE_SUP_SUB}\">${line.under}</sub>`,\n        );\n        html.push('</span>');\n        break;\n      default:\n        html.push(line.value);\n    }\n  }\n  return html.join('');\n}\n","export const ELECTRON_MASS = 5.4857990907e-4;\n","export const elementsAndIsotopes = [\n  {\n    number: 1,\n    isotopes: [\n      { nominal: 1, mass: 1.00782503223, abundance: 0.999885 },\n      { nominal: 2, mass: 2.01410177812, abundance: 0.000115 },\n      { nominal: 3, mass: 3.0160492779 },\n      { nominal: 4, mass: 4.02643 },\n      { nominal: 5, mass: 5.035311 },\n      { nominal: 6, mass: 6.04496 },\n      { nominal: 7, mass: 7.0527 },\n    ],\n    symbol: 'H',\n    mass: 1.0079407540557772,\n    name: 'Hydrogen',\n    monoisotopicMass: 1.00782503223,\n  },\n  {\n    number: 2,\n    isotopes: [\n      { nominal: 3, mass: 3.0160293201, abundance: 0.00000134 },\n      { nominal: 4, mass: 4.00260325413, abundance: 0.99999866 },\n      { nominal: 5, mass: 5.012057 },\n      { nominal: 6, mass: 6.018885891 },\n      { nominal: 7, mass: 7.0279907 },\n      { nominal: 8, mass: 8.03393439 },\n      { nominal: 9, mass: 9.043946 },\n      { nominal: 10, mass: 10.05279 },\n    ],\n    symbol: 'He',\n    mass: 4.002601932120929,\n    name: 'Helium',\n    monoisotopicMass: 4.00260325413,\n  },\n  {\n    number: 3,\n    isotopes: [\n      { nominal: 3, mass: 3.0308 },\n      { nominal: 4, mass: 4.02719 },\n      { nominal: 5, mass: 5.012538 },\n      { nominal: 6, mass: 6.0151228874, abundance: 0.0759 },\n      { nominal: 7, mass: 7.0160034366, abundance: 0.9241 },\n      { nominal: 8, mass: 8.022486246 },\n      { nominal: 9, mass: 9.02679019 },\n      { nominal: 10, mass: 10.035483 },\n      { nominal: 11, mass: 11.04372358 },\n      { nominal: 12, mass: 12.052517 },\n      { nominal: 13, mass: 13.06263 },\n    ],\n    symbol: 'Li',\n    mass: 6.94003660291572,\n    name: 'Lithium',\n    monoisotopicMass: 7.0160034366,\n  },\n  {\n    number: 4,\n    isotopes: [\n      { nominal: 5, mass: 5.0399 },\n      { nominal: 6, mass: 6.0197264 },\n      { nominal: 7, mass: 7.016928717 },\n      { nominal: 8, mass: 8.005305102 },\n      { nominal: 9, mass: 9.012183065, abundance: 1 },\n      { nominal: 10, mass: 10.013534695 },\n      { nominal: 11, mass: 11.02166108 },\n      { nominal: 12, mass: 12.0269221 },\n      { nominal: 13, mass: 13.036135 },\n      { nominal: 14, mass: 14.04289 },\n      { nominal: 15, mass: 15.05342 },\n      { nominal: 16, mass: 16.06167 },\n    ],\n    symbol: 'Be',\n    mass: 9.012183065,\n    name: 'Beryllium',\n    monoisotopicMass: 9.012183065,\n  },\n  {\n    number: 5,\n    isotopes: [\n      { nominal: 6, mass: 6.0508 },\n      { nominal: 7, mass: 7.029712 },\n      { nominal: 8, mass: 8.0246073 },\n      { nominal: 9, mass: 9.01332965 },\n      { nominal: 10, mass: 10.01293695, abundance: 0.199 },\n      { nominal: 11, mass: 11.00930536, abundance: 0.801 },\n      { nominal: 12, mass: 12.0143527 },\n      { nominal: 13, mass: 13.0177802 },\n      { nominal: 14, mass: 14.025404 },\n      { nominal: 15, mass: 15.031088 },\n      { nominal: 16, mass: 16.039842 },\n      { nominal: 17, mass: 17.04699 },\n      { nominal: 18, mass: 18.05566 },\n      { nominal: 19, mass: 19.0631 },\n      { nominal: 20, mass: 20.07207 },\n      { nominal: 21, mass: 21.08129 },\n    ],\n    symbol: 'B',\n    mass: 10.811028046410001,\n    name: 'Boron',\n    monoisotopicMass: 11.00930536,\n  },\n  {\n    number: 6,\n    isotopes: [\n      { nominal: 8, mass: 8.037643 },\n      { nominal: 9, mass: 9.0310372 },\n      { nominal: 10, mass: 10.01685331 },\n      { nominal: 11, mass: 11.0114336 },\n      { nominal: 12, mass: 12, abundance: 0.9893 },\n      { nominal: 13, mass: 13.00335483507, abundance: 0.0107 },\n      { nominal: 14, mass: 14.0032419884 },\n      { nominal: 15, mass: 15.01059926 },\n      { nominal: 16, mass: 16.0147013 },\n      { nominal: 17, mass: 17.022577 },\n      { nominal: 18, mass: 18.026751 },\n      { nominal: 19, mass: 19.0348 },\n      { nominal: 20, mass: 20.04032 },\n      { nominal: 21, mass: 21.049 },\n      { nominal: 22, mass: 22.05753 },\n      { nominal: 23, mass: 23.0689 },\n    ],\n    symbol: 'C',\n    mass: 12.010735896735248,\n    name: 'Carbon',\n    monoisotopicMass: 12,\n  },\n  {\n    number: 7,\n    isotopes: [\n      { nominal: 10, mass: 10.04165 },\n      { nominal: 11, mass: 11.026091 },\n      { nominal: 12, mass: 12.0186132 },\n      { nominal: 13, mass: 13.00573861 },\n      { nominal: 14, mass: 14.00307400443, abundance: 0.99636 },\n      { nominal: 15, mass: 15.00010889888, abundance: 0.00364 },\n      { nominal: 16, mass: 16.0061019 },\n      { nominal: 17, mass: 17.008449 },\n      { nominal: 18, mass: 18.014078 },\n      { nominal: 19, mass: 19.017022 },\n      { nominal: 20, mass: 20.023366 },\n      { nominal: 21, mass: 21.02711 },\n      { nominal: 22, mass: 22.03439 },\n      { nominal: 23, mass: 23.04114 },\n      { nominal: 24, mass: 24.05039 },\n      { nominal: 25, mass: 25.0601 },\n    ],\n    symbol: 'N',\n    mass: 14.006703211445798,\n    name: 'Nitrogen',\n    monoisotopicMass: 14.00307400443,\n  },\n  {\n    number: 8,\n    isotopes: [\n      { nominal: 12, mass: 12.034262 },\n      { nominal: 13, mass: 13.024815 },\n      { nominal: 14, mass: 14.00859636 },\n      { nominal: 15, mass: 15.00306562 },\n      { nominal: 16, mass: 15.99491461957, abundance: 0.99757 },\n      { nominal: 17, mass: 16.9991317565, abundance: 0.00038 },\n      { nominal: 18, mass: 17.99915961286, abundance: 0.00205 },\n      { nominal: 19, mass: 19.003578 },\n      { nominal: 20, mass: 20.00407535 },\n      { nominal: 21, mass: 21.008655 },\n      { nominal: 22, mass: 22.009966 },\n      { nominal: 23, mass: 23.015696 },\n      { nominal: 24, mass: 24.01986 },\n      { nominal: 25, mass: 25.02936 },\n      { nominal: 26, mass: 26.03729 },\n      { nominal: 27, mass: 27.04772 },\n      { nominal: 28, mass: 28.05591 },\n    ],\n    symbol: 'O',\n    mass: 15.999404924318277,\n    name: 'Oxygen',\n    monoisotopicMass: 15.99491461957,\n  },\n  {\n    number: 9,\n    isotopes: [\n      { nominal: 14, mass: 14.034315 },\n      { nominal: 15, mass: 15.018043 },\n      { nominal: 16, mass: 16.0114657 },\n      { nominal: 17, mass: 17.00209524 },\n      { nominal: 18, mass: 18.00093733 },\n      { nominal: 19, mass: 18.99840316273, abundance: 1 },\n      { nominal: 20, mass: 19.999981252 },\n      { nominal: 21, mass: 20.9999489 },\n      { nominal: 22, mass: 22.002999 },\n      { nominal: 23, mass: 23.003557 },\n      { nominal: 24, mass: 24.008115 },\n      { nominal: 25, mass: 25.012199 },\n      { nominal: 26, mass: 26.020038 },\n      { nominal: 27, mass: 27.02644 },\n      { nominal: 28, mass: 28.03534 },\n      { nominal: 29, mass: 29.04254 },\n      { nominal: 30, mass: 30.05165 },\n      { nominal: 31, mass: 31.05971 },\n    ],\n    symbol: 'F',\n    mass: 18.99840316273,\n    name: 'Fluorine',\n    monoisotopicMass: 18.99840316273,\n  },\n  {\n    number: 10,\n    isotopes: [\n      { nominal: 16, mass: 16.02575 },\n      { nominal: 17, mass: 17.01771396 },\n      { nominal: 18, mass: 18.0057087 },\n      { nominal: 19, mass: 19.00188091 },\n      { nominal: 20, mass: 19.9924401762, abundance: 0.9048 },\n      { nominal: 21, mass: 20.993846685, abundance: 0.0027 },\n      { nominal: 22, mass: 21.991385114, abundance: 0.0925 },\n      { nominal: 23, mass: 22.99446691 },\n      { nominal: 24, mass: 23.99361065 },\n      { nominal: 25, mass: 24.997789 },\n      { nominal: 26, mass: 26.000515 },\n      { nominal: 27, mass: 27.007553 },\n      { nominal: 28, mass: 28.01212 },\n      { nominal: 29, mass: 29.01975 },\n      { nominal: 30, mass: 30.02473 },\n      { nominal: 31, mass: 31.0331 },\n      { nominal: 32, mass: 32.03972 },\n      { nominal: 33, mass: 33.04938 },\n      { nominal: 34, mass: 34.05673 },\n    ],\n    symbol: 'Ne',\n    mass: 20.18004638052026,\n    name: 'Neon',\n    monoisotopicMass: 19.9924401762,\n  },\n  {\n    number: 11,\n    isotopes: [\n      { nominal: 18, mass: 18.02688 },\n      { nominal: 19, mass: 19.01388 },\n      { nominal: 20, mass: 20.0073544 },\n      { nominal: 21, mass: 20.99765469 },\n      { nominal: 22, mass: 21.99443741 },\n      { nominal: 23, mass: 22.989769282, abundance: 1 },\n      { nominal: 24, mass: 23.99096295 },\n      { nominal: 25, mass: 24.989954 },\n      { nominal: 26, mass: 25.9926346 },\n      { nominal: 27, mass: 26.9940765 },\n      { nominal: 28, mass: 27.998939 },\n      { nominal: 29, mass: 29.0028771 },\n      { nominal: 30, mass: 30.0090979 },\n      { nominal: 31, mass: 31.013163 },\n      { nominal: 32, mass: 32.02019 },\n      { nominal: 33, mass: 33.02573 },\n      { nominal: 34, mass: 34.03359 },\n      { nominal: 35, mass: 35.04062 },\n      { nominal: 36, mass: 36.04929 },\n      { nominal: 37, mass: 37.05705 },\n    ],\n    symbol: 'Na',\n    mass: 22.989769282,\n    name: 'Sodium',\n    monoisotopicMass: 22.989769282,\n  },\n  {\n    number: 12,\n    isotopes: [\n      { nominal: 19, mass: 19.034169 },\n      { nominal: 20, mass: 20.01885 },\n      { nominal: 21, mass: 21.011716 },\n      { nominal: 22, mass: 21.99957065 },\n      { nominal: 23, mass: 22.99412421 },\n      { nominal: 24, mass: 23.985041697, abundance: 0.7899 },\n      { nominal: 25, mass: 24.985836976, abundance: 0.1 },\n      { nominal: 26, mass: 25.982592968, abundance: 0.1101 },\n      { nominal: 27, mass: 26.984340624 },\n      { nominal: 28, mass: 27.9838767 },\n      { nominal: 29, mass: 28.988617 },\n      { nominal: 30, mass: 29.9904629 },\n      { nominal: 31, mass: 30.996648 },\n      { nominal: 32, mass: 31.9991102 },\n      { nominal: 33, mass: 33.0053271 },\n      { nominal: 34, mass: 34.008935 },\n      { nominal: 35, mass: 35.01679 },\n      { nominal: 36, mass: 36.02188 },\n      { nominal: 37, mass: 37.03037 },\n      { nominal: 38, mass: 38.03658 },\n      { nominal: 39, mass: 39.04538 },\n      { nominal: 40, mass: 40.05218 },\n    ],\n    symbol: 'Mg',\n    mass: 24.3050516198371,\n    name: 'Magnesium',\n    monoisotopicMass: 23.985041697,\n  },\n  {\n    number: 13,\n    isotopes: [\n      { nominal: 21, mass: 21.02897 },\n      { nominal: 22, mass: 22.01954 },\n      { nominal: 23, mass: 23.00724435 },\n      { nominal: 24, mass: 23.9999489 },\n      { nominal: 25, mass: 24.9904281 },\n      { nominal: 26, mass: 25.986891904 },\n      { nominal: 27, mass: 26.98153853, abundance: 1 },\n      { nominal: 28, mass: 27.98191021 },\n      { nominal: 29, mass: 28.9804565 },\n      { nominal: 30, mass: 29.98296 },\n      { nominal: 31, mass: 30.983945 },\n      { nominal: 32, mass: 31.988085 },\n      { nominal: 33, mass: 32.990909 },\n      { nominal: 34, mass: 33.996705 },\n      { nominal: 35, mass: 34.999764 },\n      { nominal: 36, mass: 36.00639 },\n      { nominal: 37, mass: 37.01053 },\n      { nominal: 38, mass: 38.0174 },\n      { nominal: 39, mass: 39.02254 },\n      { nominal: 40, mass: 40.03003 },\n      { nominal: 41, mass: 41.03638 },\n      { nominal: 42, mass: 42.04384 },\n      { nominal: 43, mass: 43.05147 },\n    ],\n    symbol: 'Al',\n    mass: 26.98153853,\n    name: 'Aluminium',\n    monoisotopicMass: 26.98153853,\n  },\n  {\n    number: 14,\n    isotopes: [\n      { nominal: 22, mass: 22.03579 },\n      { nominal: 23, mass: 23.02544 },\n      { nominal: 24, mass: 24.011535 },\n      { nominal: 25, mass: 25.004109 },\n      { nominal: 26, mass: 25.99233384 },\n      { nominal: 27, mass: 26.98670481 },\n      { nominal: 28, mass: 27.97692653465, abundance: 0.92223 },\n      { nominal: 29, mass: 28.9764946649, abundance: 0.04685 },\n      { nominal: 30, mass: 29.973770136, abundance: 0.03092 },\n      { nominal: 31, mass: 30.975363194 },\n      { nominal: 32, mass: 31.97415154 },\n      { nominal: 33, mass: 32.97797696 },\n      { nominal: 34, mass: 33.978576 },\n      { nominal: 35, mass: 34.984583 },\n      { nominal: 36, mass: 35.986695 },\n      { nominal: 37, mass: 36.992921 },\n      { nominal: 38, mass: 37.995523 },\n      { nominal: 39, mass: 39.002491 },\n      { nominal: 40, mass: 40.00583 },\n      { nominal: 41, mass: 41.01301 },\n      { nominal: 42, mass: 42.01778 },\n      { nominal: 43, mass: 43.0248 },\n      { nominal: 44, mass: 44.03061 },\n      { nominal: 45, mass: 45.03995 },\n    ],\n    symbol: 'Si',\n    mass: 28.085498705705955,\n    name: 'Silicon',\n    monoisotopicMass: 27.97692653465,\n  },\n  {\n    number: 15,\n    isotopes: [\n      { nominal: 24, mass: 24.03577 },\n      { nominal: 25, mass: 25.02119 },\n      { nominal: 26, mass: 26.01178 },\n      { nominal: 27, mass: 26.999224 },\n      { nominal: 28, mass: 27.9923266 },\n      { nominal: 29, mass: 28.98180079 },\n      { nominal: 30, mass: 29.97831375 },\n      { nominal: 31, mass: 30.97376199842, abundance: 1 },\n      { nominal: 32, mass: 31.973907643 },\n      { nominal: 33, mass: 32.9717257 },\n      { nominal: 34, mass: 33.97364589 },\n      { nominal: 35, mass: 34.9733141 },\n      { nominal: 36, mass: 35.97826 },\n      { nominal: 37, mass: 36.979607 },\n      { nominal: 38, mass: 37.984252 },\n      { nominal: 39, mass: 38.986227 },\n      { nominal: 40, mass: 39.99133 },\n      { nominal: 41, mass: 40.994654 },\n      { nominal: 42, mass: 42.00108 },\n      { nominal: 43, mass: 43.00502 },\n      { nominal: 44, mass: 44.01121 },\n      { nominal: 45, mass: 45.01645 },\n      { nominal: 46, mass: 46.02446 },\n      { nominal: 47, mass: 47.03139 },\n    ],\n    symbol: 'P',\n    mass: 30.97376199842,\n    name: 'Phosphorus',\n    monoisotopicMass: 30.97376199842,\n  },\n  {\n    number: 16,\n    isotopes: [\n      { nominal: 26, mass: 26.02907 },\n      { nominal: 27, mass: 27.01828 },\n      { nominal: 28, mass: 28.00437 },\n      { nominal: 29, mass: 28.996611 },\n      { nominal: 30, mass: 29.98490703 },\n      { nominal: 31, mass: 30.97955701 },\n      { nominal: 32, mass: 31.9720711744, abundance: 0.9499 },\n      { nominal: 33, mass: 32.9714589098, abundance: 0.0075 },\n      { nominal: 34, mass: 33.967867004, abundance: 0.0425 },\n      { nominal: 35, mass: 34.96903231 },\n      { nominal: 36, mass: 35.96708071, abundance: 0.0001 },\n      { nominal: 37, mass: 36.97112551 },\n      { nominal: 38, mass: 37.9711633 },\n      { nominal: 39, mass: 38.975134 },\n      { nominal: 40, mass: 39.9754826 },\n      { nominal: 41, mass: 40.9795935 },\n      { nominal: 42, mass: 41.9810651 },\n      { nominal: 43, mass: 42.9869076 },\n      { nominal: 44, mass: 43.9901188 },\n      { nominal: 45, mass: 44.99572 },\n      { nominal: 46, mass: 46.00004 },\n      { nominal: 47, mass: 47.00795 },\n      { nominal: 48, mass: 48.0137 },\n      { nominal: 49, mass: 49.02276 },\n    ],\n    symbol: 'S',\n    mass: 32.06478740612706,\n    name: 'Sulfur',\n    monoisotopicMass: 31.9720711744,\n  },\n  {\n    number: 17,\n    isotopes: [\n      { nominal: 28, mass: 28.02954 },\n      { nominal: 29, mass: 29.01478 },\n      { nominal: 30, mass: 30.00477 },\n      { nominal: 31, mass: 30.992414 },\n      { nominal: 32, mass: 31.98568464 },\n      { nominal: 33, mass: 32.97745199 },\n      { nominal: 34, mass: 33.973762485 },\n      { nominal: 35, mass: 34.968852682, abundance: 0.7576 },\n      { nominal: 36, mass: 35.968306809 },\n      { nominal: 37, mass: 36.965902602, abundance: 0.2424 },\n      { nominal: 38, mass: 37.96801044 },\n      { nominal: 39, mass: 38.9680082 },\n      { nominal: 40, mass: 39.970415 },\n      { nominal: 41, mass: 40.970685 },\n      { nominal: 42, mass: 41.97325 },\n      { nominal: 43, mass: 42.97389 },\n      { nominal: 44, mass: 43.97787 },\n      { nominal: 45, mass: 44.98029 },\n      { nominal: 46, mass: 45.98517 },\n      { nominal: 47, mass: 46.98916 },\n      { nominal: 48, mass: 47.99564 },\n      { nominal: 49, mass: 49.00123 },\n      { nominal: 50, mass: 50.00905 },\n      { nominal: 51, mass: 51.01554 },\n    ],\n    symbol: 'Cl',\n    mass: 35.452937582608,\n    name: 'Chlorine',\n    monoisotopicMass: 34.968852682,\n  },\n  {\n    number: 18,\n    isotopes: [\n      { nominal: 30, mass: 30.02307 },\n      { nominal: 31, mass: 31.01212 },\n      { nominal: 32, mass: 31.9976378 },\n      { nominal: 33, mass: 32.98992555 },\n      { nominal: 34, mass: 33.98027009 },\n      { nominal: 35, mass: 34.97525759 },\n      { nominal: 36, mass: 35.967545105, abundance: 0.003336 },\n      { nominal: 37, mass: 36.96677633 },\n      { nominal: 38, mass: 37.96273211, abundance: 0.000629 },\n      { nominal: 39, mass: 38.964313 },\n      { nominal: 40, mass: 39.9623831237, abundance: 0.996035 },\n      { nominal: 41, mass: 40.96450057 },\n      { nominal: 42, mass: 41.9630457 },\n      { nominal: 43, mass: 42.9656361 },\n      { nominal: 44, mass: 43.9649238 },\n      { nominal: 45, mass: 44.96803973 },\n      { nominal: 46, mass: 45.968083 },\n      { nominal: 47, mass: 46.972935 },\n      { nominal: 48, mass: 47.97591 },\n      { nominal: 49, mass: 48.9819 },\n      { nominal: 50, mass: 49.98613 },\n      { nominal: 51, mass: 50.9937 },\n      { nominal: 52, mass: 51.99896 },\n      { nominal: 53, mass: 53.00729 },\n    ],\n    symbol: 'Ar',\n    mass: 39.947798563582005,\n    name: 'Argon',\n    monoisotopicMass: 39.9623831237,\n  },\n  {\n    number: 19,\n    isotopes: [\n      { nominal: 32, mass: 32.02265 },\n      { nominal: 33, mass: 33.00756 },\n      { nominal: 34, mass: 33.99869 },\n      { nominal: 35, mass: 34.98800541 },\n      { nominal: 36, mass: 35.98130201 },\n      { nominal: 37, mass: 36.97337589 },\n      { nominal: 38, mass: 37.96908112 },\n      { nominal: 39, mass: 38.9637064864, abundance: 0.932581 },\n      { nominal: 40, mass: 39.963998166, abundance: 0.000117 },\n      { nominal: 41, mass: 40.9618252579, abundance: 0.067302 },\n      { nominal: 42, mass: 41.96240231 },\n      { nominal: 43, mass: 42.9607347 },\n      { nominal: 44, mass: 43.96158699 },\n      { nominal: 45, mass: 44.96069149 },\n      { nominal: 46, mass: 45.96198159 },\n      { nominal: 47, mass: 46.9616616 },\n      { nominal: 48, mass: 47.96534119 },\n      { nominal: 49, mass: 48.96821075 },\n      { nominal: 50, mass: 49.97238 },\n      { nominal: 51, mass: 50.975828 },\n      { nominal: 52, mass: 51.98224 },\n      { nominal: 53, mass: 52.98746 },\n      { nominal: 54, mass: 53.99463 },\n      { nominal: 55, mass: 55.00076 },\n      { nominal: 56, mass: 56.00851 },\n    ],\n    symbol: 'K',\n    mass: 39.098300910086,\n    name: 'Potassium',\n    monoisotopicMass: 38.9637064864,\n  },\n  {\n    number: 20,\n    isotopes: [\n      { nominal: 34, mass: 34.01487 },\n      { nominal: 35, mass: 35.00514 },\n      { nominal: 36, mass: 35.993074 },\n      { nominal: 37, mass: 36.98589785 },\n      { nominal: 38, mass: 37.97631922 },\n      { nominal: 39, mass: 38.97071081 },\n      { nominal: 40, mass: 39.962590863, abundance: 0.96941 },\n      { nominal: 41, mass: 40.96227792 },\n      { nominal: 42, mass: 41.95861783, abundance: 0.00647 },\n      { nominal: 43, mass: 42.95876644, abundance: 0.00135 },\n      { nominal: 44, mass: 43.95548156, abundance: 0.02086 },\n      { nominal: 45, mass: 44.95618635 },\n      { nominal: 46, mass: 45.953689, abundance: 0.00004 },\n      { nominal: 47, mass: 46.9545424 },\n      { nominal: 48, mass: 47.95252276, abundance: 0.00187 },\n      { nominal: 49, mass: 48.95566274 },\n      { nominal: 50, mass: 49.9574992 },\n      { nominal: 51, mass: 50.960989 },\n      { nominal: 52, mass: 51.963217 },\n      { nominal: 53, mass: 52.96945 },\n      { nominal: 54, mass: 53.9734 },\n      { nominal: 55, mass: 54.9803 },\n      { nominal: 56, mass: 55.98508 },\n      { nominal: 57, mass: 56.99262 },\n      { nominal: 58, mass: 57.99794 },\n    ],\n    symbol: 'Ca',\n    mass: 40.078022511017735,\n    name: 'Calcium',\n    monoisotopicMass: 39.962590863,\n  },\n  {\n    number: 21,\n    isotopes: [\n      { nominal: 36, mass: 36.01648 },\n      { nominal: 37, mass: 37.00374 },\n      { nominal: 38, mass: 37.99512 },\n      { nominal: 39, mass: 38.984785 },\n      { nominal: 40, mass: 39.9779673 },\n      { nominal: 41, mass: 40.969251105 },\n      { nominal: 42, mass: 41.96551653 },\n      { nominal: 43, mass: 42.9611505 },\n      { nominal: 44, mass: 43.9594029 },\n      { nominal: 45, mass: 44.95590828, abundance: 1 },\n      { nominal: 46, mass: 45.95516826 },\n      { nominal: 47, mass: 46.9524037 },\n      { nominal: 48, mass: 47.9522236 },\n      { nominal: 49, mass: 48.9500146 },\n      { nominal: 50, mass: 49.952176 },\n      { nominal: 51, mass: 50.953592 },\n      { nominal: 52, mass: 51.95688 },\n      { nominal: 53, mass: 52.95909 },\n      { nominal: 54, mass: 53.96393 },\n      { nominal: 55, mass: 54.96782 },\n      { nominal: 56, mass: 55.97345 },\n      { nominal: 57, mass: 56.97777 },\n      { nominal: 58, mass: 57.98403 },\n      { nominal: 59, mass: 58.98894 },\n      { nominal: 60, mass: 59.99565 },\n      { nominal: 61, mass: 61.001 },\n    ],\n    symbol: 'Sc',\n    mass: 44.95590828,\n    name: 'Scandium',\n    monoisotopicMass: 44.95590828,\n  },\n  {\n    number: 22,\n    isotopes: [\n      { nominal: 38, mass: 38.01145 },\n      { nominal: 39, mass: 39.00236 },\n      { nominal: 40, mass: 39.9905 },\n      { nominal: 41, mass: 40.983148 },\n      { nominal: 42, mass: 41.97304903 },\n      { nominal: 43, mass: 42.9685225 },\n      { nominal: 44, mass: 43.95968995 },\n      { nominal: 45, mass: 44.95812198 },\n      { nominal: 46, mass: 45.95262772, abundance: 0.0825 },\n      { nominal: 47, mass: 46.95175879, abundance: 0.0744 },\n      { nominal: 48, mass: 47.94794198, abundance: 0.7372 },\n      { nominal: 49, mass: 48.94786568, abundance: 0.0541 },\n      { nominal: 50, mass: 49.94478689, abundance: 0.0518 },\n      { nominal: 51, mass: 50.94661065 },\n      { nominal: 52, mass: 51.946893 },\n      { nominal: 53, mass: 52.94973 },\n      { nominal: 54, mass: 53.95105 },\n      { nominal: 55, mass: 54.95527 },\n      { nominal: 56, mass: 55.95791 },\n      { nominal: 57, mass: 56.96364 },\n      { nominal: 58, mass: 57.9666 },\n      { nominal: 59, mass: 58.97247 },\n      { nominal: 60, mass: 59.97603 },\n      { nominal: 61, mass: 60.98245 },\n      { nominal: 62, mass: 61.98651 },\n      { nominal: 63, mass: 62.99375 },\n    ],\n    symbol: 'Ti',\n    mass: 47.866744962721995,\n    name: 'Titanium',\n    monoisotopicMass: 47.94794198,\n  },\n  {\n    number: 23,\n    isotopes: [\n      { nominal: 40, mass: 40.01276 },\n      { nominal: 41, mass: 41.00021 },\n      { nominal: 42, mass: 41.99182 },\n      { nominal: 43, mass: 42.980766 },\n      { nominal: 44, mass: 43.97411 },\n      { nominal: 45, mass: 44.9657748 },\n      { nominal: 46, mass: 45.96019878 },\n      { nominal: 47, mass: 46.95490491 },\n      { nominal: 48, mass: 47.9522522 },\n      { nominal: 49, mass: 48.9485118 },\n      { nominal: 50, mass: 49.94715601, abundance: 0.0025 },\n      { nominal: 51, mass: 50.94395704, abundance: 0.9975 },\n      { nominal: 52, mass: 51.94477301 },\n      { nominal: 53, mass: 52.9443367 },\n      { nominal: 54, mass: 53.946439 },\n      { nominal: 55, mass: 54.94724 },\n      { nominal: 56, mass: 55.95048 },\n      { nominal: 57, mass: 56.95252 },\n      { nominal: 58, mass: 57.95672 },\n      { nominal: 59, mass: 58.95939 },\n      { nominal: 60, mass: 59.96431 },\n      { nominal: 61, mass: 60.96725 },\n      { nominal: 62, mass: 61.97265 },\n      { nominal: 63, mass: 62.97639 },\n      { nominal: 64, mass: 63.98264 },\n      { nominal: 65, mass: 64.9875 },\n      { nominal: 66, mass: 65.99398 },\n    ],\n    symbol: 'V',\n    mass: 50.941465037425004,\n    name: 'Vanadium',\n    monoisotopicMass: 50.94395704,\n  },\n  {\n    number: 24,\n    isotopes: [\n      { nominal: 42, mass: 42.0067 },\n      { nominal: 43, mass: 42.99753 },\n      { nominal: 44, mass: 43.98536 },\n      { nominal: 45, mass: 44.97905 },\n      { nominal: 46, mass: 45.968359 },\n      { nominal: 47, mass: 46.9628974 },\n      { nominal: 48, mass: 47.9540291 },\n      { nominal: 49, mass: 48.9513333 },\n      { nominal: 50, mass: 49.94604183, abundance: 0.04345 },\n      { nominal: 51, mass: 50.94476502 },\n      { nominal: 52, mass: 51.94050623, abundance: 0.83789 },\n      { nominal: 53, mass: 52.94064815, abundance: 0.09501 },\n      { nominal: 54, mass: 53.93887916, abundance: 0.02365 },\n      { nominal: 55, mass: 54.94083843 },\n      { nominal: 56, mass: 55.9406531 },\n      { nominal: 57, mass: 56.943613 },\n      { nominal: 58, mass: 57.94435 },\n      { nominal: 59, mass: 58.94859 },\n      { nominal: 60, mass: 59.95008 },\n      { nominal: 61, mass: 60.95442 },\n      { nominal: 62, mass: 61.9561 },\n      { nominal: 63, mass: 62.96165 },\n      { nominal: 64, mass: 63.96408 },\n      { nominal: 65, mass: 64.96996 },\n      { nominal: 66, mass: 65.97366 },\n      { nominal: 67, mass: 66.98016 },\n      { nominal: 68, mass: 67.98403 },\n    ],\n    symbol: 'Cr',\n    mass: 51.9961317554337,\n    name: 'Chromium',\n    monoisotopicMass: 51.94050623,\n  },\n  {\n    number: 25,\n    isotopes: [\n      { nominal: 44, mass: 44.00715 },\n      { nominal: 45, mass: 44.99449 },\n      { nominal: 46, mass: 45.98609 },\n      { nominal: 47, mass: 46.975775 },\n      { nominal: 48, mass: 47.96852 },\n      { nominal: 49, mass: 48.959595 },\n      { nominal: 50, mass: 49.95423778 },\n      { nominal: 51, mass: 50.94820847 },\n      { nominal: 52, mass: 51.9455639 },\n      { nominal: 53, mass: 52.94128889 },\n      { nominal: 54, mass: 53.9403576 },\n      { nominal: 55, mass: 54.93804391, abundance: 1 },\n      { nominal: 56, mass: 55.93890369 },\n      { nominal: 57, mass: 56.9382861 },\n      { nominal: 58, mass: 57.9400666 },\n      { nominal: 59, mass: 58.9403911 },\n      { nominal: 60, mass: 59.9431366 },\n      { nominal: 61, mass: 60.9444525 },\n      { nominal: 62, mass: 61.94795 },\n      { nominal: 63, mass: 62.9496647 },\n      { nominal: 64, mass: 63.9538494 },\n      { nominal: 65, mass: 64.9560198 },\n      { nominal: 66, mass: 65.960547 },\n      { nominal: 67, mass: 66.96424 },\n      { nominal: 68, mass: 67.96962 },\n      { nominal: 69, mass: 68.97366 },\n      { nominal: 70, mass: 69.97937 },\n      { nominal: 71, mass: 70.98368 },\n    ],\n    symbol: 'Mn',\n    mass: 54.93804391,\n    name: 'Manganese',\n    monoisotopicMass: 54.93804391,\n  },\n  {\n    number: 26,\n    isotopes: [\n      { nominal: 45, mass: 45.01442 },\n      { nominal: 46, mass: 46.00063 },\n      { nominal: 47, mass: 46.99185 },\n      { nominal: 48, mass: 47.98023 },\n      { nominal: 49, mass: 48.973429 },\n      { nominal: 50, mass: 49.962975 },\n      { nominal: 51, mass: 50.956841 },\n      { nominal: 52, mass: 51.9481131 },\n      { nominal: 53, mass: 52.9453064 },\n      { nominal: 54, mass: 53.93960899, abundance: 0.05845 },\n      { nominal: 55, mass: 54.93829199 },\n      { nominal: 56, mass: 55.93493633, abundance: 0.91754 },\n      { nominal: 57, mass: 56.93539284, abundance: 0.02119 },\n      { nominal: 58, mass: 57.93327443, abundance: 0.00282 },\n      { nominal: 59, mass: 58.93487434 },\n      { nominal: 60, mass: 59.9340711 },\n      { nominal: 61, mass: 60.9367462 },\n      { nominal: 62, mass: 61.9367918 },\n      { nominal: 63, mass: 62.9402727 },\n      { nominal: 64, mass: 63.9409878 },\n      { nominal: 65, mass: 64.9450115 },\n      { nominal: 66, mass: 65.94625 },\n      { nominal: 67, mass: 66.95054 },\n      { nominal: 68, mass: 67.95295 },\n      { nominal: 69, mass: 68.95807 },\n      { nominal: 70, mass: 69.96102 },\n      { nominal: 71, mass: 70.96672 },\n      { nominal: 72, mass: 71.96983 },\n      { nominal: 73, mass: 72.97572 },\n      { nominal: 74, mass: 73.97935 },\n    ],\n    symbol: 'Fe',\n    mass: 55.845144433865904,\n    name: 'Iron',\n    monoisotopicMass: 55.93493633,\n  },\n  {\n    number: 27,\n    isotopes: [\n      { nominal: 47, mass: 47.01057 },\n      { nominal: 48, mass: 48.00093 },\n      { nominal: 49, mass: 48.98891 },\n      { nominal: 50, mass: 49.98091 },\n      { nominal: 51, mass: 50.970647 },\n      { nominal: 52, mass: 51.96351 },\n      { nominal: 53, mass: 52.9542041 },\n      { nominal: 54, mass: 53.94845987 },\n      { nominal: 55, mass: 54.9419972 },\n      { nominal: 56, mass: 55.9398388 },\n      { nominal: 57, mass: 56.93629057 },\n      { nominal: 58, mass: 57.9357521 },\n      { nominal: 59, mass: 58.93319429, abundance: 1 },\n      { nominal: 60, mass: 59.9338163 },\n      { nominal: 61, mass: 60.93247662 },\n      { nominal: 62, mass: 61.934059 },\n      { nominal: 63, mass: 62.9336 },\n      { nominal: 64, mass: 63.935811 },\n      { nominal: 65, mass: 64.9364621 },\n      { nominal: 66, mass: 65.939443 },\n      { nominal: 67, mass: 66.9406096 },\n      { nominal: 68, mass: 67.94426 },\n      { nominal: 69, mass: 68.94614 },\n      { nominal: 70, mass: 69.94963 },\n      { nominal: 71, mass: 70.95237 },\n      { nominal: 72, mass: 71.95729 },\n      { nominal: 73, mass: 72.96039 },\n      { nominal: 74, mass: 73.96515 },\n      { nominal: 75, mass: 74.96876 },\n      { nominal: 76, mass: 75.97413 },\n    ],\n    symbol: 'Co',\n    mass: 58.93319429,\n    name: 'Cobalt',\n    monoisotopicMass: 58.93319429,\n  },\n  {\n    number: 28,\n    isotopes: [\n      { nominal: 48, mass: 48.01769 },\n      { nominal: 49, mass: 49.0077 },\n      { nominal: 50, mass: 49.99474 },\n      { nominal: 51, mass: 50.98611 },\n      { nominal: 52, mass: 51.9748 },\n      { nominal: 53, mass: 52.96819 },\n      { nominal: 54, mass: 53.957892 },\n      { nominal: 55, mass: 54.95133063 },\n      { nominal: 56, mass: 55.94212855 },\n      { nominal: 57, mass: 56.93979218 },\n      { nominal: 58, mass: 57.93534241, abundance: 0.68077 },\n      { nominal: 59, mass: 58.9343462 },\n      { nominal: 60, mass: 59.93078588, abundance: 0.26223 },\n      { nominal: 61, mass: 60.93105557, abundance: 0.011399 },\n      { nominal: 62, mass: 61.92834537, abundance: 0.036346 },\n      { nominal: 63, mass: 62.92966963 },\n      { nominal: 64, mass: 63.92796682, abundance: 0.009255 },\n      { nominal: 65, mass: 64.93008517 },\n      { nominal: 66, mass: 65.9291393 },\n      { nominal: 67, mass: 66.9315694 },\n      { nominal: 68, mass: 67.9318688 },\n      { nominal: 69, mass: 68.9356103 },\n      { nominal: 70, mass: 69.9364313 },\n      { nominal: 71, mass: 70.940519 },\n      { nominal: 72, mass: 71.9417859 },\n      { nominal: 73, mass: 72.9462067 },\n      { nominal: 74, mass: 73.94798 },\n      { nominal: 75, mass: 74.9525 },\n      { nominal: 76, mass: 75.95533 },\n      { nominal: 77, mass: 76.96055 },\n      { nominal: 78, mass: 77.96336 },\n      { nominal: 79, mass: 78.97025 },\n    ],\n    symbol: 'Ni',\n    mass: 58.69334710994765,\n    name: 'Nickel',\n    monoisotopicMass: 57.93534241,\n  },\n  {\n    number: 29,\n    isotopes: [\n      { nominal: 52, mass: 51.99671 },\n      { nominal: 53, mass: 52.98459 },\n      { nominal: 54, mass: 53.97666 },\n      { nominal: 55, mass: 54.96604 },\n      { nominal: 56, mass: 55.95895 },\n      { nominal: 57, mass: 56.9492125 },\n      { nominal: 58, mass: 57.94453305 },\n      { nominal: 59, mass: 58.93949748 },\n      { nominal: 60, mass: 59.9373645 },\n      { nominal: 61, mass: 60.9334576 },\n      { nominal: 62, mass: 61.93259541 },\n      { nominal: 63, mass: 62.92959772, abundance: 0.6915 },\n      { nominal: 64, mass: 63.92976434 },\n      { nominal: 65, mass: 64.9277897, abundance: 0.3085 },\n      { nominal: 66, mass: 65.92886903 },\n      { nominal: 67, mass: 66.9277303 },\n      { nominal: 68, mass: 67.9296109 },\n      { nominal: 69, mass: 68.9294293 },\n      { nominal: 70, mass: 69.9323921 },\n      { nominal: 71, mass: 70.9326768 },\n      { nominal: 72, mass: 71.9358203 },\n      { nominal: 73, mass: 72.9366744 },\n      { nominal: 74, mass: 73.9398749 },\n      { nominal: 75, mass: 74.9415226 },\n      { nominal: 76, mass: 75.945275 },\n      { nominal: 77, mass: 76.94792 },\n      { nominal: 78, mass: 77.95223 },\n      { nominal: 79, mass: 78.95502 },\n      { nominal: 80, mass: 79.96089 },\n      { nominal: 81, mass: 80.96587 },\n      { nominal: 82, mass: 81.97244 },\n    ],\n    symbol: 'Cu',\n    mass: 63.54603994583,\n    name: 'Copper',\n    monoisotopicMass: 62.92959772,\n  },\n  {\n    number: 30,\n    isotopes: [\n      { nominal: 54, mass: 53.99204 },\n      { nominal: 55, mass: 54.98398 },\n      { nominal: 56, mass: 55.97254 },\n      { nominal: 57, mass: 56.96506 },\n      { nominal: 58, mass: 57.954591 },\n      { nominal: 59, mass: 58.94931266 },\n      { nominal: 60, mass: 59.9418421 },\n      { nominal: 61, mass: 60.939507 },\n      { nominal: 62, mass: 61.93433397 },\n      { nominal: 63, mass: 62.9332115 },\n      { nominal: 64, mass: 63.92914201, abundance: 0.4917 },\n      { nominal: 65, mass: 64.92924077 },\n      { nominal: 66, mass: 65.92603381, abundance: 0.2773 },\n      { nominal: 67, mass: 66.92712775, abundance: 0.0404 },\n      { nominal: 68, mass: 67.92484455, abundance: 0.1845 },\n      { nominal: 69, mass: 68.9265507 },\n      { nominal: 70, mass: 69.9253192, abundance: 0.0061 },\n      { nominal: 71, mass: 70.9277196 },\n      { nominal: 72, mass: 71.9268428 },\n      { nominal: 73, mass: 72.9295826 },\n      { nominal: 74, mass: 73.9294073 },\n      { nominal: 75, mass: 74.9328402 },\n      { nominal: 76, mass: 75.933115 },\n      { nominal: 77, mass: 76.9368872 },\n      { nominal: 78, mass: 77.9382892 },\n      { nominal: 79, mass: 78.9426381 },\n      { nominal: 80, mass: 79.9445529 },\n      { nominal: 81, mass: 80.9504026 },\n      { nominal: 82, mass: 81.95426 },\n      { nominal: 83, mass: 82.96056 },\n      { nominal: 84, mass: 83.96521 },\n      { nominal: 85, mass: 84.97226 },\n    ],\n    symbol: 'Zn',\n    mass: 65.37778252952499,\n    name: 'Zinc',\n    monoisotopicMass: 63.92914201,\n  },\n  {\n    number: 31,\n    isotopes: [\n      { nominal: 56, mass: 55.99536 },\n      { nominal: 57, mass: 56.9832 },\n      { nominal: 58, mass: 57.97478 },\n      { nominal: 59, mass: 58.96353 },\n      { nominal: 60, mass: 59.95729 },\n      { nominal: 61, mass: 60.949399 },\n      { nominal: 62, mass: 61.94419025 },\n      { nominal: 63, mass: 62.9392942 },\n      { nominal: 64, mass: 63.9368404 },\n      { nominal: 65, mass: 64.93273459 },\n      { nominal: 66, mass: 65.9315894 },\n      { nominal: 67, mass: 66.9282025 },\n      { nominal: 68, mass: 67.9279805 },\n      { nominal: 69, mass: 68.9255735, abundance: 0.60108 },\n      { nominal: 70, mass: 69.9260219 },\n      { nominal: 71, mass: 70.92470258, abundance: 0.39892 },\n      { nominal: 72, mass: 71.92636747 },\n      { nominal: 73, mass: 72.9251747 },\n      { nominal: 74, mass: 73.9269457 },\n      { nominal: 75, mass: 74.9265002 },\n      { nominal: 76, mass: 75.9288276 },\n      { nominal: 77, mass: 76.9291543 },\n      { nominal: 78, mass: 77.9316088 },\n      { nominal: 79, mass: 78.9328523 },\n      { nominal: 80, mass: 79.9364208 },\n      { nominal: 81, mass: 80.9381338 },\n      { nominal: 82, mass: 81.9431765 },\n      { nominal: 83, mass: 82.9471203 },\n      { nominal: 84, mass: 83.95246 },\n      { nominal: 85, mass: 84.95699 },\n      { nominal: 86, mass: 85.96301 },\n      { nominal: 87, mass: 86.96824 },\n    ],\n    symbol: 'Ga',\n    mass: 69.7230660725936,\n    name: 'Gallium',\n    monoisotopicMass: 68.9255735,\n  },\n  {\n    number: 32,\n    isotopes: [\n      { nominal: 58, mass: 57.99172 },\n      { nominal: 59, mass: 58.98249 },\n      { nominal: 60, mass: 59.97036 },\n      { nominal: 61, mass: 60.96379 },\n      { nominal: 62, mass: 61.95502 },\n      { nominal: 63, mass: 62.949628 },\n      { nominal: 64, mass: 63.9416899 },\n      { nominal: 65, mass: 64.9393681 },\n      { nominal: 66, mass: 65.9338621 },\n      { nominal: 67, mass: 66.9327339 },\n      { nominal: 68, mass: 67.9280953 },\n      { nominal: 69, mass: 68.9279645 },\n      { nominal: 70, mass: 69.92424875, abundance: 0.2057 },\n      { nominal: 71, mass: 70.92495233 },\n      { nominal: 72, mass: 71.922075826, abundance: 0.2745 },\n      { nominal: 73, mass: 72.923458956, abundance: 0.0775 },\n      { nominal: 74, mass: 73.921177761, abundance: 0.365 },\n      { nominal: 75, mass: 74.92285837 },\n      { nominal: 76, mass: 75.921402726, abundance: 0.0773 },\n      { nominal: 77, mass: 76.923549843 },\n      { nominal: 78, mass: 77.9228529 },\n      { nominal: 79, mass: 78.92536 },\n      { nominal: 80, mass: 79.9253508 },\n      { nominal: 81, mass: 80.9288329 },\n      { nominal: 82, mass: 81.929774 },\n      { nominal: 83, mass: 82.9345391 },\n      { nominal: 84, mass: 83.9375751 },\n      { nominal: 85, mass: 84.9429697 },\n      { nominal: 86, mass: 85.94658 },\n      { nominal: 87, mass: 86.95268 },\n      { nominal: 88, mass: 87.95691 },\n      { nominal: 89, mass: 88.96379 },\n      { nominal: 90, mass: 89.96863 },\n    ],\n    symbol: 'Ge',\n    mass: 72.6275501646868,\n    name: 'Germanium',\n    monoisotopicMass: 73.921177761,\n  },\n  {\n    number: 33,\n    isotopes: [\n      { nominal: 60, mass: 59.99388 },\n      { nominal: 61, mass: 60.98112 },\n      { nominal: 62, mass: 61.97361 },\n      { nominal: 63, mass: 62.9639 },\n      { nominal: 64, mass: 63.95743 },\n      { nominal: 65, mass: 64.949611 },\n      { nominal: 66, mass: 65.9441488 },\n      { nominal: 67, mass: 66.93925111 },\n      { nominal: 68, mass: 67.9367741 },\n      { nominal: 69, mass: 68.932246 },\n      { nominal: 70, mass: 69.930926 },\n      { nominal: 71, mass: 70.9271138 },\n      { nominal: 72, mass: 71.9267523 },\n      { nominal: 73, mass: 72.9238291 },\n      { nominal: 74, mass: 73.9239286 },\n      { nominal: 75, mass: 74.92159457, abundance: 1 },\n      { nominal: 76, mass: 75.92239202 },\n      { nominal: 77, mass: 76.9206476 },\n      { nominal: 78, mass: 77.921828 },\n      { nominal: 79, mass: 78.9209484 },\n      { nominal: 80, mass: 79.9224746 },\n      { nominal: 81, mass: 80.9221323 },\n      { nominal: 82, mass: 81.9247412 },\n      { nominal: 83, mass: 82.9252069 },\n      { nominal: 84, mass: 83.9293033 },\n      { nominal: 85, mass: 84.9321637 },\n      { nominal: 86, mass: 85.9367015 },\n      { nominal: 87, mass: 86.9402917 },\n      { nominal: 88, mass: 87.94555 },\n      { nominal: 89, mass: 88.94976 },\n      { nominal: 90, mass: 89.95563 },\n      { nominal: 91, mass: 90.96039 },\n      { nominal: 92, mass: 91.96674 },\n    ],\n    symbol: 'As',\n    mass: 74.92159457,\n    name: 'Arsenic',\n    monoisotopicMass: 74.92159457,\n  },\n  {\n    number: 34,\n    isotopes: [\n      { nominal: 64, mass: 63.97109 },\n      { nominal: 65, mass: 64.9644 },\n      { nominal: 66, mass: 65.95559 },\n      { nominal: 67, mass: 66.949994 },\n      { nominal: 68, mass: 67.94182524 },\n      { nominal: 69, mass: 68.9394148 },\n      { nominal: 70, mass: 69.9335155 },\n      { nominal: 71, mass: 70.9322094 },\n      { nominal: 72, mass: 71.9271405 },\n      { nominal: 73, mass: 72.9267549 },\n      { nominal: 74, mass: 73.922475934, abundance: 0.0089 },\n      { nominal: 75, mass: 74.92252287 },\n      { nominal: 76, mass: 75.919213704, abundance: 0.0937 },\n      { nominal: 77, mass: 76.919914154, abundance: 0.0763 },\n      { nominal: 78, mass: 77.91730928, abundance: 0.2377 },\n      { nominal: 79, mass: 78.91849929 },\n      { nominal: 80, mass: 79.9165218, abundance: 0.4961 },\n      { nominal: 81, mass: 80.917993 },\n      { nominal: 82, mass: 81.9166995, abundance: 0.0873 },\n      { nominal: 83, mass: 82.9191186 },\n      { nominal: 84, mass: 83.9184668 },\n      { nominal: 85, mass: 84.9222608 },\n      { nominal: 86, mass: 85.9243117 },\n      { nominal: 87, mass: 86.9286886 },\n      { nominal: 88, mass: 87.9314175 },\n      { nominal: 89, mass: 88.9366691 },\n      { nominal: 90, mass: 89.9401 },\n      { nominal: 91, mass: 90.94596 },\n      { nominal: 92, mass: 91.94984 },\n      { nominal: 93, mass: 92.95629 },\n      { nominal: 94, mass: 93.96049 },\n      { nominal: 95, mass: 94.9673 },\n    ],\n    symbol: 'Se',\n    mass: 78.95938855701361,\n    name: 'Selenium',\n    monoisotopicMass: 79.9165218,\n  },\n  {\n    number: 35,\n    isotopes: [\n      { nominal: 67, mass: 66.96465 },\n      { nominal: 68, mass: 67.95873 },\n      { nominal: 69, mass: 68.950497 },\n      { nominal: 70, mass: 69.944792 },\n      { nominal: 71, mass: 70.9393422 },\n      { nominal: 72, mass: 71.9365886 },\n      { nominal: 73, mass: 72.9316715 },\n      { nominal: 74, mass: 73.9299102 },\n      { nominal: 75, mass: 74.9258105 },\n      { nominal: 76, mass: 75.924542 },\n      { nominal: 77, mass: 76.9213792 },\n      { nominal: 78, mass: 77.9211459 },\n      { nominal: 79, mass: 78.9183376, abundance: 0.5069 },\n      { nominal: 80, mass: 79.9185298 },\n      { nominal: 81, mass: 80.9162897, abundance: 0.4931 },\n      { nominal: 82, mass: 81.9168032 },\n      { nominal: 83, mass: 82.9151756 },\n      { nominal: 84, mass: 83.916496 },\n      { nominal: 85, mass: 84.9156458 },\n      { nominal: 86, mass: 85.9188054 },\n      { nominal: 87, mass: 86.920674 },\n      { nominal: 88, mass: 87.9240833 },\n      { nominal: 89, mass: 88.9267046 },\n      { nominal: 90, mass: 89.9312928 },\n      { nominal: 91, mass: 90.9343986 },\n      { nominal: 92, mass: 91.9396316 },\n      { nominal: 93, mass: 92.94313 },\n      { nominal: 94, mass: 93.9489 },\n      { nominal: 95, mass: 94.95301 },\n      { nominal: 96, mass: 95.95903 },\n      { nominal: 97, mass: 96.96344 },\n      { nominal: 98, mass: 97.96946 },\n    ],\n    symbol: 'Br',\n    mass: 79.90352778050999,\n    name: 'Bromine',\n    monoisotopicMass: 78.9183376,\n  },\n  {\n    number: 36,\n    isotopes: [\n      { nominal: 69, mass: 68.96518 },\n      { nominal: 70, mass: 69.95604 },\n      { nominal: 71, mass: 70.95027 },\n      { nominal: 72, mass: 71.9420924 },\n      { nominal: 73, mass: 72.9392892 },\n      { nominal: 74, mass: 73.933084 },\n      { nominal: 75, mass: 74.9309457 },\n      { nominal: 76, mass: 75.9259103 },\n      { nominal: 77, mass: 76.92467 },\n      { nominal: 78, mass: 77.92036494, abundance: 0.00355 },\n      { nominal: 79, mass: 78.9200829 },\n      { nominal: 80, mass: 79.91637808, abundance: 0.02286 },\n      { nominal: 81, mass: 80.9165912 },\n      { nominal: 82, mass: 81.91348273, abundance: 0.11593 },\n      { nominal: 83, mass: 82.91412716, abundance: 0.115 },\n      { nominal: 84, mass: 83.9114977282, abundance: 0.56987 },\n      { nominal: 85, mass: 84.9125273 },\n      { nominal: 86, mass: 85.9106106269, abundance: 0.17279 },\n      { nominal: 87, mass: 86.91335476 },\n      { nominal: 88, mass: 87.9144479 },\n      { nominal: 89, mass: 88.9178355 },\n      { nominal: 90, mass: 89.9195279 },\n      { nominal: 91, mass: 90.9238063 },\n      { nominal: 92, mass: 91.9261731 },\n      { nominal: 93, mass: 92.9311472 },\n      { nominal: 94, mass: 93.93414 },\n      { nominal: 95, mass: 94.939711 },\n      { nominal: 96, mass: 95.943017 },\n      { nominal: 97, mass: 96.94909 },\n      { nominal: 98, mass: 97.95243 },\n      { nominal: 99, mass: 98.95839 },\n      { nominal: 100, mass: 99.96237 },\n      { nominal: 101, mass: 100.96873 },\n    ],\n    symbol: 'Kr',\n    mass: 83.7979999953261,\n    name: 'Krypton',\n    monoisotopicMass: 83.9114977282,\n  },\n  {\n    number: 37,\n    isotopes: [\n      { nominal: 71, mass: 70.96532 },\n      { nominal: 72, mass: 71.95908 },\n      { nominal: 73, mass: 72.95053 },\n      { nominal: 74, mass: 73.9442659 },\n      { nominal: 75, mass: 74.9385732 },\n      { nominal: 76, mass: 75.935073 },\n      { nominal: 77, mass: 76.9304016 },\n      { nominal: 78, mass: 77.9281419 },\n      { nominal: 79, mass: 78.9239899 },\n      { nominal: 80, mass: 79.9225164 },\n      { nominal: 81, mass: 80.9189939 },\n      { nominal: 82, mass: 81.918209 },\n      { nominal: 83, mass: 82.9151142 },\n      { nominal: 84, mass: 83.9143752 },\n      { nominal: 85, mass: 84.9117897379, abundance: 0.7217 },\n      { nominal: 86, mass: 85.91116743 },\n      { nominal: 87, mass: 86.909180531, abundance: 0.2783 },\n      { nominal: 88, mass: 87.91131559 },\n      { nominal: 89, mass: 88.9122783 },\n      { nominal: 90, mass: 89.9147985 },\n      { nominal: 91, mass: 90.9165372 },\n      { nominal: 92, mass: 91.9197284 },\n      { nominal: 93, mass: 92.9220393 },\n      { nominal: 94, mass: 93.9263948 },\n      { nominal: 95, mass: 94.92926 },\n      { nominal: 96, mass: 95.9341334 },\n      { nominal: 97, mass: 96.9371771 },\n      { nominal: 98, mass: 97.9416869 },\n      { nominal: 99, mass: 98.94503 },\n      { nominal: 100, mass: 99.95003 },\n      { nominal: 101, mass: 100.95404 },\n      { nominal: 102, mass: 101.95952 },\n      { nominal: 103, mass: 102.96392 },\n    ],\n    symbol: 'Rb',\n    mass: 85.46766359561973,\n    name: 'Rubidium',\n    monoisotopicMass: 84.9117897379,\n  },\n  {\n    number: 38,\n    isotopes: [\n      { nominal: 73, mass: 72.9657 },\n      { nominal: 74, mass: 73.95617 },\n      { nominal: 75, mass: 74.94995 },\n      { nominal: 76, mass: 75.941763 },\n      { nominal: 77, mass: 76.9379455 },\n      { nominal: 78, mass: 77.93218 },\n      { nominal: 79, mass: 78.9297077 },\n      { nominal: 80, mass: 79.9245175 },\n      { nominal: 81, mass: 80.9232114 },\n      { nominal: 82, mass: 81.9183999 },\n      { nominal: 83, mass: 82.9175544 },\n      { nominal: 84, mass: 83.9134191, abundance: 0.0056 },\n      { nominal: 85, mass: 84.912932 },\n      { nominal: 86, mass: 85.9092606, abundance: 0.0986 },\n      { nominal: 87, mass: 86.9088775, abundance: 0.07 },\n      { nominal: 88, mass: 87.9056125, abundance: 0.8258 },\n      { nominal: 89, mass: 88.9074511 },\n      { nominal: 90, mass: 89.90773 },\n      { nominal: 91, mass: 90.9101954 },\n      { nominal: 92, mass: 91.9110382 },\n      { nominal: 93, mass: 92.9140242 },\n      { nominal: 94, mass: 93.9153556 },\n      { nominal: 95, mass: 94.9193529 },\n      { nominal: 96, mass: 95.9217066 },\n      { nominal: 97, mass: 96.926374 },\n      { nominal: 98, mass: 97.9286888 },\n      { nominal: 99, mass: 98.9328907 },\n      { nominal: 100, mass: 99.93577 },\n      { nominal: 101, mass: 100.940352 },\n      { nominal: 102, mass: 101.943791 },\n      { nominal: 103, mass: 102.94909 },\n      { nominal: 104, mass: 103.95265 },\n      { nominal: 105, mass: 104.95855 },\n      { nominal: 106, mass: 105.96265 },\n      { nominal: 107, mass: 106.96897 },\n    ],\n    symbol: 'Sr',\n    mass: 87.61664446962,\n    name: 'Strontium',\n    monoisotopicMass: 87.9056125,\n  },\n  {\n    number: 39,\n    isotopes: [\n      { nominal: 76, mass: 75.95856 },\n      { nominal: 77, mass: 76.949781 },\n      { nominal: 78, mass: 77.94361 },\n      { nominal: 79, mass: 78.93735 },\n      { nominal: 80, mass: 79.9343561 },\n      { nominal: 81, mass: 80.9294556 },\n      { nominal: 82, mass: 81.9269314 },\n      { nominal: 83, mass: 82.922485 },\n      { nominal: 84, mass: 83.9206721 },\n      { nominal: 85, mass: 84.916433 },\n      { nominal: 86, mass: 85.914886 },\n      { nominal: 87, mass: 86.9108761 },\n      { nominal: 88, mass: 87.9095016 },\n      { nominal: 89, mass: 88.9058403, abundance: 1 },\n      { nominal: 90, mass: 89.9071439 },\n      { nominal: 91, mass: 90.9072974 },\n      { nominal: 92, mass: 91.9089451 },\n      { nominal: 93, mass: 92.909578 },\n      { nominal: 94, mass: 93.9115906 },\n      { nominal: 95, mass: 94.9128161 },\n      { nominal: 96, mass: 95.9158968 },\n      { nominal: 97, mass: 96.9182741 },\n      { nominal: 98, mass: 97.9223821 },\n      { nominal: 99, mass: 98.924148 },\n      { nominal: 100, mass: 99.927715 },\n      { nominal: 101, mass: 100.9301477 },\n      { nominal: 102, mass: 101.9343277 },\n      { nominal: 103, mass: 102.937243 },\n      { nominal: 104, mass: 103.94196 },\n      { nominal: 105, mass: 104.94544 },\n      { nominal: 106, mass: 105.95056 },\n      { nominal: 107, mass: 106.95452 },\n      { nominal: 108, mass: 107.95996 },\n      { nominal: 109, mass: 108.96436 },\n    ],\n    symbol: 'Y',\n    mass: 88.9058403,\n    name: 'Yttrium',\n    monoisotopicMass: 88.9058403,\n  },\n  {\n    number: 40,\n    isotopes: [\n      { nominal: 78, mass: 77.95566 },\n      { nominal: 79, mass: 78.94948 },\n      { nominal: 80, mass: 79.9404 },\n      { nominal: 81, mass: 80.93731 },\n      { nominal: 82, mass: 81.93135 },\n      { nominal: 83, mass: 82.9292421 },\n      { nominal: 84, mass: 83.9233269 },\n      { nominal: 85, mass: 84.9214444 },\n      { nominal: 86, mass: 85.9162972 },\n      { nominal: 87, mass: 86.914818 },\n      { nominal: 88, mass: 87.9102213 },\n      { nominal: 89, mass: 88.9088814 },\n      { nominal: 90, mass: 89.9046977, abundance: 0.5145 },\n      { nominal: 91, mass: 90.9056396, abundance: 0.1122 },\n      { nominal: 92, mass: 91.9050347, abundance: 0.1715 },\n      { nominal: 93, mass: 92.9064699 },\n      { nominal: 94, mass: 93.9063108, abundance: 0.1738 },\n      { nominal: 95, mass: 94.9080385 },\n      { nominal: 96, mass: 95.9082714, abundance: 0.028 },\n      { nominal: 97, mass: 96.9109512 },\n      { nominal: 98, mass: 97.9127289 },\n      { nominal: 99, mass: 98.916667 },\n      { nominal: 100, mass: 99.9180006 },\n      { nominal: 101, mass: 100.921448 },\n      { nominal: 102, mass: 101.9231409 },\n      { nominal: 103, mass: 102.927191 },\n      { nominal: 104, mass: 103.929436 },\n      { nominal: 105, mass: 104.934008 },\n      { nominal: 106, mass: 105.93676 },\n      { nominal: 107, mass: 106.94174 },\n      { nominal: 108, mass: 107.94487 },\n      { nominal: 109, mass: 108.95041 },\n      { nominal: 110, mass: 109.95396 },\n      { nominal: 111, mass: 110.95968 },\n      { nominal: 112, mass: 111.9637 },\n    ],\n    symbol: 'Zr',\n    mass: 91.22364159706,\n    name: 'Zirconium',\n    monoisotopicMass: 89.9046977,\n  },\n  {\n    number: 41,\n    isotopes: [\n      { nominal: 81, mass: 80.9496 },\n      { nominal: 82, mass: 81.94396 },\n      { nominal: 83, mass: 82.93729 },\n      { nominal: 84, mass: 83.93449 },\n      { nominal: 85, mass: 84.9288458 },\n      { nominal: 86, mass: 85.9257828 },\n      { nominal: 87, mass: 86.9206937 },\n      { nominal: 88, mass: 87.918222 },\n      { nominal: 89, mass: 88.913445 },\n      { nominal: 90, mass: 89.9112584 },\n      { nominal: 91, mass: 90.9069897 },\n      { nominal: 92, mass: 91.9071881 },\n      { nominal: 93, mass: 92.906373, abundance: 1 },\n      { nominal: 94, mass: 93.9072788 },\n      { nominal: 95, mass: 94.9068324 },\n      { nominal: 96, mass: 95.9080973 },\n      { nominal: 97, mass: 96.9080959 },\n      { nominal: 98, mass: 97.9103265 },\n      { nominal: 99, mass: 98.911613 },\n      { nominal: 100, mass: 99.9143276 },\n      { nominal: 101, mass: 100.9153103 },\n      { nominal: 102, mass: 101.9180772 },\n      { nominal: 103, mass: 102.9194572 },\n      { nominal: 104, mass: 103.9228925 },\n      { nominal: 105, mass: 104.9249465 },\n      { nominal: 106, mass: 105.9289317 },\n      { nominal: 107, mass: 106.9315937 },\n      { nominal: 108, mass: 107.9360748 },\n      { nominal: 109, mass: 108.93922 },\n      { nominal: 110, mass: 109.94403 },\n      { nominal: 111, mass: 110.94753 },\n      { nominal: 112, mass: 111.95247 },\n      { nominal: 113, mass: 112.95651 },\n      { nominal: 114, mass: 113.96201 },\n      { nominal: 115, mass: 114.96634 },\n    ],\n    symbol: 'Nb',\n    mass: 92.906373,\n    name: 'Niobium',\n    monoisotopicMass: 92.906373,\n  },\n  {\n    number: 42,\n    isotopes: [\n      { nominal: 83, mass: 82.94988 },\n      { nominal: 84, mass: 83.94149 },\n      { nominal: 85, mass: 84.938261 },\n      { nominal: 86, mass: 85.9311748 },\n      { nominal: 87, mass: 86.9281962 },\n      { nominal: 88, mass: 87.9219678 },\n      { nominal: 89, mass: 88.9194682 },\n      { nominal: 90, mass: 89.9139309 },\n      { nominal: 91, mass: 90.9117453 },\n      { nominal: 92, mass: 91.90680796, abundance: 0.1453 },\n      { nominal: 93, mass: 92.90680958 },\n      { nominal: 94, mass: 93.9050849, abundance: 0.0915 },\n      { nominal: 95, mass: 94.90583877, abundance: 0.1584 },\n      { nominal: 96, mass: 95.90467612, abundance: 0.1667 },\n      { nominal: 97, mass: 96.90601812, abundance: 0.096 },\n      { nominal: 98, mass: 97.90540482, abundance: 0.2439 },\n      { nominal: 99, mass: 98.90770851 },\n      { nominal: 100, mass: 99.9074718, abundance: 0.0982 },\n      { nominal: 101, mass: 100.9103414 },\n      { nominal: 102, mass: 101.9102834 },\n      { nominal: 103, mass: 102.913079 },\n      { nominal: 104, mass: 103.9137344 },\n      { nominal: 105, mass: 104.916969 },\n      { nominal: 106, mass: 105.918259 },\n      { nominal: 107, mass: 106.922106 },\n      { nominal: 108, mass: 107.924033 },\n      { nominal: 109, mass: 108.928424 },\n      { nominal: 110, mass: 109.930704 },\n      { nominal: 111, mass: 110.935654 },\n      { nominal: 112, mass: 111.93831 },\n      { nominal: 113, mass: 112.94335 },\n      { nominal: 114, mass: 113.94653 },\n      { nominal: 115, mass: 114.95196 },\n      { nominal: 116, mass: 115.95545 },\n      { nominal: 117, mass: 116.96117 },\n    ],\n    symbol: 'Mo',\n    mass: 95.959788541188,\n    name: 'Molybdenum',\n    monoisotopicMass: 97.90540482,\n  },\n  {\n    number: 43,\n    isotopes: [\n      { nominal: 85, mass: 84.95058 },\n      { nominal: 86, mass: 85.94493 },\n      { nominal: 87, mass: 86.9380672 },\n      { nominal: 88, mass: 87.93378 },\n      { nominal: 89, mass: 88.9276487 },\n      { nominal: 90, mass: 89.9240739 },\n      { nominal: 91, mass: 90.9184254 },\n      { nominal: 92, mass: 91.9152698 },\n      { nominal: 93, mass: 92.910246 },\n      { nominal: 94, mass: 93.9096536 },\n      { nominal: 95, mass: 94.9076536 },\n      { nominal: 96, mass: 95.907868 },\n      { nominal: 97, mass: 96.9063667 },\n      { nominal: 98, mass: 97.9072124 },\n      { nominal: 99, mass: 98.9062508 },\n      { nominal: 100, mass: 99.9076539 },\n      { nominal: 101, mass: 100.907309 },\n      { nominal: 102, mass: 101.9092097 },\n      { nominal: 103, mass: 102.909176 },\n      { nominal: 104, mass: 103.911425 },\n      { nominal: 105, mass: 104.911655 },\n      { nominal: 106, mass: 105.914358 },\n      { nominal: 107, mass: 106.9154606 },\n      { nominal: 108, mass: 107.9184957 },\n      { nominal: 109, mass: 108.920256 },\n      { nominal: 110, mass: 109.923744 },\n      { nominal: 111, mass: 110.925901 },\n      { nominal: 112, mass: 111.9299458 },\n      { nominal: 113, mass: 112.932569 },\n      { nominal: 114, mass: 113.93691 },\n      { nominal: 115, mass: 114.93998 },\n      { nominal: 116, mass: 115.94476 },\n      { nominal: 117, mass: 116.94806 },\n      { nominal: 118, mass: 117.95299 },\n      { nominal: 119, mass: 118.95666 },\n      { nominal: 120, mass: 119.96187 },\n    ],\n    symbol: 'Tc',\n    mass: null,\n    name: 'Technetium',\n  },\n  {\n    number: 44,\n    isotopes: [\n      { nominal: 87, mass: 86.95069 },\n      { nominal: 88, mass: 87.9416 },\n      { nominal: 89, mass: 88.93762 },\n      { nominal: 90, mass: 89.9303444 },\n      { nominal: 91, mass: 90.9267419 },\n      { nominal: 92, mass: 91.9202344 },\n      { nominal: 93, mass: 92.9171044 },\n      { nominal: 94, mass: 93.9113429 },\n      { nominal: 95, mass: 94.910406 },\n      { nominal: 96, mass: 95.90759025, abundance: 0.0554 },\n      { nominal: 97, mass: 96.9075471 },\n      { nominal: 98, mass: 97.9052868, abundance: 0.0187 },\n      { nominal: 99, mass: 98.9059341, abundance: 0.1276 },\n      { nominal: 100, mass: 99.9042143, abundance: 0.126 },\n      { nominal: 101, mass: 100.9055769, abundance: 0.1706 },\n      { nominal: 102, mass: 101.9043441, abundance: 0.3155 },\n      { nominal: 103, mass: 102.9063186 },\n      { nominal: 104, mass: 103.9054275, abundance: 0.1862 },\n      { nominal: 105, mass: 104.9077476 },\n      { nominal: 106, mass: 105.9073291 },\n      { nominal: 107, mass: 106.909972 },\n      { nominal: 108, mass: 107.910188 },\n      { nominal: 109, mass: 108.913326 },\n      { nominal: 110, mass: 109.9140407 },\n      { nominal: 111, mass: 110.91757 },\n      { nominal: 112, mass: 111.918809 },\n      { nominal: 113, mass: 112.922844 },\n      { nominal: 114, mass: 113.9246136 },\n      { nominal: 115, mass: 114.92882 },\n      { nominal: 116, mass: 115.9312192 },\n      { nominal: 117, mass: 116.9361 },\n      { nominal: 118, mass: 117.93853 },\n      { nominal: 119, mass: 118.94357 },\n      { nominal: 120, mass: 119.94631 },\n      { nominal: 121, mass: 120.95164 },\n      { nominal: 122, mass: 121.95447 },\n      { nominal: 123, mass: 122.95989 },\n      { nominal: 124, mass: 123.96305 },\n    ],\n    symbol: 'Ru',\n    mass: 101.06494013916,\n    name: 'Ruthenium',\n    monoisotopicMass: 101.9043441,\n  },\n  {\n    number: 45,\n    isotopes: [\n      { nominal: 89, mass: 88.95058 },\n      { nominal: 90, mass: 89.94422 },\n      { nominal: 91, mass: 90.93688 },\n      { nominal: 92, mass: 91.9323677 },\n      { nominal: 93, mass: 92.9259128 },\n      { nominal: 94, mass: 93.9217305 },\n      { nominal: 95, mass: 94.9158979 },\n      { nominal: 96, mass: 95.914453 },\n      { nominal: 97, mass: 96.911329 },\n      { nominal: 98, mass: 97.910708 },\n      { nominal: 99, mass: 98.9081282 },\n      { nominal: 100, mass: 99.908117 },\n      { nominal: 101, mass: 100.9061606 },\n      { nominal: 102, mass: 101.9068374 },\n      { nominal: 103, mass: 102.905498, abundance: 1 },\n      { nominal: 104, mass: 103.9066492 },\n      { nominal: 105, mass: 104.9056885 },\n      { nominal: 106, mass: 105.9072868 },\n      { nominal: 107, mass: 106.906748 },\n      { nominal: 108, mass: 107.908714 },\n      { nominal: 109, mass: 108.9087488 },\n      { nominal: 110, mass: 109.911079 },\n      { nominal: 111, mass: 110.9116423 },\n      { nominal: 112, mass: 111.914403 },\n      { nominal: 113, mass: 112.9154393 },\n      { nominal: 114, mass: 113.918718 },\n      { nominal: 115, mass: 114.9203116 },\n      { nominal: 116, mass: 115.924059 },\n      { nominal: 117, mass: 116.9260354 },\n      { nominal: 118, mass: 117.93034 },\n      { nominal: 119, mass: 118.932557 },\n      { nominal: 120, mass: 119.93686 },\n      { nominal: 121, mass: 120.93942 },\n      { nominal: 122, mass: 121.94399 },\n      { nominal: 123, mass: 122.94685 },\n      { nominal: 124, mass: 123.95151 },\n      { nominal: 125, mass: 124.95469 },\n      { nominal: 126, mass: 125.95946 },\n    ],\n    symbol: 'Rh',\n    mass: 102.905498,\n    name: 'Rhodium',\n    monoisotopicMass: 102.905498,\n  },\n  {\n    number: 46,\n    isotopes: [\n      { nominal: 91, mass: 90.95032 },\n      { nominal: 92, mass: 91.94088 },\n      { nominal: 93, mass: 92.93651 },\n      { nominal: 94, mass: 93.9290376 },\n      { nominal: 95, mass: 94.9248898 },\n      { nominal: 96, mass: 95.9182151 },\n      { nominal: 97, mass: 96.916472 },\n      { nominal: 98, mass: 97.9126983 },\n      { nominal: 99, mass: 98.9117748 },\n      { nominal: 100, mass: 99.908505 },\n      { nominal: 101, mass: 100.9082864 },\n      { nominal: 102, mass: 101.9056022, abundance: 0.0102 },\n      { nominal: 103, mass: 102.9060809 },\n      { nominal: 104, mass: 103.9040305, abundance: 0.1114 },\n      { nominal: 105, mass: 104.9050796, abundance: 0.2233 },\n      { nominal: 106, mass: 105.9034804, abundance: 0.2733 },\n      { nominal: 107, mass: 106.9051282 },\n      { nominal: 108, mass: 107.9038916, abundance: 0.2646 },\n      { nominal: 109, mass: 108.9059504 },\n      { nominal: 110, mass: 109.9051722, abundance: 0.1172 },\n      { nominal: 111, mass: 110.90768968 },\n      { nominal: 112, mass: 111.9073297 },\n      { nominal: 113, mass: 112.910261 },\n      { nominal: 114, mass: 113.9103686 },\n      { nominal: 115, mass: 114.913659 },\n      { nominal: 116, mass: 115.914297 },\n      { nominal: 117, mass: 116.9179547 },\n      { nominal: 118, mass: 117.9190667 },\n      { nominal: 119, mass: 118.9233402 },\n      { nominal: 120, mass: 119.9245511 },\n      { nominal: 121, mass: 120.9289503 },\n      { nominal: 122, mass: 121.930632 },\n      { nominal: 123, mass: 122.93514 },\n      { nominal: 124, mass: 123.93714 },\n      { nominal: 125, mass: 124.94179 },\n      { nominal: 126, mass: 125.94416 },\n      { nominal: 127, mass: 126.94907 },\n      { nominal: 128, mass: 127.95183 },\n    ],\n    symbol: 'Pd',\n    mass: 106.41532750734,\n    name: 'Palladium',\n    monoisotopicMass: 105.9034804,\n  },\n  {\n    number: 47,\n    isotopes: [\n      { nominal: 93, mass: 92.95033 },\n      { nominal: 94, mass: 93.94373 },\n      { nominal: 95, mass: 94.93602 },\n      { nominal: 96, mass: 95.930744 },\n      { nominal: 97, mass: 96.92397 },\n      { nominal: 98, mass: 97.92156 },\n      { nominal: 99, mass: 98.9176458 },\n      { nominal: 100, mass: 99.9161154 },\n      { nominal: 101, mass: 100.912684 },\n      { nominal: 102, mass: 101.9117047 },\n      { nominal: 103, mass: 102.9089631 },\n      { nominal: 104, mass: 103.9086239 },\n      { nominal: 105, mass: 104.9065256 },\n      { nominal: 106, mass: 105.9066636 },\n      { nominal: 107, mass: 106.9050916, abundance: 0.51839 },\n      { nominal: 108, mass: 107.9059503 },\n      { nominal: 109, mass: 108.9047553, abundance: 0.48161 },\n      { nominal: 110, mass: 109.9061102 },\n      { nominal: 111, mass: 110.9052959 },\n      { nominal: 112, mass: 111.9070486 },\n      { nominal: 113, mass: 112.906573 },\n      { nominal: 114, mass: 113.908823 },\n      { nominal: 115, mass: 114.908767 },\n      { nominal: 116, mass: 115.9113868 },\n      { nominal: 117, mass: 116.911774 },\n      { nominal: 118, mass: 117.9145955 },\n      { nominal: 119, mass: 118.91557 },\n      { nominal: 120, mass: 119.9187848 },\n      { nominal: 121, mass: 120.920125 },\n      { nominal: 122, mass: 121.923664 },\n      { nominal: 123, mass: 122.925337 },\n      { nominal: 124, mass: 123.92893 },\n      { nominal: 125, mass: 124.93105 },\n      { nominal: 126, mass: 125.93475 },\n      { nominal: 127, mass: 126.93711 },\n      { nominal: 128, mass: 127.94106 },\n      { nominal: 129, mass: 128.94395 },\n      { nominal: 130, mass: 129.9507 },\n    ],\n    symbol: 'Ag',\n    mass: 107.868149634557,\n    name: 'Silver',\n    monoisotopicMass: 106.9050916,\n  },\n  {\n    number: 48,\n    isotopes: [\n      { nominal: 95, mass: 94.94994 },\n      { nominal: 96, mass: 95.94034 },\n      { nominal: 97, mass: 96.9351 },\n      { nominal: 98, mass: 97.927389 },\n      { nominal: 99, mass: 98.9249258 },\n      { nominal: 100, mass: 99.9203488 },\n      { nominal: 101, mass: 100.9185862 },\n      { nominal: 102, mass: 101.914482 },\n      { nominal: 103, mass: 102.9134165 },\n      { nominal: 104, mass: 103.9098564 },\n      { nominal: 105, mass: 104.9094639 },\n      { nominal: 106, mass: 105.9064599, abundance: 0.0125 },\n      { nominal: 107, mass: 106.9066121 },\n      { nominal: 108, mass: 107.9041834, abundance: 0.0089 },\n      { nominal: 109, mass: 108.9049867 },\n      { nominal: 110, mass: 109.90300661, abundance: 0.1249 },\n      { nominal: 111, mass: 110.90418287, abundance: 0.128 },\n      { nominal: 112, mass: 111.90276287, abundance: 0.2413 },\n      { nominal: 113, mass: 112.90440813, abundance: 0.1222 },\n      { nominal: 114, mass: 113.90336509, abundance: 0.2873 },\n      { nominal: 115, mass: 114.90543751 },\n      { nominal: 116, mass: 115.90476315, abundance: 0.0749 },\n      { nominal: 117, mass: 116.907226 },\n      { nominal: 118, mass: 117.906922 },\n      { nominal: 119, mass: 118.909847 },\n      { nominal: 120, mass: 119.9098681 },\n      { nominal: 121, mass: 120.9129637 },\n      { nominal: 122, mass: 121.9134591 },\n      { nominal: 123, mass: 122.9168925 },\n      { nominal: 124, mass: 123.9176574 },\n      { nominal: 125, mass: 124.9212576 },\n      { nominal: 126, mass: 125.9224291 },\n      { nominal: 127, mass: 126.926472 },\n      { nominal: 128, mass: 127.9278129 },\n      { nominal: 129, mass: 128.93182 },\n      { nominal: 130, mass: 129.93394 },\n      { nominal: 131, mass: 130.9406 },\n      { nominal: 132, mass: 131.94604 },\n      { nominal: 133, mass: 132.95285 },\n    ],\n    symbol: 'Cd',\n    mass: 112.411557818268,\n    name: 'Cadmium',\n    monoisotopicMass: 113.90336509,\n  },\n  {\n    number: 49,\n    isotopes: [\n      { nominal: 97, mass: 96.94934 },\n      { nominal: 98, mass: 97.94214 },\n      { nominal: 99, mass: 98.93411 },\n      { nominal: 100, mass: 99.93096 },\n      { nominal: 101, mass: 100.92634 },\n      { nominal: 102, mass: 101.9241071 },\n      { nominal: 103, mass: 102.9198819 },\n      { nominal: 104, mass: 103.9182145 },\n      { nominal: 105, mass: 104.914502 },\n      { nominal: 106, mass: 105.913464 },\n      { nominal: 107, mass: 106.91029 },\n      { nominal: 108, mass: 107.9096935 },\n      { nominal: 109, mass: 108.9071514 },\n      { nominal: 110, mass: 109.90717 },\n      { nominal: 111, mass: 110.9051085 },\n      { nominal: 112, mass: 111.9055377 },\n      { nominal: 113, mass: 112.90406184, abundance: 0.0429 },\n      { nominal: 114, mass: 113.90491791 },\n      { nominal: 115, mass: 114.903878776, abundance: 0.9571 },\n      { nominal: 116, mass: 115.90525999 },\n      { nominal: 117, mass: 116.9045157 },\n      { nominal: 118, mass: 117.9063566 },\n      { nominal: 119, mass: 118.9058507 },\n      { nominal: 120, mass: 119.907967 },\n      { nominal: 121, mass: 120.907851 },\n      { nominal: 122, mass: 121.910281 },\n      { nominal: 123, mass: 122.910434 },\n      { nominal: 124, mass: 123.913182 },\n      { nominal: 125, mass: 124.913605 },\n      { nominal: 126, mass: 125.916507 },\n      { nominal: 127, mass: 126.917446 },\n      { nominal: 128, mass: 127.9204 },\n      { nominal: 129, mass: 128.9218053 },\n      { nominal: 130, mass: 129.924977 },\n      { nominal: 131, mass: 130.9269715 },\n      { nominal: 132, mass: 131.933001 },\n      { nominal: 133, mass: 132.93831 },\n      { nominal: 134, mass: 133.94454 },\n      { nominal: 135, mass: 134.95005 },\n    ],\n    symbol: 'In',\n    mass: 114.81808662944559,\n    name: 'Indium',\n    monoisotopicMass: 114.903878776,\n  },\n  {\n    number: 50,\n    isotopes: [\n      { nominal: 99, mass: 98.94853 },\n      { nominal: 100, mass: 99.9385 },\n      { nominal: 101, mass: 100.93526 },\n      { nominal: 102, mass: 101.93029 },\n      { nominal: 103, mass: 102.928105 },\n      { nominal: 104, mass: 103.9231052 },\n      { nominal: 105, mass: 104.9212684 },\n      { nominal: 106, mass: 105.9169574 },\n      { nominal: 107, mass: 106.9157137 },\n      { nominal: 108, mass: 107.9118943 },\n      { nominal: 109, mass: 108.9112921 },\n      { nominal: 110, mass: 109.907845 },\n      { nominal: 111, mass: 110.9077401 },\n      { nominal: 112, mass: 111.90482387, abundance: 0.0097 },\n      { nominal: 113, mass: 112.9051757 },\n      { nominal: 114, mass: 113.9027827, abundance: 0.0066 },\n      { nominal: 115, mass: 114.903344699, abundance: 0.0034 },\n      { nominal: 116, mass: 115.9017428, abundance: 0.1454 },\n      { nominal: 117, mass: 116.90295398, abundance: 0.0768 },\n      { nominal: 118, mass: 117.90160657, abundance: 0.2422 },\n      { nominal: 119, mass: 118.90331117, abundance: 0.0859 },\n      { nominal: 120, mass: 119.90220163, abundance: 0.3258 },\n      { nominal: 121, mass: 120.9042426 },\n      { nominal: 122, mass: 121.9034438, abundance: 0.0463 },\n      { nominal: 123, mass: 122.9057252 },\n      { nominal: 124, mass: 123.9052766, abundance: 0.0579 },\n      { nominal: 125, mass: 124.9077864 },\n      { nominal: 126, mass: 125.907659 },\n      { nominal: 127, mass: 126.91039 },\n      { nominal: 128, mass: 127.910507 },\n      { nominal: 129, mass: 128.913465 },\n      { nominal: 130, mass: 129.9139738 },\n      { nominal: 131, mass: 130.917045 },\n      { nominal: 132, mass: 131.9178267 },\n      { nominal: 133, mass: 132.9239134 },\n      { nominal: 134, mass: 133.9286821 },\n      { nominal: 135, mass: 134.9349086 },\n      { nominal: 136, mass: 135.93999 },\n      { nominal: 137, mass: 136.94655 },\n      { nominal: 138, mass: 137.95184 },\n    ],\n    symbol: 'Sn',\n    mass: 118.71011259301059,\n    name: 'Tin',\n    monoisotopicMass: 119.90220163,\n  },\n  {\n    number: 51,\n    isotopes: [\n      { nominal: 103, mass: 102.93969 },\n      { nominal: 104, mass: 103.93648 },\n      { nominal: 105, mass: 104.931276 },\n      { nominal: 106, mass: 105.928638 },\n      { nominal: 107, mass: 106.9241506 },\n      { nominal: 108, mass: 107.9222267 },\n      { nominal: 109, mass: 108.9181411 },\n      { nominal: 110, mass: 109.9168543 },\n      { nominal: 111, mass: 110.9132182 },\n      { nominal: 112, mass: 111.9124 },\n      { nominal: 113, mass: 112.909375 },\n      { nominal: 114, mass: 113.90929 },\n      { nominal: 115, mass: 114.906598 },\n      { nominal: 116, mass: 115.9067931 },\n      { nominal: 117, mass: 116.9048415 },\n      { nominal: 118, mass: 117.9055321 },\n      { nominal: 119, mass: 118.9039455 },\n      { nominal: 120, mass: 119.9050794 },\n      { nominal: 121, mass: 120.903812, abundance: 0.5721 },\n      { nominal: 122, mass: 121.9051699 },\n      { nominal: 123, mass: 122.9042132, abundance: 0.4279 },\n      { nominal: 124, mass: 123.905935 },\n      { nominal: 125, mass: 124.905253 },\n      { nominal: 126, mass: 125.907253 },\n      { nominal: 127, mass: 126.9069243 },\n      { nominal: 128, mass: 127.909146 },\n      { nominal: 129, mass: 128.909147 },\n      { nominal: 130, mass: 129.911662 },\n      { nominal: 131, mass: 130.9119888 },\n      { nominal: 132, mass: 131.9145077 },\n      { nominal: 133, mass: 132.9152732 },\n      { nominal: 134, mass: 133.9205357 },\n      { nominal: 135, mass: 134.9251851 },\n      { nominal: 136, mass: 135.9307459 },\n      { nominal: 137, mass: 136.93555 },\n      { nominal: 138, mass: 137.94145 },\n      { nominal: 139, mass: 138.94655 },\n      { nominal: 140, mass: 139.95283 },\n    ],\n    symbol: 'Sb',\n    mass: 121.75978367348,\n    name: 'Antimony',\n    monoisotopicMass: 120.903812,\n  },\n  {\n    number: 52,\n    isotopes: [\n      { nominal: 105, mass: 104.9433 },\n      { nominal: 106, mass: 105.9375 },\n      { nominal: 107, mass: 106.935012 },\n      { nominal: 108, mass: 107.9293805 },\n      { nominal: 109, mass: 108.9273045 },\n      { nominal: 110, mass: 109.9224581 },\n      { nominal: 111, mass: 110.9210006 },\n      { nominal: 112, mass: 111.9167279 },\n      { nominal: 113, mass: 112.915891 },\n      { nominal: 114, mass: 113.912089 },\n      { nominal: 115, mass: 114.911902 },\n      { nominal: 116, mass: 115.90846 },\n      { nominal: 117, mass: 116.908646 },\n      { nominal: 118, mass: 117.905854 },\n      { nominal: 119, mass: 118.9064071 },\n      { nominal: 120, mass: 119.9040593, abundance: 0.0009 },\n      { nominal: 121, mass: 120.904944 },\n      { nominal: 122, mass: 121.9030435, abundance: 0.0255 },\n      { nominal: 123, mass: 122.9042698, abundance: 0.0089 },\n      { nominal: 124, mass: 123.9028171, abundance: 0.0474 },\n      { nominal: 125, mass: 124.9044299, abundance: 0.0707 },\n      { nominal: 126, mass: 125.9033109, abundance: 0.1884 },\n      { nominal: 127, mass: 126.9052257 },\n      { nominal: 128, mass: 127.90446128, abundance: 0.3174 },\n      { nominal: 129, mass: 128.90659646 },\n      { nominal: 130, mass: 129.906222748, abundance: 0.3408 },\n      { nominal: 131, mass: 130.908522213 },\n      { nominal: 132, mass: 131.9085467 },\n      { nominal: 133, mass: 132.9109688 },\n      { nominal: 134, mass: 133.911394 },\n      { nominal: 135, mass: 134.9165557 },\n      { nominal: 136, mass: 135.9201006 },\n      { nominal: 137, mass: 136.9255989 },\n      { nominal: 138, mass: 137.9294722 },\n      { nominal: 139, mass: 138.9353672 },\n      { nominal: 140, mass: 139.939499 },\n      { nominal: 141, mass: 140.9458 },\n      { nominal: 142, mass: 141.95022 },\n      { nominal: 143, mass: 142.95676 },\n    ],\n    symbol: 'Te',\n    mass: 127.6031264846604,\n    name: 'Tellurium',\n    monoisotopicMass: 129.906222748,\n  },\n  {\n    number: 53,\n    isotopes: [\n      { nominal: 107, mass: 106.94678 },\n      { nominal: 108, mass: 107.94348 },\n      { nominal: 109, mass: 108.9380853 },\n      { nominal: 110, mass: 109.935089 },\n      { nominal: 111, mass: 110.9302692 },\n      { nominal: 112, mass: 111.928005 },\n      { nominal: 113, mass: 112.9236501 },\n      { nominal: 114, mass: 113.92185 },\n      { nominal: 115, mass: 114.918048 },\n      { nominal: 116, mass: 115.91681 },\n      { nominal: 117, mass: 116.913648 },\n      { nominal: 118, mass: 117.913074 },\n      { nominal: 119, mass: 118.910074 },\n      { nominal: 120, mass: 119.910087 },\n      { nominal: 121, mass: 120.9074051 },\n      { nominal: 122, mass: 121.9075888 },\n      { nominal: 123, mass: 122.9055885 },\n      { nominal: 124, mass: 123.906209 },\n      { nominal: 125, mass: 124.9046294 },\n      { nominal: 126, mass: 125.9056233 },\n      { nominal: 127, mass: 126.9044719, abundance: 1 },\n      { nominal: 128, mass: 127.9058086 },\n      { nominal: 129, mass: 128.9049837 },\n      { nominal: 130, mass: 129.9066702 },\n      { nominal: 131, mass: 130.9061263 },\n      { nominal: 132, mass: 131.9079935 },\n      { nominal: 133, mass: 132.907797 },\n      { nominal: 134, mass: 133.9097588 },\n      { nominal: 135, mass: 134.9100488 },\n      { nominal: 136, mass: 135.914604 },\n      { nominal: 137, mass: 136.9180282 },\n      { nominal: 138, mass: 137.9227264 },\n      { nominal: 139, mass: 138.926506 },\n      { nominal: 140, mass: 139.93173 },\n      { nominal: 141, mass: 140.93569 },\n      { nominal: 142, mass: 141.9412 },\n      { nominal: 143, mass: 142.94565 },\n      { nominal: 144, mass: 143.95139 },\n      { nominal: 145, mass: 144.95605 },\n    ],\n    symbol: 'I',\n    mass: 126.9044719,\n    name: 'Iodine',\n    monoisotopicMass: 126.9044719,\n  },\n  {\n    number: 54,\n    isotopes: [\n      { nominal: 109, mass: 108.95043 },\n      { nominal: 110, mass: 109.94426 },\n      { nominal: 111, mass: 110.941607 },\n      { nominal: 112, mass: 111.935559 },\n      { nominal: 113, mass: 112.9332217 },\n      { nominal: 114, mass: 113.92798 },\n      { nominal: 115, mass: 114.926294 },\n      { nominal: 116, mass: 115.921581 },\n      { nominal: 117, mass: 116.920359 },\n      { nominal: 118, mass: 117.916179 },\n      { nominal: 119, mass: 118.915411 },\n      { nominal: 120, mass: 119.911784 },\n      { nominal: 121, mass: 120.911453 },\n      { nominal: 122, mass: 121.908368 },\n      { nominal: 123, mass: 122.908482 },\n      { nominal: 124, mass: 123.905892, abundance: 0.000952 },\n      { nominal: 125, mass: 124.9063944 },\n      { nominal: 126, mass: 125.9042983, abundance: 0.00089 },\n      { nominal: 127, mass: 126.9051829 },\n      { nominal: 128, mass: 127.903531, abundance: 0.019102 },\n      { nominal: 129, mass: 128.9047808611, abundance: 0.264006 },\n      { nominal: 130, mass: 129.903509349, abundance: 0.04071 },\n      { nominal: 131, mass: 130.90508406, abundance: 0.212324 },\n      { nominal: 132, mass: 131.9041550856, abundance: 0.269086 },\n      { nominal: 133, mass: 132.9059108 },\n      { nominal: 134, mass: 133.90539466, abundance: 0.104357 },\n      { nominal: 135, mass: 134.9072278 },\n      { nominal: 136, mass: 135.907214484, abundance: 0.088573 },\n      { nominal: 137, mass: 136.91155778 },\n      { nominal: 138, mass: 137.9141463 },\n      { nominal: 139, mass: 138.9187922 },\n      { nominal: 140, mass: 139.9216458 },\n      { nominal: 141, mass: 140.9267872 },\n      { nominal: 142, mass: 141.9299731 },\n      { nominal: 143, mass: 142.9353696 },\n      { nominal: 144, mass: 143.9389451 },\n      { nominal: 145, mass: 144.94472 },\n      { nominal: 146, mass: 145.948518 },\n      { nominal: 147, mass: 146.95426 },\n      { nominal: 148, mass: 147.95813 },\n    ],\n    symbol: 'Xe',\n    mass: 131.29276144779053,\n    name: 'Xenon',\n    monoisotopicMass: 131.9041550856,\n  },\n  {\n    number: 55,\n    isotopes: [\n      { nominal: 112, mass: 111.950309 },\n      { nominal: 113, mass: 112.9444291 },\n      { nominal: 114, mass: 113.941296 },\n      { nominal: 115, mass: 114.93591 },\n      { nominal: 116, mass: 115.93337 },\n      { nominal: 117, mass: 116.928617 },\n      { nominal: 118, mass: 117.92656 },\n      { nominal: 119, mass: 118.922377 },\n      { nominal: 120, mass: 119.920677 },\n      { nominal: 121, mass: 120.917227 },\n      { nominal: 122, mass: 121.916108 },\n      { nominal: 123, mass: 122.912996 },\n      { nominal: 124, mass: 123.9122578 },\n      { nominal: 125, mass: 124.909728 },\n      { nominal: 126, mass: 125.909446 },\n      { nominal: 127, mass: 126.9074174 },\n      { nominal: 128, mass: 127.9077487 },\n      { nominal: 129, mass: 128.9060657 },\n      { nominal: 130, mass: 129.9067093 },\n      { nominal: 131, mass: 130.9054649 },\n      { nominal: 132, mass: 131.9064339 },\n      { nominal: 133, mass: 132.905451961, abundance: 1 },\n      { nominal: 134, mass: 133.906718503 },\n      { nominal: 135, mass: 134.905977 },\n      { nominal: 136, mass: 135.9073114 },\n      { nominal: 137, mass: 136.90708923 },\n      { nominal: 138, mass: 137.9110171 },\n      { nominal: 139, mass: 138.9133638 },\n      { nominal: 140, mass: 139.9172831 },\n      { nominal: 141, mass: 140.9200455 },\n      { nominal: 142, mass: 141.924296 },\n      { nominal: 143, mass: 142.927349 },\n      { nominal: 144, mass: 143.932076 },\n      { nominal: 145, mass: 144.935527 },\n      { nominal: 146, mass: 145.940344 },\n      { nominal: 147, mass: 146.944156 },\n      { nominal: 148, mass: 147.94923 },\n      { nominal: 149, mass: 148.95302 },\n      { nominal: 150, mass: 149.95833 },\n      { nominal: 151, mass: 150.96258 },\n    ],\n    symbol: 'Cs',\n    mass: 132.905451961,\n    name: 'Caesium',\n    monoisotopicMass: 132.905451961,\n  },\n  {\n    number: 56,\n    isotopes: [\n      { nominal: 114, mass: 113.95066 },\n      { nominal: 115, mass: 114.94737 },\n      { nominal: 116, mass: 115.94128 },\n      { nominal: 117, mass: 116.93814 },\n      { nominal: 118, mass: 117.93306 },\n      { nominal: 119, mass: 118.93066 },\n      { nominal: 120, mass: 119.92605 },\n      { nominal: 121, mass: 120.92405 },\n      { nominal: 122, mass: 121.919904 },\n      { nominal: 123, mass: 122.918781 },\n      { nominal: 124, mass: 123.915094 },\n      { nominal: 125, mass: 124.914472 },\n      { nominal: 126, mass: 125.91125 },\n      { nominal: 127, mass: 126.911091 },\n      { nominal: 128, mass: 127.908342 },\n      { nominal: 129, mass: 128.908681 },\n      { nominal: 130, mass: 129.9063207, abundance: 0.00106 },\n      { nominal: 131, mass: 130.906941 },\n      { nominal: 132, mass: 131.9050611, abundance: 0.00101 },\n      { nominal: 133, mass: 132.9060074 },\n      { nominal: 134, mass: 133.90450818, abundance: 0.02417 },\n      { nominal: 135, mass: 134.90568838, abundance: 0.06592 },\n      { nominal: 136, mass: 135.90457573, abundance: 0.07854 },\n      { nominal: 137, mass: 136.90582714, abundance: 0.11232 },\n      { nominal: 138, mass: 137.905247, abundance: 0.71698 },\n      { nominal: 139, mass: 138.9088411 },\n      { nominal: 140, mass: 139.9106057 },\n      { nominal: 141, mass: 140.9144033 },\n      { nominal: 142, mass: 141.9164324 },\n      { nominal: 143, mass: 142.9206253 },\n      { nominal: 144, mass: 143.9229549 },\n      { nominal: 145, mass: 144.9275184 },\n      { nominal: 146, mass: 145.930284 },\n      { nominal: 147, mass: 146.935304 },\n      { nominal: 148, mass: 147.938171 },\n      { nominal: 149, mass: 148.94308 },\n      { nominal: 150, mass: 149.94605 },\n      { nominal: 151, mass: 150.95127 },\n      { nominal: 152, mass: 151.95481 },\n      { nominal: 153, mass: 152.96036 },\n    ],\n    symbol: 'Ba',\n    mass: 137.3268916286322,\n    name: 'Barium',\n    monoisotopicMass: 137.905247,\n  },\n  {\n    number: 57,\n    isotopes: [\n      { nominal: 116, mass: 115.9563 },\n      { nominal: 117, mass: 116.94999 },\n      { nominal: 118, mass: 117.94673 },\n      { nominal: 119, mass: 118.94099 },\n      { nominal: 120, mass: 119.93807 },\n      { nominal: 121, mass: 120.93315 },\n      { nominal: 122, mass: 121.93071 },\n      { nominal: 123, mass: 122.9263 },\n      { nominal: 124, mass: 123.924574 },\n      { nominal: 125, mass: 124.920816 },\n      { nominal: 126, mass: 125.919513 },\n      { nominal: 127, mass: 126.916375 },\n      { nominal: 128, mass: 127.915592 },\n      { nominal: 129, mass: 128.912694 },\n      { nominal: 130, mass: 129.912369 },\n      { nominal: 131, mass: 130.91007 },\n      { nominal: 132, mass: 131.910119 },\n      { nominal: 133, mass: 132.908218 },\n      { nominal: 134, mass: 133.908514 },\n      { nominal: 135, mass: 134.906984 },\n      { nominal: 136, mass: 135.907635 },\n      { nominal: 137, mass: 136.9064504 },\n      { nominal: 138, mass: 137.9071149, abundance: 0.0008881 },\n      { nominal: 139, mass: 138.9063563, abundance: 0.9991119 },\n      { nominal: 140, mass: 139.9094806 },\n      { nominal: 141, mass: 140.910966 },\n      { nominal: 142, mass: 141.9140909 },\n      { nominal: 143, mass: 142.9160795 },\n      { nominal: 144, mass: 143.919646 },\n      { nominal: 145, mass: 144.921808 },\n      { nominal: 146, mass: 145.925875 },\n      { nominal: 147, mass: 146.928418 },\n      { nominal: 148, mass: 147.932679 },\n      { nominal: 149, mass: 148.93535 },\n      { nominal: 150, mass: 149.93947 },\n      { nominal: 151, mass: 150.94232 },\n      { nominal: 152, mass: 151.94682 },\n      { nominal: 153, mass: 152.95036 },\n      { nominal: 154, mass: 153.95517 },\n      { nominal: 155, mass: 154.95901 },\n    ],\n    symbol: 'La',\n    mass: 138.90546887371266,\n    name: 'Lanthanum',\n    monoisotopicMass: 138.9063563,\n  },\n  {\n    number: 58,\n    isotopes: [\n      { nominal: 119, mass: 118.95271 },\n      { nominal: 120, mass: 119.94654 },\n      { nominal: 121, mass: 120.94335 },\n      { nominal: 122, mass: 121.93787 },\n      { nominal: 123, mass: 122.93528 },\n      { nominal: 124, mass: 123.93031 },\n      { nominal: 125, mass: 124.92844 },\n      { nominal: 126, mass: 125.923971 },\n      { nominal: 127, mass: 126.922727 },\n      { nominal: 128, mass: 127.918911 },\n      { nominal: 129, mass: 128.918102 },\n      { nominal: 130, mass: 129.914736 },\n      { nominal: 131, mass: 130.914429 },\n      { nominal: 132, mass: 131.911464 },\n      { nominal: 133, mass: 132.91152 },\n      { nominal: 134, mass: 133.908928 },\n      { nominal: 135, mass: 134.909161 },\n      { nominal: 136, mass: 135.90712921, abundance: 0.00185 },\n      { nominal: 137, mass: 136.90776236 },\n      { nominal: 138, mass: 137.905991, abundance: 0.00251 },\n      { nominal: 139, mass: 138.9066551 },\n      { nominal: 140, mass: 139.9054431, abundance: 0.8845 },\n      { nominal: 141, mass: 140.9082807 },\n      { nominal: 142, mass: 141.9092504, abundance: 0.11114 },\n      { nominal: 143, mass: 142.9123921 },\n      { nominal: 144, mass: 143.9136529 },\n      { nominal: 145, mass: 144.917265 },\n      { nominal: 146, mass: 145.918802 },\n      { nominal: 147, mass: 146.9226899 },\n      { nominal: 148, mass: 147.924424 },\n      { nominal: 149, mass: 148.928427 },\n      { nominal: 150, mass: 149.930384 },\n      { nominal: 151, mass: 150.934272 },\n      { nominal: 152, mass: 151.9366 },\n      { nominal: 153, mass: 152.94093 },\n      { nominal: 154, mass: 153.9438 },\n      { nominal: 155, mass: 154.94855 },\n      { nominal: 156, mass: 155.95183 },\n      { nominal: 157, mass: 156.95705 },\n    ],\n    symbol: 'Ce',\n    mass: 140.1157307378545,\n    name: 'Cerium',\n    monoisotopicMass: 139.9054431,\n  },\n  {\n    number: 59,\n    isotopes: [\n      { nominal: 121, mass: 120.95532 },\n      { nominal: 122, mass: 121.95175 },\n      { nominal: 123, mass: 122.94596 },\n      { nominal: 124, mass: 123.94294 },\n      { nominal: 125, mass: 124.9377 },\n      { nominal: 126, mass: 125.93524 },\n      { nominal: 127, mass: 126.93071 },\n      { nominal: 128, mass: 127.928791 },\n      { nominal: 129, mass: 128.925095 },\n      { nominal: 130, mass: 129.92359 },\n      { nominal: 131, mass: 130.920235 },\n      { nominal: 132, mass: 131.919255 },\n      { nominal: 133, mass: 132.916331 },\n      { nominal: 134, mass: 133.915697 },\n      { nominal: 135, mass: 134.913112 },\n      { nominal: 136, mass: 135.912677 },\n      { nominal: 137, mass: 136.9106792 },\n      { nominal: 138, mass: 137.910754 },\n      { nominal: 139, mass: 138.9089408 },\n      { nominal: 140, mass: 139.9090803 },\n      { nominal: 141, mass: 140.9076576, abundance: 1 },\n      { nominal: 142, mass: 141.9100496 },\n      { nominal: 143, mass: 142.9108228 },\n      { nominal: 144, mass: 143.9133109 },\n      { nominal: 145, mass: 144.9145182 },\n      { nominal: 146, mass: 145.91768 },\n      { nominal: 147, mass: 146.919008 },\n      { nominal: 148, mass: 147.92213 },\n      { nominal: 149, mass: 148.923736 },\n      { nominal: 150, mass: 149.9266765 },\n      { nominal: 151, mass: 150.928309 },\n      { nominal: 152, mass: 151.931553 },\n      { nominal: 153, mass: 152.933904 },\n      { nominal: 154, mass: 153.93753 },\n      { nominal: 155, mass: 154.940509 },\n      { nominal: 156, mass: 155.94464 },\n      { nominal: 157, mass: 156.94789 },\n      { nominal: 158, mass: 157.95241 },\n      { nominal: 159, mass: 158.95589 },\n    ],\n    symbol: 'Pr',\n    mass: 140.9076576,\n    name: 'Praseodymium',\n    monoisotopicMass: 140.9076576,\n  },\n  {\n    number: 60,\n    isotopes: [\n      { nominal: 124, mass: 123.9522 },\n      { nominal: 125, mass: 124.9489 },\n      { nominal: 126, mass: 125.94311 },\n      { nominal: 127, mass: 126.94038 },\n      { nominal: 128, mass: 127.93525 },\n      { nominal: 129, mass: 128.9331 },\n      { nominal: 130, mass: 129.928506 },\n      { nominal: 131, mass: 130.927248 },\n      { nominal: 132, mass: 131.923321 },\n      { nominal: 133, mass: 132.922348 },\n      { nominal: 134, mass: 133.91879 },\n      { nominal: 135, mass: 134.918181 },\n      { nominal: 136, mass: 135.914976 },\n      { nominal: 137, mass: 136.914562 },\n      { nominal: 138, mass: 137.91195 },\n      { nominal: 139, mass: 138.911954 },\n      { nominal: 140, mass: 139.90955 },\n      { nominal: 141, mass: 140.9096147 },\n      { nominal: 142, mass: 141.907729, abundance: 0.27152 },\n      { nominal: 143, mass: 142.90982, abundance: 0.12174 },\n      { nominal: 144, mass: 143.910093, abundance: 0.23798 },\n      { nominal: 145, mass: 144.9125793, abundance: 0.08293 },\n      { nominal: 146, mass: 145.9131226, abundance: 0.17189 },\n      { nominal: 147, mass: 146.9161061 },\n      { nominal: 148, mass: 147.9168993, abundance: 0.05756 },\n      { nominal: 149, mass: 148.9201548 },\n      { nominal: 150, mass: 149.9209022, abundance: 0.05638 },\n      { nominal: 151, mass: 150.9238403 },\n      { nominal: 152, mass: 151.924692 },\n      { nominal: 153, mass: 152.927718 },\n      { nominal: 154, mass: 153.92948 },\n      { nominal: 155, mass: 154.9331357 },\n      { nominal: 156, mass: 155.93508 },\n      { nominal: 157, mass: 156.939386 },\n      { nominal: 158, mass: 157.94197 },\n      { nominal: 159, mass: 158.94653 },\n      { nominal: 160, mass: 159.9494 },\n      { nominal: 161, mass: 160.95428 },\n    ],\n    symbol: 'Nd',\n    mass: 144.241596031827,\n    name: 'Neodymium',\n    monoisotopicMass: 141.907729,\n  },\n  {\n    number: 61,\n    isotopes: [\n      { nominal: 126, mass: 125.95792 },\n      { nominal: 127, mass: 126.95192 },\n      { nominal: 128, mass: 127.9487 },\n      { nominal: 129, mass: 128.94323 },\n      { nominal: 130, mass: 129.94053 },\n      { nominal: 131, mass: 130.93567 },\n      { nominal: 132, mass: 131.93384 },\n      { nominal: 133, mass: 132.929782 },\n      { nominal: 134, mass: 133.928353 },\n      { nominal: 135, mass: 134.924823 },\n      { nominal: 136, mass: 135.923585 },\n      { nominal: 137, mass: 136.92048 },\n      { nominal: 138, mass: 137.919548 },\n      { nominal: 139, mass: 138.9168 },\n      { nominal: 140, mass: 139.91604 },\n      { nominal: 141, mass: 140.913555 },\n      { nominal: 142, mass: 141.91289 },\n      { nominal: 143, mass: 142.9109383 },\n      { nominal: 144, mass: 143.9125964 },\n      { nominal: 145, mass: 144.9127559 },\n      { nominal: 146, mass: 145.9147024 },\n      { nominal: 147, mass: 146.915145 },\n      { nominal: 148, mass: 147.9174819 },\n      { nominal: 149, mass: 148.9183423 },\n      { nominal: 150, mass: 149.920991 },\n      { nominal: 151, mass: 150.9212175 },\n      { nominal: 152, mass: 151.923506 },\n      { nominal: 153, mass: 152.9241567 },\n      { nominal: 154, mass: 153.926472 },\n      { nominal: 155, mass: 154.928137 },\n      { nominal: 156, mass: 155.9311175 },\n      { nominal: 157, mass: 156.9331214 },\n      { nominal: 158, mass: 157.936565 },\n      { nominal: 159, mass: 158.939287 },\n      { nominal: 160, mass: 159.9431 },\n      { nominal: 161, mass: 160.94607 },\n      { nominal: 162, mass: 161.95022 },\n      { nominal: 163, mass: 162.95357 },\n    ],\n    symbol: 'Pm',\n    mass: null,\n    name: 'Promethium',\n  },\n  {\n    number: 62,\n    isotopes: [\n      { nominal: 128, mass: 127.95842 },\n      { nominal: 129, mass: 128.95476 },\n      { nominal: 130, mass: 129.949 },\n      { nominal: 131, mass: 130.94618 },\n      { nominal: 132, mass: 131.94087 },\n      { nominal: 133, mass: 132.93856 },\n      { nominal: 134, mass: 133.93411 },\n      { nominal: 135, mass: 134.93252 },\n      { nominal: 136, mass: 135.928276 },\n      { nominal: 137, mass: 136.926971 },\n      { nominal: 138, mass: 137.923244 },\n      { nominal: 139, mass: 138.922297 },\n      { nominal: 140, mass: 139.918995 },\n      { nominal: 141, mass: 140.9184816 },\n      { nominal: 142, mass: 141.9152044 },\n      { nominal: 143, mass: 142.9146353 },\n      { nominal: 144, mass: 143.9120065, abundance: 0.0307 },\n      { nominal: 145, mass: 144.9134173 },\n      { nominal: 146, mass: 145.913047 },\n      { nominal: 147, mass: 146.9149044, abundance: 0.1499 },\n      { nominal: 148, mass: 147.9148292, abundance: 0.1124 },\n      { nominal: 149, mass: 148.9171921, abundance: 0.1382 },\n      { nominal: 150, mass: 149.9172829, abundance: 0.0738 },\n      { nominal: 151, mass: 150.9199398 },\n      { nominal: 152, mass: 151.9197397, abundance: 0.2675 },\n      { nominal: 153, mass: 152.9221047 },\n      { nominal: 154, mass: 153.9222169, abundance: 0.2275 },\n      { nominal: 155, mass: 154.9246477 },\n      { nominal: 156, mass: 155.925536 },\n      { nominal: 157, mass: 156.9284187 },\n      { nominal: 158, mass: 157.929951 },\n      { nominal: 159, mass: 158.9332172 },\n      { nominal: 160, mass: 159.9353353 },\n      { nominal: 161, mass: 160.9391602 },\n      { nominal: 162, mass: 161.94146 },\n      { nominal: 163, mass: 162.94555 },\n      { nominal: 164, mass: 163.94836 },\n      { nominal: 165, mass: 164.95297 },\n    ],\n    symbol: 'Sm',\n    mass: 150.36635571193,\n    name: 'Samarium',\n    monoisotopicMass: 151.9197397,\n  },\n  {\n    number: 63,\n    isotopes: [\n      { nominal: 130, mass: 129.96369 },\n      { nominal: 131, mass: 130.95784 },\n      { nominal: 132, mass: 131.95467 },\n      { nominal: 133, mass: 132.94929 },\n      { nominal: 134, mass: 133.9464 },\n      { nominal: 135, mass: 134.94187 },\n      { nominal: 136, mass: 135.93962 },\n      { nominal: 137, mass: 136.93546 },\n      { nominal: 138, mass: 137.933709 },\n      { nominal: 139, mass: 138.929792 },\n      { nominal: 140, mass: 139.928088 },\n      { nominal: 141, mass: 140.924932 },\n      { nominal: 142, mass: 141.923442 },\n      { nominal: 143, mass: 142.920299 },\n      { nominal: 144, mass: 143.91882 },\n      { nominal: 145, mass: 144.9162726 },\n      { nominal: 146, mass: 145.917211 },\n      { nominal: 147, mass: 146.9167527 },\n      { nominal: 148, mass: 147.918089 },\n      { nominal: 149, mass: 148.9179378 },\n      { nominal: 150, mass: 149.9197077 },\n      { nominal: 151, mass: 150.9198578, abundance: 0.4781 },\n      { nominal: 152, mass: 151.9217522 },\n      { nominal: 153, mass: 152.921238, abundance: 0.5219 },\n      { nominal: 154, mass: 153.922987 },\n      { nominal: 155, mass: 154.9229011 },\n      { nominal: 156, mass: 155.9247605 },\n      { nominal: 157, mass: 156.9254334 },\n      { nominal: 158, mass: 157.927799 },\n      { nominal: 159, mass: 158.9291001 },\n      { nominal: 160, mass: 159.931851 },\n      { nominal: 161, mass: 160.933664 },\n      { nominal: 162, mass: 161.936989 },\n      { nominal: 163, mass: 162.939196 },\n      { nominal: 164, mass: 163.94274 },\n      { nominal: 165, mass: 164.94559 },\n      { nominal: 166, mass: 165.94962 },\n      { nominal: 167, mass: 166.95289 },\n    ],\n    symbol: 'Eu',\n    mass: 151.96437812637998,\n    name: 'Europium',\n    monoisotopicMass: 152.921238,\n  },\n  {\n    number: 64,\n    isotopes: [\n      { nominal: 133, mass: 132.96133 },\n      { nominal: 134, mass: 133.95566 },\n      { nominal: 135, mass: 134.95245 },\n      { nominal: 136, mass: 135.9473 },\n      { nominal: 137, mass: 136.94502 },\n      { nominal: 138, mass: 137.94025 },\n      { nominal: 139, mass: 138.93813 },\n      { nominal: 140, mass: 139.933674 },\n      { nominal: 141, mass: 140.932126 },\n      { nominal: 142, mass: 141.928116 },\n      { nominal: 143, mass: 142.92675 },\n      { nominal: 144, mass: 143.922963 },\n      { nominal: 145, mass: 144.921713 },\n      { nominal: 146, mass: 145.9183188 },\n      { nominal: 147, mass: 146.9191014 },\n      { nominal: 148, mass: 147.9181215 },\n      { nominal: 149, mass: 148.9193481 },\n      { nominal: 150, mass: 149.9186644 },\n      { nominal: 151, mass: 150.920356 },\n      { nominal: 152, mass: 151.9197995, abundance: 0.002 },\n      { nominal: 153, mass: 152.921758 },\n      { nominal: 154, mass: 153.9208741, abundance: 0.0218 },\n      { nominal: 155, mass: 154.9226305, abundance: 0.148 },\n      { nominal: 156, mass: 155.9221312, abundance: 0.2047 },\n      { nominal: 157, mass: 156.9239686, abundance: 0.1565 },\n      { nominal: 158, mass: 157.9241123, abundance: 0.2484 },\n      { nominal: 159, mass: 158.926397 },\n      { nominal: 160, mass: 159.9270624, abundance: 0.2186 },\n      { nominal: 161, mass: 160.9296775 },\n      { nominal: 162, mass: 161.930993 },\n      { nominal: 163, mass: 162.9341769 },\n      { nominal: 164, mass: 163.93583 },\n      { nominal: 165, mass: 164.93936 },\n      { nominal: 166, mass: 165.94146 },\n      { nominal: 167, mass: 166.94545 },\n      { nominal: 168, mass: 167.94808 },\n      { nominal: 169, mass: 168.9526 },\n    ],\n    symbol: 'Gd',\n    mass: 157.25213064687998,\n    name: 'Gadolinium',\n    monoisotopicMass: 157.9241123,\n  },\n  {\n    number: 65,\n    isotopes: [\n      { nominal: 135, mass: 134.96476 },\n      { nominal: 136, mass: 135.96129 },\n      { nominal: 137, mass: 136.95602 },\n      { nominal: 138, mass: 137.95312 },\n      { nominal: 139, mass: 138.94833 },\n      { nominal: 140, mass: 139.94581 },\n      { nominal: 141, mass: 140.94145 },\n      { nominal: 142, mass: 141.93928 },\n      { nominal: 143, mass: 142.935137 },\n      { nominal: 144, mass: 143.933045 },\n      { nominal: 145, mass: 144.92882 },\n      { nominal: 146, mass: 145.927253 },\n      { nominal: 147, mass: 146.9240548 },\n      { nominal: 148, mass: 147.924282 },\n      { nominal: 149, mass: 148.9232535 },\n      { nominal: 150, mass: 149.9236649 },\n      { nominal: 151, mass: 150.9231096 },\n      { nominal: 152, mass: 151.924083 },\n      { nominal: 153, mass: 152.9234424 },\n      { nominal: 154, mass: 153.924685 },\n      { nominal: 155, mass: 154.923511 },\n      { nominal: 156, mass: 155.9247552 },\n      { nominal: 157, mass: 156.924033 },\n      { nominal: 158, mass: 157.9254209 },\n      { nominal: 159, mass: 158.9253547, abundance: 1 },\n      { nominal: 160, mass: 159.9271756 },\n      { nominal: 161, mass: 160.9275778 },\n      { nominal: 162, mass: 161.929495 },\n      { nominal: 163, mass: 162.9306547 },\n      { nominal: 164, mass: 163.93336 },\n      { nominal: 165, mass: 164.93498 },\n      { nominal: 166, mass: 165.93786 },\n      { nominal: 167, mass: 166.93996 },\n      { nominal: 168, mass: 167.9434 },\n      { nominal: 169, mass: 168.94597 },\n      { nominal: 170, mass: 169.94984 },\n      { nominal: 171, mass: 170.95273 },\n    ],\n    symbol: 'Tb',\n    mass: 158.9253547,\n    name: 'Terbium',\n    monoisotopicMass: 158.9253547,\n  },\n  {\n    number: 66,\n    isotopes: [\n      { nominal: 138, mass: 137.9625 },\n      { nominal: 139, mass: 138.95959 },\n      { nominal: 140, mass: 139.95402 },\n      { nominal: 141, mass: 140.95128 },\n      { nominal: 142, mass: 141.94619 },\n      { nominal: 143, mass: 142.943994 },\n      { nominal: 144, mass: 143.9392695 },\n      { nominal: 145, mass: 144.937474 },\n      { nominal: 146, mass: 145.9328445 },\n      { nominal: 147, mass: 146.9310827 },\n      { nominal: 148, mass: 147.927157 },\n      { nominal: 149, mass: 148.927322 },\n      { nominal: 150, mass: 149.9255933 },\n      { nominal: 151, mass: 150.9261916 },\n      { nominal: 152, mass: 151.9247253 },\n      { nominal: 153, mass: 152.9257724 },\n      { nominal: 154, mass: 153.9244293 },\n      { nominal: 155, mass: 154.925759 },\n      { nominal: 156, mass: 155.9242847, abundance: 0.00056 },\n      { nominal: 157, mass: 156.9254707 },\n      { nominal: 158, mass: 157.9244159, abundance: 0.00095 },\n      { nominal: 159, mass: 158.925747 },\n      { nominal: 160, mass: 159.9252046, abundance: 0.02329 },\n      { nominal: 161, mass: 160.9269405, abundance: 0.18889 },\n      { nominal: 162, mass: 161.9268056, abundance: 0.25475 },\n      { nominal: 163, mass: 162.9287383, abundance: 0.24896 },\n      { nominal: 164, mass: 163.9291819, abundance: 0.2826 },\n      { nominal: 165, mass: 164.9317105 },\n      { nominal: 166, mass: 165.9328139 },\n      { nominal: 167, mass: 166.935661 },\n      { nominal: 168, mass: 167.93713 },\n      { nominal: 169, mass: 168.94031 },\n      { nominal: 170, mass: 169.94239 },\n      { nominal: 171, mass: 170.94612 },\n      { nominal: 172, mass: 171.94846 },\n      { nominal: 173, mass: 172.95283 },\n    ],\n    symbol: 'Dy',\n    mass: 162.499472819424,\n    name: 'Dysprosium',\n    monoisotopicMass: 163.9291819,\n  },\n  {\n    number: 67,\n    isotopes: [\n      { nominal: 140, mass: 139.96859 },\n      { nominal: 141, mass: 140.96311 },\n      { nominal: 142, mass: 141.96001 },\n      { nominal: 143, mass: 142.95486 },\n      { nominal: 144, mass: 143.9521097 },\n      { nominal: 145, mass: 144.9472674 },\n      { nominal: 146, mass: 145.9449935 },\n      { nominal: 147, mass: 146.9401423 },\n      { nominal: 148, mass: 147.937744 },\n      { nominal: 149, mass: 148.933803 },\n      { nominal: 150, mass: 149.933498 },\n      { nominal: 151, mass: 150.9316983 },\n      { nominal: 152, mass: 151.931724 },\n      { nominal: 153, mass: 152.9302064 },\n      { nominal: 154, mass: 153.9306068 },\n      { nominal: 155, mass: 154.929104 },\n      { nominal: 156, mass: 155.929706 },\n      { nominal: 157, mass: 156.928254 },\n      { nominal: 158, mass: 157.928946 },\n      { nominal: 159, mass: 158.9277197 },\n      { nominal: 160, mass: 159.928737 },\n      { nominal: 161, mass: 160.9278615 },\n      { nominal: 162, mass: 161.9291023 },\n      { nominal: 163, mass: 162.928741 },\n      { nominal: 164, mass: 163.9302403 },\n      { nominal: 165, mass: 164.9303288, abundance: 1 },\n      { nominal: 166, mass: 165.9322909 },\n      { nominal: 167, mass: 166.9331385 },\n      { nominal: 168, mass: 167.935522 },\n      { nominal: 169, mass: 168.936878 },\n      { nominal: 170, mass: 169.939625 },\n      { nominal: 171, mass: 170.94147 },\n      { nominal: 172, mass: 171.94473 },\n      { nominal: 173, mass: 172.94702 },\n      { nominal: 174, mass: 173.95095 },\n      { nominal: 175, mass: 174.95362 },\n    ],\n    symbol: 'Ho',\n    mass: 164.9303288,\n    name: 'Holmium',\n    monoisotopicMass: 164.9303288,\n  },\n  {\n    number: 68,\n    isotopes: [\n      { nominal: 142, mass: 141.9701 },\n      { nominal: 143, mass: 142.96662 },\n      { nominal: 144, mass: 143.9607 },\n      { nominal: 145, mass: 144.95805 },\n      { nominal: 146, mass: 145.9524184 },\n      { nominal: 147, mass: 146.949964 },\n      { nominal: 148, mass: 147.944735 },\n      { nominal: 149, mass: 148.942306 },\n      { nominal: 150, mass: 149.937916 },\n      { nominal: 151, mass: 150.937449 },\n      { nominal: 152, mass: 151.935057 },\n      { nominal: 153, mass: 152.93508 },\n      { nominal: 154, mass: 153.9327908 },\n      { nominal: 155, mass: 154.9332159 },\n      { nominal: 156, mass: 155.931067 },\n      { nominal: 157, mass: 156.931949 },\n      { nominal: 158, mass: 157.929893 },\n      { nominal: 159, mass: 158.9306918 },\n      { nominal: 160, mass: 159.929077 },\n      { nominal: 161, mass: 160.9300046 },\n      { nominal: 162, mass: 161.9287884, abundance: 0.00139 },\n      { nominal: 163, mass: 162.9300408 },\n      { nominal: 164, mass: 163.9292088, abundance: 0.01601 },\n      { nominal: 165, mass: 164.9307345 },\n      { nominal: 166, mass: 165.9302995, abundance: 0.33503 },\n      { nominal: 167, mass: 166.9320546, abundance: 0.22869 },\n      { nominal: 168, mass: 167.9323767, abundance: 0.26978 },\n      { nominal: 169, mass: 168.9345968 },\n      { nominal: 170, mass: 169.9354702, abundance: 0.1491 },\n      { nominal: 171, mass: 170.9380357 },\n      { nominal: 172, mass: 171.9393619 },\n      { nominal: 173, mass: 172.9424 },\n      { nominal: 174, mass: 173.94423 },\n      { nominal: 175, mass: 174.94777 },\n      { nominal: 176, mass: 175.94994 },\n      { nominal: 177, mass: 176.95399 },\n    ],\n    symbol: 'Er',\n    mass: 167.259082649669,\n    name: 'Erbium',\n    monoisotopicMass: 165.9302995,\n  },\n  {\n    number: 69,\n    isotopes: [\n      { nominal: 144, mass: 143.97628 },\n      { nominal: 145, mass: 144.97039 },\n      { nominal: 146, mass: 145.96684 },\n      { nominal: 147, mass: 146.9613799 },\n      { nominal: 148, mass: 147.958384 },\n      { nominal: 149, mass: 148.95289 },\n      { nominal: 150, mass: 149.95009 },\n      { nominal: 151, mass: 150.945488 },\n      { nominal: 152, mass: 151.944422 },\n      { nominal: 153, mass: 152.94204 },\n      { nominal: 154, mass: 153.94157 },\n      { nominal: 155, mass: 154.93921 },\n      { nominal: 156, mass: 155.938992 },\n      { nominal: 157, mass: 156.936944 },\n      { nominal: 158, mass: 157.93698 },\n      { nominal: 159, mass: 158.934975 },\n      { nominal: 160, mass: 159.935263 },\n      { nominal: 161, mass: 160.933549 },\n      { nominal: 162, mass: 161.934002 },\n      { nominal: 163, mass: 162.9326592 },\n      { nominal: 164, mass: 163.933544 },\n      { nominal: 165, mass: 164.9324431 },\n      { nominal: 166, mass: 165.933561 },\n      { nominal: 167, mass: 166.9328562 },\n      { nominal: 168, mass: 167.9341774 },\n      { nominal: 169, mass: 168.9342179, abundance: 1 },\n      { nominal: 170, mass: 169.935806 },\n      { nominal: 171, mass: 170.9364339 },\n      { nominal: 172, mass: 171.9384055 },\n      { nominal: 173, mass: 172.9396084 },\n      { nominal: 174, mass: 173.942173 },\n      { nominal: 175, mass: 174.943841 },\n      { nominal: 176, mass: 175.947 },\n      { nominal: 177, mass: 176.94904 },\n      { nominal: 178, mass: 177.95264 },\n      { nominal: 179, mass: 178.95534 },\n    ],\n    symbol: 'Tm',\n    mass: 168.9342179,\n    name: 'Thulium',\n    monoisotopicMass: 168.9342179,\n  },\n  {\n    number: 70,\n    isotopes: [\n      { nominal: 148, mass: 147.96758 },\n      { nominal: 149, mass: 148.96436 },\n      { nominal: 150, mass: 149.95852 },\n      { nominal: 151, mass: 150.9554 },\n      { nominal: 152, mass: 151.95027 },\n      { nominal: 153, mass: 152.94932 },\n      { nominal: 154, mass: 153.946396 },\n      { nominal: 155, mass: 154.945783 },\n      { nominal: 156, mass: 155.942825 },\n      { nominal: 157, mass: 156.942645 },\n      { nominal: 158, mass: 157.9398705 },\n      { nominal: 159, mass: 158.940055 },\n      { nominal: 160, mass: 159.937557 },\n      { nominal: 161, mass: 160.937907 },\n      { nominal: 162, mass: 161.935774 },\n      { nominal: 163, mass: 162.93634 },\n      { nominal: 164, mass: 163.934495 },\n      { nominal: 165, mass: 164.93527 },\n      { nominal: 166, mass: 165.9338747 },\n      { nominal: 167, mass: 166.934953 },\n      { nominal: 168, mass: 167.9338896, abundance: 0.00123 },\n      { nominal: 169, mass: 168.9351825 },\n      { nominal: 170, mass: 169.9347664, abundance: 0.02982 },\n      { nominal: 171, mass: 170.9363302, abundance: 0.1409 },\n      { nominal: 172, mass: 171.9363859, abundance: 0.2168 },\n      { nominal: 173, mass: 172.9382151, abundance: 0.16103 },\n      { nominal: 174, mass: 173.9388664, abundance: 0.32026 },\n      { nominal: 175, mass: 174.9412808 },\n      { nominal: 176, mass: 175.9425764, abundance: 0.12996 },\n      { nominal: 177, mass: 176.9452656 },\n      { nominal: 178, mass: 177.946651 },\n      { nominal: 179, mass: 178.95004 },\n      { nominal: 180, mass: 179.95212 },\n      { nominal: 181, mass: 180.95589 },\n    ],\n    symbol: 'Yb',\n    mass: 173.05415016631702,\n    name: 'Ytterbium',\n    monoisotopicMass: 173.9388664,\n  },\n  {\n    number: 71,\n    isotopes: [\n      { nominal: 150, mass: 149.97355 },\n      { nominal: 151, mass: 150.96768 },\n      { nominal: 152, mass: 151.96412 },\n      { nominal: 153, mass: 152.95875 },\n      { nominal: 154, mass: 153.95736 },\n      { nominal: 155, mass: 154.954321 },\n      { nominal: 156, mass: 155.953033 },\n      { nominal: 157, mass: 156.950127 },\n      { nominal: 158, mass: 157.949316 },\n      { nominal: 159, mass: 158.946636 },\n      { nominal: 160, mass: 159.946033 },\n      { nominal: 161, mass: 160.943572 },\n      { nominal: 162, mass: 161.943283 },\n      { nominal: 163, mass: 162.941179 },\n      { nominal: 164, mass: 163.941339 },\n      { nominal: 165, mass: 164.939407 },\n      { nominal: 166, mass: 165.939859 },\n      { nominal: 167, mass: 166.93827 },\n      { nominal: 168, mass: 167.938736 },\n      { nominal: 169, mass: 168.9376441 },\n      { nominal: 170, mass: 169.938478 },\n      { nominal: 171, mass: 170.937917 },\n      { nominal: 172, mass: 171.9390891 },\n      { nominal: 173, mass: 172.938934 },\n      { nominal: 174, mass: 173.9403409 },\n      { nominal: 175, mass: 174.9407752, abundance: 0.97401 },\n      { nominal: 176, mass: 175.9426897, abundance: 0.02599 },\n      { nominal: 177, mass: 176.9437615 },\n      { nominal: 178, mass: 177.945958 },\n      { nominal: 179, mass: 178.9473309 },\n      { nominal: 180, mass: 179.949888 },\n      { nominal: 181, mass: 180.95191 },\n      { nominal: 182, mass: 181.95504 },\n      { nominal: 183, mass: 182.957363 },\n      { nominal: 184, mass: 183.96091 },\n      { nominal: 185, mass: 184.96362 },\n    ],\n    symbol: 'Lu',\n    mass: 174.96681495785498,\n    name: 'Lutetium',\n    monoisotopicMass: 174.9407752,\n  },\n  {\n    number: 72,\n    isotopes: [\n      { nominal: 153, mass: 152.97069 },\n      { nominal: 154, mass: 153.96486 },\n      { nominal: 155, mass: 154.96311 },\n      { nominal: 156, mass: 155.95935 },\n      { nominal: 157, mass: 156.95824 },\n      { nominal: 158, mass: 157.954801 },\n      { nominal: 159, mass: 158.953996 },\n      { nominal: 160, mass: 159.950691 },\n      { nominal: 161, mass: 160.950278 },\n      { nominal: 162, mass: 161.9472148 },\n      { nominal: 163, mass: 162.947113 },\n      { nominal: 164, mass: 163.944371 },\n      { nominal: 165, mass: 164.944567 },\n      { nominal: 166, mass: 165.94218 },\n      { nominal: 167, mass: 166.9426 },\n      { nominal: 168, mass: 167.940568 },\n      { nominal: 169, mass: 168.941259 },\n      { nominal: 170, mass: 169.939609 },\n      { nominal: 171, mass: 170.940492 },\n      { nominal: 172, mass: 171.93945 },\n      { nominal: 173, mass: 172.940513 },\n      { nominal: 174, mass: 173.9400461, abundance: 0.0016 },\n      { nominal: 175, mass: 174.9415092 },\n      { nominal: 176, mass: 175.9414076, abundance: 0.0526 },\n      { nominal: 177, mass: 176.9432277, abundance: 0.186 },\n      { nominal: 178, mass: 177.9437058, abundance: 0.2728 },\n      { nominal: 179, mass: 178.9458232, abundance: 0.1362 },\n      { nominal: 180, mass: 179.946557, abundance: 0.3508 },\n      { nominal: 181, mass: 180.9491083 },\n      { nominal: 182, mass: 181.9505612 },\n      { nominal: 183, mass: 182.95353 },\n      { nominal: 184, mass: 183.955446 },\n      { nominal: 185, mass: 184.958862 },\n      { nominal: 186, mass: 185.960897 },\n      { nominal: 187, mass: 186.96477 },\n      { nominal: 188, mass: 187.96685 },\n      { nominal: 189, mass: 188.97084 },\n    ],\n    symbol: 'Hf',\n    mass: 178.4849787234,\n    name: 'Hafnium',\n    monoisotopicMass: 179.946557,\n  },\n  {\n    number: 73,\n    isotopes: [\n      { nominal: 155, mass: 154.97424 },\n      { nominal: 156, mass: 155.97203 },\n      { nominal: 157, mass: 156.96818 },\n      { nominal: 158, mass: 157.96654 },\n      { nominal: 159, mass: 158.963023 },\n      { nominal: 160, mass: 159.961488 },\n      { nominal: 161, mass: 160.958452 },\n      { nominal: 162, mass: 161.957294 },\n      { nominal: 163, mass: 162.954337 },\n      { nominal: 164, mass: 163.953534 },\n      { nominal: 165, mass: 164.950781 },\n      { nominal: 166, mass: 165.950512 },\n      { nominal: 167, mass: 166.948093 },\n      { nominal: 168, mass: 167.948047 },\n      { nominal: 169, mass: 168.946011 },\n      { nominal: 170, mass: 169.946175 },\n      { nominal: 171, mass: 170.944476 },\n      { nominal: 172, mass: 171.944895 },\n      { nominal: 173, mass: 172.94375 },\n      { nominal: 174, mass: 173.944454 },\n      { nominal: 175, mass: 174.943737 },\n      { nominal: 176, mass: 175.944857 },\n      { nominal: 177, mass: 176.9444795 },\n      { nominal: 178, mass: 177.945678 },\n      { nominal: 179, mass: 178.9459366 },\n      { nominal: 180, mass: 179.9474648, abundance: 0.0001201 },\n      { nominal: 181, mass: 180.9479958, abundance: 0.9998799 },\n      { nominal: 182, mass: 181.9501519 },\n      { nominal: 183, mass: 182.9513726 },\n      { nominal: 184, mass: 183.954008 },\n      { nominal: 185, mass: 184.955559 },\n      { nominal: 186, mass: 185.958551 },\n      { nominal: 187, mass: 186.960386 },\n      { nominal: 188, mass: 187.963916 },\n      { nominal: 189, mass: 188.96583 },\n      { nominal: 190, mass: 189.96939 },\n      { nominal: 191, mass: 190.97156 },\n      { nominal: 192, mass: 191.97514 },\n    ],\n    symbol: 'Ta',\n    mass: 180.9478756362269,\n    name: 'Tantalum',\n    monoisotopicMass: 180.9479958,\n  },\n  {\n    number: 74,\n    isotopes: [\n      { nominal: 157, mass: 156.97884 },\n      { nominal: 158, mass: 157.97456 },\n      { nominal: 159, mass: 158.97264 },\n      { nominal: 160, mass: 159.96846 },\n      { nominal: 161, mass: 160.9672 },\n      { nominal: 162, mass: 161.963499 },\n      { nominal: 163, mass: 162.962524 },\n      { nominal: 164, mass: 163.958961 },\n      { nominal: 165, mass: 164.958281 },\n      { nominal: 166, mass: 165.955031 },\n      { nominal: 167, mass: 166.954805 },\n      { nominal: 168, mass: 167.951806 },\n      { nominal: 169, mass: 168.951779 },\n      { nominal: 170, mass: 169.949232 },\n      { nominal: 171, mass: 170.949451 },\n      { nominal: 172, mass: 171.947292 },\n      { nominal: 173, mass: 172.947689 },\n      { nominal: 174, mass: 173.946079 },\n      { nominal: 175, mass: 174.946717 },\n      { nominal: 176, mass: 175.945634 },\n      { nominal: 177, mass: 176.946643 },\n      { nominal: 178, mass: 177.945883 },\n      { nominal: 179, mass: 178.947077 },\n      { nominal: 180, mass: 179.9467108, abundance: 0.0012 },\n      { nominal: 181, mass: 180.9481978 },\n      { nominal: 182, mass: 181.94820394, abundance: 0.265 },\n      { nominal: 183, mass: 182.95022275, abundance: 0.1431 },\n      { nominal: 184, mass: 183.95093092, abundance: 0.3064 },\n      { nominal: 185, mass: 184.95341897 },\n      { nominal: 186, mass: 185.9543628, abundance: 0.2843 },\n      { nominal: 187, mass: 186.9571588 },\n      { nominal: 188, mass: 187.9584862 },\n      { nominal: 189, mass: 188.961763 },\n      { nominal: 190, mass: 189.963091 },\n      { nominal: 191, mass: 190.966531 },\n      { nominal: 192, mass: 191.96817 },\n      { nominal: 193, mass: 192.97178 },\n      { nominal: 194, mass: 193.97367 },\n    ],\n    symbol: 'W',\n    mass: 183.841777550513,\n    name: 'Tungsten',\n    monoisotopicMass: 183.95093092,\n  },\n  {\n    number: 75,\n    isotopes: [\n      { nominal: 159, mass: 158.98418 },\n      { nominal: 160, mass: 159.98182 },\n      { nominal: 161, mass: 160.97757 },\n      { nominal: 162, mass: 161.97584 },\n      { nominal: 163, mass: 162.97208 },\n      { nominal: 164, mass: 163.970453 },\n      { nominal: 165, mass: 164.967103 },\n      { nominal: 166, mass: 165.965761 },\n      { nominal: 167, mass: 166.962595 },\n      { nominal: 168, mass: 167.961573 },\n      { nominal: 169, mass: 168.958766 },\n      { nominal: 170, mass: 169.95822 },\n      { nominal: 171, mass: 170.955716 },\n      { nominal: 172, mass: 171.95542 },\n      { nominal: 173, mass: 172.953243 },\n      { nominal: 174, mass: 173.953115 },\n      { nominal: 175, mass: 174.951381 },\n      { nominal: 176, mass: 175.951623 },\n      { nominal: 177, mass: 176.950328 },\n      { nominal: 178, mass: 177.950989 },\n      { nominal: 179, mass: 178.949989 },\n      { nominal: 180, mass: 179.950792 },\n      { nominal: 181, mass: 180.950058 },\n      { nominal: 182, mass: 181.95121 },\n      { nominal: 183, mass: 182.9508196 },\n      { nominal: 184, mass: 183.9525228 },\n      { nominal: 185, mass: 184.9529545, abundance: 0.374 },\n      { nominal: 186, mass: 185.9549856 },\n      { nominal: 187, mass: 186.9557501, abundance: 0.626 },\n      { nominal: 188, mass: 187.9581115 },\n      { nominal: 189, mass: 188.959226 },\n      { nominal: 190, mass: 189.961744 },\n      { nominal: 191, mass: 190.963122 },\n      { nominal: 192, mass: 191.966088 },\n      { nominal: 193, mass: 192.967541 },\n      { nominal: 194, mass: 193.97076 },\n      { nominal: 195, mass: 194.97254 },\n      { nominal: 196, mass: 195.9758 },\n      { nominal: 197, mass: 196.97799 },\n      { nominal: 198, mass: 197.9816 },\n    ],\n    symbol: 'Re',\n    mass: 186.20670454560002,\n    name: 'Rhenium',\n    monoisotopicMass: 186.9557501,\n  },\n  {\n    number: 76,\n    isotopes: [\n      { nominal: 161, mass: 160.98903 },\n      { nominal: 162, mass: 161.98443 },\n      { nominal: 163, mass: 162.98241 },\n      { nominal: 164, mass: 163.97802 },\n      { nominal: 165, mass: 164.9766 },\n      { nominal: 166, mass: 165.972692 },\n      { nominal: 167, mass: 166.971549 },\n      { nominal: 168, mass: 167.967808 },\n      { nominal: 169, mass: 168.967018 },\n      { nominal: 170, mass: 169.963578 },\n      { nominal: 171, mass: 170.963174 },\n      { nominal: 172, mass: 171.960017 },\n      { nominal: 173, mass: 172.959808 },\n      { nominal: 174, mass: 173.957064 },\n      { nominal: 175, mass: 174.956945 },\n      { nominal: 176, mass: 175.954806 },\n      { nominal: 177, mass: 176.954966 },\n      { nominal: 178, mass: 177.953254 },\n      { nominal: 179, mass: 178.953817 },\n      { nominal: 180, mass: 179.952375 },\n      { nominal: 181, mass: 180.953247 },\n      { nominal: 182, mass: 181.95211 },\n      { nominal: 183, mass: 182.953125 },\n      { nominal: 184, mass: 183.9524885, abundance: 0.0002 },\n      { nominal: 185, mass: 184.9540417 },\n      { nominal: 186, mass: 185.953835, abundance: 0.0159 },\n      { nominal: 187, mass: 186.9557474, abundance: 0.0196 },\n      { nominal: 188, mass: 187.9558352, abundance: 0.1324 },\n      { nominal: 189, mass: 188.9581442, abundance: 0.1615 },\n      { nominal: 190, mass: 189.9584437, abundance: 0.2626 },\n      { nominal: 191, mass: 190.9609264 },\n      { nominal: 192, mass: 191.961477, abundance: 0.4078 },\n      { nominal: 193, mass: 192.9641479 },\n      { nominal: 194, mass: 193.9651772 },\n      { nominal: 195, mass: 194.968318 },\n      { nominal: 196, mass: 195.969641 },\n      { nominal: 197, mass: 196.97283 },\n      { nominal: 198, mass: 197.97441 },\n      { nominal: 199, mass: 198.97801 },\n      { nominal: 200, mass: 199.97984 },\n      { nominal: 201, mass: 200.98364 },\n      { nominal: 202, mass: 201.98595 },\n    ],\n    symbol: 'Os',\n    mass: 190.22485962823998,\n    name: 'Osmium',\n    monoisotopicMass: 191.961477,\n  },\n  {\n    number: 77,\n    isotopes: [\n      { nominal: 164, mass: 163.99191 },\n      { nominal: 165, mass: 164.9875 },\n      { nominal: 166, mass: 165.98566 },\n      { nominal: 167, mass: 166.981666 },\n      { nominal: 168, mass: 167.979907 },\n      { nominal: 169, mass: 168.976298 },\n      { nominal: 170, mass: 169.974922 },\n      { nominal: 171, mass: 170.97164 },\n      { nominal: 172, mass: 171.970607 },\n      { nominal: 173, mass: 172.967506 },\n      { nominal: 174, mass: 173.966861 },\n      { nominal: 175, mass: 174.96415 },\n      { nominal: 176, mass: 175.96365 },\n      { nominal: 177, mass: 176.961301 },\n      { nominal: 178, mass: 177.961082 },\n      { nominal: 179, mass: 178.95912 },\n      { nominal: 180, mass: 179.959229 },\n      { nominal: 181, mass: 180.957625 },\n      { nominal: 182, mass: 181.958076 },\n      { nominal: 183, mass: 182.95684 },\n      { nominal: 184, mass: 183.957476 },\n      { nominal: 185, mass: 184.956698 },\n      { nominal: 186, mass: 185.957944 },\n      { nominal: 187, mass: 186.957542 },\n      { nominal: 188, mass: 187.958828 },\n      { nominal: 189, mass: 188.958715 },\n      { nominal: 190, mass: 189.9605412 },\n      { nominal: 191, mass: 190.9605893, abundance: 0.373 },\n      { nominal: 192, mass: 191.9626002 },\n      { nominal: 193, mass: 192.9629216, abundance: 0.627 },\n      { nominal: 194, mass: 193.9650735 },\n      { nominal: 195, mass: 194.9659747 },\n      { nominal: 196, mass: 195.968397 },\n      { nominal: 197, mass: 196.969655 },\n      { nominal: 198, mass: 197.97228 },\n      { nominal: 199, mass: 198.973805 },\n      { nominal: 200, mass: 199.9768 },\n      { nominal: 201, mass: 200.97864 },\n      { nominal: 202, mass: 201.98199 },\n      { nominal: 203, mass: 202.98423 },\n      { nominal: 204, mass: 203.9896 },\n    ],\n    symbol: 'Ir',\n    mass: 192.2160516521,\n    name: 'Iridium',\n    monoisotopicMass: 192.9629216,\n  },\n  {\n    number: 78,\n    isotopes: [\n      { nominal: 166, mass: 165.99486 },\n      { nominal: 167, mass: 166.99269 },\n      { nominal: 168, mass: 167.98813 },\n      { nominal: 169, mass: 168.98657 },\n      { nominal: 170, mass: 169.982496 },\n      { nominal: 171, mass: 170.981245 },\n      { nominal: 172, mass: 171.977351 },\n      { nominal: 173, mass: 172.976443 },\n      { nominal: 174, mass: 173.97282 },\n      { nominal: 175, mass: 174.97241 },\n      { nominal: 176, mass: 175.968938 },\n      { nominal: 177, mass: 176.96847 },\n      { nominal: 178, mass: 177.96565 },\n      { nominal: 179, mass: 178.965359 },\n      { nominal: 180, mass: 179.963032 },\n      { nominal: 181, mass: 180.963098 },\n      { nominal: 182, mass: 181.961172 },\n      { nominal: 183, mass: 182.961597 },\n      { nominal: 184, mass: 183.959915 },\n      { nominal: 185, mass: 184.960614 },\n      { nominal: 186, mass: 185.959351 },\n      { nominal: 187, mass: 186.960617 },\n      { nominal: 188, mass: 187.9593889 },\n      { nominal: 189, mass: 188.960831 },\n      { nominal: 190, mass: 189.9599297, abundance: 0.00012 },\n      { nominal: 191, mass: 190.9616729 },\n      { nominal: 192, mass: 191.9610387, abundance: 0.00782 },\n      { nominal: 193, mass: 192.9629824 },\n      { nominal: 194, mass: 193.9626809, abundance: 0.3286 },\n      { nominal: 195, mass: 194.9647917, abundance: 0.3378 },\n      { nominal: 196, mass: 195.96495209, abundance: 0.2521 },\n      { nominal: 197, mass: 196.96734069 },\n      { nominal: 198, mass: 197.9678949, abundance: 0.07356 },\n      { nominal: 199, mass: 198.9705952 },\n      { nominal: 200, mass: 199.971443 },\n      { nominal: 201, mass: 200.974513 },\n      { nominal: 202, mass: 201.975639 },\n      { nominal: 203, mass: 202.97893 },\n      { nominal: 204, mass: 203.98076 },\n      { nominal: 205, mass: 204.98608 },\n      { nominal: 206, mass: 205.98966 },\n    ],\n    symbol: 'Pt',\n    mass: 195.084456864931,\n    name: 'Platinum',\n    monoisotopicMass: 194.9647917,\n  },\n  {\n    number: 79,\n    isotopes: [\n      { nominal: 169, mass: 168.99808 },\n      { nominal: 170, mass: 169.99597 },\n      { nominal: 171, mass: 170.991876 },\n      { nominal: 172, mass: 171.989942 },\n      { nominal: 173, mass: 172.986241 },\n      { nominal: 174, mass: 173.984717 },\n      { nominal: 175, mass: 174.981304 },\n      { nominal: 176, mass: 175.98025 },\n      { nominal: 177, mass: 176.97687 },\n      { nominal: 178, mass: 177.976032 },\n      { nominal: 179, mass: 178.973174 },\n      { nominal: 180, mass: 179.972523 },\n      { nominal: 181, mass: 180.970079 },\n      { nominal: 182, mass: 181.969618 },\n      { nominal: 183, mass: 182.967591 },\n      { nominal: 184, mass: 183.967452 },\n      { nominal: 185, mass: 184.96579 },\n      { nominal: 186, mass: 185.965953 },\n      { nominal: 187, mass: 186.964543 },\n      { nominal: 188, mass: 187.965349 },\n      { nominal: 189, mass: 188.963948 },\n      { nominal: 190, mass: 189.964698 },\n      { nominal: 191, mass: 190.963702 },\n      { nominal: 192, mass: 191.964814 },\n      { nominal: 193, mass: 192.9641373 },\n      { nominal: 194, mass: 193.9654178 },\n      { nominal: 195, mass: 194.9650352 },\n      { nominal: 196, mass: 195.9665699 },\n      { nominal: 197, mass: 196.96656879, abundance: 1 },\n      { nominal: 198, mass: 197.96824242 },\n      { nominal: 199, mass: 198.96876528 },\n      { nominal: 200, mass: 199.970756 },\n      { nominal: 201, mass: 200.9716575 },\n      { nominal: 202, mass: 201.973856 },\n      { nominal: 203, mass: 202.9751544 },\n      { nominal: 204, mass: 203.97783 },\n      { nominal: 205, mass: 204.97985 },\n      { nominal: 206, mass: 205.98474 },\n      { nominal: 207, mass: 206.9884 },\n      { nominal: 208, mass: 207.99345 },\n      { nominal: 209, mass: 208.99735 },\n      { nominal: 210, mass: 210.0025 },\n    ],\n    symbol: 'Au',\n    mass: 196.96656879,\n    name: 'Gold',\n    monoisotopicMass: 196.96656879,\n  },\n  {\n    number: 80,\n    isotopes: [\n      { nominal: 171, mass: 171.00353 },\n      { nominal: 172, mass: 171.99881 },\n      { nominal: 173, mass: 172.99709 },\n      { nominal: 174, mass: 173.992865 },\n      { nominal: 175, mass: 174.991441 },\n      { nominal: 176, mass: 175.987361 },\n      { nominal: 177, mass: 176.986277 },\n      { nominal: 178, mass: 177.982484 },\n      { nominal: 179, mass: 178.981831 },\n      { nominal: 180, mass: 179.97826 },\n      { nominal: 181, mass: 180.977819 },\n      { nominal: 182, mass: 181.974689 },\n      { nominal: 183, mass: 182.9744448 },\n      { nominal: 184, mass: 183.971714 },\n      { nominal: 185, mass: 184.971899 },\n      { nominal: 186, mass: 185.969362 },\n      { nominal: 187, mass: 186.969814 },\n      { nominal: 188, mass: 187.967567 },\n      { nominal: 189, mass: 188.968195 },\n      { nominal: 190, mass: 189.966323 },\n      { nominal: 191, mass: 190.967157 },\n      { nominal: 192, mass: 191.965635 },\n      { nominal: 193, mass: 192.966653 },\n      { nominal: 194, mass: 193.9654491 },\n      { nominal: 195, mass: 194.966721 },\n      { nominal: 196, mass: 195.9658326, abundance: 0.0015 },\n      { nominal: 197, mass: 196.9672128 },\n      { nominal: 198, mass: 197.9667686, abundance: 0.0997 },\n      { nominal: 199, mass: 198.96828064, abundance: 0.1687 },\n      { nominal: 200, mass: 199.96832659, abundance: 0.231 },\n      { nominal: 201, mass: 200.97030284, abundance: 0.1318 },\n      { nominal: 202, mass: 201.9706434, abundance: 0.2986 },\n      { nominal: 203, mass: 202.9728728 },\n      { nominal: 204, mass: 203.97349398, abundance: 0.0687 },\n      { nominal: 205, mass: 204.9760734 },\n      { nominal: 206, mass: 205.977514 },\n      { nominal: 207, mass: 206.9823 },\n      { nominal: 208, mass: 207.985759 },\n      { nominal: 209, mass: 208.99072 },\n      { nominal: 210, mass: 209.99424 },\n      { nominal: 211, mass: 210.99933 },\n      { nominal: 212, mass: 212.00296 },\n      { nominal: 213, mass: 213.00823 },\n      { nominal: 214, mass: 214.012 },\n      { nominal: 215, mass: 215.0174 },\n      { nominal: 216, mass: 216.02132 },\n    ],\n    symbol: 'Hg',\n    mass: 200.59916703455602,\n    name: 'Mercury',\n    monoisotopicMass: 201.9706434,\n  },\n  {\n    number: 81,\n    isotopes: [\n      { nominal: 176, mass: 176.000624 },\n      { nominal: 177, mass: 176.996431 },\n      { nominal: 178, mass: 177.99485 },\n      { nominal: 179, mass: 178.991111 },\n      { nominal: 180, mass: 179.990057 },\n      { nominal: 181, mass: 180.98626 },\n      { nominal: 182, mass: 181.985713 },\n      { nominal: 183, mass: 182.982193 },\n      { nominal: 184, mass: 183.981886 },\n      { nominal: 185, mass: 184.978789 },\n      { nominal: 186, mass: 185.978651 },\n      { nominal: 187, mass: 186.9759063 },\n      { nominal: 188, mass: 187.976021 },\n      { nominal: 189, mass: 188.973588 },\n      { nominal: 190, mass: 189.973828 },\n      { nominal: 191, mass: 190.9717842 },\n      { nominal: 192, mass: 191.972225 },\n      { nominal: 193, mass: 192.970502 },\n      { nominal: 194, mass: 193.971081 },\n      { nominal: 195, mass: 194.969774 },\n      { nominal: 196, mass: 195.970481 },\n      { nominal: 197, mass: 196.969576 },\n      { nominal: 198, mass: 197.970483 },\n      { nominal: 199, mass: 198.969877 },\n      { nominal: 200, mass: 199.9709633 },\n      { nominal: 201, mass: 200.970822 },\n      { nominal: 202, mass: 201.972102 },\n      { nominal: 203, mass: 202.9723446, abundance: 0.2952 },\n      { nominal: 204, mass: 203.9738639 },\n      { nominal: 205, mass: 204.9744278, abundance: 0.7048 },\n      { nominal: 206, mass: 205.9761106 },\n      { nominal: 207, mass: 206.9774197 },\n      { nominal: 208, mass: 207.982019 },\n      { nominal: 209, mass: 208.9853594 },\n      { nominal: 210, mass: 209.990074 },\n      { nominal: 211, mass: 210.993475 },\n      { nominal: 212, mass: 211.99834 },\n      { nominal: 213, mass: 213.001915 },\n      { nominal: 214, mass: 214.00694 },\n      { nominal: 215, mass: 215.01064 },\n      { nominal: 216, mass: 216.0158 },\n      { nominal: 217, mass: 217.01966 },\n      { nominal: 218, mass: 218.02479 },\n    ],\n    symbol: 'Tl',\n    mass: 204.38341283936,\n    name: 'Thallium',\n    monoisotopicMass: 204.9744278,\n  },\n  {\n    number: 82,\n    isotopes: [\n      { nominal: 178, mass: 178.003831 },\n      { nominal: 179, mass: 179.002201 },\n      { nominal: 180, mass: 179.997928 },\n      { nominal: 181, mass: 180.996653 },\n      { nominal: 182, mass: 181.992672 },\n      { nominal: 183, mass: 182.991872 },\n      { nominal: 184, mass: 183.988136 },\n      { nominal: 185, mass: 184.98761 },\n      { nominal: 186, mass: 185.984238 },\n      { nominal: 187, mass: 186.9839109 },\n      { nominal: 188, mass: 187.980875 },\n      { nominal: 189, mass: 188.980807 },\n      { nominal: 190, mass: 189.978082 },\n      { nominal: 191, mass: 190.978276 },\n      { nominal: 192, mass: 191.975775 },\n      { nominal: 193, mass: 192.976173 },\n      { nominal: 194, mass: 193.974012 },\n      { nominal: 195, mass: 194.974543 },\n      { nominal: 196, mass: 195.972774 },\n      { nominal: 197, mass: 196.9734312 },\n      { nominal: 198, mass: 197.972034 },\n      { nominal: 199, mass: 198.972913 },\n      { nominal: 200, mass: 199.971819 },\n      { nominal: 201, mass: 200.972883 },\n      { nominal: 202, mass: 201.972152 },\n      { nominal: 203, mass: 202.9733911 },\n      { nominal: 204, mass: 203.973044, abundance: 0.014 },\n      { nominal: 205, mass: 204.9744822 },\n      { nominal: 206, mass: 205.9744657, abundance: 0.241 },\n      { nominal: 207, mass: 206.9758973, abundance: 0.221 },\n      { nominal: 208, mass: 207.9766525, abundance: 0.524 },\n      { nominal: 209, mass: 208.9810905 },\n      { nominal: 210, mass: 209.9841889 },\n      { nominal: 211, mass: 210.9887371 },\n      { nominal: 212, mass: 211.9918977 },\n      { nominal: 213, mass: 212.9965629 },\n      { nominal: 214, mass: 213.9998059 },\n      { nominal: 215, mass: 215.00474 },\n      { nominal: 216, mass: 216.00803 },\n      { nominal: 217, mass: 217.01314 },\n      { nominal: 218, mass: 218.01659 },\n      { nominal: 219, mass: 219.02177 },\n      { nominal: 220, mass: 220.02541 },\n    ],\n    symbol: 'Pb',\n    mass: 207.216908063,\n    name: 'Lead',\n    monoisotopicMass: 207.9766525,\n  },\n  {\n    number: 83,\n    isotopes: [\n      { nominal: 184, mass: 184.001275 },\n      { nominal: 185, mass: 184.9976 },\n      { nominal: 186, mass: 185.996644 },\n      { nominal: 187, mass: 186.993147 },\n      { nominal: 188, mass: 187.992287 },\n      { nominal: 189, mass: 188.989195 },\n      { nominal: 190, mass: 189.988622 },\n      { nominal: 191, mass: 190.9857866 },\n      { nominal: 192, mass: 191.985469 },\n      { nominal: 193, mass: 192.98296 },\n      { nominal: 194, mass: 193.982785 },\n      { nominal: 195, mass: 194.9806488 },\n      { nominal: 196, mass: 195.980667 },\n      { nominal: 197, mass: 196.9788651 },\n      { nominal: 198, mass: 197.979206 },\n      { nominal: 199, mass: 198.977673 },\n      { nominal: 200, mass: 199.978131 },\n      { nominal: 201, mass: 200.97701 },\n      { nominal: 202, mass: 201.977734 },\n      { nominal: 203, mass: 202.976893 },\n      { nominal: 204, mass: 203.9778361 },\n      { nominal: 205, mass: 204.9773867 },\n      { nominal: 206, mass: 205.9784993 },\n      { nominal: 207, mass: 206.978471 },\n      { nominal: 208, mass: 207.9797425 },\n      { nominal: 209, mass: 208.9803991, abundance: 1 },\n      { nominal: 210, mass: 209.9841207 },\n      { nominal: 211, mass: 210.9872697 },\n      { nominal: 212, mass: 211.991286 },\n      { nominal: 213, mass: 212.9943851 },\n      { nominal: 214, mass: 213.998712 },\n      { nominal: 215, mass: 215.00177 },\n      { nominal: 216, mass: 216.006306 },\n      { nominal: 217, mass: 217.009372 },\n      { nominal: 218, mass: 218.014188 },\n      { nominal: 219, mass: 219.01748 },\n      { nominal: 220, mass: 220.02235 },\n      { nominal: 221, mass: 221.02587 },\n      { nominal: 222, mass: 222.03078 },\n      { nominal: 223, mass: 223.0345 },\n      { nominal: 224, mass: 224.03947 },\n    ],\n    symbol: 'Bi',\n    mass: 208.9803991,\n    name: 'Bismuth',\n    monoisotopicMass: 208.9803991,\n  },\n  {\n    number: 84,\n    isotopes: [\n      { nominal: 186, mass: 186.004393 },\n      { nominal: 187, mass: 187.003041 },\n      { nominal: 188, mass: 187.999416 },\n      { nominal: 189, mass: 188.998473 },\n      { nominal: 190, mass: 189.995101 },\n      { nominal: 191, mass: 190.9945585 },\n      { nominal: 192, mass: 191.991336 },\n      { nominal: 193, mass: 192.991026 },\n      { nominal: 194, mass: 193.988186 },\n      { nominal: 195, mass: 194.988126 },\n      { nominal: 196, mass: 195.985526 },\n      { nominal: 197, mass: 196.98566 },\n      { nominal: 198, mass: 197.983389 },\n      { nominal: 199, mass: 198.983667 },\n      { nominal: 200, mass: 199.981799 },\n      { nominal: 201, mass: 200.9822598 },\n      { nominal: 202, mass: 201.980758 },\n      { nominal: 203, mass: 202.9814161 },\n      { nominal: 204, mass: 203.98031 },\n      { nominal: 205, mass: 204.981203 },\n      { nominal: 206, mass: 205.980474 },\n      { nominal: 207, mass: 206.9815938 },\n      { nominal: 208, mass: 207.9812461 },\n      { nominal: 209, mass: 208.9824308 },\n      { nominal: 210, mass: 209.9828741 },\n      { nominal: 211, mass: 210.9866536 },\n      { nominal: 212, mass: 211.9888684 },\n      { nominal: 213, mass: 212.9928576 },\n      { nominal: 214, mass: 213.9952017 },\n      { nominal: 215, mass: 214.9994201 },\n      { nominal: 216, mass: 216.0019152 },\n      { nominal: 217, mass: 217.0063182 },\n      { nominal: 218, mass: 218.0089735 },\n      { nominal: 219, mass: 219.013614 },\n      { nominal: 220, mass: 220.016386 },\n      { nominal: 221, mass: 221.021228 },\n      { nominal: 222, mass: 222.02414 },\n      { nominal: 223, mass: 223.02907 },\n      { nominal: 224, mass: 224.03211 },\n      { nominal: 225, mass: 225.03707 },\n      { nominal: 226, mass: 226.04031 },\n      { nominal: 227, mass: 227.04539 },\n    ],\n    symbol: 'Po',\n    mass: null,\n    name: 'Polonium',\n  },\n  {\n    number: 85,\n    isotopes: [\n      { nominal: 191, mass: 191.004148 },\n      { nominal: 192, mass: 192.003152 },\n      { nominal: 193, mass: 192.999927 },\n      { nominal: 194, mass: 193.999236 },\n      { nominal: 195, mass: 194.9962685 },\n      { nominal: 196, mass: 195.9958 },\n      { nominal: 197, mass: 196.993189 },\n      { nominal: 198, mass: 197.992784 },\n      { nominal: 199, mass: 198.9905277 },\n      { nominal: 200, mass: 199.990351 },\n      { nominal: 201, mass: 200.9884171 },\n      { nominal: 202, mass: 201.98863 },\n      { nominal: 203, mass: 202.986943 },\n      { nominal: 204, mass: 203.987251 },\n      { nominal: 205, mass: 204.986076 },\n      { nominal: 206, mass: 205.986657 },\n      { nominal: 207, mass: 206.9858 },\n      { nominal: 208, mass: 207.9866133 },\n      { nominal: 209, mass: 208.9861702 },\n      { nominal: 210, mass: 209.9871479 },\n      { nominal: 211, mass: 210.9874966 },\n      { nominal: 212, mass: 211.9907377 },\n      { nominal: 213, mass: 212.992937 },\n      { nominal: 214, mass: 213.9963721 },\n      { nominal: 215, mass: 214.9986528 },\n      { nominal: 216, mass: 216.0024236 },\n      { nominal: 217, mass: 217.0047192 },\n      { nominal: 218, mass: 218.008695 },\n      { nominal: 219, mass: 219.0111618 },\n      { nominal: 220, mass: 220.015433 },\n      { nominal: 221, mass: 221.018017 },\n      { nominal: 222, mass: 222.022494 },\n      { nominal: 223, mass: 223.025151 },\n      { nominal: 224, mass: 224.029749 },\n      { nominal: 225, mass: 225.03263 },\n      { nominal: 226, mass: 226.03716 },\n      { nominal: 227, mass: 227.04024 },\n      { nominal: 228, mass: 228.04475 },\n      { nominal: 229, mass: 229.04812 },\n    ],\n    symbol: 'At',\n    mass: null,\n    name: 'Astatine',\n  },\n  {\n    number: 86,\n    isotopes: [\n      { nominal: 193, mass: 193.009708 },\n      { nominal: 194, mass: 194.006144 },\n      { nominal: 195, mass: 195.005422 },\n      { nominal: 196, mass: 196.002116 },\n      { nominal: 197, mass: 197.001585 },\n      { nominal: 198, mass: 197.998679 },\n      { nominal: 199, mass: 198.99839 },\n      { nominal: 200, mass: 199.99569 },\n      { nominal: 201, mass: 200.995628 },\n      { nominal: 202, mass: 201.993264 },\n      { nominal: 203, mass: 202.993388 },\n      { nominal: 204, mass: 203.99143 },\n      { nominal: 205, mass: 204.991719 },\n      { nominal: 206, mass: 205.990214 },\n      { nominal: 207, mass: 206.9907303 },\n      { nominal: 208, mass: 207.989635 },\n      { nominal: 209, mass: 208.990415 },\n      { nominal: 210, mass: 209.9896891 },\n      { nominal: 211, mass: 210.9906011 },\n      { nominal: 212, mass: 211.9907039 },\n      { nominal: 213, mass: 212.9938831 },\n      { nominal: 214, mass: 213.995363 },\n      { nominal: 215, mass: 214.9987459 },\n      { nominal: 216, mass: 216.0002719 },\n      { nominal: 217, mass: 217.003928 },\n      { nominal: 218, mass: 218.0056016 },\n      { nominal: 219, mass: 219.0094804 },\n      { nominal: 220, mass: 220.0113941 },\n      { nominal: 221, mass: 221.0155371 },\n      { nominal: 222, mass: 222.0175782 },\n      { nominal: 223, mass: 223.0218893 },\n      { nominal: 224, mass: 224.024096 },\n      { nominal: 225, mass: 225.028486 },\n      { nominal: 226, mass: 226.030861 },\n      { nominal: 227, mass: 227.035304 },\n      { nominal: 228, mass: 228.037835 },\n      { nominal: 229, mass: 229.042257 },\n      { nominal: 230, mass: 230.04514 },\n      { nominal: 231, mass: 231.04987 },\n    ],\n    symbol: 'Rn',\n    mass: null,\n    name: 'Radon',\n  },\n  {\n    number: 87,\n    isotopes: [\n      { nominal: 199, mass: 199.007259 },\n      { nominal: 200, mass: 200.006586 },\n      { nominal: 201, mass: 201.003867 },\n      { nominal: 202, mass: 202.00332 },\n      { nominal: 203, mass: 203.0009407 },\n      { nominal: 204, mass: 204.000652 },\n      { nominal: 205, mass: 204.9985939 },\n      { nominal: 206, mass: 205.998666 },\n      { nominal: 207, mass: 206.996946 },\n      { nominal: 208, mass: 207.997138 },\n      { nominal: 209, mass: 208.995955 },\n      { nominal: 210, mass: 209.996422 },\n      { nominal: 211, mass: 210.995556 },\n      { nominal: 212, mass: 211.9962257 },\n      { nominal: 213, mass: 212.996186 },\n      { nominal: 214, mass: 213.9989713 },\n      { nominal: 215, mass: 215.0003418 },\n      { nominal: 216, mass: 216.0031899 },\n      { nominal: 217, mass: 217.0046323 },\n      { nominal: 218, mass: 218.0075787 },\n      { nominal: 219, mass: 219.0092524 },\n      { nominal: 220, mass: 220.0123277 },\n      { nominal: 221, mass: 221.0142552 },\n      { nominal: 222, mass: 222.017552 },\n      { nominal: 223, mass: 223.019736 },\n      { nominal: 224, mass: 224.023398 },\n      { nominal: 225, mass: 225.025573 },\n      { nominal: 226, mass: 226.029566 },\n      { nominal: 227, mass: 227.031869 },\n      { nominal: 228, mass: 228.035823 },\n      { nominal: 229, mass: 229.038298 },\n      { nominal: 230, mass: 230.042416 },\n      { nominal: 231, mass: 231.045158 },\n      { nominal: 232, mass: 232.04937 },\n      { nominal: 233, mass: 233.05264 },\n    ],\n    symbol: 'Fr',\n    mass: null,\n    name: 'Francium',\n  },\n  {\n    number: 88,\n    isotopes: [\n      { nominal: 201, mass: 201.01271 },\n      { nominal: 202, mass: 202.00976 },\n      { nominal: 203, mass: 203.009304 },\n      { nominal: 204, mass: 204.006492 },\n      { nominal: 205, mass: 205.006268 },\n      { nominal: 206, mass: 206.003828 },\n      { nominal: 207, mass: 207.003799 },\n      { nominal: 208, mass: 208.001841 },\n      { nominal: 209, mass: 209.00199 },\n      { nominal: 210, mass: 210.000494 },\n      { nominal: 211, mass: 211.0008932 },\n      { nominal: 212, mass: 211.999787 },\n      { nominal: 213, mass: 213.000384 },\n      { nominal: 214, mass: 214.0000997 },\n      { nominal: 215, mass: 215.0027204 },\n      { nominal: 216, mass: 216.0035334 },\n      { nominal: 217, mass: 217.0063207 },\n      { nominal: 218, mass: 218.007141 },\n      { nominal: 219, mass: 219.0100855 },\n      { nominal: 220, mass: 220.0110259 },\n      { nominal: 221, mass: 221.0139177 },\n      { nominal: 222, mass: 222.0153748 },\n      { nominal: 223, mass: 223.0185023 },\n      { nominal: 224, mass: 224.020212 },\n      { nominal: 225, mass: 225.0236119 },\n      { nominal: 226, mass: 226.0254103 },\n      { nominal: 227, mass: 227.0291783 },\n      { nominal: 228, mass: 228.0310707 },\n      { nominal: 229, mass: 229.034942 },\n      { nominal: 230, mass: 230.037055 },\n      { nominal: 231, mass: 231.041027 },\n      { nominal: 232, mass: 232.0434753 },\n      { nominal: 233, mass: 233.047582 },\n      { nominal: 234, mass: 234.050342 },\n      { nominal: 235, mass: 235.05497 },\n    ],\n    symbol: 'Ra',\n    mass: null,\n    name: 'Radium',\n  },\n  {\n    number: 89,\n    isotopes: [\n      { nominal: 206, mass: 206.014452 },\n      { nominal: 207, mass: 207.011966 },\n      { nominal: 208, mass: 208.01155 },\n      { nominal: 209, mass: 209.009495 },\n      { nominal: 210, mass: 210.009436 },\n      { nominal: 211, mass: 211.007732 },\n      { nominal: 212, mass: 212.007813 },\n      { nominal: 213, mass: 213.006609 },\n      { nominal: 214, mass: 214.006918 },\n      { nominal: 215, mass: 215.006475 },\n      { nominal: 216, mass: 216.008743 },\n      { nominal: 217, mass: 217.009344 },\n      { nominal: 218, mass: 218.011642 },\n      { nominal: 219, mass: 219.012421 },\n      { nominal: 220, mass: 220.0147549 },\n      { nominal: 221, mass: 221.015592 },\n      { nominal: 222, mass: 222.0178442 },\n      { nominal: 223, mass: 223.0191377 },\n      { nominal: 224, mass: 224.0217232 },\n      { nominal: 225, mass: 225.02323 },\n      { nominal: 226, mass: 226.0260984 },\n      { nominal: 227, mass: 227.0277523 },\n      { nominal: 228, mass: 228.0310215 },\n      { nominal: 229, mass: 229.032956 },\n      { nominal: 230, mass: 230.036327 },\n      { nominal: 231, mass: 231.038393 },\n      { nominal: 232, mass: 232.042034 },\n      { nominal: 233, mass: 233.044346 },\n      { nominal: 234, mass: 234.048139 },\n      { nominal: 235, mass: 235.05084 },\n      { nominal: 236, mass: 236.054988 },\n      { nominal: 237, mass: 237.05827 },\n    ],\n    symbol: 'Ac',\n    mass: null,\n    name: 'Actinium',\n  },\n  {\n    number: 90,\n    isotopes: [\n      { nominal: 208, mass: 208.0179 },\n      { nominal: 209, mass: 209.017753 },\n      { nominal: 210, mass: 210.015094 },\n      { nominal: 211, mass: 211.014929 },\n      { nominal: 212, mass: 212.012988 },\n      { nominal: 213, mass: 213.013009 },\n      { nominal: 214, mass: 214.0115 },\n      { nominal: 215, mass: 215.0117248 },\n      { nominal: 216, mass: 216.011056 },\n      { nominal: 217, mass: 217.013117 },\n      { nominal: 218, mass: 218.013276 },\n      { nominal: 219, mass: 219.015537 },\n      { nominal: 220, mass: 220.015748 },\n      { nominal: 221, mass: 221.018184 },\n      { nominal: 222, mass: 222.018469 },\n      { nominal: 223, mass: 223.0208119 },\n      { nominal: 224, mass: 224.021464 },\n      { nominal: 225, mass: 225.0239514 },\n      { nominal: 226, mass: 226.0249034 },\n      { nominal: 227, mass: 227.0277042 },\n      { nominal: 228, mass: 228.0287413 },\n      { nominal: 229, mass: 229.0317627 },\n      { nominal: 230, mass: 230.0331341 },\n      { nominal: 231, mass: 231.0363046 },\n      { nominal: 232, mass: 232.0380558, abundance: 1 },\n      { nominal: 233, mass: 233.0415823 },\n      { nominal: 234, mass: 234.0436014 },\n      { nominal: 235, mass: 235.047255 },\n      { nominal: 236, mass: 236.049657 },\n      { nominal: 237, mass: 237.053629 },\n      { nominal: 238, mass: 238.0565 },\n      { nominal: 239, mass: 239.06077 },\n    ],\n    symbol: 'Th',\n    mass: 232.0380558,\n    name: 'Thorium',\n    monoisotopicMass: 232.0380558,\n  },\n  {\n    number: 91,\n    isotopes: [\n      { nominal: 212, mass: 212.023203 },\n      { nominal: 213, mass: 213.021109 },\n      { nominal: 214, mass: 214.020918 },\n      { nominal: 215, mass: 215.019183 },\n      { nominal: 216, mass: 216.019109 },\n      { nominal: 217, mass: 217.018325 },\n      { nominal: 218, mass: 218.020059 },\n      { nominal: 219, mass: 219.019904 },\n      { nominal: 220, mass: 220.021705 },\n      { nominal: 221, mass: 221.021875 },\n      { nominal: 222, mass: 222.023784 },\n      { nominal: 223, mass: 223.023963 },\n      { nominal: 224, mass: 224.0256176 },\n      { nominal: 225, mass: 225.026131 },\n      { nominal: 226, mass: 226.027948 },\n      { nominal: 227, mass: 227.0288054 },\n      { nominal: 228, mass: 228.0310517 },\n      { nominal: 229, mass: 229.0320972 },\n      { nominal: 230, mass: 230.034541 },\n      { nominal: 231, mass: 231.0358842, abundance: 1 },\n      { nominal: 232, mass: 232.0385917 },\n      { nominal: 233, mass: 233.0402472 },\n      { nominal: 234, mass: 234.0433072 },\n      { nominal: 235, mass: 235.045399 },\n      { nominal: 236, mass: 236.048668 },\n      { nominal: 237, mass: 237.051023 },\n      { nominal: 238, mass: 238.054637 },\n      { nominal: 239, mass: 239.05726 },\n      { nominal: 240, mass: 240.06098 },\n      { nominal: 241, mass: 241.06408 },\n    ],\n    symbol: 'Pa',\n    mass: 231.0358842,\n    name: 'Protactinium',\n    monoisotopicMass: 231.0358842,\n  },\n  {\n    number: 92,\n    isotopes: [\n      { nominal: 217, mass: 217.02466 },\n      { nominal: 218, mass: 218.023523 },\n      { nominal: 219, mass: 219.024999 },\n      { nominal: 220, mass: 220.02462 },\n      { nominal: 221, mass: 221.02628 },\n      { nominal: 222, mass: 222.026 },\n      { nominal: 223, mass: 223.027739 },\n      { nominal: 224, mass: 224.027605 },\n      { nominal: 225, mass: 225.029391 },\n      { nominal: 226, mass: 226.029339 },\n      { nominal: 227, mass: 227.031157 },\n      { nominal: 228, mass: 228.031371 },\n      { nominal: 229, mass: 229.0335063 },\n      { nominal: 230, mass: 230.0339401 },\n      { nominal: 231, mass: 231.0362939 },\n      { nominal: 232, mass: 232.0371563 },\n      { nominal: 233, mass: 233.0396355 },\n      { nominal: 234, mass: 234.0409523, abundance: 0.000054 },\n      { nominal: 235, mass: 235.0439301, abundance: 0.007204 },\n      { nominal: 236, mass: 236.0455682 },\n      { nominal: 237, mass: 237.0487304 },\n      { nominal: 238, mass: 238.0507884, abundance: 0.992742 },\n      { nominal: 239, mass: 239.0542935 },\n      { nominal: 240, mass: 240.0565934 },\n      { nominal: 241, mass: 241.06033 },\n      { nominal: 242, mass: 242.06293 },\n      { nominal: 243, mass: 243.06699 },\n    ],\n    symbol: 'U',\n    mass: 238.0289104616574,\n    name: 'Uranium',\n    monoisotopicMass: 238.0507884,\n  },\n  {\n    number: 93,\n    isotopes: [\n      { nominal: 219, mass: 219.03143 },\n      { nominal: 220, mass: 220.03254 },\n      { nominal: 221, mass: 221.03204 },\n      { nominal: 222, mass: 222.0333 },\n      { nominal: 223, mass: 223.03285 },\n      { nominal: 224, mass: 224.03422 },\n      { nominal: 225, mass: 225.033911 },\n      { nominal: 226, mass: 226.035188 },\n      { nominal: 227, mass: 227.034957 },\n      { nominal: 228, mass: 228.036067 },\n      { nominal: 229, mass: 229.036264 },\n      { nominal: 230, mass: 230.037828 },\n      { nominal: 231, mass: 231.038245 },\n      { nominal: 232, mass: 232.04011 },\n      { nominal: 233, mass: 233.040741 },\n      { nominal: 234, mass: 234.0428953 },\n      { nominal: 235, mass: 235.0440635 },\n      { nominal: 236, mass: 236.04657 },\n      { nominal: 237, mass: 237.0481736 },\n      { nominal: 238, mass: 238.0509466 },\n      { nominal: 239, mass: 239.0529392 },\n      { nominal: 240, mass: 240.056165 },\n      { nominal: 241, mass: 241.058253 },\n      { nominal: 242, mass: 242.06164 },\n      { nominal: 243, mass: 243.06428 },\n      { nominal: 244, mass: 244.06785 },\n      { nominal: 245, mass: 245.0708 },\n    ],\n    symbol: 'Np',\n    mass: null,\n    name: 'Neptunium',\n  },\n  {\n    number: 94,\n    isotopes: [\n      { nominal: 228, mass: 228.038732 },\n      { nominal: 229, mass: 229.040144 },\n      { nominal: 230, mass: 230.03965 },\n      { nominal: 231, mass: 231.041102 },\n      { nominal: 232, mass: 232.041185 },\n      { nominal: 233, mass: 233.042998 },\n      { nominal: 234, mass: 234.0433174 },\n      { nominal: 235, mass: 235.045286 },\n      { nominal: 236, mass: 236.0460581 },\n      { nominal: 237, mass: 237.0484098 },\n      { nominal: 238, mass: 238.0495601 },\n      { nominal: 239, mass: 239.0521636 },\n      { nominal: 240, mass: 240.0538138 },\n      { nominal: 241, mass: 241.0568517 },\n      { nominal: 242, mass: 242.0587428 },\n      { nominal: 243, mass: 243.0620036 },\n      { nominal: 244, mass: 244.0642053 },\n      { nominal: 245, mass: 245.067826 },\n      { nominal: 246, mass: 246.070205 },\n      { nominal: 247, mass: 247.07419 },\n    ],\n    symbol: 'Pu',\n    mass: null,\n    name: 'Plutonium',\n  },\n  {\n    number: 95,\n    isotopes: [\n      { nominal: 230, mass: 230.04609 },\n      { nominal: 231, mass: 231.04556 },\n      { nominal: 232, mass: 232.04645 },\n      { nominal: 233, mass: 233.04644 },\n      { nominal: 234, mass: 234.04773 },\n      { nominal: 235, mass: 235.047908 },\n      { nominal: 236, mass: 236.04943 },\n      { nominal: 237, mass: 237.049996 },\n      { nominal: 238, mass: 238.051985 },\n      { nominal: 239, mass: 239.0530247 },\n      { nominal: 240, mass: 240.0553 },\n      { nominal: 241, mass: 241.0568293 },\n      { nominal: 242, mass: 242.0595494 },\n      { nominal: 243, mass: 243.0613813 },\n      { nominal: 244, mass: 244.0642851 },\n      { nominal: 245, mass: 245.0664548 },\n      { nominal: 246, mass: 246.069775 },\n      { nominal: 247, mass: 247.07209 },\n      { nominal: 248, mass: 248.07575 },\n      { nominal: 249, mass: 249.07848 },\n    ],\n    symbol: 'Am',\n    name: 'Americium',\n    mass: null,\n  },\n  {\n    number: 96,\n    isotopes: [\n      { nominal: 232, mass: 232.04982 },\n      { nominal: 233, mass: 233.05077 },\n      { nominal: 234, mass: 234.05016 },\n      { nominal: 235, mass: 235.05154 },\n      { nominal: 236, mass: 236.051374 },\n      { nominal: 237, mass: 237.052869 },\n      { nominal: 238, mass: 238.053081 },\n      { nominal: 239, mass: 239.05491 },\n      { nominal: 240, mass: 240.0555297 },\n      { nominal: 241, mass: 241.0576532 },\n      { nominal: 242, mass: 242.058836 },\n      { nominal: 243, mass: 243.0613893 },\n      { nominal: 244, mass: 244.0627528 },\n      { nominal: 245, mass: 245.0654915 },\n      { nominal: 246, mass: 246.0672238 },\n      { nominal: 247, mass: 247.0703541 },\n      { nominal: 248, mass: 248.0723499 },\n      { nominal: 249, mass: 249.0759548 },\n      { nominal: 250, mass: 250.078358 },\n      { nominal: 251, mass: 251.082286 },\n      { nominal: 252, mass: 252.08487 },\n    ],\n    symbol: 'Cm',\n    name: 'Curium',\n    mass: null,\n  },\n  {\n    number: 97,\n    isotopes: [\n      { nominal: 234, mass: 234.05727 },\n      { nominal: 235, mass: 235.05658 },\n      { nominal: 236, mass: 236.05748 },\n      { nominal: 237, mass: 237.0571 },\n      { nominal: 238, mass: 238.0582 },\n      { nominal: 239, mass: 239.05824 },\n      { nominal: 240, mass: 240.05976 },\n      { nominal: 241, mass: 241.06016 },\n      { nominal: 242, mass: 242.06198 },\n      { nominal: 243, mass: 243.0630078 },\n      { nominal: 244, mass: 244.065181 },\n      { nominal: 245, mass: 245.0663618 },\n      { nominal: 246, mass: 246.068673 },\n      { nominal: 247, mass: 247.0703073 },\n      { nominal: 248, mass: 248.073088 },\n      { nominal: 249, mass: 249.0749877 },\n      { nominal: 250, mass: 250.0783167 },\n      { nominal: 251, mass: 251.080762 },\n      { nominal: 252, mass: 252.08431 },\n      { nominal: 253, mass: 253.08688 },\n      { nominal: 254, mass: 254.0906 },\n    ],\n    symbol: 'Bk',\n    name: 'Berkelium',\n    mass: null,\n  },\n  {\n    number: 98,\n    isotopes: [\n      { nominal: 237, mass: 237.062198 },\n      { nominal: 238, mass: 238.06149 },\n      { nominal: 239, mass: 239.06253 },\n      { nominal: 240, mass: 240.062256 },\n      { nominal: 241, mass: 241.06369 },\n      { nominal: 242, mass: 242.063754 },\n      { nominal: 243, mass: 243.06548 },\n      { nominal: 244, mass: 244.0660008 },\n      { nominal: 245, mass: 245.0680487 },\n      { nominal: 246, mass: 246.0688055 },\n      { nominal: 247, mass: 247.070965 },\n      { nominal: 248, mass: 248.0721851 },\n      { nominal: 249, mass: 249.0748539 },\n      { nominal: 250, mass: 250.0764062 },\n      { nominal: 251, mass: 251.0795886 },\n      { nominal: 252, mass: 252.0816272 },\n      { nominal: 253, mass: 253.0851345 },\n      { nominal: 254, mass: 254.087324 },\n      { nominal: 255, mass: 255.09105 },\n      { nominal: 256, mass: 256.09344 },\n    ],\n    symbol: 'Cf',\n    name: 'Californium',\n    mass: null,\n  },\n  {\n    number: 99,\n    isotopes: [\n      { nominal: 239, mass: 239.06823 },\n      { nominal: 240, mass: 240.06892 },\n      { nominal: 241, mass: 241.06856 },\n      { nominal: 242, mass: 242.06957 },\n      { nominal: 243, mass: 243.06951 },\n      { nominal: 244, mass: 244.07088 },\n      { nominal: 245, mass: 245.07125 },\n      { nominal: 246, mass: 246.0729 },\n      { nominal: 247, mass: 247.073622 },\n      { nominal: 248, mass: 248.075471 },\n      { nominal: 249, mass: 249.076411 },\n      { nominal: 250, mass: 250.07861 },\n      { nominal: 251, mass: 251.0799936 },\n      { nominal: 252, mass: 252.08298 },\n      { nominal: 253, mass: 253.0848257 },\n      { nominal: 254, mass: 254.0880222 },\n      { nominal: 255, mass: 255.090275 },\n      { nominal: 256, mass: 256.0936 },\n      { nominal: 257, mass: 257.09598 },\n      { nominal: 258, mass: 258.09952 },\n    ],\n    symbol: 'Es',\n    name: 'Einsteinium',\n    mass: null,\n  },\n  {\n    number: 100,\n    isotopes: [\n      { nominal: 241, mass: 241.07421 },\n      { nominal: 242, mass: 242.07343 },\n      { nominal: 243, mass: 243.07446 },\n      { nominal: 244, mass: 244.07404 },\n      { nominal: 245, mass: 245.07535 },\n      { nominal: 246, mass: 246.07535 },\n      { nominal: 247, mass: 247.07694 },\n      { nominal: 248, mass: 248.0771865 },\n      { nominal: 249, mass: 249.0789275 },\n      { nominal: 250, mass: 250.079521 },\n      { nominal: 251, mass: 251.08154 },\n      { nominal: 252, mass: 252.0824671 },\n      { nominal: 253, mass: 253.0851846 },\n      { nominal: 254, mass: 254.0868544 },\n      { nominal: 255, mass: 255.089964 },\n      { nominal: 256, mass: 256.0917745 },\n      { nominal: 257, mass: 257.0951061 },\n      { nominal: 258, mass: 258.09708 },\n      { nominal: 259, mass: 259.1006 },\n      { nominal: 260, mass: 260.10281 },\n    ],\n    symbol: 'Fm',\n    name: 'Fermium',\n    mass: null,\n  },\n  {\n    number: 101,\n    isotopes: [\n      { nominal: 245, mass: 245.08081 },\n      { nominal: 246, mass: 246.08171 },\n      { nominal: 247, mass: 247.08152 },\n      { nominal: 248, mass: 248.08282 },\n      { nominal: 249, mass: 249.08291 },\n      { nominal: 250, mass: 250.08441 },\n      { nominal: 251, mass: 251.084774 },\n      { nominal: 252, mass: 252.08643 },\n      { nominal: 253, mass: 253.087144 },\n      { nominal: 254, mass: 254.08959 },\n      { nominal: 255, mass: 255.0910841 },\n      { nominal: 256, mass: 256.09389 },\n      { nominal: 257, mass: 257.0955424 },\n      { nominal: 258, mass: 258.0984315 },\n      { nominal: 259, mass: 259.10051 },\n      { nominal: 260, mass: 260.10365 },\n      { nominal: 261, mass: 261.10583 },\n      { nominal: 262, mass: 262.1091 },\n    ],\n    symbol: 'Md',\n    name: 'Mendelevium',\n    mass: null,\n  },\n  {\n    number: 102,\n    isotopes: [\n      { nominal: 248, mass: 248.08655 },\n      { nominal: 249, mass: 249.0878 },\n      { nominal: 250, mass: 250.08756 },\n      { nominal: 251, mass: 251.08894 },\n      { nominal: 252, mass: 252.088967 },\n      { nominal: 253, mass: 253.0905641 },\n      { nominal: 254, mass: 254.090956 },\n      { nominal: 255, mass: 255.093191 },\n      { nominal: 256, mass: 256.0942829 },\n      { nominal: 257, mass: 257.0968878 },\n      { nominal: 258, mass: 258.09821 },\n      { nominal: 259, mass: 259.10103 },\n      { nominal: 260, mass: 260.10264 },\n      { nominal: 261, mass: 261.1057 },\n      { nominal: 262, mass: 262.10746 },\n      { nominal: 263, mass: 263.11071 },\n      { nominal: 264, mass: 264.11273 },\n    ],\n    symbol: 'No',\n    name: 'Nobelium',\n    mass: null,\n  },\n  {\n    number: 103,\n    isotopes: [\n      { nominal: 251, mass: 251.09418 },\n      { nominal: 252, mass: 252.09526 },\n      { nominal: 253, mass: 253.09509 },\n      { nominal: 254, mass: 254.09648 },\n      { nominal: 255, mass: 255.096562 },\n      { nominal: 256, mass: 256.098494 },\n      { nominal: 257, mass: 257.099418 },\n      { nominal: 258, mass: 258.10176 },\n      { nominal: 259, mass: 259.102902 },\n      { nominal: 260, mass: 260.1055 },\n      { nominal: 261, mass: 261.10688 },\n      { nominal: 262, mass: 262.10961 },\n      { nominal: 263, mass: 263.11136 },\n      { nominal: 264, mass: 264.1142 },\n      { nominal: 265, mass: 265.11619 },\n      { nominal: 266, mass: 266.11983 },\n    ],\n    symbol: 'Lr',\n    name: 'Lawrencium',\n    mass: null,\n  },\n  {\n    number: 104,\n    isotopes: [\n      { nominal: 253, mass: 253.10044 },\n      { nominal: 254, mass: 254.10005 },\n      { nominal: 255, mass: 255.10127 },\n      { nominal: 256, mass: 256.101152 },\n      { nominal: 257, mass: 257.102918 },\n      { nominal: 258, mass: 258.103428 },\n      { nominal: 259, mass: 259.105596 },\n      { nominal: 260, mass: 260.10644 },\n      { nominal: 261, mass: 261.108773 },\n      { nominal: 262, mass: 262.10992 },\n      { nominal: 263, mass: 263.11249 },\n      { nominal: 264, mass: 264.11388 },\n      { nominal: 265, mass: 265.11668 },\n      { nominal: 266, mass: 266.11817 },\n      { nominal: 267, mass: 267.12179 },\n      { nominal: 268, mass: 268.12397 },\n    ],\n    symbol: 'Rf',\n    name: 'Rutherfordium',\n    mass: null,\n  },\n  {\n    number: 105,\n    isotopes: [\n      { nominal: 255, mass: 255.10707 },\n      { nominal: 256, mass: 256.10789 },\n      { nominal: 257, mass: 257.10758 },\n      { nominal: 258, mass: 258.10928 },\n      { nominal: 259, mass: 259.109492 },\n      { nominal: 260, mass: 260.1113 },\n      { nominal: 261, mass: 261.11192 },\n      { nominal: 262, mass: 262.11407 },\n      { nominal: 263, mass: 263.11499 },\n      { nominal: 264, mass: 264.11741 },\n      { nominal: 265, mass: 265.11861 },\n      { nominal: 266, mass: 266.12103 },\n      { nominal: 267, mass: 267.12247 },\n      { nominal: 268, mass: 268.12567 },\n      { nominal: 269, mass: 269.12791 },\n      { nominal: 270, mass: 270.13136 },\n    ],\n    symbol: 'Db',\n    name: 'Dubnium',\n    mass: null,\n  },\n  {\n    number: 106,\n    isotopes: [\n      { nominal: 258, mass: 258.11298 },\n      { nominal: 259, mass: 259.1144 },\n      { nominal: 260, mass: 260.114384 },\n      { nominal: 261, mass: 261.115949 },\n      { nominal: 262, mass: 262.116337 },\n      { nominal: 263, mass: 263.11829 },\n      { nominal: 264, mass: 264.11893 },\n      { nominal: 265, mass: 265.12109 },\n      { nominal: 266, mass: 266.12198 },\n      { nominal: 267, mass: 267.12436 },\n      { nominal: 268, mass: 268.12539 },\n      { nominal: 269, mass: 269.12863 },\n      { nominal: 270, mass: 270.13043 },\n      { nominal: 271, mass: 271.13393 },\n      { nominal: 272, mass: 272.13589 },\n      { nominal: 273, mass: 273.13958 },\n    ],\n    symbol: 'Sg',\n    name: 'Seaborgium',\n    mass: null,\n  },\n  {\n    number: 107,\n    isotopes: [\n      { nominal: 260, mass: 260.12166 },\n      { nominal: 261, mass: 261.12145 },\n      { nominal: 262, mass: 262.12297 },\n      { nominal: 263, mass: 263.12292 },\n      { nominal: 264, mass: 264.12459 },\n      { nominal: 265, mass: 265.12491 },\n      { nominal: 266, mass: 266.12679 },\n      { nominal: 267, mass: 267.1275 },\n      { nominal: 268, mass: 268.12969 },\n      { nominal: 269, mass: 269.13042 },\n      { nominal: 270, mass: 270.13336 },\n      { nominal: 271, mass: 271.13526 },\n      { nominal: 272, mass: 272.13826 },\n      { nominal: 273, mass: 273.14024 },\n      { nominal: 274, mass: 274.14355 },\n      { nominal: 275, mass: 275.14567 },\n    ],\n    symbol: 'Bh',\n    name: 'Bohrium',\n    mass: null,\n  },\n  {\n    number: 108,\n    isotopes: [\n      { nominal: 263, mass: 263.12852 },\n      { nominal: 264, mass: 264.128357 },\n      { nominal: 265, mass: 265.129793 },\n      { nominal: 266, mass: 266.130046 },\n      { nominal: 267, mass: 267.13167 },\n      { nominal: 268, mass: 268.13186 },\n      { nominal: 269, mass: 269.13375 },\n      { nominal: 270, mass: 270.13429 },\n      { nominal: 271, mass: 271.13717 },\n      { nominal: 272, mass: 272.1385 },\n      { nominal: 273, mass: 273.14168 },\n      { nominal: 274, mass: 274.1433 },\n      { nominal: 275, mass: 275.14667 },\n      { nominal: 276, mass: 276.14846 },\n      { nominal: 277, mass: 277.1519 },\n    ],\n    symbol: 'Hs',\n    name: 'Hassium',\n    mass: null,\n  },\n  {\n    number: 109,\n    isotopes: [\n      { nominal: 265, mass: 265.136 },\n      { nominal: 266, mass: 266.13737 },\n      { nominal: 267, mass: 267.13719 },\n      { nominal: 268, mass: 268.13865 },\n      { nominal: 269, mass: 269.13882 },\n      { nominal: 270, mass: 270.14033 },\n      { nominal: 271, mass: 271.14074 },\n      { nominal: 272, mass: 272.14341 },\n      { nominal: 273, mass: 273.1444 },\n      { nominal: 274, mass: 274.14724 },\n      { nominal: 275, mass: 275.14882 },\n      { nominal: 276, mass: 276.15159 },\n      { nominal: 277, mass: 277.15327 },\n      { nominal: 278, mass: 278.15631 },\n      { nominal: 279, mass: 279.15808 },\n    ],\n    symbol: 'Mt',\n    name: 'Meitnerium',\n    mass: null,\n  },\n  {\n    number: 110,\n    isotopes: [\n      { nominal: 267, mass: 267.14377 },\n      { nominal: 268, mass: 268.14348 },\n      { nominal: 269, mass: 269.144752 },\n      { nominal: 270, mass: 270.144584 },\n      { nominal: 271, mass: 271.14595 },\n      { nominal: 272, mass: 272.14602 },\n      { nominal: 273, mass: 273.14856 },\n      { nominal: 274, mass: 274.14941 },\n      { nominal: 275, mass: 275.15203 },\n      { nominal: 276, mass: 276.15303 },\n      { nominal: 277, mass: 277.15591 },\n      { nominal: 278, mass: 278.15704 },\n      { nominal: 279, mass: 279.1601 },\n      { nominal: 280, mass: 280.16131 },\n      { nominal: 281, mass: 281.16451 },\n    ],\n    symbol: 'Ds',\n    name: 'Darmstadtium',\n    mass: null,\n  },\n  {\n    number: 111,\n    isotopes: [\n      { nominal: 272, mass: 272.15327 },\n      { nominal: 273, mass: 273.15313 },\n      { nominal: 274, mass: 274.15525 },\n      { nominal: 275, mass: 275.15594 },\n      { nominal: 276, mass: 276.15833 },\n      { nominal: 277, mass: 277.15907 },\n      { nominal: 278, mass: 278.16149 },\n      { nominal: 279, mass: 279.16272 },\n      { nominal: 280, mass: 280.16514 },\n      { nominal: 281, mass: 281.16636 },\n      { nominal: 282, mass: 282.16912 },\n      { nominal: 283, mass: 283.17054 },\n    ],\n    symbol: 'Rg',\n    name: 'Roentgenium',\n    mass: null,\n  },\n  {\n    number: 112,\n    isotopes: [\n      { nominal: 276, mass: 276.16141 },\n      { nominal: 277, mass: 277.16364 },\n      { nominal: 278, mass: 278.16416 },\n      { nominal: 279, mass: 279.16654 },\n      { nominal: 280, mass: 280.16715 },\n      { nominal: 281, mass: 281.16975 },\n      { nominal: 282, mass: 282.1705 },\n      { nominal: 283, mass: 283.17327 },\n      { nominal: 284, mass: 284.17416 },\n      { nominal: 285, mass: 285.17712 },\n    ],\n    symbol: 'Cn',\n    name: 'Copernicium',\n    mass: null,\n  },\n  {\n    number: 113,\n    isotopes: [\n      { nominal: 278, mass: 278.17058 },\n      { nominal: 279, mass: 279.17095 },\n      { nominal: 280, mass: 280.17293 },\n      { nominal: 281, mass: 281.17348 },\n      { nominal: 282, mass: 282.17567 },\n      { nominal: 283, mass: 283.17657 },\n      { nominal: 284, mass: 284.17873 },\n      { nominal: 285, mass: 285.17973 },\n      { nominal: 286, mass: 286.18221 },\n      { nominal: 287, mass: 287.18339 },\n    ],\n    symbol: 'Nh',\n    name: 'Nihonium',\n    mass: null,\n  },\n  {\n    number: 114,\n    isotopes: [\n      { nominal: 285, mass: 285.18364 },\n      { nominal: 286, mass: 286.18423 },\n      { nominal: 287, mass: 287.18678 },\n      { nominal: 288, mass: 288.18757 },\n      { nominal: 289, mass: 289.19042 },\n    ],\n    symbol: 'Fl',\n    name: 'Flerovium',\n    mass: null,\n  },\n  {\n    number: 115,\n    isotopes: [\n      { nominal: 287, mass: 287.1907 },\n      { nominal: 288, mass: 288.19274 },\n      { nominal: 289, mass: 289.19363 },\n      { nominal: 290, mass: 290.19598 },\n      { nominal: 291, mass: 291.19707 },\n    ],\n    symbol: 'Mc',\n    name: 'Moscovium',\n    mass: null,\n  },\n  {\n    number: 116,\n    isotopes: [\n      { nominal: 289, mass: 289.19816 },\n      { nominal: 290, mass: 290.19864 },\n      { nominal: 291, mass: 291.20108 },\n      { nominal: 292, mass: 292.20174 },\n      { nominal: 293, mass: 293.20449 },\n    ],\n    symbol: 'Lv',\n    name: 'Livermorium',\n    mass: null,\n  },\n  {\n    number: 117,\n    isotopes: [\n      { nominal: 291, mass: 291.20553 },\n      { nominal: 292, mass: 292.20746 },\n      { nominal: 293, mass: 293.20824 },\n      { nominal: 294, mass: 294.21046 },\n    ],\n    symbol: 'Ts',\n    name: 'Teennessine',\n    mass: null,\n  },\n  {\n    number: 118,\n    isotopes: [\n      { nominal: 293, mass: 293.21356 },\n      { nominal: 294, mass: 294.21392 },\n      { nominal: 295, mass: 295.21624 },\n    ],\n    symbol: 'Og',\n    name: 'Oganesson',\n    mass: null,\n  },\n];\n","import { elementsAndIsotopes } from './elementsAndIsotopes.js';\n\nexport const elements = elementsAndIsotopes.map((element) => ({\n  number: element.number,\n  symbol: element.symbol,\n  mass: element.mass,\n  name: element.name,\n  monoisotopicMass: element.monoisotopicMass,\n}));\n","import { elementsAndIsotopes } from './elementsAndIsotopes.js';\n\nexport const elementsAndIsotopesObject = {};\nelementsAndIsotopes.forEach((element) => {\n  elementsAndIsotopesObject[element.symbol] = element;\n});\n","import { elementsAndIsotopes } from './elementsAndIsotopes.js';\n\nexport const elementsAndStableIsotopes = JSON.parse(\n  JSON.stringify(elementsAndIsotopes),\n);\n\nelementsAndStableIsotopes.forEach((element) => {\n  element.isotopes = element.isotopes.filter((i) => i.abundance > 0);\n});\n","import { elementsAndStableIsotopes } from './elementsAndStableIsotopes.js';\n\nexport const elementsAndStableIsotopesObject = {};\nelementsAndStableIsotopes.forEach((element) => {\n  elementsAndStableIsotopesObject[element.symbol] = element;\n});\n","import { elements } from './elements.js';\n\nexport const elementsObject = {};\nelements.forEach((element) => {\n  elementsObject[element.symbol] = element;\n});\n","import { elementsAndIsotopes } from './elementsAndIsotopes.js';\n\nexport const stableIsotopesObject = {};\nfor (const element of elementsAndIsotopes) {\n  let abundance = 0;\n  let mostAbundant = 0;\n  for (const isotope of element.isotopes) {\n    if (isotope.abundance > abundance) {\n      abundance = isotope.abundance;\n      mostAbundant = isotope.nominal;\n    }\n  }\n\n  for (const isotope of element.isotopes) {\n    if (isotope.abundance === 0) continue;\n\n    const entry = {\n      name: element.name,\n      mass: isotope.mass,\n      symbol: element.symbol,\n    };\n    if (isotope.nominal === mostAbundant) {\n      entry.mostAbundant = true;\n    }\n    stableIsotopesObject[isotope.nominal + element.symbol] = entry;\n  }\n}\n","import { elementsAndIsotopesObject as elements } from './elementsAndIsotopesObject';\n\nexport const isotopesObject = {};\nObject.keys(elements).forEach((key) => {\n  let e = elements[key];\n  e.isotopes.forEach((i) => {\n    isotopesObject[i.nominal + key] = {\n      abundance: i.abundance,\n      mass: i.mass,\n    };\n  });\n});\n","export const unsaturationsObject = {\n  O: 0,\n  N: 1,\n  H: -1,\n  Na: -1,\n  K: -1,\n  Li: -1,\n  Ca: -2,\n  C: 2,\n  F: -1,\n  Si: 2,\n  Cl: -1,\n  Br: -1,\n  I: -1,\n  S: 0,\n  P: 1,\n};\n","import { elementsObject } from 'chemical-elements';\n\nconst elements = Object.keys(elementsObject).sort(\n  (a, b) => b.length - a.length,\n);\n\n/**\n * Ensure that the mf has been entered with capital letters and not only lowercase\n * If there is only lowercase we try to capitalize the mf\n * @param {string} mf\n */\n\nexport function ensureCase(mf) {\n  for (let i = 0; i < mf.length; i++) {\n    if (mf.charCodeAt(i) > 64 && mf.charCodeAt(i) < 91) {\n      return mf;\n    }\n  }\n  let parts = mf.replace(/([a-z]*)([^a-z]*)/g, '$1 $2 ').split(/ +/);\n  for (let i = 0; i < parts.length; i++) {\n    if (parts[i].match(/^[a-z]$/)) {\n      parts[i] = parts[i].toUpperCase();\n    } else if (parts[i].match(/^[a-z]+$/)) {\n      let newPart = '';\n      for (let j = 0; j < parts[i].length; j++) {\n        let two = parts[i].substr(j, 2);\n        let one = parts[i].charAt(j).toUpperCase();\n        if (\n          ['c', 'h', 'o', 'n'].includes(two.charAt(0)) &&\n          ['h', 'o', 'n'].includes(two.charAt(1))\n        ) {\n          newPart += two.toUpperCase();\n          j++;\n        } else {\n          two = two.charAt(0).toUpperCase() + two.charAt(1);\n          if (elements.includes(two)) {\n            newPart += two;\n            j++;\n          } else if (elements.includes(one)) {\n            newPart += one;\n          } else {\n            return mf;\n          }\n        }\n      }\n      parts[i] = newPart;\n    }\n  }\n  return parts.join('');\n}\n","export const groups = [{\"symbol\":\"Abu\",\"name\":\"2-Aminobutyric acid diradical\",\"mf\":\"C4H7NO\",\"ocl\":{\"value\":\"dazHPBPOEgEInVZjcH@\",\"coordinates\":\"!Bb@I~@Ha}_c~H@m]}bGt\"},\"mass\":85.10463700109551,\"monoisotopicMass\":85.05276384961,\"unsaturation\":2,\"elements\":[{\"symbol\":\"C\",\"number\":4},{\"symbol\":\"H\",\"number\":7},{\"symbol\":\"N\",\"number\":1},{\"symbol\":\"O\",\"number\":1}]},{\"symbol\":\"Acet\",\"name\":\"Acetyl\",\"mf\":\"C2H3O\",\"ocl\":{\"value\":\"gCaHDEeIi`@\",\"coordinates\":\"!BbOq~@Ha}\"},\"mass\":43.04469897995611,\"monoisotopicMass\":43.01838971626,\"unsaturation\":1,\"elements\":[{\"symbol\":\"C\",\"number\":2},{\"symbol\":\"H\",\"number\":3},{\"symbol\":\"O\",\"number\":1}]},{\"symbol\":\"Acm\",\"name\":\"Acetamidomethyl\",\"mf\":\"C3H6NO\",\"ocl\":{\"value\":\"gGYHDPliJuS@@\",\"coordinates\":\"!BbOrH_Xc|_`BH_P\"},\"mass\":72.08596035030448,\"monoisotopicMass\":72.04493881738,\"unsaturation\":1,\"elements\":[{\"symbol\":\"C\",\"number\":3},{\"symbol\":\"H\",\"number\":6},{\"symbol\":\"N\",\"number\":1},{\"symbol\":\"O\",\"number\":1}]},{\"symbol\":\"Adao\",\"name\":\"Adamantyloxy\",\"mf\":\"C10H15O\",\"ocl\":{\"value\":\"dc\\\\H`HAYRVeV^dUGZjjjj@@\",\"coordinates\":\"!B]BOXN`EP}CdB\\\\tbZ@Ijh~hRELdOBBp\"},\"mass\":151.2258752025074,\"monoisotopicMass\":151.11229010302,\"unsaturation\":5,\"elements\":[{\"symbol\":\"C\",\"number\":10},{\"symbol\":\"H\",\"number\":15},{\"symbol\":\"O\",\"number\":1}]},{\"symbol\":\"Aib\",\"name\":\"alpha-Aminoisobutyric acid diradical\",\"mf\":\"C4H7NO\",\"ocl\":{\"value\":\"dazHPBPOGgEInfZj@@\",\"coordinates\":\"!Bb@I~@Ha}b@K|uwwWbGt\"},\"mass\":85.10463700109551,\"monoisotopicMass\":85.05276384961,\"unsaturation\":2,\"elements\":[{\"symbol\":\"C\",\"number\":4},{\"symbol\":\"H\",\"number\":7},{\"symbol\":\"N\",\"number\":1},{\"symbol\":\"O\",\"number\":1}]},{\"symbol\":\"Ala\",\"name\":\"Alanine diradical\",\"mf\":\"C3H5NO\",\"kind\":\"aa\",\"oneLetter\":\"A\",\"alternativeOneLetter\":\"α\",\"ocl\":{\"value\":\"gNyDBaxmqR[fZjZ@\",\"coordinates\":\"!BbOr~@H`}bOr~Wxb}\"},\"mass\":71.07801959624871,\"monoisotopicMass\":71.03711378515,\"unsaturation\":2,\"elements\":[{\"symbol\":\"C\",\"number\":3},{\"symbol\":\"H\",\"number\":5},{\"symbol\":\"N\",\"number\":1},{\"symbol\":\"O\",\"number\":1}]},{\"symbol\":\"Arg\",\"name\":\"Arginine diradical\",\"mf\":\"C6H12N4O\",\"kind\":\"aa\",\"oneLetter\":\"R\",\"alternativeOneLetter\":\"ρ\",\"ocl\":{\"value\":\"dkLhPBgSPOEgEInWUijjihr@@\",\"coordinates\":\"!Bb@I~@Ha}_c~H@m]}bGvHHa}b@I~@Ha}\"},\"mass\":156.18592219918227,\"monoisotopicMass\":156.10111102405,\"unsaturation\":4,\"elements\":[{\"symbol\":\"C\",\"number\":6},{\"symbol\":\"H\",\"number\":12},{\"symbol\":\"N\",\"number\":4},{\"symbol\":\"O\",\"number\":1}]},{\"symbol\":\"Argp\",\"name\":\"Arginine triradical\",\"mf\":\"C6H11N4O\",\"ocl\":{\"value\":\"dglhpHpil@gWDEI[UYZfjji`T@\",\"coordinates\":\"!BbGvHGx@bGvH@ha}bOrH_Wxb@KW_Wx@bGt\"},\"mass\":155.1779814451265,\"monoisotopicMass\":155.09328599182,\"unsaturation\":5,\"elements\":[{\"symbol\":\"C\",\"number\":6},{\"symbol\":\"H\",\"number\":11},{\"symbol\":\"N\",\"number\":4},{\"symbol\":\"O\",\"number\":1}]},{\"symbol\":\"Asn\",\"name\":\"Asparagine diradical\",\"mf\":\"C4H6N2O2\",\"kind\":\"aa\",\"oneLetter\":\"N\",\"alternativeOneLetter\":\"η\",\"ocl\":{\"value\":\"deeDPBeACqYqR[ezZjZL`@\",\"coordinates\":\"!BbGu~Ox`B_`BH_X`Bb@I~@Ha}\"},\"mass\":114.10280438280381,\"monoisotopicMass\":114.04292744137999,\"unsaturation\":4,\"elements\":[{\"symbol\":\"C\",\"number\":4},{\"symbol\":\"H\",\"number\":6},{\"symbol\":\"N\",\"number\":2},{\"symbol\":\"O\",\"number\":2}]},{\"symbol\":\"Asnp\",\"name\":\"Asparagine triradical\",\"mf\":\"C4H5N2O2\",\"ocl\":{\"value\":\"dmUDpH[E@IEqgqRVvVijjXi@@\",\"coordinates\":\"!Bb@JH_Wxb@JH_Wxb@KW_Wx@bGt\"},\"mass\":113.09486362874803,\"monoisotopicMass\":113.03510240915,\"unsaturation\":5,\"elements\":[{\"symbol\":\"C\",\"number\":4},{\"symbol\":\"H\",\"number\":5},{\"symbol\":\"N\",\"number\":2},{\"symbol\":\"O\",\"number\":2}]},{\"symbol\":\"Asp\",\"name\":\"Aspartic acid diradical\",\"mf\":\"C4H5NO3\",\"kind\":\"aa\",\"oneLetter\":\"D\",\"alternativeOneLetter\":\"δ\",\"ocl\":{\"value\":\"defLPBPYCqYqR[ezZjZL`@\",\"coordinates\":\"!BbGu~Ox`B_`BH_X`Bb@I~@Ha}\"},\"mass\":115.08756534162052,\"monoisotopicMass\":115.02694302429,\"unsaturation\":4,\"elements\":[{\"symbol\":\"C\",\"number\":4},{\"symbol\":\"H\",\"number\":5},{\"symbol\":\"N\",\"number\":1},{\"symbol\":\"O\",\"number\":3}]},{\"symbol\":\"Aspp\",\"name\":\"Aspartic acid triradical\",\"mf\":\"C4H4NO3\",\"ocl\":{\"value\":\"dmVLpFcE@IEqgqRVvVijjXi@@\",\"coordinates\":\"!Bb@JH_Wxb@JH_Wxb@KW_Wx@bGt\"},\"mass\":114.07962458756472,\"monoisotopicMass\":114.01911799206,\"unsaturation\":5,\"elements\":[{\"symbol\":\"C\",\"number\":4},{\"symbol\":\"H\",\"number\":4},{\"symbol\":\"N\",\"number\":1},{\"symbol\":\"O\",\"number\":3}]},{\"symbol\":\"Asu\",\"name\":\"alpha-Aminosuberic acid diradical\",\"mf\":\"C8H13NO3\",\"ocl\":{\"value\":\"dgnLPBP{CqYqR[euVfjjihr@@\",\"coordinates\":\"!BbGu~Ox`B_`BH_Xc|bOrH_X`BbGvHGx@bGt\"},\"mass\":171.19403496100773,\"monoisotopicMass\":171.08954328213002,\"unsaturation\":4,\"elements\":[{\"symbol\":\"C\",\"number\":8},{\"symbol\":\"H\",\"number\":13},{\"symbol\":\"N\",\"number\":1},{\"symbol\":\"O\",\"number\":3}]},{\"symbol\":\"Asup\",\"name\":\"alpha-Aminosuberic acid triradical\",\"mf\":\"C8H12NO3\",\"ocl\":{\"value\":\"do^LpEcG@IMqoqRVuUejZjjibT@\",\"coordinates\":\"!BbOrH_Wxb@JH_Xc|bGvHHa}_c~H@m]}_`BH_P\"},\"mass\":170.18609420695194,\"monoisotopicMass\":170.0817182499,\"unsaturation\":5,\"elements\":[{\"symbol\":\"C\",\"number\":8},{\"symbol\":\"H\",\"number\":12},{\"symbol\":\"N\",\"number\":1},{\"symbol\":\"O\",\"number\":3}]},{\"symbol\":\"Boc\",\"name\":\"t-Butoxycarbonyl\",\"mf\":\"C5H9O2\",\"ocl\":{\"value\":\"daxD`DpEeImjZj@@\",\"coordinates\":\"!B|Ou~_A||Ow}mC}_O@\"},\"mass\":101.12395611881479,\"monoisotopicMass\":101.06025452921,\"unsaturation\":1,\"elements\":[{\"symbol\":\"C\",\"number\":5},{\"symbol\":\"H\",\"number\":9},{\"symbol\":\"O\",\"number\":2}]},{\"symbol\":\"Bom\",\"name\":\"Benzyloxymethyl\",\"mf\":\"C8H9O\",\"ocl\":{\"value\":\"deTH`DAYRUYTYj`@@@\",\"coordinates\":\"!B|Gsp__A||Owp_Gy|Gwp_Wy\"},\"mass\":121.15675888470227,\"monoisotopicMass\":121.06533990964,\"unsaturation\":7,\"elements\":[{\"symbol\":\"C\",\"number\":8},{\"symbol\":\"H\",\"number\":9},{\"symbol\":\"O\",\"number\":1}]},{\"symbol\":\"Brz\",\"name\":\"2-Bromobenzyloxycarbonyl\",\"mf\":\"C8H6BrO2\",\"ocl\":{\"value\":\"dcLDPDpEd\\\\QImYgWYjB@@@\",\"coordinates\":\"!Bb@I~@Hb}b@JH_X`B_c}~@Hb}bGu~Op\"},\"mass\":214.03586932736317,\"monoisotopicMass\":212.95511703252,\"unsaturation\":9,\"elements\":[{\"symbol\":\"C\",\"number\":8},{\"symbol\":\"H\",\"number\":6},{\"symbol\":\"Br\",\"number\":1},{\"symbol\":\"O\",\"number\":2}]},{\"symbol\":\"Bu\",\"name\":\"Butyl\",\"mf\":\"C4H9\",\"ocl\":{\"value\":\"gJPH@liJuP@\",\"coordinates\":\"!B@Fp@XpAl@FL\"},\"mass\":57.114410373442986,\"monoisotopicMass\":57.07042529007,\"unsaturation\":-1,\"elements\":[{\"symbol\":\"C\",\"number\":4},{\"symbol\":\"H\",\"number\":9}]},{\"symbol\":\"Bum\",\"name\":\"t-Butoxymethyl\",\"mf\":\"C5H11O\",\"ocl\":{\"value\":\"gNqHDEeIVjj`@\",\"coordinates\":\"!B@FL@[@AcXs|@Xvp@\"},\"mass\":87.14043270260808,\"monoisotopicMass\":87.08098997409999,\"unsaturation\":-1,\"elements\":[{\"symbol\":\"C\",\"number\":5},{\"symbol\":\"H\",\"number\":11},{\"symbol\":\"O\",\"number\":1}]},{\"symbol\":\"Bz\",\"name\":\"Benzoyl\",\"mf\":\"C7H5O\",\"ocl\":{\"value\":\"didH`DAYR[e^FX@@@@\",\"coordinates\":\"!BbOq~@Ha}b@I~Oxa}bGu~Op\"},\"mass\":105.1142599717439,\"monoisotopicMass\":105.03403978072,\"unsaturation\":9,\"elements\":[{\"symbol\":\"C\",\"number\":7},{\"symbol\":\"H\",\"number\":5},{\"symbol\":\"O\",\"number\":1}]},{\"symbol\":\"Bzl\",\"name\":\"Benzyl\",\"mf\":\"C7H7\",\"ocl\":{\"value\":\"daD@`@VTeeVz`@@@\",\"coordinates\":\"!B|Gsp_A|_gp_A}_g|\"},\"mass\":91.13073655553718,\"monoisotopicMass\":91.05477522561,\"unsaturation\":7,\"elements\":[{\"symbol\":\"C\",\"number\":7},{\"symbol\":\"H\",\"number\":7}]},{\"symbol\":\"Bn\",\"name\":\"Benzyl\",\"mf\":\"C7H7\",\"ocl\":{\"value\":\"daD@`@VTeeVz`@@@\",\"coordinates\":\"!B|Gsp_A|_gp_A}_g|\"},\"mass\":91.13073655553718,\"monoisotopicMass\":91.05477522561,\"unsaturation\":7,\"elements\":[{\"symbol\":\"C\",\"number\":7},{\"symbol\":\"H\",\"number\":7}]},{\"symbol\":\"Bzlo\",\"name\":\"Benzyloxy\",\"mf\":\"C7H7O\",\"ocl\":{\"value\":\"didH`HAYRUe^Fh@@@@\",\"coordinates\":\"!B|Gwp_OC}|Gq~_A}|Gu~_p\"},\"mass\":107.13014147985547,\"monoisotopicMass\":107.04968984518,\"unsaturation\":7,\"elements\":[{\"symbol\":\"C\",\"number\":7},{\"symbol\":\"H\",\"number\":7},{\"symbol\":\"O\",\"number\":1}]},{\"symbol\":\"Cha\",\"name\":\"beta-Cyclohexylalanine diradical\",\"mf\":\"C9H15NO\",\"ocl\":{\"value\":\"dknHPBPOEgEInWe]NZjjjcH@\",\"coordinates\":\"!Bb@I~@Ha}_c~H@m]}bGvH@gxbGvH_Wx\"},\"mass\":153.22184251721796,\"monoisotopicMass\":153.11536410745,\"unsaturation\":4,\"elements\":[{\"symbol\":\"C\",\"number\":9},{\"symbol\":\"H\",\"number\":15},{\"symbol\":\"N\",\"number\":1},{\"symbol\":\"O\",\"number\":1}]},{\"symbol\":\"Chxo\",\"name\":\"Cyclohexyloxy\",\"mf\":\"C6H11O\",\"ocl\":{\"value\":\"daDH`HAYRVU[jjj@@\",\"coordinates\":\"!B|Gsp_A|_gp_A}_g|\"},\"mass\":99.15116859934332,\"monoisotopicMass\":99.08098997409999,\"unsaturation\":1,\"elements\":[{\"symbol\":\"C\",\"number\":6},{\"symbol\":\"H\",\"number\":11},{\"symbol\":\"O\",\"number\":1}]},{\"symbol\":\"Cit\",\"name\":\"Citrulline diradical\",\"mf\":\"C6H11N3O2\",\"ocl\":{\"value\":\"dkODPBdttOEgEInWUijjihr@@\",\"coordinates\":\"!Bb@I~@Ha}_c~H@m]}bGvHHa}b@I~@Ha}\"},\"mass\":157.170683157999,\"monoisotopicMass\":157.08512660696,\"unsaturation\":4,\"elements\":[{\"symbol\":\"C\",\"number\":6},{\"symbol\":\"H\",\"number\":11},{\"symbol\":\"N\",\"number\":3},{\"symbol\":\"O\",\"number\":2}]},{\"symbol\":\"Citp\",\"name\":\"Citrulline triradical\",\"mf\":\"C6H10N3O2\",\"ocl\":{\"value\":\"dgoDpHJ\\\\l@gWDEI[UYZfjji`T@\",\"coordinates\":\"!BbGvHGx@bGvH@ha}bOrH_Wxb@KW_Wx@bGt\"},\"mass\":156.16274240394318,\"monoisotopicMass\":156.07730157473,\"unsaturation\":5,\"elements\":[{\"symbol\":\"C\",\"number\":6},{\"symbol\":\"H\",\"number\":10},{\"symbol\":\"N\",\"number\":3},{\"symbol\":\"O\",\"number\":2}]},{\"symbol\":\"Clz\",\"name\":\"2-Chlorobenzyloxycarbonyl\",\"mf\":\"C8H6ClO2\",\"ocl\":{\"value\":\"dcLDPDpEdXaImYgWYjB@@@\",\"coordinates\":\"!Bb@I~@Hb}b@JH_X`B_c}~@Hb}bGu~Op\"},\"mass\":169.58527912946118,\"monoisotopicMass\":169.00563211451998,\"unsaturation\":9,\"elements\":[{\"symbol\":\"C\",\"number\":8},{\"symbol\":\"H\",\"number\":6},{\"symbol\":\"Cl\",\"number\":1},{\"symbol\":\"O\",\"number\":2}]},{\"symbol\":\"Cp\",\"name\":\"Cyclopentadienyl\",\"mf\":\"C5H5\",\"ocl\":{\"value\":\"gFpH@liLimRp@\",\"coordinates\":\"!B\\\\OtPThyEGl@fP\"},\"mass\":65.09338325395512,\"monoisotopicMass\":65.03912516115,\"unsaturation\":5,\"elements\":[{\"symbol\":\"C\",\"number\":5},{\"symbol\":\"H\",\"number\":5}]},{\"symbol\":\"Cys\",\"name\":\"Cysteine diradical\",\"mf\":\"C3H5NOS\",\"kind\":\"aa\",\"oneLetter\":\"C\",\"alternativeOneLetter\":\"ς\",\"ocl\":{\"value\":\"dazHpBPOEgG`aInVZjcH@\",\"coordinates\":\"!Bb@I~@Ha}_c~H@m]}bGt\"},\"mass\":103.14280700237578,\"monoisotopicMass\":103.00918495955,\"unsaturation\":2,\"elements\":[{\"symbol\":\"C\",\"number\":3},{\"symbol\":\"H\",\"number\":5},{\"symbol\":\"N\",\"number\":1},{\"symbol\":\"O\",\"number\":1},{\"symbol\":\"S\",\"number\":1}]},{\"symbol\":\"Cysp\",\"name\":\"Cysteine triradical\",\"mf\":\"C3H4NOS\",\"ocl\":{\"value\":\"diFHHBD@f@agGoEIVVjjfLP@\",\"coordinates\":\"!BbGvHHa}_c~HM]}_`BH_P\"},\"mass\":102.13486624831998,\"monoisotopicMass\":102.00135992732,\"unsaturation\":3,\"elements\":[{\"symbol\":\"C\",\"number\":3},{\"symbol\":\"H\",\"number\":4},{\"symbol\":\"N\",\"number\":1},{\"symbol\":\"O\",\"number\":1},{\"symbol\":\"S\",\"number\":1}]},{\"symbol\":\"D\",\"name\":\"Deuterium\",\"mf\":\"[2H]\",\"ocl\":{\"value\":\"eFAAYhBLCEH@\",\"coordinates\":\"!B@BL\"},\"mass\":2.01410177812,\"monoisotopicMass\":2.01410177812,\"unsaturation\":-1,\"elements\":[{\"symbol\":\"H\",\"number\":1,\"isotope\":2}]},{\"symbol\":\"Dde\",\"name\":\"Dde\",\"mf\":\"C10H13O2\",\"ocl\":{\"value\":\"dklD`FDEgHhihicIVZfZj@@\",\"coordinates\":\"!Bb@I~@Ha}upJH@m]}_`BH_Wx@b@I}bOrH\"},\"mass\":165.20939861871415,\"monoisotopicMass\":165.09155465812998,\"unsaturation\":7,\"elements\":[{\"symbol\":\"C\",\"number\":10},{\"symbol\":\"H\",\"number\":13},{\"symbol\":\"O\",\"number\":2}]},{\"symbol\":\"Dnp\",\"name\":\"2,4-Dinitrophenyl\",\"mf\":\"C6H3N2O4\",\"ocl\":{\"value\":\"dkmB`bWatpVRd^VS{HhheEUFfBAbX@@\",\"coordinates\":\"!B_c~H_]]}b@I~Owx_`BH_]]}_c~H_]]}\"},\"mass\":167.09926376274353,\"monoisotopicMass\":167.00928158383,\"unsaturation\":11,\"elements\":[{\"symbol\":\"C\",\"number\":6},{\"symbol\":\"H\",\"number\":3},{\"symbol\":\"N\",\"number\":2},{\"symbol\":\"O\",\"number\":4}]},{\"symbol\":\"Et\",\"name\":\"Ethyl\",\"mf\":\"C2H5\",\"ocl\":{\"value\":\"eMBAYRZ@\",\"coordinates\":\"!B@Fp@Xp\"},\"mass\":29.061175563749384,\"monoisotopicMass\":29.03912516115,\"unsaturation\":-1,\"elements\":[{\"symbol\":\"C\",\"number\":2},{\"symbol\":\"H\",\"number\":5}]},{\"symbol\":\"Fmoc\",\"name\":\"Fluorenylmethoxycarbonyl\",\"mf\":\"C15H11O2\",\"ocl\":{\"value\":\"fde@b@DX@liMkLrjxeVCzLuT@@@P@@@\",\"coordinates\":\"!BbOq~@Ha}bOrH_]ARcm}Tv~i`pAeKv|@fpB[j[~iozfAKvp\"},\"mass\":223.24719659427882,\"monoisotopicMass\":223.07590459367,\"unsaturation\":19,\"elements\":[{\"symbol\":\"C\",\"number\":15},{\"symbol\":\"H\",\"number\":11},{\"symbol\":\"O\",\"number\":2}]},{\"symbol\":\"For\",\"name\":\"Formyl\",\"mf\":\"CHO\",\"ocl\":{\"value\":\"eMJDVTfP@\",\"coordinates\":\"!B@Fp@Xp\"},\"mass\":29.018081575109303,\"monoisotopicMass\":29.0027396518,\"unsaturation\":1,\"elements\":[{\"symbol\":\"C\",\"number\":1},{\"symbol\":\"H\",\"number\":1},{\"symbol\":\"O\",\"number\":1}]},{\"symbol\":\"Gln\",\"name\":\"Glutamine diradical\",\"mf\":\"C5H8N2O2\",\"kind\":\"aa\",\"oneLetter\":\"Q\",\"alternativeOneLetter\":\"ξ\",\"ocl\":{\"value\":\"dmUDPBUICqYqR[evfjihr@@\",\"coordinates\":\"!Bb@I~@Ha}_c~H@m]}bGvHGx@bGt\"},\"mass\":128.12942178765059,\"monoisotopicMass\":128.05857750584,\"unsaturation\":4,\"elements\":[{\"symbol\":\"C\",\"number\":5},{\"symbol\":\"H\",\"number\":8},{\"symbol\":\"N\",\"number\":2},{\"symbol\":\"O\",\"number\":2}]},{\"symbol\":\"Glnp\",\"name\":\"Glutamine triradical\",\"mf\":\"C5H7N2O2\",\"ocl\":{\"value\":\"dcuDpH{MAYeqWqRVuejZjiad@\",\"coordinates\":\"!BbGvHGx@bGvH@ha}_c~HM]}_`BH_P\"},\"mass\":127.12148103359483,\"monoisotopicMass\":127.05075247361,\"unsaturation\":5,\"elements\":[{\"symbol\":\"C\",\"number\":5},{\"symbol\":\"H\",\"number\":7},{\"symbol\":\"N\",\"number\":2},{\"symbol\":\"O\",\"number\":2}]},{\"symbol\":\"Glp\",\"name\":\"Pyroglutamine\",\"mf\":\"C5H5NO2\",\"ocl\":{\"value\":\"deVDPBRP|V\\\\TfygxYjjZL`@\",\"coordinates\":\"!Bb@I~@Ha}tEJNwr[@UMo@FXBN\"},\"mass\":111.09889631403748,\"monoisotopicMass\":111.03202840472,\"unsaturation\":6,\"elements\":[{\"symbol\":\"C\",\"number\":5},{\"symbol\":\"H\",\"number\":5},{\"symbol\":\"N\",\"number\":1},{\"symbol\":\"O\",\"number\":2}]},{\"symbol\":\"Glu\",\"name\":\"Glutamic acid diradical\",\"mf\":\"C5H7NO3\",\"kind\":\"aa\",\"oneLetter\":\"E\",\"alternativeOneLetter\":\"ε\",\"ocl\":{\"value\":\"dmVLPBRUCqYqR[evfjihr@@\",\"coordinates\":\"!Bb@I~@Ha}_c~H@m]}bGvHGx@bGt\"},\"mass\":129.11418274646732,\"monoisotopicMass\":129.04259308875,\"unsaturation\":4,\"elements\":[{\"symbol\":\"C\",\"number\":5},{\"symbol\":\"H\",\"number\":7},{\"symbol\":\"N\",\"number\":1},{\"symbol\":\"O\",\"number\":3}]},{\"symbol\":\"Glup\",\"name\":\"Glutamic acid triradical\",\"mf\":\"C5H6NO3\",\"ocl\":{\"value\":\"dcvLpNcM@IeqWqRVuejZjiad@\",\"coordinates\":\"!BbGvHGx@bGvH@ha}_c~HM]}_`BH_P\"},\"mass\":128.10624199241153,\"monoisotopicMass\":128.03476805652002,\"unsaturation\":5,\"elements\":[{\"symbol\":\"C\",\"number\":5},{\"symbol\":\"H\",\"number\":6},{\"symbol\":\"N\",\"number\":1},{\"symbol\":\"O\",\"number\":3}]},{\"symbol\":\"Gly\",\"name\":\"Glycine diradical\",\"mf\":\"C2H3NO\",\"kind\":\"aa\",\"oneLetter\":\"G\",\"alternativeOneLetter\":\"γ\",\"ocl\":{\"value\":\"gGYDBaxuqR[Yj@@\",\"coordinates\":\"!BbOq~@Ha}bOrH_P\"},\"mass\":57.051402191401905,\"monoisotopicMass\":57.021463720689994,\"unsaturation\":2,\"elements\":[{\"symbol\":\"C\",\"number\":2},{\"symbol\":\"H\",\"number\":3},{\"symbol\":\"N\",\"number\":1},{\"symbol\":\"O\",\"number\":1}]},{\"symbol\":\"Hci\",\"name\":\"Homocitrulline diradical\",\"mf\":\"C7H13N3O2\",\"ocl\":{\"value\":\"dgoDPBVtLOEgEInWUZZjjfcH@\",\"coordinates\":\"!BbGu~Ox`B_`BH_Xc|bOrH_X`BbGvHGx@bGt\"},\"mass\":171.19730056284578,\"monoisotopicMass\":171.10077667142,\"unsaturation\":4,\"elements\":[{\"symbol\":\"C\",\"number\":7},{\"symbol\":\"H\",\"number\":13},{\"symbol\":\"N\",\"number\":3},{\"symbol\":\"O\",\"number\":2}]},{\"symbol\":\"Hcip\",\"name\":\"Homocitrulline triradical\",\"mf\":\"C7H12N3O2\",\"ocl\":{\"value\":\"do_DpHI\\\\\\\\EdwFEI[UVVijjjfIP@\",\"coordinates\":\"!BbOrH_Wxb@JH_Xc|bGvHHa}_c~H@m]}_`BH_P\"},\"mass\":170.18935980879002,\"monoisotopicMass\":170.09295163918998,\"unsaturation\":5,\"elements\":[{\"symbol\":\"C\",\"number\":7},{\"symbol\":\"H\",\"number\":12},{\"symbol\":\"N\",\"number\":3},{\"symbol\":\"O\",\"number\":2}]},{\"symbol\":\"His\",\"name\":\"Histidine diradical\",\"mf\":\"C6H7N3O\",\"kind\":\"aa\",\"oneLetter\":\"H\",\"alternativeOneLetter\":\"ζ\",\"ocl\":{\"value\":\"dcOHPBGTCqYqR[eyUvZjejL`@\",\"coordinates\":\"!Bb@I~@Ha}_c~H@m]}bGwPTh{_UMo@FP\"},\"mass\":137.13951521745759,\"monoisotopicMass\":137.05891185847,\"unsaturation\":8,\"elements\":[{\"symbol\":\"C\",\"number\":6},{\"symbol\":\"H\",\"number\":7},{\"symbol\":\"N\",\"number\":3},{\"symbol\":\"O\",\"number\":1}]},{\"symbol\":\"Hisp\",\"name\":\"Histidine triradical\",\"mf\":\"C6H6N3O\",\"ocl\":{\"value\":\"dkoHpHHSAYUqwqRY]YXjjVjihy@@\",\"coordinates\":\"!BTmA}bL@fUHRN`H`BbGu~Ox`Buwu~@Ha}\"},\"mass\":136.13157446340182,\"monoisotopicMass\":136.05108682624,\"unsaturation\":9,\"elements\":[{\"symbol\":\"C\",\"number\":6},{\"symbol\":\"H\",\"number\":6},{\"symbol\":\"N\",\"number\":3},{\"symbol\":\"O\",\"number\":1}]},{\"symbol\":\"Hser\",\"name\":\"Homoserine diradical\",\"mf\":\"C4H7NO2\",\"ocl\":{\"value\":\"diFDPBPP|V\\\\Tfy^Zjhr@@\",\"coordinates\":\"!BbGu~Ox`B_`BH_X`Bb@JH_P\"},\"mass\":101.10404192541378,\"monoisotopicMass\":101.04767846918,\"unsaturation\":2,\"elements\":[{\"symbol\":\"C\",\"number\":4},{\"symbol\":\"H\",\"number\":7},{\"symbol\":\"N\",\"number\":1},{\"symbol\":\"O\",\"number\":2}]},{\"symbol\":\"Hserp\",\"name\":\"Homoserine triradical\",\"mf\":\"C4H6NO2\",\"ocl\":{\"value\":\"defDpJbPV^\\\\Q|TeVVjji`d@\",\"coordinates\":\"!Bb@JH_X`BbGu~Oxc|uwu~@Ha}\"},\"mass\":100.09610117135801,\"monoisotopicMass\":100.03985343695001,\"unsaturation\":3,\"elements\":[{\"symbol\":\"C\",\"number\":4},{\"symbol\":\"H\",\"number\":6},{\"symbol\":\"N\",\"number\":1},{\"symbol\":\"O\",\"number\":2}]},{\"symbol\":\"Hyp\",\"name\":\"Hydroxyproline diradical\",\"mf\":\"C5H7NO2\",\"ocl\":{\"value\":\"deVDPBRP|V\\\\\\\\bfbbOCMUUIdE@@\",\"coordinates\":\"!Bb@I~@Ha}tEJNwr[@UMo@FUJO\"},\"mass\":113.11477782214904,\"monoisotopicMass\":113.04767846918,\"unsaturation\":4,\"elements\":[{\"symbol\":\"C\",\"number\":5},{\"symbol\":\"H\",\"number\":7},{\"symbol\":\"N\",\"number\":1},{\"symbol\":\"O\",\"number\":2}]},{\"symbol\":\"Hypp\",\"name\":\"Hydroxyproline triradical\",\"mf\":\"C5H6NO2\",\"ocl\":{\"value\":\"dmvDpJaPB^\\\\Y|TeeWjZjjidRL`@\",\"coordinates\":\"!BBOpH_UARcc}TNtBY@HyRSpCQDr\\\\\"},\"mass\":112.10683706809326,\"monoisotopicMass\":112.03985343695001,\"unsaturation\":5,\"elements\":[{\"symbol\":\"C\",\"number\":5},{\"symbol\":\"H\",\"number\":6},{\"symbol\":\"N\",\"number\":1},{\"symbol\":\"O\",\"number\":2}]},{\"symbol\":\"Ile\",\"name\":\"Isoleucine diradical\",\"mf\":\"C6H11NO\",\"kind\":\"aa\",\"oneLetter\":\"I\",\"alternativeOneLetter\":\"ι\",\"ocl\":{\"value\":\"defHPBPOEgEInVyjjdrT`@\",\"coordinates\":\"!BbGu~Oxc|_`BH_Xc|b@I~Oxa}\"},\"mass\":113.15787181078912,\"monoisotopicMass\":113.08406397853,\"unsaturation\":2,\"elements\":[{\"symbol\":\"C\",\"number\":6},{\"symbol\":\"H\",\"number\":11},{\"symbol\":\"N\",\"number\":1},{\"symbol\":\"O\",\"number\":1}]},{\"symbol\":\"Ivdde\",\"name\":\"1-[4,4-dimethyl-2,6-dioxocyclohexylidene)-3-methylbutyl\",\"mf\":\"C13H19O2\",\"ocl\":{\"value\":\"f`a@b@NR@lyEEDhhigEVfjYjj`@@\",\"coordinates\":\"!BbOq~@Ha}urHGxuwu~@Ha}_`CW_Xa}bOq}b@JH\"},\"mass\":207.28925083325453,\"monoisotopicMass\":207.13850485151,\"unsaturation\":7,\"elements\":[{\"symbol\":\"C\",\"number\":13},{\"symbol\":\"H\",\"number\":19},{\"symbol\":\"O\",\"number\":2}]},{\"symbol\":\"Leu\",\"name\":\"Leucine diradical\",\"mf\":\"C6H11NO\",\"kind\":\"aa\",\"oneLetter\":\"L\",\"alternativeOneLetter\":\"λ\",\"ocl\":{\"value\":\"defHPBPOEgEInWijjhr@@\",\"coordinates\":\"!BbGu~Ox`B_`BH_X`Bb@I~@Ha}\"},\"mass\":113.15787181078912,\"monoisotopicMass\":113.08406397853,\"unsaturation\":2,\"elements\":[{\"symbol\":\"C\",\"number\":6},{\"symbol\":\"H\",\"number\":11},{\"symbol\":\"N\",\"number\":1},{\"symbol\":\"O\",\"number\":1}]},{\"symbol\":\"Lys\",\"name\":\"Lysine diradical\",\"mf\":\"C6H12N2O\",\"kind\":\"aa\",\"oneLetter\":\"K\",\"alternativeOneLetter\":\"κ\",\"ocl\":{\"value\":\"dmUHPBU@|V\\\\Tfy]YjjjL`@\",\"coordinates\":\"!BbGu~Ox`B_`BHoX`Bb@JH_X`BbKt\"},\"mass\":128.17251577629068,\"monoisotopicMass\":128.09496301519,\"unsaturation\":2,\"elements\":[{\"symbol\":\"C\",\"number\":6},{\"symbol\":\"H\",\"number\":12},{\"symbol\":\"N\",\"number\":2},{\"symbol\":\"O\",\"number\":1}]},{\"symbol\":\"Lysp\",\"name\":\"Lysine triradical\",\"mf\":\"C6H11N2O\",\"ocl\":{\"value\":\"dcuHpH{PVY\\\\U|TeUYZjjjXY@@\",\"coordinates\":\"!Bb@JH_X`BbGvH@ha}_c~H@m]}_`BH_P\"},\"mass\":127.16457502223491,\"monoisotopicMass\":127.08713798295999,\"unsaturation\":3,\"elements\":[{\"symbol\":\"C\",\"number\":6},{\"symbol\":\"H\",\"number\":11},{\"symbol\":\"N\",\"number\":2},{\"symbol\":\"O\",\"number\":1}]},{\"symbol\":\"Mbh\",\"name\":\"4,4'-Dimethoxybenzhydryl\",\"mf\":\"C15H15O2\",\"ocl\":{\"value\":\"fdy@b@G^@liLsJkzlcZmT@@@UP@@@\",\"coordinates\":\"!BbGvHGx_`BH_Xa}uwvHHc|_c}~Oxa}uwvHGxbGwW_P\"},\"mass\":227.27895961050194,\"monoisotopicMass\":227.10720472258998,\"unsaturation\":15,\"elements\":[{\"symbol\":\"C\",\"number\":15},{\"symbol\":\"H\",\"number\":15},{\"symbol\":\"O\",\"number\":2}]},{\"symbol\":\"Me\",\"name\":\"Methyl\",\"mf\":\"CH3\",\"ocl\":{\"value\":\"eFBAYc@@\",\"coordinates\":\"!B@FL\"},\"mass\":15.03455815890258,\"monoisotopicMass\":15.02347509669,\"unsaturation\":-1,\"elements\":[{\"symbol\":\"C\",\"number\":1},{\"symbol\":\"H\",\"number\":3}]},{\"symbol\":\"Mebzl\",\"name\":\"4-Methylbenzyl\",\"mf\":\"C8H9\",\"ocl\":{\"value\":\"did@`@VTee]nh@H@@\",\"coordinates\":\"!B|Gsp__A|_gp_C}_gp_P\"},\"mass\":105.15735396038399,\"monoisotopicMass\":105.07042529007,\"unsaturation\":7,\"elements\":[{\"symbol\":\"C\",\"number\":8},{\"symbol\":\"H\",\"number\":9}]},{\"symbol\":\"Meobzl\",\"name\":\"4-Methoxybenzyl\",\"mf\":\"C8H9O\",\"ocl\":{\"value\":\"deTH`AAYRVUunh@J@@\",\"coordinates\":\"!B|Gsp__A|_gp_A}_gp_Wy\"},\"mass\":121.15675888470227,\"monoisotopicMass\":121.06533990964,\"unsaturation\":7,\"elements\":[{\"symbol\":\"C\",\"number\":8},{\"symbol\":\"H\",\"number\":9},{\"symbol\":\"O\",\"number\":1}]},{\"symbol\":\"Met\",\"name\":\"Methionine diradical\",\"mf\":\"C5H9NOS\",\"kind\":\"aa\",\"oneLetter\":\"M\",\"alternativeOneLetter\":\"μ\",\"ocl\":{\"value\":\"defHpBPOEgDPaInWYjjhr@@\",\"coordinates\":\"!Bb@I~@Ha}_c~H@m]}bGvHHa}\"},\"mass\":131.19604181206938,\"monoisotopicMass\":131.04048508847,\"unsaturation\":2,\"elements\":[{\"symbol\":\"C\",\"number\":5},{\"symbol\":\"H\",\"number\":9},{\"symbol\":\"N\",\"number\":1},{\"symbol\":\"O\",\"number\":1},{\"symbol\":\"S\",\"number\":1}]},{\"symbol\":\"Mmt\",\"name\":\"4-Methoxytrityl\",\"mf\":\"C20H17O\",\"ocl\":{\"value\":\"ffcAB@B`V\\\\bdTTTRRRVvIhnRGMT@@@@AP@@@\",\"coordinates\":\"!BbKvHM^}_c}~@Hb}dXWHb}j|nHHc|AqOWoWxJV^Ho]\\\\BuwvHHb}\"},\"mass\":273.3491156779715,\"monoisotopicMass\":273.12794016748,\"unsaturation\":23,\"elements\":[{\"symbol\":\"C\",\"number\":20},{\"symbol\":\"H\",\"number\":17},{\"symbol\":\"O\",\"number\":1}]},{\"symbol\":\"Mtc\",\"name\":\"2,2,5,7,8-pentamethylchroman-6-sulphonyl\",\"mf\":\"C14H19O3S\",\"ocl\":{\"value\":\"fleAa@DX\\\\AY`DYEHXhhilmiKW`rpDQUUD@@\",\"coordinates\":\"!BbGtBbGwWbGvHGxbGu~@Ha}uwu~Ox`B_c~H_Xa}b@H@_osW\"},\"mass\":267.36417906043516,\"monoisotopicMass\":267.10549064548,\"unsaturation\":9,\"elements\":[{\"symbol\":\"C\",\"number\":14},{\"symbol\":\"H\",\"number\":19},{\"symbol\":\"O\",\"number\":3},{\"symbol\":\"S\",\"number\":1}]},{\"symbol\":\"Mtr\",\"name\":\"4-Methoxy-2,3,6-trimethylbenzenesulphonyl\",\"mf\":\"C10H13O3S\",\"ocl\":{\"value\":\"do|LPDrpVXBLbdLTTTngYXBHj@@\",\"coordinates\":\"!BbOq}b@KWb@I~@Ha}bOsWHc|_c~H_Wx@b@JH_P\"},\"mass\":213.27359094915948,\"monoisotopicMass\":213.05854045209998,\"unsaturation\":7,\"elements\":[{\"symbol\":\"C\",\"number\":10},{\"symbol\":\"H\",\"number\":13},{\"symbol\":\"O\",\"number\":3},{\"symbol\":\"S\",\"number\":1}]},{\"symbol\":\"Mts\",\"name\":\"Mesitylene-2-sulphonyl\",\"mf\":\"C9H11O2S\",\"ocl\":{\"value\":\"d@\"},\"mass\":183.24756861999438,\"monoisotopicMass\":183.04797576807,\"unsaturation\":7,\"elements\":[{\"symbol\":\"C\",\"number\":9},{\"symbol\":\"H\",\"number\":11},{\"symbol\":\"O\",\"number\":2},{\"symbol\":\"S\",\"number\":1}]},{\"symbol\":\"Mtt\",\"name\":\"4-Methyltrityl\",\"mf\":\"C20H17\",\"ocl\":{\"value\":\"d@\"},\"mass\":257.3497107536532,\"monoisotopicMass\":257.13302554791,\"unsaturation\":23,\"elements\":[{\"symbol\":\"C\",\"number\":20},{\"symbol\":\"H\",\"number\":17}]},{\"symbol\":\"Nle\",\"name\":\"Norleucine diradical\",\"mf\":\"C6H11NO\",\"ocl\":{\"value\":\"defHPBPOEgEInWYjjhr@@\",\"coordinates\":\"!Bb@I~@Ha}_c~H@m]}bGvHHa}\"},\"mass\":113.15787181078912,\"monoisotopicMass\":113.08406397853,\"unsaturation\":2,\"elements\":[{\"symbol\":\"C\",\"number\":6},{\"symbol\":\"H\",\"number\":11},{\"symbol\":\"N\",\"number\":1},{\"symbol\":\"O\",\"number\":1}]},{\"symbol\":\"Npys\",\"name\":\"3-Nitro-2-pyridinesulphenyl\",\"mf\":\"C5H3N2O2S\",\"ocl\":{\"value\":\"d@\",\"coordinates\":\"\"},\"mass\":155.1545054234988,\"monoisotopicMass\":154.99152351908998,\"unsaturation\":9,\"elements\":[{\"symbol\":\"C\",\"number\":5},{\"symbol\":\"H\",\"number\":3},{\"symbol\":\"N\",\"number\":2},{\"symbol\":\"O\",\"number\":2},{\"symbol\":\"S\",\"number\":1}]},{\"symbol\":\"Nva\",\"name\":\"Norvaline diradical\",\"mf\":\"C5H9NO\",\"ocl\":{\"value\":\"diFHPBPOEgEInWfjjL`@\",\"coordinates\":\"!BbGu~Ox`B_`BH_X`Bb@JH_P\"},\"mass\":99.13125440594231,\"monoisotopicMass\":99.06841391407,\"unsaturation\":2,\"elements\":[{\"symbol\":\"C\",\"number\":5},{\"symbol\":\"H\",\"number\":9},{\"symbol\":\"N\",\"number\":1},{\"symbol\":\"O\",\"number\":1}]},{\"symbol\":\"Odmab\",\"name\":\"Odmab\",\"mf\":\"C20H26NO3\",\"ocl\":{\"value\":\"d@\"},\"mass\":328.4260955245558,\"monoisotopicMass\":328.19126870111995,\"unsaturation\":15,\"elements\":[{\"symbol\":\"C\",\"number\":20},{\"symbol\":\"H\",\"number\":26},{\"symbol\":\"N\",\"number\":1},{\"symbol\":\"O\",\"number\":3}]},{\"symbol\":\"Orn\",\"name\":\"Ornithine diradical\",\"mf\":\"C5H10N2O\",\"ocl\":{\"value\":\"deeHPBe@|V\\\\Tfy]fjjcH@\",\"coordinates\":\"!Bb@I~@Ha}_c~H@m]}bGvHHa}\"},\"mass\":114.14589837144388,\"monoisotopicMass\":114.07931295072999,\"unsaturation\":2,\"elements\":[{\"symbol\":\"C\",\"number\":5},{\"symbol\":\"H\",\"number\":10},{\"symbol\":\"N\",\"number\":2},{\"symbol\":\"O\",\"number\":1}]},{\"symbol\":\"Ornp\",\"name\":\"Ornithine triradical\",\"mf\":\"C5H9N2O\",\"ocl\":{\"value\":\"dmUHpHYPBQ\\\\Y|TeUejjjfJP@\",\"coordinates\":\"!BbGvHHa}b@JH_Wxb@KW_Wx@bGt\"},\"mass\":113.13795761738811,\"monoisotopicMass\":113.0714879185,\"unsaturation\":3,\"elements\":[{\"symbol\":\"C\",\"number\":5},{\"symbol\":\"H\",\"number\":9},{\"symbol\":\"N\",\"number\":2},{\"symbol\":\"O\",\"number\":1}]},{\"symbol\":\"Pbf\",\"name\":\"2,2,4,6,7-pentamethyldihydrobenzofurane-5-sulfonyl\",\"mf\":\"C13H17O3S\",\"ocl\":{\"value\":\"d@\",\"coordinates\":\"\"},\"mass\":253.33756165558833,\"monoisotopicMass\":253.08984058101998,\"unsaturation\":9,\"elements\":[{\"symbol\":\"C\",\"number\":13},{\"symbol\":\"H\",\"number\":17},{\"symbol\":\"O\",\"number\":3},{\"symbol\":\"S\",\"number\":1}]},{\"symbol\":\"Pen\",\"name\":\"Penicillamine diradical\",\"mf\":\"C5H9NOS\",\"ocl\":{\"value\":\"d@\",\"coordinates\":\"\"},\"mass\":131.19604181206938,\"monoisotopicMass\":131.04048508847,\"unsaturation\":2,\"elements\":[{\"symbol\":\"C\",\"number\":5},{\"symbol\":\"H\",\"number\":9},{\"symbol\":\"N\",\"number\":1},{\"symbol\":\"O\",\"number\":1},{\"symbol\":\"S\",\"number\":1}]},{\"symbol\":\"Penp\",\"name\":\"Penicillamine triradical\",\"mf\":\"C5H8NOS\",\"ocl\":{\"value\":\"d@\",\"coordinates\":\"\"},\"mass\":130.1881010580136,\"monoisotopicMass\":130.03266005624,\"unsaturation\":3,\"elements\":[{\"symbol\":\"C\",\"number\":5},{\"symbol\":\"H\",\"number\":8},{\"symbol\":\"N\",\"number\":1},{\"symbol\":\"O\",\"number\":1},{\"symbol\":\"S\",\"number\":1}]},{\"symbol\":\"Ph\",\"name\":\"Phenyl\",\"mf\":\"C6H5\",\"ocl\":{\"value\":\"gOpH@liLkW@@@@\",\"coordinates\":\"!B|Owp_Gy|OwpWy\"},\"mass\":77.10411915069038,\"monoisotopicMass\":77.03912516115,\"unsaturation\":7,\"elements\":[{\"symbol\":\"C\",\"number\":6},{\"symbol\":\"H\",\"number\":5}]},{\"symbol\":\"Phe\",\"name\":\"Phenylalanine diradical\",\"mf\":\"C9H9NO\",\"kind\":\"aa\",\"oneLetter\":\"F\",\"alternativeOneLetter\":\"φ\",\"ocl\":{\"value\":\"dknHPBPOEgEInWe]NZj@@cH@\",\"coordinates\":\"!Bb@I~@Ha}_c~H@m]}bGvH@gxbGvH_Wx\"},\"mass\":147.1741979928833,\"monoisotopicMass\":147.06841391407002,\"unsaturation\":10,\"elements\":[{\"symbol\":\"C\",\"number\":9},{\"symbol\":\"H\",\"number\":9},{\"symbol\":\"N\",\"number\":1},{\"symbol\":\"O\",\"number\":1}]},{\"symbol\":\"Phepcl\",\"name\":\"4-Chlorophenylalanine diradical\",\"mf\":\"C9H8ClNO\",\"ocl\":{\"value\":\"dg^HpBPOEgFxaInWe_Sfj`@bL`@\",\"coordinates\":\"!BbOq~@Ha}_c~H@m]}bGvH@gxbGvH_WxbGt\"},\"mass\":181.6191948214355,\"monoisotopicMass\":181.02944156384,\"unsaturation\":10,\"elements\":[{\"symbol\":\"C\",\"number\":9},{\"symbol\":\"H\",\"number\":8},{\"symbol\":\"Cl\",\"number\":1},{\"symbol\":\"N\",\"number\":1},{\"symbol\":\"O\",\"number\":1}]},{\"symbol\":\"Pmc\",\"name\":\"2,2,5,7,8-Pentamethylchroman-6-sulphonyl\",\"mf\":\"C14H19O3S\",\"ocl\":{\"value\":\"d@\",\"coordinates\":\"\"},\"mass\":267.36417906043516,\"monoisotopicMass\":267.10549064548,\"unsaturation\":9,\"elements\":[{\"symbol\":\"C\",\"number\":14},{\"symbol\":\"H\",\"number\":19},{\"symbol\":\"O\",\"number\":3},{\"symbol\":\"S\",\"number\":1}]},{\"symbol\":\"Pro\",\"name\":\"Proline diradical\",\"mf\":\"C5H7NO\",\"kind\":\"aa\",\"oneLetter\":\"P\",\"alternativeOneLetter\":\"π\",\"ocl\":{\"value\":\"difHPBPOEgEInYxYjjhr@@\",\"coordinates\":\"!Bb@I~@Ha}tEJNwr[@UMo@FP\"},\"mass\":97.11537289783075,\"monoisotopicMass\":97.05276384961,\"unsaturation\":4,\"elements\":[{\"symbol\":\"C\",\"number\":5},{\"symbol\":\"H\",\"number\":7},{\"symbol\":\"N\",\"number\":1},{\"symbol\":\"O\",\"number\":1}]},{\"symbol\":\"Pyr\",\"name\":\"Pyroglutamine\",\"mf\":\"C5H5NO2\",\"ocl\":{\"value\":\"deVDPBRP|V\\\\TfygxYjjZL`@\",\"coordinates\":\"!Bb@I~@Ha}tEJNwr[@UMo@FXBN\"},\"mass\":111.09889631403748,\"monoisotopicMass\":111.03202840472,\"unsaturation\":6,\"elements\":[{\"symbol\":\"C\",\"number\":5},{\"symbol\":\"H\",\"number\":5},{\"symbol\":\"N\",\"number\":1},{\"symbol\":\"O\",\"number\":2}]},{\"symbol\":\"Sar\",\"name\":\"Sarcosine diradical\",\"mf\":\"C3H5NO\",\"ocl\":{\"value\":\"d@\",\"coordinates\":\"\"},\"mass\":71.07801959624871,\"monoisotopicMass\":71.03711378515,\"unsaturation\":2,\"elements\":[{\"symbol\":\"C\",\"number\":3},{\"symbol\":\"H\",\"number\":5},{\"symbol\":\"N\",\"number\":1},{\"symbol\":\"O\",\"number\":1}]},{\"symbol\":\"Ser\",\"name\":\"Serine diradical\",\"mf\":\"C3H5NO2\",\"kind\":\"aa\",\"oneLetter\":\"S\",\"alternativeOneLetter\":\"σ\",\"ocl\":{\"value\":\"dazDPBS`|V\\\\TfyYjjL`@\",\"coordinates\":\"!Bb@I~@Ha}_c~H@m]}bGt\"},\"mass\":87.07742452056698,\"monoisotopicMass\":87.03202840472,\"unsaturation\":2,\"elements\":[{\"symbol\":\"C\",\"number\":3},{\"symbol\":\"H\",\"number\":5},{\"symbol\":\"N\",\"number\":1},{\"symbol\":\"O\",\"number\":2}]},{\"symbol\":\"Serp\",\"name\":\"Serine triradical\",\"mf\":\"C3H4NO2\",\"ocl\":{\"value\":\"diFDpB`PBV\\\\^|TeYZjjXq@@\",\"coordinates\":\"!BbGvHHa}_c~HM]}_`BH_P\"},\"mass\":86.06948376651121,\"monoisotopicMass\":86.02420337249,\"unsaturation\":3,\"elements\":[{\"symbol\":\"C\",\"number\":3},{\"symbol\":\"H\",\"number\":4},{\"symbol\":\"N\",\"number\":1},{\"symbol\":\"O\",\"number\":2}]},{\"symbol\":\"Sta\",\"name\":\"Statine diradical\",\"mf\":\"C8H15NO2\",\"ocl\":{\"value\":\"d@\"},\"mass\":157.210511544801,\"monoisotopicMass\":157.11027872702002,\"unsaturation\":2,\"elements\":[{\"symbol\":\"C\",\"number\":8},{\"symbol\":\"H\",\"number\":15},{\"symbol\":\"N\",\"number\":1},{\"symbol\":\"O\",\"number\":2}]},{\"symbol\":\"Stap\",\"name\":\"Statine triradical\",\"mf\":\"C8H14NO2\",\"ocl\":{\"value\":\"d@\"},\"mass\":156.2025707907452,\"monoisotopicMass\":156.10245369479,\"unsaturation\":3,\"elements\":[{\"symbol\":\"C\",\"number\":8},{\"symbol\":\"H\",\"number\":14},{\"symbol\":\"N\",\"number\":1},{\"symbol\":\"O\",\"number\":2}]},{\"symbol\":\"Tacm\",\"name\":\"Trimethylacetamidomethyl\",\"mf\":\"C6H12NO\",\"ocl\":{\"value\":\"d@\"},\"mass\":114.16581256484488,\"monoisotopicMass\":114.09188901076,\"unsaturation\":1,\"elements\":[{\"symbol\":\"C\",\"number\":6},{\"symbol\":\"H\",\"number\":12},{\"symbol\":\"N\",\"number\":1},{\"symbol\":\"O\",\"number\":1}]},{\"symbol\":\"Tbdms\",\"name\":\"t-Butyldimethylsilyl\",\"mf\":\"C6H15Si\",\"ocl\":{\"value\":\"d@\"},\"mass\":115.2690253969541,\"monoisotopicMass\":115.09430201810001,\"unsaturation\":-1,\"elements\":[{\"symbol\":\"C\",\"number\":6},{\"symbol\":\"H\",\"number\":15},{\"symbol\":\"Si\",\"number\":1}]},{\"symbol\":\"Tbu\",\"name\":\"t-Butyl\",\"mf\":\"C4H9\",\"ocl\":{\"value\":\"d@\"},\"mass\":57.114410373442986,\"monoisotopicMass\":57.07042529007,\"unsaturation\":-1,\"elements\":[{\"symbol\":\"C\",\"number\":4},{\"symbol\":\"H\",\"number\":9}]},{\"symbol\":\"Tbuo\",\"name\":\"t-Butoxy\",\"mf\":\"C4H9O\",\"ocl\":{\"value\":\"d@\"},\"mass\":73.11381529776126,\"monoisotopicMass\":73.06533990964,\"unsaturation\":-1,\"elements\":[{\"symbol\":\"C\",\"number\":4},{\"symbol\":\"H\",\"number\":9},{\"symbol\":\"O\",\"number\":1}]},{\"symbol\":\"Tbuthio\",\"name\":\"t-Butylthio\",\"mf\":\"C4H9S\",\"ocl\":{\"value\":\"d@\"},\"mass\":89.17919777957005,\"monoisotopicMass\":89.04249646446999,\"unsaturation\":-1,\"elements\":[{\"symbol\":\"C\",\"number\":4},{\"symbol\":\"H\",\"number\":9},{\"symbol\":\"S\",\"number\":1}]},{\"symbol\":\"Tfa\",\"name\":\"Trifluoroacetyl\",\"mf\":\"C2F3O\",\"ocl\":{\"value\":\"d@\"},\"mass\":97.01608620597878,\"monoisotopicMass\":96.99012410776,\"unsaturation\":1,\"elements\":[{\"symbol\":\"C\",\"number\":2},{\"symbol\":\"F\",\"number\":3},{\"symbol\":\"O\",\"number\":1}]},{\"symbol\":\"Thr\",\"name\":\"Threonine diradical\",\"mf\":\"C4H7NO2\",\"kind\":\"aa\",\"oneLetter\":\"T\",\"alternativeOneLetter\":\"τ\",\"ocl\":{\"value\":\"d@\"},\"mass\":101.10404192541378,\"monoisotopicMass\":101.04767846918,\"unsaturation\":2,\"elements\":[{\"symbol\":\"C\",\"number\":4},{\"symbol\":\"H\",\"number\":7},{\"symbol\":\"N\",\"number\":1},{\"symbol\":\"O\",\"number\":2}]},{\"symbol\":\"Thrp\",\"name\":\"Threonine triradical\",\"mf\":\"C4H6NO2\",\"ocl\":{\"value\":\"d@\"},\"mass\":100.09610117135801,\"monoisotopicMass\":100.03985343695001,\"unsaturation\":3,\"elements\":[{\"symbol\":\"C\",\"number\":4},{\"symbol\":\"H\",\"number\":6},{\"symbol\":\"N\",\"number\":1},{\"symbol\":\"O\",\"number\":2}]},{\"symbol\":\"Tfsi\",\"name\":\"(Bis)(trifluoromethanesulfonyl)imide\",\"mf\":\"C2F6NO4S2\",\"ocl\":{\"value\":\"d@\"},\"mass\":280.1457884908235,\"monoisotopicMass\":279.91729380789,\"unsaturation\":-1,\"elements\":[{\"symbol\":\"C\",\"number\":2},{\"symbol\":\"F\",\"number\":6},{\"symbol\":\"N\",\"number\":1},{\"symbol\":\"O\",\"number\":4},{\"symbol\":\"S\",\"number\":2}]},{\"symbol\":\"Tips\",\"name\":\"Triisopropylsilyl\",\"mf\":\"C9H21Si\",\"ocl\":{\"value\":\"dmT@P@VX\\\\DffYjjjh@@\",\"coordinates\":\"!B_a@gHb\\\\]FBIuWxP^zi~KwxPFAt\"},\"mass\":157.34887761149452,\"monoisotopicMass\":157.14125221148,\"unsaturation\":-1,\"elements\":[{\"symbol\":\"C\",\"number\":9},{\"symbol\":\"H\",\"number\":21},{\"symbol\":\"Si\",\"number\":1}]},{\"symbol\":\"Tms\",\"name\":\"Trimethylsilyl\",\"mf\":\"C3H9Si\",\"ocl\":{\"value\":\"gJPD@lqpRZj`@\",\"coordinates\":\"!BbOq~@GxbGt\"},\"mass\":73.1891731824137,\"monoisotopicMass\":73.04735182472,\"unsaturation\":-1,\"elements\":[{\"symbol\":\"C\",\"number\":3},{\"symbol\":\"H\",\"number\":9},{\"symbol\":\"Si\",\"number\":1}]},{\"symbol\":\"Tos\",\"name\":\"Tosyl\",\"mf\":\"C7H7O2S\",\"ocl\":{\"value\":\"dmtDPDpEf@cHiCDeafV@B@@\",\"coordinates\":\"!B|Ou||Ovw|Gwp_Gy|GwpWy|Gt\"},\"mass\":155.1943338103008,\"monoisotopicMass\":155.01667563914998,\"unsaturation\":7,\"elements\":[{\"symbol\":\"C\",\"number\":7},{\"symbol\":\"H\",\"number\":7},{\"symbol\":\"O\",\"number\":2},{\"symbol\":\"S\",\"number\":1}]},{\"symbol\":\"Trp\",\"name\":\"Tryptophan diradical\",\"mf\":\"C11H10N2O\",\"kind\":\"aa\",\"oneLetter\":\"W\",\"alternativeOneLetter\":\"ω\",\"ocl\":{\"value\":\"f`qQA@BFPCqXxiMr|rnhsoSUTa@QCD@@\",\"coordinates\":\"!BbOq~@Ha}_c~H@m]}bGwPTh{_UMojXL@YpB[@Ini`\"},\"mass\":186.21031375185538,\"monoisotopicMass\":186.07931295073,\"unsaturation\":14,\"elements\":[{\"symbol\":\"C\",\"number\":11},{\"symbol\":\"H\",\"number\":10},{\"symbol\":\"N\",\"number\":2},{\"symbol\":\"O\",\"number\":1}]},{\"symbol\":\"Trpp\",\"name\":\"Tryptophan triradical\",\"mf\":\"C11H9N2O\",\"ocl\":{\"value\":\"fhiQC@HFB@I\\\\x~|TfYU_ebLDjhDHjibFd@\",\"coordinates\":\"!BTmA}bL@fUHR_Ihz@iVBeXHc|grZH_WxbOsW_Wx@bGt\"},\"mass\":185.20237299779959,\"monoisotopicMass\":185.07148791850003,\"unsaturation\":15,\"elements\":[{\"symbol\":\"C\",\"number\":11},{\"symbol\":\"H\",\"number\":9},{\"symbol\":\"N\",\"number\":2},{\"symbol\":\"O\",\"number\":1}]},{\"symbol\":\"Trt\",\"name\":\"Trityl\",\"mf\":\"C19H15\",\"ocl\":{\"value\":\"fbm@B@@KJSSLrjkyhnRGMT@@@@@@@@\",\"coordinates\":\"!BrHI~PGy_rMvW@l`BQCvWw\\\\bBAg}~PGy@]i}~W|c]cNwH`i_]_e|\"},\"mass\":243.32309334880637,\"monoisotopicMass\":243.11737548345,\"unsaturation\":23,\"elements\":[{\"symbol\":\"C\",\"number\":19},{\"symbol\":\"H\",\"number\":15}]},{\"symbol\":\"Tyr\",\"name\":\"Tyrosine diradical\",\"mf\":\"C9H9NO2\",\"kind\":\"aa\",\"oneLetter\":\"Y\",\"alternativeOneLetter\":\"ψ\",\"ocl\":{\"value\":\"dg^DPBRp|V\\\\Tfy^U}NZj@BHr@@\",\"coordinates\":\"!BbOq~@Ha}_c~H@m]}bGvH@gxbGvH_WxbGt\"},\"mass\":163.1736029172016,\"monoisotopicMass\":163.06332853364,\"unsaturation\":10,\"elements\":[{\"symbol\":\"C\",\"number\":9},{\"symbol\":\"H\",\"number\":9},{\"symbol\":\"N\",\"number\":1},{\"symbol\":\"O\",\"number\":2}]},{\"symbol\":\"Tyrp\",\"name\":\"Tyrosine triradical\",\"mf\":\"C9H8NO2\",\"ocl\":{\"value\":\"do~DpEapBS\\\\[|Tee]YYnh@JjdbT@\",\"coordinates\":\"!B_`BHGx@bGvH@h`BbKvH@ha}_c~H@m]}_`BHoP\"},\"mass\":162.16566216314578,\"monoisotopicMass\":162.05550350141,\"unsaturation\":11,\"elements\":[{\"symbol\":\"C\",\"number\":9},{\"symbol\":\"H\",\"number\":8},{\"symbol\":\"N\",\"number\":1},{\"symbol\":\"O\",\"number\":2}]},{\"symbol\":\"Val\",\"name\":\"Valine\",\"mf\":\"C5H9NO\",\"kind\":\"aa\",\"oneLetter\":\"V\",\"alternativeOneLetter\":\"ν\",\"ocl\":{\"value\":\"diFHPBPOEgEInVfjjL`@\",\"coordinates\":\"!Bb@I~@Ha}_c~H@m]}_`BH_P\"},\"mass\":99.13125440594231,\"monoisotopicMass\":99.06841391407,\"unsaturation\":2,\"elements\":[{\"symbol\":\"C\",\"number\":5},{\"symbol\":\"H\",\"number\":9},{\"symbol\":\"N\",\"number\":1},{\"symbol\":\"O\",\"number\":1}]},{\"symbol\":\"Valoh\",\"name\":\"beta-Hydroxyvaline diradical\",\"mf\":\"C5H9NO2\",\"ocl\":{\"value\":\"defDPBS`|V\\\\TfyZfjjcH@\",\"coordinates\":\"!Bb@I~@Ha}b@I~Oxa}Owy~OpA~\"},\"mass\":115.13065933026058,\"monoisotopicMass\":115.06332853364,\"unsaturation\":2,\"elements\":[{\"symbol\":\"C\",\"number\":5},{\"symbol\":\"H\",\"number\":9},{\"symbol\":\"N\",\"number\":1},{\"symbol\":\"O\",\"number\":2}]},{\"symbol\":\"Valohp\",\"name\":\"beta-Hydroxyvaline triradical\",\"mf\":\"C5H8NO2\",\"ocl\":{\"value\":\"dmVDpFaPBQ\\\\Y|\\\\bTbaTjjjXq@@\",\"coordinates\":\"!BbGvHHa}_Xc|bGxb@KW_Wx@bGt\"},\"mass\":114.1227185762048,\"monoisotopicMass\":114.05550350141002,\"unsaturation\":3,\"elements\":[{\"symbol\":\"C\",\"number\":5},{\"symbol\":\"H\",\"number\":8},{\"symbol\":\"N\",\"number\":1},{\"symbol\":\"O\",\"number\":2}]},{\"symbol\":\"Xan\",\"name\":\"Xanthyl\",\"mf\":\"C13H9O\",\"ocl\":{\"value\":\"d@\",\"coordinates\":\"\"},\"mass\":181.21043836837848,\"monoisotopicMass\":181.06533990964002,\"unsaturation\":17,\"elements\":[{\"symbol\":\"C\",\"number\":13},{\"symbol\":\"H\",\"number\":9},{\"symbol\":\"O\",\"number\":1}]},{\"symbol\":\"Xle\",\"name\":\"Leucine or Isoleucine diradical\",\"mf\":\"C6H11NO\",\"kind\":\"aa\",\"oneLetter\":\"J\",\"mass\":113.15787181078912,\"monoisotopicMass\":113.08406397853,\"unsaturation\":2,\"elements\":[{\"symbol\":\"C\",\"number\":6},{\"symbol\":\"H\",\"number\":11},{\"symbol\":\"N\",\"number\":1},{\"symbol\":\"O\",\"number\":1}]},{\"symbol\":\"Z\",\"name\":\"Benzyloxycarbonyl\",\"mf\":\"C8H7O2\",\"ocl\":{\"value\":\"dmtD`DpEeImYVUfh@@@@\",\"coordinates\":\"!Bb@I~@Ha}b@JH_Xc|_c~H_Xa}_c|\"},\"mass\":135.14028230090898,\"monoisotopicMass\":135.04460446475,\"unsaturation\":9,\"elements\":[{\"symbol\":\"C\",\"number\":8},{\"symbol\":\"H\",\"number\":7},{\"symbol\":\"O\",\"number\":2}]},{\"symbol\":\"Damp\",\"name\":\"Desoxyadenosine monophosphate diradical\",\"mf\":\"C10H12N5O5P\",\"kind\":\"DNAp\",\"oneLetter\":\"A\",\"alternativeOneLetter\":\"α\",\"ocl\":{\"value\":\"fnsiS@IASUlJB]xGbkplxyDhhldhiEEUeSdTekUUUULBATXPlKd@@\",\"coordinates\":\"!Bqc}{JxyO|XoSWC}W]poGQ\\\\Ou}]rmx\\\\Ou}]{qpza|qb}MJwlk^sFO|X\"},\"mass\":313.2069506932622,\"monoisotopicMass\":313.05760550518,\"unsaturation\":14,\"elements\":[{\"symbol\":\"C\",\"number\":10},{\"symbol\":\"H\",\"number\":12},{\"symbol\":\"N\",\"number\":5},{\"symbol\":\"O\",\"number\":5},{\"symbol\":\"P\",\"number\":1}]},{\"symbol\":\"Dcmp\",\"name\":\"Desoxycytidine monophosphate diradical\",\"mf\":\"C9H12N3O6P\",\"kind\":\"DNAp\",\"oneLetter\":\"C\",\"alternativeOneLetter\":\"ς\",\"ocl\":{\"value\":\"fjmps@IQKB`g^BCqUxV\\\\\\\\bTTVRTTbb^iqNZjjjifVkBEa\\\\`@\",\"coordinates\":\"!Bqc}{JxyO|XoSWA}_W]poGQ\\\\GuMKuMh\\\\Gu}]{qpSF]tWQTvatP\"},\"mass\":289.18221329795364,\"monoisotopicMass\":289.04637211589,\"unsaturation\":10,\"elements\":[{\"symbol\":\"C\",\"number\":9},{\"symbol\":\"H\",\"number\":12},{\"symbol\":\"N\",\"number\":3},{\"symbol\":\"O\",\"number\":6},{\"symbol\":\"P\",\"number\":1}]},{\"symbol\":\"Dgmp\",\"name\":\"Desoxyguanosine monophosphate diradical\",\"mf\":\"C10H12N5O6P\",\"kind\":\"DNAp\",\"oneLetter\":\"G\",\"alternativeOneLetter\":\"γ\",\"ocl\":{\"value\":\"fakhs@IASUlJB]{hOEWaYqrIQQYIQRJJkQTyEIZuUUUSRtsUaBpnP@\",\"coordinates\":\"!Bqc}{JxyO|XoSWA}W]poGQ\\\\Gu}]rmx\\\\Ou}]{qpza|qb}MJwlk^sFza|q`\"},\"mass\":329.20635561758047,\"monoisotopicMass\":329.05252012475,\"unsaturation\":14,\"elements\":[{\"symbol\":\"C\",\"number\":10},{\"symbol\":\"H\",\"number\":12},{\"symbol\":\"N\",\"number\":5},{\"symbol\":\"O\",\"number\":6},{\"symbol\":\"P\",\"number\":1}]},{\"symbol\":\"Dtmp\",\"name\":\"Desoxythymidine monophosphate diradical\",\"mf\":\"C10H13N2O7P\",\"kind\":\"DNAp\",\"oneLetter\":\"T\",\"alternativeOneLetter\":\"τ\",\"ocl\":{\"value\":\"ff}Qs@IQaPSoAjCqUxV\\\\\\\\bTTVRTTbbZUNIsUUUULsSVDKBy@@\",\"coordinates\":\"!Bqc}{JxyO|XoSWC}_W]poGQ\\\\GuMKuMh\\\\Gu}]{qpSF]tWQTvaSZGQ\"},\"mass\":304.1935916616171,\"monoisotopicMass\":304.04603776326,\"unsaturation\":10,\"elements\":[{\"symbol\":\"C\",\"number\":10},{\"symbol\":\"H\",\"number\":13},{\"symbol\":\"N\",\"number\":2},{\"symbol\":\"O\",\"number\":7},{\"symbol\":\"P\",\"number\":1}]},{\"symbol\":\"Dump\",\"name\":\"Desoxyuridine monophosphate diradical\",\"mf\":\"C9H11N2O7P\",\"kind\":\"DNAp\",\"oneLetter\":\"U\",\"alternativeOneLetter\":\"υ\",\"ocl\":{\"value\":\"fjmQs@IQaPSoAJCqUxV\\\\\\\\bTTVRTTbb^iqNZjjjifYkBEa\\\\`@\",\"coordinates\":\"!Bqc}{JxyO|XoSWA}_W]poGQ\\\\GuMKuMh\\\\Gu}]{qpSF]tWQTvatP\"},\"mass\":290.1669742567703,\"monoisotopicMass\":290.0303876988,\"unsaturation\":10,\"elements\":[{\"symbol\":\"C\",\"number\":9},{\"symbol\":\"H\",\"number\":11},{\"symbol\":\"N\",\"number\":2},{\"symbol\":\"O\",\"number\":7},{\"symbol\":\"P\",\"number\":1}]},{\"symbol\":\"Drmp\",\"name\":\"Desoxyribose monophosphate diradical\",\"mf\":\"C5H7O5P\",\"kind\":\"DNAp\",\"ocl\":{\"value\":\"d@\",\"coordinates\":\"\"},\"mass\":178.08005138207807,\"monoisotopicMass\":178.00311032188,\"unsaturation\":4,\"elements\":[{\"symbol\":\"C\",\"number\":5},{\"symbol\":\"H\",\"number\":7},{\"symbol\":\"O\",\"number\":5},{\"symbol\":\"P\",\"number\":1}]},{\"symbol\":\"Dadp\",\"name\":\"Desoxyadenosine diphosphate diradical\",\"mf\":\"C10H13N5O8P2\",\"kind\":\"DNApp\",\"oneLetter\":\"A\",\"ocl\":{\"value\":\"fmwhH`IASM\\\\JBl{wQ`|U^F_AkbdlsjsSOoRtyEMYuUUUM@pSEQaBpnP@\",\"coordinates\":\"!BIi[Rx{_grZOSXa}_]^H@mQbGu}utnDM^HGwWzf~_Ih}M_`AKvto[_`@_`A~grZ_I`\"},\"mass\":393.1868682186928,\"monoisotopicMass\":393.02393639454,\"unsaturation\":14,\"elements\":[{\"symbol\":\"C\",\"number\":10},{\"symbol\":\"H\",\"number\":13},{\"symbol\":\"N\",\"number\":5},{\"symbol\":\"O\",\"number\":8},{\"symbol\":\"P\",\"number\":2}]},{\"symbol\":\"Dcdp\",\"name\":\"Desoxycytidine diphosphate diradical\",\"mf\":\"C9H13N3O9P2\",\"kind\":\"DNApp\",\"oneLetter\":\"C\",\"ocl\":{\"value\":\"fikqH`IQGB`kN|EoP^JoCOaUqrIQQYIQRJKGRJgDejjjjZYfZkBEa\\\\`@\",\"coordinates\":\"!BIi[Rx{_grZOSXa}_]^H@mQbGuMcqLX@m^H@gwWKB__t]Q_`@SFGx@Owx@_mQ\"},\"mass\":369.16213082338425,\"monoisotopicMass\":369.01270300525005,\"unsaturation\":10,\"elements\":[{\"symbol\":\"C\",\"number\":9},{\"symbol\":\"H\",\"number\":13},{\"symbol\":\"N\",\"number\":3},{\"symbol\":\"O\",\"number\":9},{\"symbol\":\"P\",\"number\":2}]},{\"symbol\":\"Dgdp\",\"name\":\"Desoxyguanosine diphosphate diradical\",\"mf\":\"C10H13N5O9P2\",\"kind\":\"DNApp\",\"oneLetter\":\"G\",\"ocl\":{\"value\":\"fcoiH`IASM\\\\JBl{wQ{Axj|L~CWEIYgUff_^fZ\\\\bflzjjjfiZifZlHVEr@@\",\"coordinates\":\"!BIi[Rx{_grZOSXa}_]^H@mQbGu}utnD@m^H@gwWzf~_Ih}M_`AKvto[_`@_`A~gr[j[y|f\"},\"mass\":409.186273143011,\"monoisotopicMass\":409.01885101411,\"unsaturation\":14,\"elements\":[{\"symbol\":\"C\",\"number\":10},{\"symbol\":\"H\",\"number\":13},{\"symbol\":\"N\",\"number\":5},{\"symbol\":\"O\",\"number\":9},{\"symbol\":\"P\",\"number\":2}]},{\"symbol\":\"Dtdp\",\"name\":\"Desoxythymidine diphosphate diradical\",\"mf\":\"C10H14N2O10P2\",\"kind\":\"DNApp\",\"oneLetter\":\"T\",\"ocl\":{\"value\":\"fe{Ph`IQaPUg^Ct\\\\p^JoCO`uqrIQQYIQRJKEJQTxdmUUUSSMTsVDKBy@@\",\"coordinates\":\"!BIi[Rx{_grZOSXa}_]^HMQbGuMcqLX@m^H@gwWKB__t]Q_`@SFALX_`@_`A~w}D\"},\"mass\":384.1735091870477,\"monoisotopicMass\":384.01236865262,\"unsaturation\":10,\"elements\":[{\"symbol\":\"C\",\"number\":10},{\"symbol\":\"H\",\"number\":14},{\"symbol\":\"N\",\"number\":2},{\"symbol\":\"O\",\"number\":10},{\"symbol\":\"P\",\"number\":2}]},{\"symbol\":\"Dudp\",\"name\":\"Desoxyuridine diphosphate diradical\",\"mf\":\"C9H12N2O10P2\",\"kind\":\"DNApp\",\"oneLetter\":\"U\",\"ocl\":{\"value\":\"fikPh`IQaPUg^Bwhp^JoCOaUqrIQQYIQRJKGRJgDejjjjZYjYkBEa\\\\`@\",\"coordinates\":\"!BIi[Rx{_grZOSXa}_]^H@mQbGuMcqLX@m^H@gwWKB__t]Q_`@SFGx@Owx@_mQ\"},\"mass\":370.1468917822009,\"monoisotopicMass\":369.99671858816,\"unsaturation\":10,\"elements\":[{\"symbol\":\"C\",\"number\":9},{\"symbol\":\"H\",\"number\":12},{\"symbol\":\"N\",\"number\":2},{\"symbol\":\"O\",\"number\":10},{\"symbol\":\"P\",\"number\":2}]},{\"symbol\":\"Datp\",\"name\":\"Desoxyadenosine triphosphate diradical\",\"mf\":\"C10H14N5O11P3\",\"kind\":\"DNAppp\",\"oneLetter\":\"A\",\"ocl\":{\"value\":\"eohZMJ@I@diehJAKGOFnakg`OESpr|Mo@yqrIQQYIQRJKYZQKVRcbIJjZjjjihFAhjZcAAXKb@@\",\"coordinates\":\"!BIi[Rx{_grZOSXa}_]^H@mQbGu}utnDM^H@gwWzf~_Ih}M_`AKvto[@hcW@`A~grZ_Igx@_`@@_c}~\"},\"mass\":473.16678574412344,\"monoisotopicMass\":472.9902672839,\"unsaturation\":14,\"elements\":[{\"symbol\":\"C\",\"number\":10},{\"symbol\":\"H\",\"number\":14},{\"symbol\":\"N\",\"number\":5},{\"symbol\":\"O\",\"number\":11},{\"symbol\":\"P\",\"number\":3}]},{\"symbol\":\"Dctp\",\"name\":\"Desoxycytidine triphosphate diradical\",\"mf\":\"C9H14N3O12P3\",\"kind\":\"DNAppp\",\"oneLetter\":\"C\",\"ocl\":{\"value\":\"fkopZ`IQGB`kN|Fk^{NCqUxY|I~BwGHeEEdeEHhl]HlYJ\\\\RVjjjiifVjfkBEa\\\\`@\",\"coordinates\":\"!BIi[Rx{_grZOSXa}_]^H@mQbGuMcqLX@m^H@gwWKB__t]Q_`@SFOrHupH@_mQ_`A~@@A~Owx\"},\"mass\":449.14204834881485,\"monoisotopicMass\":448.97903389461004,\"unsaturation\":10,\"elements\":[{\"symbol\":\"C\",\"number\":9},{\"symbol\":\"H\",\"number\":14},{\"symbol\":\"N\",\"number\":3},{\"symbol\":\"O\",\"number\":12},{\"symbol\":\"P\",\"number\":3}]},{\"symbol\":\"Dgtp\",\"name\":\"Desoxyguanosine triphosphate diradical\",\"mf\":\"C10H14N5O12P3\",\"kind\":\"DNAppp\",\"oneLetter\":\"G\",\"ocl\":{\"value\":\"e`TZCJ@I@diehJAKGOFnamgo`OESpr|CoByqrIQQYIQRJKYZQQYrT\\\\QIUSUUUUMRuMLtuVBBpWD@@\",\"coordinates\":\"!BIi[Rx{_grZOSXa}_]^H@mQbGu}utnD@m^H@gwWzf~_Ih}M_`AKvto[@hcW@`A~gr[j[y|f_`A~@@A~Owx\"},\"mass\":489.16619066844174,\"monoisotopicMass\":488.98518190347005,\"unsaturation\":14,\"elements\":[{\"symbol\":\"C\",\"number\":10},{\"symbol\":\"H\",\"number\":14},{\"symbol\":\"N\",\"number\":5},{\"symbol\":\"O\",\"number\":12},{\"symbol\":\"P\",\"number\":3}]},{\"symbol\":\"Dttp\",\"name\":\"Desoxythymidine triphosphate diradical\",\"mf\":\"C10H15N2O13P3\",\"kind\":\"DNAppp\",\"oneLetter\":\"T\",\"ocl\":{\"value\":\"fgQZ`IQaPUg^BwhygnCqUxY|E~FwGHeEEdeEHhlTiDSISbRuUUUMLuMMMVDKBy@@\",\"coordinates\":\"!BIi[Rx{_grZOSXa}_]^H@mQbGuMcqLX@m^H@gwWKB__t]Q_`@SFALXHcW@`A~w}E~@Gx@@Gx_`\"},\"mass\":464.15342671247834,\"monoisotopicMass\":463.97869954198,\"unsaturation\":10,\"elements\":[{\"symbol\":\"C\",\"number\":10},{\"symbol\":\"H\",\"number\":15},{\"symbol\":\"N\",\"number\":2},{\"symbol\":\"O\",\"number\":13},{\"symbol\":\"P\",\"number\":3}]},{\"symbol\":\"Dutp\",\"name\":\"Desoxyuridine triphosphate diradical\",\"mf\":\"C9H13N2O13P3\",\"kind\":\"DNAppp\",\"oneLetter\":\"U\",\"ocl\":{\"value\":\"fkoQZ`IQaPUg^CUoQ{NCqUxY|I~BwGHeEEdeEHhl]HlYJ\\\\RVjjjiiffffkBEa\\\\`@\",\"coordinates\":\"!BIi[Rx{_grZOSXa}_]^H@mQbGuMcqLX@m^H@gwWKB__t]Q_`@SFOrHupH@_mQ_`A~@@A~Owx\"},\"mass\":450.1268093076315,\"monoisotopicMass\":449.96304947752,\"unsaturation\":10,\"elements\":[{\"symbol\":\"C\",\"number\":9},{\"symbol\":\"H\",\"number\":13},{\"symbol\":\"N\",\"number\":2},{\"symbol\":\"O\",\"number\":13},{\"symbol\":\"P\",\"number\":3}]},{\"symbol\":\"Dade\",\"name\":\"Desoxyadenosine diradical\",\"mf\":\"C10H11N5O2\",\"kind\":\"DNA\",\"oneLetter\":\"A\",\"ocl\":{\"value\":\"fluha@IF]ELJ@|QNJRsN|rntyYpXuUUTBATXPlKd@@\",\"coordinates\":\"!B\\\\KqpQARcg|T^|X@@Id`zeHo@Ie}]vaLcg|T^qAMDDvN_xy\"},\"mass\":233.22703316783156,\"monoisotopicMass\":233.09127461582,\"unsaturation\":14,\"elements\":[{\"symbol\":\"C\",\"number\":10},{\"symbol\":\"H\",\"number\":11},{\"symbol\":\"N\",\"number\":5},{\"symbol\":\"O\",\"number\":2}]},{\"symbol\":\"Dcyt\",\"name\":\"Desoxycytidine diradical\",\"mf\":\"C9H11N3O3\",\"kind\":\"DNA\",\"oneLetter\":\"C\",\"ocl\":{\"value\":\"fhiqa@IVCBa`^HgEIYg^Y~gG^jjjiejpaXWH@\",\"coordinates\":\"!BBOpH_UARcc}TN|Y@PIe`zeIO@MDSIrpXTd}RSqLgTd|\"},\"mass\":209.202295772523,\"monoisotopicMass\":209.08004122653,\"unsaturation\":10,\"elements\":[{\"symbol\":\"C\",\"number\":9},{\"symbol\":\"H\",\"number\":11},{\"symbol\":\"N\",\"number\":3},{\"symbol\":\"O\",\"number\":3}]},{\"symbol\":\"Dgua\",\"name\":\"Desoxyguanosine diradical\",\"mf\":\"C10H11N5O3\",\"kind\":\"DNA\",\"oneLetter\":\"G\",\"ocl\":{\"value\":\"fbmia@IF]ELJYAxb\\\\Tef]ye^Z\\\\lxLZjjjeZfkBEa\\\\`@\",\"coordinates\":\"!B\\\\KqpQARcg|T^|X@@Id`zeHo@Ie}]vaLcg|T^qAMDDvN_vaLcg|\"},\"mass\":249.22643809214986,\"monoisotopicMass\":249.08618923539,\"unsaturation\":14,\"elements\":[{\"symbol\":\"C\",\"number\":10},{\"symbol\":\"H\",\"number\":11},{\"symbol\":\"N\",\"number\":5},{\"symbol\":\"O\",\"number\":3}]},{\"symbol\":\"Dthy\",\"name\":\"Desoxythymidine diradical\",\"mf\":\"C10H12N2O4\",\"kind\":\"DNA\",\"oneLetter\":\"T\",\"ocl\":{\"value\":\"fdyPQ@IVaPtP^HgEIYg^YuiqwjjjjYikBEa\\\\`@\",\"coordinates\":\"!BBOpH_UARcc}TN|Y@PIe`zeIO@MDSIrpXTd}RSqLgDr]RSp\"},\"mass\":224.2136741361865,\"monoisotopicMass\":224.07970687390002,\"unsaturation\":10,\"elements\":[{\"symbol\":\"C\",\"number\":10},{\"symbol\":\"H\",\"number\":12},{\"symbol\":\"N\",\"number\":2},{\"symbol\":\"O\",\"number\":4}]},{\"symbol\":\"Dura\",\"name\":\"Desoxyuridine diradical\",\"mf\":\"C9H10N2O4\",\"kind\":\"DNA\",\"oneLetter\":\"U\",\"ocl\":{\"value\":\"fhiPQ@IVaPpP^HgEIYg^Y~gG^jjjifZpaXWH@\",\"coordinates\":\"!BBOpH_UARcc}TN|Y@PIe`zeIO@MDSIrpXTd}RSqLgTd|\"},\"mass\":210.1870567313397,\"monoisotopicMass\":210.06405680944,\"unsaturation\":10,\"elements\":[{\"symbol\":\"C\",\"number\":9},{\"symbol\":\"H\",\"number\":10},{\"symbol\":\"N\",\"number\":2},{\"symbol\":\"O\",\"number\":4}]},{\"symbol\":\"Amp\",\"name\":\"Adenosine monophosphate diradical\",\"mf\":\"C10H12N5O6P\",\"kind\":\"RNAp\",\"oneLetter\":\"A\",\"alternativeOneLetter\":\"α\",\"ocl\":{\"value\":\"fakhs@INBwlJ\\\\TgHOFwaEqrIQQSYQJIRIMLyxMVuUUUPLpEPQDqBId@@\",\"coordinates\":\"!BNuSFPDlDTEHt_pHtP@H_TuPBOq_qopHBGtgD}D@RpPH_Wtw@aOTd}RtPCQ@@\"},\"mass\":329.20635561758047,\"monoisotopicMass\":329.05252012475,\"unsaturation\":14,\"elements\":[{\"symbol\":\"C\",\"number\":10},{\"symbol\":\"H\",\"number\":12},{\"symbol\":\"N\",\"number\":5},{\"symbol\":\"O\",\"number\":6},{\"symbol\":\"P\",\"number\":1}]},{\"symbol\":\"Cmp\",\"name\":\"Cytidine monophosphate diradical\",\"mf\":\"C9H12N3O7P\",\"kind\":\"RNAp\",\"oneLetter\":\"C\",\"alternativeOneLetter\":\"ς\",\"ocl\":{\"value\":\"ff}qs@I^kBgENSdGc[pbxyDhhilheDiLv\\\\BVjjjjYfZbHfHQL`@\",\"coordinates\":\"!BNuSFPDlDTEHt_pHtP@H_TuPBOpcbpXBGtSItuPSU@H_Wtw@`lFDuP\"},\"mass\":305.1816182222719,\"monoisotopicMass\":305.04128673546,\"unsaturation\":10,\"elements\":[{\"symbol\":\"C\",\"number\":9},{\"symbol\":\"H\",\"number\":12},{\"symbol\":\"N\",\"number\":3},{\"symbol\":\"O\",\"number\":7},{\"symbol\":\"P\",\"number\":1}]},{\"symbol\":\"Gmp\",\"name\":\"Guanosine monophosphate diradical\",\"mf\":\"C10H12N5O7P\",\"kind\":\"RNAp\",\"oneLetter\":\"G\",\"alternativeOneLetter\":\"γ\",\"ocl\":{\"value\":\"fi{is@INBwlJ\\\\TgHp^MoBKcdRbbfrbTRdR\\\\SN^CUmUUUUKMSMQDSDHfP@\",\"coordinates\":\"!BNuSFPDlDTEHt_pHtP@H_TuPBOq_qopHBGtgD}D@RpPH_Wtw@aOTd}RtP@gD}D@\"},\"mass\":345.20576054189877,\"monoisotopicMass\":345.04743474432,\"unsaturation\":14,\"elements\":[{\"symbol\":\"C\",\"number\":10},{\"symbol\":\"H\",\"number\":12},{\"symbol\":\"N\",\"number\":5},{\"symbol\":\"O\",\"number\":7},{\"symbol\":\"P\",\"number\":1}]},{\"symbol\":\"Tmp\",\"name\":\"Thymidine monophosphate diradical\",\"mf\":\"C10H13N2O8P\",\"kind\":\"RNAp\",\"oneLetter\":\"T\",\"alternativeOneLetter\":\"τ\",\"ocl\":{\"value\":\"fncPK@I^aSbgIrtGc[pbxyDhhilheDiLjs`RuUUUSLuMDQLPbY@@\",\"coordinates\":\"!BNuSFPDlDTEHt_pHtP@H_TuPBOpcbpXBGtSItuPSU@H_Wtw@`lFBpXSU@\"},\"mass\":320.1929965859354,\"monoisotopicMass\":320.04095238282997,\"unsaturation\":10,\"elements\":[{\"symbol\":\"C\",\"number\":10},{\"symbol\":\"H\",\"number\":13},{\"symbol\":\"N\",\"number\":2},{\"symbol\":\"O\",\"number\":8},{\"symbol\":\"P\",\"number\":1}]},{\"symbol\":\"Ump\",\"name\":\"Uridine monophosphate diradical\",\"mf\":\"C9H11N2O8P\",\"kind\":\"RNAp\",\"oneLetter\":\"U\",\"alternativeOneLetter\":\"υ\",\"ocl\":{\"value\":\"ff}PK@I^aSbgIsTGc[pbxyDhhilheDiLv\\\\BVjjjjYffbHfHQL`@\",\"coordinates\":\"!BNuSFPDlDTEHt_pHtP@H_TuPBOpcbpXBGtSItuPSU@H_Wtw@`lFDuP\"},\"mass\":306.1663791810886,\"monoisotopicMass\":306.02530231837,\"unsaturation\":10,\"elements\":[{\"symbol\":\"C\",\"number\":9},{\"symbol\":\"H\",\"number\":11},{\"symbol\":\"N\",\"number\":2},{\"symbol\":\"O\",\"number\":8},{\"symbol\":\"P\",\"number\":1}]},{\"symbol\":\"Rmp\",\"name\":\"Ribose monophosphate diradical\",\"mf\":\"C5H7O6P\",\"kind\":\"RNAp\",\"ocl\":{\"value\":\"d@\",\"coordinates\":\"\"},\"mass\":194.07945630639637,\"monoisotopicMass\":193.99802494145,\"unsaturation\":4,\"elements\":[{\"symbol\":\"C\",\"number\":5},{\"symbol\":\"H\",\"number\":7},{\"symbol\":\"O\",\"number\":6},{\"symbol\":\"P\",\"number\":1}]},{\"symbol\":\"Adp\",\"name\":\"Adenosine diphosphate diradical\",\"mf\":\"C10H13N5O9P2\",\"kind\":\"RNApp\",\"oneLetter\":\"A\",\"ocl\":{\"value\":\"fcoiH`INCt\\\\J\\\\UENU{Axv|F~DwGHeEEMeDheHd\\\\eHsg`u{UUUU@mAEMPQDqBId@@\",\"coordinates\":\"!BvuPfpDnDtEK_t_rHtXBH_TwPbOr_IorHbGtgD}F@RxPBuxc|_]^OTh}R_`CQ`MF@_`@_`A~\"},\"mass\":409.186273143011,\"monoisotopicMass\":409.01885101411,\"unsaturation\":14,\"elements\":[{\"symbol\":\"C\",\"number\":10},{\"symbol\":\"H\",\"number\":13},{\"symbol\":\"N\",\"number\":5},{\"symbol\":\"O\",\"number\":9},{\"symbol\":\"P\",\"number\":2}]},{\"symbol\":\"Cdp\",\"name\":\"Cytidine diphosphate diradical\",\"mf\":\"C9H13N3O10P2\",\"kind\":\"RNApp\",\"oneLetter\":\"C\",\"ocl\":{\"value\":\"fe{ph`I^[BgENbgHy`|[^E_CkcdRbbfrbTRdqrdYpIZjjjieijZbHfHQL`@\",\"coordinates\":\"!BvuPfpDnDtEK_t_rHtXBH_TwPb@K_cbpXbKtSItwPS]@Bux`Bo]\\\\lFGx@S]A~@C}~@Gx\"},\"mass\":385.1615357477025,\"monoisotopicMass\":385.00761762482,\"unsaturation\":10,\"elements\":[{\"symbol\":\"C\",\"number\":9},{\"symbol\":\"H\",\"number\":13},{\"symbol\":\"N\",\"number\":3},{\"symbol\":\"O\",\"number\":10},{\"symbol\":\"P\",\"number\":2}]},{\"symbol\":\"Gdp\",\"name\":\"Guanosine diphosphate diradical\",\"mf\":\"C10H13N5O10P2\",\"kind\":\"RNApp\",\"oneLetter\":\"G\",\"ocl\":{\"value\":\"fkhh`INCt\\\\J\\\\UENY{NCqmxM|EnNQJJJ[JIQJQHzIRLyxM^uUUUTkUSLuQDSDHfP@\",\"coordinates\":\"!BvuPfpDnDtEK_tPJHtXBH_TwPb@J_I`JHbGtgD}F@RxPBux`B_]^OTh}R_`CQ`B\\\\StXA~@C}~@Gx\"},\"mass\":425.1856780673293,\"monoisotopicMass\":425.01376563368,\"unsaturation\":14,\"elements\":[{\"symbol\":\"C\",\"number\":10},{\"symbol\":\"H\",\"number\":13},{\"symbol\":\"N\",\"number\":5},{\"symbol\":\"O\",\"number\":10},{\"symbol\":\"P\",\"number\":2}]},{\"symbol\":\"Tdp\",\"name\":\"Thymidine diphosphate diradical\",\"mf\":\"C10H14N2O11P2\",\"kind\":\"RNApp\",\"oneLetter\":\"T\",\"ocl\":{\"value\":\"fmgQh`I^aSbgQSglu`|[^C_@[bdls^rruo}LxDmUUUTruTsTQDqBId@@\",\"coordinates\":\"!BvuPfpDnDtEK_tPJHtXBH_TwPbOs_cbpXbGtSItwPS]@Bux`B_]\\\\lFBpX_`AMtGx@Owx@_`\"},\"mass\":400.172914111366,\"monoisotopicMass\":400.00728327219,\"unsaturation\":10,\"elements\":[{\"symbol\":\"C\",\"number\":10},{\"symbol\":\"H\",\"number\":14},{\"symbol\":\"N\",\"number\":2},{\"symbol\":\"O\",\"number\":11},{\"symbol\":\"P\",\"number\":2}]},{\"symbol\":\"Udp\",\"name\":\"Uridine diphosphate diradical\",\"mf\":\"C9H12N2O11P2\",\"kind\":\"RNApp\",\"oneLetter\":\"U\",\"ocl\":{\"value\":\"fe{Qh`I^aSbgQSehy`|[^E_CkcdRbbfrbTRdqrdYpIZjjjiejfZbHfHQL`@\",\"coordinates\":\"!BvuPfpDnDtEK_t_rHtXBH_TwPb@K_cbpXbKtSItwPS]@Bux`Bo]\\\\lFGx@S]A~@C}~@Gx\"},\"mass\":386.14629670651925,\"monoisotopicMass\":385.99163320773005,\"unsaturation\":10,\"elements\":[{\"symbol\":\"C\",\"number\":9},{\"symbol\":\"H\",\"number\":12},{\"symbol\":\"N\",\"number\":2},{\"symbol\":\"O\",\"number\":11},{\"symbol\":\"P\",\"number\":2}]},{\"symbol\":\"Atp\",\"name\":\"Adenosine triphosphate diradical\",\"mf\":\"C10H14N5O12P3\",\"kind\":\"RNAppp\",\"oneLetter\":\"A\",\"ocl\":{\"value\":\"e`TZCJ@IG@nahJNEHdliemgo`OFspZ|CoByqrIQQSYQJIRIGIRWRL\\\\^AU]UUUUPKPQMTuABDpaBX`@\",\"coordinates\":\"!BvuPfpDnDtEK_t_rHtXBH_TwPbOr_IorHbGtgD}F@RxS|uxc|_]^OTh}R_`CQ`MF@@hcW@A~_`A~@@A~Owx\"},\"mass\":489.16619066844174,\"monoisotopicMass\":488.98518190347005,\"unsaturation\":14,\"elements\":[{\"symbol\":\"C\",\"number\":10},{\"symbol\":\"H\",\"number\":14},{\"symbol\":\"N\",\"number\":5},{\"symbol\":\"O\",\"number\":12},{\"symbol\":\"P\",\"number\":3}]},{\"symbol\":\"Ctp\",\"name\":\"Cytidine triphosphate diradical\",\"mf\":\"C9H14N3O13P3\",\"kind\":\"RNAppp\",\"oneLetter\":\"C\",\"ocl\":{\"value\":\"fgqZ`I^[BgENbgOQsO\\\\Gc[pkxK|MnNQJJJ[JIQJSGJPzQg@ejjjjfVffjZbHfHQL`@\",\"coordinates\":\"!BvuPfpDnDtEK_tPJHtXBH_TwPb@K_cbpXbGtSItwPS]C|ux`B_]\\\\lFGx@S]@BbM\\\\B@Gy~@Gx@@Gx_`\"},\"mass\":465.1414532731331,\"monoisotopicMass\":464.97394851418,\"unsaturation\":10,\"elements\":[{\"symbol\":\"C\",\"number\":9},{\"symbol\":\"H\",\"number\":14},{\"symbol\":\"N\",\"number\":3},{\"symbol\":\"O\",\"number\":13},{\"symbol\":\"P\",\"number\":3}]},{\"symbol\":\"Gtp\",\"name\":\"Guanosine triphosphate diradical\",\"mf\":\"C10H14N5O13P3\",\"kind\":\"RNAppp\",\"oneLetter\":\"G\",\"ocl\":{\"value\":\"eh\\\\ZKJ@IG@nahJNEHdliemco`POFspZ|KoAyqrIQQSYQJIRIGQJQzQccpJkjjjjjeZjYZijbDIaBDq@@\",\"coordinates\":\"!BvuPfpDnDtEK_tPJHtXBH_TwPb@J_I`JHbGtgD}F@RxPBux`B_]^OTh}R_`CQ`B\\\\StX@BbM_|@Gy~@Gx@@Gx_`\"},\"mass\":505.16559559276,\"monoisotopicMass\":504.98009652304,\"unsaturation\":14,\"elements\":[{\"symbol\":\"C\",\"number\":10},{\"symbol\":\"H\",\"number\":14},{\"symbol\":\"N\",\"number\":5},{\"symbol\":\"O\",\"number\":13},{\"symbol\":\"P\",\"number\":3}]},{\"symbol\":\"Ttp\",\"name\":\"Thymidine triphosphate diradical\",\"mf\":\"C10H15N2O14P3\",\"kind\":\"RNAppp\",\"oneLetter\":\"T\",\"ocl\":{\"value\":\"eo`TGJ@IOHJNEGHdlnaiekg`OFspZ|Mo@yqrIQQSYQJIRY[ZPzQc`HjjjjjYZjVjZbDIaBDq@@\",\"coordinates\":\"!BvuPfpDnDtEK_tPJHtXBH_TwPb@K_cbpXbGtSItwPS]@Bux`B_]\\\\lFBpX_`AMt@JHupH@_gx@_`@@_c}~\"},\"mass\":480.15283163679663,\"monoisotopicMass\":479.97361416155,\"unsaturation\":10,\"elements\":[{\"symbol\":\"C\",\"number\":10},{\"symbol\":\"H\",\"number\":15},{\"symbol\":\"N\",\"number\":2},{\"symbol\":\"O\",\"number\":14},{\"symbol\":\"P\",\"number\":3}]},{\"symbol\":\"Utp\",\"name\":\"Uridine triphosphate diradical\",\"mf\":\"C9H13N2O14P3\",\"kind\":\"RNAppp\",\"oneLetter\":\"U\",\"ocl\":{\"value\":\"fgPz`I^aSbgQSeoQsO\\\\Gc[pkxK|MnNQJJJ[JIQJSGJPzQg@ejjjjfVjVjZbHfHQL`@\",\"coordinates\":\"!BvuPfpDnDtEK_tPJHtXBH_TwPb@K_cbpXbGtSItwPS]C|ux`B_]\\\\lFGx@S]@BbM\\\\B@Gy~@Gx@@Gx_`\"},\"mass\":466.12621423194986,\"monoisotopicMass\":465.95796409709004,\"unsaturation\":10,\"elements\":[{\"symbol\":\"C\",\"number\":9},{\"symbol\":\"H\",\"number\":13},{\"symbol\":\"N\",\"number\":2},{\"symbol\":\"O\",\"number\":14},{\"symbol\":\"P\",\"number\":3}]},{\"symbol\":\"Ade\",\"name\":\"Adenosine diradical\",\"mf\":\"C10H11N5O3\",\"kind\":\"RNA\",\"oneLetter\":\"A\",\"ocl\":{\"value\":\"fbmia@IV|gLJ\\\\Axj\\\\Tef[vyWV\\\\]zJZjjj`PJ`bIbDSH@\",\"coordinates\":\"!BBOpH_UARccFPEP{PId{RpBN[~i|BEP{iVA@fUARU@QTADBYPId\"},\"mass\":249.22643809214986,\"monoisotopicMass\":249.08618923539,\"unsaturation\":14,\"elements\":[{\"symbol\":\"C\",\"number\":10},{\"symbol\":\"H\",\"number\":11},{\"symbol\":\"N\",\"number\":5},{\"symbol\":\"O\",\"number\":3}]},{\"symbol\":\"Cyt\",\"name\":\"Cytidine diradical\",\"mf\":\"C9H11N3O4\",\"kind\":\"RNA\",\"oneLetter\":\"C\",\"ocl\":{\"value\":\"fdypQ@INcBgK@|UNJRsM{\\\\~sg`uUUULmQDSDHfP@\",\"coordinates\":\"!BBOpH_UARccFPEP{PId{RpBN[~iRTBpgDq`@c`BNKB\\\\@c`\"},\"mass\":225.20170069684127,\"monoisotopicMass\":225.0749558461,\"unsaturation\":10,\"elements\":[{\"symbol\":\"C\",\"number\":9},{\"symbol\":\"H\",\"number\":11},{\"symbol\":\"N\",\"number\":3},{\"symbol\":\"O\",\"number\":4}]},{\"symbol\":\"Gua\",\"name\":\"Guanosine diradical\",\"mf\":\"C10H11N5O4\",\"kind\":\"RNA\",\"oneLetter\":\"G\",\"ocl\":{\"value\":\"fj}hQ@IV|gLJ\\\\JCqTxiKLwmroKNN}EMUUUTkTuDQLPbY@@\",\"coordinates\":\"!BBOpH_UARccFPEP{PId{RpBN[~k|BEP{iVA@fUARU@QTADBYiVA@fP\"},\"mass\":265.22584301646816,\"monoisotopicMass\":265.08110385496,\"unsaturation\":14,\"elements\":[{\"symbol\":\"C\",\"number\":10},{\"symbol\":\"H\",\"number\":11},{\"symbol\":\"N\",\"number\":5},{\"symbol\":\"O\",\"number\":4}]},{\"symbol\":\"Thy\",\"name\":\"Thymidine diradical\",\"mf\":\"C10H12N2O5\",\"kind\":\"RNA\",\"oneLetter\":\"T\",\"ocl\":{\"value\":\"fleQQ@INaSed`|UNJRsM{\\\\zlyxMUUUSMMDQLPbY@@\",\"coordinates\":\"!BBOpH_UARccFPEP{PId{RpBN[~iRTBpgDq`@c`BNKB\\\\lIpBN\"},\"mass\":240.21307906050478,\"monoisotopicMass\":240.07462149347,\"unsaturation\":10,\"elements\":[{\"symbol\":\"C\",\"number\":10},{\"symbol\":\"H\",\"number\":12},{\"symbol\":\"N\",\"number\":2},{\"symbol\":\"O\",\"number\":5}]},{\"symbol\":\"Ura\",\"name\":\"Uridine diradical\",\"mf\":\"C9H10N2O5\",\"kind\":\"RNA\",\"oneLetter\":\"U\",\"ocl\":{\"value\":\"fdyQQ@INaSeh`|UNJRsM{\\\\~sg`uUUULsQDSDHfP@\",\"coordinates\":\"!BBOpH_UARccFPEP{PId{RpBN[~iRTBpgDq`@c`BNKB\\\\@c`\"},\"mass\":226.18646165565798,\"monoisotopicMass\":226.05897142901,\"unsaturation\":10,\"elements\":[{\"symbol\":\"C\",\"number\":9},{\"symbol\":\"H\",\"number\":10},{\"symbol\":\"N\",\"number\":2},{\"symbol\":\"O\",\"number\":5}]},{\"symbol\":\"Dam\",\"name\":\"1,2′-O-dimethyladenosine monophosphate diradical 01A\",\"mf\":\"C12H16N5O6P\",\"kind\":\"NucleotideP\",\"oneLetter\":\"œ\",\"ocl\":{\"value\":\"feghs@E^ct\\\\J\\\\udhOEw`eqrIQQQKZIQJQIiLxFK^uUUUUKLtuQDSDHfP@\",\"coordinates\":\"!BNuSFPDlDTEHt_pHtP@H_TuOSU@HEF@`H_R\\\\StPAKA@a}_S_|BD}RSuKQ@MD@SuH\"},\"mass\":357.2595904272741,\"monoisotopicMass\":357.08382025367,\"unsaturation\":14,\"elements\":[{\"symbol\":\"C\",\"number\":12},{\"symbol\":\"H\",\"number\":16},{\"symbol\":\"N\",\"number\":5},{\"symbol\":\"O\",\"number\":6},{\"symbol\":\"P\",\"number\":1}]},{\"symbol\":\"Dgm\",\"name\":\"1,2′-O-dimethylguanosine monophosphate diradical 01G\",\"mf\":\"C12H16N5O7P\",\"kind\":\"NucleotideP\",\"oneLetter\":\"ε\",\"ocl\":{\"value\":\"fmwis@E^ct\\\\J\\\\udlp^KoAKcdRbbbVtRbTbSbSNAbwmUUUURsMSUDQLPbY@@\",\"coordinates\":\"!BNuSFPDlDTEHt_pHtP@H_TuOSU@HEF@`H_R\\\\StPAKA@a}_S_|BD}RSuKQ@B\\\\StPAOT`\"},\"mass\":373.2589953515923,\"monoisotopicMass\":373.07873487324,\"unsaturation\":14,\"elements\":[{\"symbol\":\"C\",\"number\":12},{\"symbol\":\"H\",\"number\":16},{\"symbol\":\"N\",\"number\":5},{\"symbol\":\"O\",\"number\":7},{\"symbol\":\"P\",\"number\":1}]},{\"symbol\":\"Dim\",\"name\":\"1,2′-O-dimethylinosine monophosphate diradical 019A\",\"mf\":\"C12O7N4H15P\",\"kind\":\"NucleotideP\",\"oneLetter\":\"ξ\",\"ocl\":{\"value\":\"fegIs@E^cvENZrTXOEw`eqrIQQQKZIQJQIiLxFK^uUUUUKLtuQDSDHfP@\",\"coordinates\":\"!BNuSFPDlDTEHt_pHtP@H_TuOSU@HEF@`H_R\\\\StPAKA@a}_S_|BD}RSuKQ@MD@SuH\"},\"mass\":358.2443513860907,\"monoisotopicMass\":358.06783583658,\"unsaturation\":14,\"elements\":[{\"symbol\":\"C\",\"number\":12},{\"symbol\":\"H\",\"number\":15},{\"symbol\":\"N\",\"number\":4},{\"symbol\":\"O\",\"number\":7},{\"symbol\":\"P\",\"number\":1}]},{\"symbol\":\"Tia\",\"name\":\"2- methylthiomethylenethio-N6-isopentenyl-adenosine monophosphate diradical\",\"mf\":\"C17H24N5O6PS2\",\"kind\":\"NucleotideP\",\"oneLetter\":\"£\",\"ocl\":{\"value\":\"eh\\\\ZFJ@IG@nahJNEDl`OFspb\\\\V`cXHrIQQSYQJIRINIYIKQccpJkjjjjjAfBJjfjBDIaBDq@@\",\"coordinates\":\"!BpBYTvxBNFY|bEJObGvOS\\\\@Yt]~DUEJOctu~@Ha}`HzOSTwPTh~H@hc|_`BH_Xa}b@JH@gx@bGvH@h`B_`BH_P\"},\"mass\":489.50637075565066,\"monoisotopicMass\":489.09056286031,\"unsaturation\":16,\"elements\":[{\"symbol\":\"C\",\"number\":17},{\"symbol\":\"H\",\"number\":24},{\"symbol\":\"N\",\"number\":5},{\"symbol\":\"O\",\"number\":6},{\"symbol\":\"P\",\"number\":1},{\"symbol\":\"S\",\"number\":2}]},{\"symbol\":\"Mhc\",\"name\":\"2′‐O‐Methyl-5-hydroxymethylcytidine monophosphate diradical\",\"mf\":\"C11H16N3O8P\",\"kind\":\"NucleotideP\",\"oneLetter\":\"¡\",\"ocl\":{\"value\":\"fikpK@EA{BgM^rTXOEw`eqrIQQQKZIQJSJigHujjjjifYjkBHQL`@\",\"coordinates\":\"!BNuSFPDlDTEHt_pHtP@HoTuOSU@HC~NKA`HoQLgSUAMT@a}oS_|BBpXKAaMT@CQ\"},\"mass\":349.2342579562838,\"monoisotopicMass\":349.06750148395,\"unsaturation\":10,\"elements\":[{\"symbol\":\"C\",\"number\":11},{\"symbol\":\"H\",\"number\":16},{\"symbol\":\"N\",\"number\":3},{\"symbol\":\"O\",\"number\":8},{\"symbol\":\"P\",\"number\":1}]},{\"symbol\":\"Odg\",\"name\":\"N2,2′-O-dimethylguanosine monophosphate diradical 02G\",\"mf\":\"C12H16N5O7P\",\"kind\":\"NucleotideP\",\"oneLetter\":\"γ\",\"ocl\":{\"value\":\"fmwis@E^ct\\\\J\\\\udlp^KoAKcdRbbbVtRbTbSbsNAbwmUUUURsMSUDQLPbY@@\",\"coordinates\":\"!BNuSFPDlDTEHt_pHtP@H_TuOSU@HEF@`H_R\\\\StPAKA@a}_S_|BD}RSuKQ@B\\\\StPAOT`\"},\"mass\":373.2589953515923,\"monoisotopicMass\":373.07873487324,\"unsaturation\":14,\"elements\":[{\"symbol\":\"C\",\"number\":12},{\"symbol\":\"H\",\"number\":16},{\"symbol\":\"N\",\"number\":5},{\"symbol\":\"O\",\"number\":7},{\"symbol\":\"P\",\"number\":1}]},{\"symbol\":\"Ntg\",\"name\":\"N2,N2,2′-O-trimethylguanosine monophosphate diradical 022G\",\"mf\":\"C13H18N5O7P\",\"kind\":\"NucleotideP\",\"oneLetter\":\"|\",\"ocl\":{\"value\":\"fcois@E^ct\\\\J\\\\udlp^KoAKcdRbbbVtRbTbSbTYpLVcjjjjjVYjZjHbXaDr@@\",\"coordinates\":\"!BNuSFPDlDTEHt_pHtP@H_TuOSU@HEF@`H_R\\\\StPAKA@a}_S_|BD}RSuHgD}D@tPBNOt}R\"},\"mass\":387.2856127564392,\"monoisotopicMass\":387.0943849377,\"unsaturation\":14,\"elements\":[{\"symbol\":\"C\",\"number\":13},{\"symbol\":\"H\",\"number\":18},{\"symbol\":\"N\",\"number\":5},{\"symbol\":\"O\",\"number\":7},{\"symbol\":\"P\",\"number\":1}]},{\"symbol\":\"Otg\",\"name\":\"N2,7,2′-O-trimethylguanosine monophosphate diradical 027G\",\"mf\":\"C13H20N5O7P\",\"kind\":\"NucleotideP\",\"oneLetter\":\"æ\",\"ocl\":{\"value\":\"fcoisBE^bN\\\\J\\\\udjp^KoAKcFU}dRbbbVtRbTbRlQYpLVcjjjjjVYjjjHbXaDr@@\",\"coordinates\":\"!BNuSFPDlDTEHt_pHtP@H_TuOSU@HEF@`H_R\\\\StPAKA@a}_S_|BD}RSuKFPMD@IqOQ@D}R\"},\"mass\":389.30149426455074,\"monoisotopicMass\":389.11003500216,\"unsaturation\":12,\"elements\":[{\"symbol\":\"C\",\"number\":13},{\"symbol\":\"H\",\"number\":20},{\"symbol\":\"N\",\"number\":5},{\"symbol\":\"O\",\"number\":7},{\"symbol\":\"P\",\"number\":1}]},{\"symbol\":\"Rya\",\"name\":\"2′-O-ribosyladenosine monophosphate diradical 00A\",\"mf\":\"C15H20N5O9P\",\"kind\":\"NucleotideP\",\"oneLetter\":\"^\",\"ocl\":{\"value\":\"e`\\\\ZIL@DaegobFAIO@hlm`OGSp\\\\\\\\\\\\bbbfrRbdTT\\\\rbRQUCDQTrusuUUUUMUU@pET@@@\",\"coordinates\":\"!BIlAKaMARw}DBbMF@bGuMtHc|KAbH_ZU`@GzH_WwW@h`XKFjKB_jXB\\\\SiVA`zmG_Irp_hQKctvOSR\\\\lIrp\"},\"mass\":445.3217759066577,\"monoisotopicMass\":445.09986424130005,\"unsaturation\":16,\"elements\":[{\"symbol\":\"C\",\"number\":15},{\"symbol\":\"H\",\"number\":20},{\"symbol\":\"N\",\"number\":5},{\"symbol\":\"O\",\"number\":9},{\"symbol\":\"P\",\"number\":1}]},{\"symbol\":\"Ryg\",\"name\":\"2′-O-ribosylguanosine monophosphate diradical 00G\",\"mf\":\"C15H20N5O10P\",\"kind\":\"NucleotideP\",\"oneLetter\":\"ℑ\",\"ocl\":{\"value\":\"ehRZEL@DaegobFAIO@hlm`POGSp\\\\\\\\\\\\bbbfrRbdTT\\\\rbRQbhXbJfVn^jjjjijjjVZfj@@@\",\"coordinates\":\"!BIlAKaMARw}DBbMF@bGuMtH`BKAbH_ZU`@GzH_WwW@h`XKFjKB_jXB\\\\SiVA`zmG_Irp_hQKctvOSR\\\\lt]|gK@\"},\"mass\":461.321180830976,\"monoisotopicMass\":461.09477886087,\"unsaturation\":16,\"elements\":[{\"symbol\":\"C\",\"number\":15},{\"symbol\":\"H\",\"number\":20},{\"symbol\":\"N\",\"number\":5},{\"symbol\":\"O\",\"number\":10},{\"symbol\":\"P\",\"number\":1}]},{\"symbol\":\"Tmu\",\"name\":\"2-thio-2′-O-methyluridine monophosphate diradical 02U\",\"mf\":\"C10H13N2O7PS\",\"kind\":\"NucleotideP\",\"oneLetter\":\"∏\",\"ocl\":{\"value\":\"fncQp`EAaSfleZCq]x^BDnNQJJJI[QJIRYlyFmUUUULsSQDSDHfP@\",\"coordinates\":\"!BNuSFPDlDTEHt_pHtP@H_TuOSU@HC~NKA`H_QLgSUAMT@a}_S_|BBpXSU@\"},\"mass\":336.25837906774416,\"monoisotopicMass\":336.01810893766003,\"unsaturation\":10,\"elements\":[{\"symbol\":\"C\",\"number\":10},{\"symbol\":\"H\",\"number\":13},{\"symbol\":\"N\",\"number\":2},{\"symbol\":\"O\",\"number\":7},{\"symbol\":\"P\",\"number\":1},{\"symbol\":\"S\",\"number\":1}]},{\"symbol\":\"Dmut\",\"name\":\"3,2′-O-dimethyluridine monophosphate diradical 03U\",\"mf\":\"C11H15N2O8P\",\"kind\":\"NucleotideP\",\"oneLetter\":\"σ\",\"ocl\":{\"value\":\"fasPK@EAaSfoYKtGb{pRxyDhhhemDheIhv\\\\cVjjjjfYjZHbXaDr@@\",\"coordinates\":\"!BNuSFPDlDTEHt_pHtP@H_TuOSU@HC~NKA`H_QLgSUAMT@a}_S_|BBpXOxyMT@\"},\"mass\":334.2196139907822,\"monoisotopicMass\":334.05660244729,\"unsaturation\":10,\"elements\":[{\"symbol\":\"C\",\"number\":11},{\"symbol\":\"H\",\"number\":15},{\"symbol\":\"N\",\"number\":2},{\"symbol\":\"O\",\"number\":8},{\"symbol\":\"P\",\"number\":1}]},{\"symbol\":\"Amc\",\"name\":\"N4-acetyl-2′-O-methylcytidine monophosphate diradical 042C\",\"mf\":\"C12H16N3O8P\",\"kind\":\"NucleotideP\",\"oneLetter\":\"ℵ\",\"ocl\":{\"value\":\"fe{pK@EA[BgM^rTXOEw`eqrIQQQKZIQJSMJLyFmUUUULsMMQDSDHfP@\",\"coordinates\":\"!BNuSFPDlDTEHt_pHtP@H_TuOSU@HC~NKA`H_QLgSUAMT@a}_S_|BBpXSU@caLgSU@\"},\"mass\":361.244993853019,\"monoisotopicMass\":361.06750148395,\"unsaturation\":12,\"elements\":[{\"symbol\":\"C\",\"number\":12},{\"symbol\":\"H\",\"number\":16},{\"symbol\":\"N\",\"number\":3},{\"symbol\":\"O\",\"number\":8},{\"symbol\":\"P\",\"number\":1}]},{\"symbol\":\"Tmc\",\"name\":\"N4,N4,2′-O-trimethylcytidine monophosphate diradical 044C\",\"mf\":\"C12H18N3O7P\",\"kind\":\"NucleotideP\",\"oneLetter\":\"β\",\"ocl\":{\"value\":\"fikqs@EA[BgM^rTGb{pRxyDhhhemDheIfhsdZuUUUTsLuTQDqBId@@\",\"coordinates\":\"!BNuSFPDlDTEHt_pHtP@H_TuOSU@HC~NKA`H_QLgSUAMT@a}_S_|BBpXSU@cbpX\"},\"mass\":347.2614704368123,\"monoisotopicMass\":347.08823692884005,\"unsaturation\":10,\"elements\":[{\"symbol\":\"C\",\"number\":12},{\"symbol\":\"H\",\"number\":18},{\"symbol\":\"N\",\"number\":3},{\"symbol\":\"O\",\"number\":7},{\"symbol\":\"P\",\"number\":1}]},{\"symbol\":\"Dcy\",\"name\":\"N4,2′-O-dimethylcytidine monophosphate diradical 04C\",\"mf\":\"C11H16N3O7P\",\"kind\":\"NucleotideP\",\"oneLetter\":\"λ\",\"ocl\":{\"value\":\"fasqs@EA[BgM^rTGb{pRxyDhhhemDheIff\\\\cVjjjjfYfjHbXaDr@@\",\"coordinates\":\"!BNuSFPDlDTEHt_pHtP@H_TuOSU@HC~NKA`H_QLgSUAMT@a}_S_|BBpXSU@lF@\"},\"mass\":333.23485303196554,\"monoisotopicMass\":333.07258686438,\"unsaturation\":10,\"elements\":[{\"symbol\":\"C\",\"number\":11},{\"symbol\":\"H\",\"number\":16},{\"symbol\":\"N\",\"number\":3},{\"symbol\":\"O\",\"number\":7},{\"symbol\":\"P\",\"number\":1}]},{\"symbol\":\"Muo\",\"name\":\"2′-O-methyluridine 5-oxyacetic acid methyl ester monophosphate diradical 0503U\",\"mf\":\"C13H17N2O11P\",\"kind\":\"NucleotideP\",\"oneLetter\":\"Ͽ\",\"ocl\":{\"value\":\"fkoQk@EAaSfoYJwj}`|W^BWGHeEEDmheDiLjlif\\\\cVjjjjfYjZZhbIbDSH@\",\"coordinates\":\"!BKAb@tURD@m\\\\YpMAMpBYMcvjbOplIwx@bGuMc}\\\\Bb@JH@dvOcuKPSXa}bGvHH`BbGu~Oxc|bGt\"},\"mass\":408.25518206531905,\"monoisotopicMass\":408.05699637046,\"unsaturation\":12,\"elements\":[{\"symbol\":\"C\",\"number\":13},{\"symbol\":\"H\",\"number\":17},{\"symbol\":\"N\",\"number\":2},{\"symbol\":\"O\",\"number\":11},{\"symbol\":\"P\",\"number\":1}]},{\"symbol\":\"Xmu\",\"name\":\"5-carboxymethylaminomethyl-2′-O-methyluridine monophosphate diradical 051U\",\"mf\":\"C13H18N3O10P\",\"kind\":\"NucleotideP\",\"oneLetter\":\")\",\"ocl\":{\"value\":\"fkopk@EAGBgM^rWns`|W^BWGHeEEDmheDiLjleF\\\\cVjjjjfYjZfhbIbDSH@\",\"coordinates\":\"!BKAb@tURD@m\\\\YpMAMpBYMcvjb@HlIwx@bGuMc}\\\\Bb@JH@dvOcuKPSXa}bGvH@h`BbGvH@gx@bKt\"},\"mass\":407.2704211065024,\"monoisotopicMass\":407.07298078755,\"unsaturation\":12,\"elements\":[{\"symbol\":\"C\",\"number\":13},{\"symbol\":\"H\",\"number\":18},{\"symbol\":\"N\",\"number\":3},{\"symbol\":\"O\",\"number\":10},{\"symbol\":\"P\",\"number\":1}]},{\"symbol\":\"Mmu\",\"name\":\"5-methoxycarbonylmethyl-2′-O-methyluridine monophosphate diradical 0521U\",\"mf\":\"C13H17N2O10P\",\"kind\":\"NucleotideP\",\"oneLetter\":\"∩\",\"ocl\":{\"value\":\"fcwPk@EAaSfoYKvZp^KoAKcdRbbbVtRbTfUVfYrMZjjjjYfifjHbXaDr@@\",\"coordinates\":\"!BS]@lFJU`@Gyoza`lzf@lI}mK_`B@cm\\\\Bb@HlI}]}_`A~@BpgIqLXKH`Bb@I~@Ha}_c~HHa}\"},\"mass\":392.2557771410008,\"monoisotopicMass\":392.06208175089,\"unsaturation\":12,\"elements\":[{\"symbol\":\"C\",\"number\":13},{\"symbol\":\"H\",\"number\":17},{\"symbol\":\"N\",\"number\":2},{\"symbol\":\"O\",\"number\":10},{\"symbol\":\"P\",\"number\":1}]},{\"symbol\":\"Cue\",\"name\":\"5-(carboxyhydroxymethyl)-2′-O-methyluridine methyl ester monophosphate diradical 0522U\",\"mf\":\"C13H17N2O11P\",\"kind\":\"NucleotideP\",\"oneLetter\":\"∩\",\"ocl\":{\"value\":\"fkoQk@EAaSfoYKtZ}`|W^BWGHeEEDmheDiLjhYf\\\\cVjjjjfYjZZhbIbDSH@\",\"coordinates\":\"!BS]@lFJU`@Gyoza`lzf@lI}mK_`B@cm\\\\Bb@HlI}]}_`A~@BpgIqLXKH`Bb@I~@Gx@bGu~Oxc|bGt\"},\"mass\":408.25518206531905,\"monoisotopicMass\":408.05699637046,\"unsaturation\":12,\"elements\":[{\"symbol\":\"C\",\"number\":13},{\"symbol\":\"H\",\"number\":17},{\"symbol\":\"N\",\"number\":2},{\"symbol\":\"O\",\"number\":11},{\"symbol\":\"P\",\"number\":1}]},{\"symbol\":\"Cyu\",\"name\":\"5-carbamoylmethyl-2′-O-methyluridine monophosphate diradical 053U\",\"mf\":\"C12H16N3O9P\",\"kind\":\"NucleotideP\",\"oneLetter\":\"~\",\"ocl\":{\"value\":\"fmgqK@EAWBgM^rWlp^KoAKcdRbbbVtRbTfUVcNQkUUUUSLuLuDQLPbY@@\",\"coordinates\":\"!BNuSFPDlDTEHt_pHtP@H_TuOSU@HC~NKA`H_QLgSUAMT@a}_S_|BBpXKAaMTDuPOxxlF@\"},\"mass\":377.24439877733727,\"monoisotopicMass\":377.06241610352,\"unsaturation\":12,\"elements\":[{\"symbol\":\"C\",\"number\":12},{\"symbol\":\"H\",\"number\":16},{\"symbol\":\"N\",\"number\":3},{\"symbol\":\"O\",\"number\":9},{\"symbol\":\"P\",\"number\":1}]},{\"symbol\":\"Ipu\",\"name\":\"5-(isopentenylaminomethyl)-2′-O-methyluridine monophosphate diradical 0583U\",\"mf\":\"C16H24N3O8P\",\"kind\":\"NucleotideP\",\"oneLetter\":\"¼\",\"ocl\":{\"value\":\"fgpK@EAGBgM^rWhOEw`eqrIQQQKZIQJSJkIJLyFmUUUULsTuMTQDqBId@@\",\"coordinates\":\"!BS]@lFJU`@Gyoza`lzf@lI}mK_`B@cm\\\\Bb@HlI}]}_`A~@BpgIqLXKH`Bb@I~@Ha}b@JH_Xc|_`BH_P\"},\"mass\":417.35146347240624,\"monoisotopicMass\":417.13010174179004,\"unsaturation\":12,\"elements\":[{\"symbol\":\"C\",\"number\":16},{\"symbol\":\"H\",\"number\":24},{\"symbol\":\"N\",\"number\":3},{\"symbol\":\"O\",\"number\":8},{\"symbol\":\"P\",\"number\":1}]},{\"symbol\":\"Mcy\",\"name\":\"5,2′-O-dimethylcytidine monophosphate diradical monophosphate diradical 05C\",\"mf\":\"C11H16N3O7P\",\"kind\":\"NucleotideP\",\"oneLetter\":\"τ\",\"ocl\":{\"value\":\"fasqs@EA{BgM^rTGb{pRxyDhhhemDheIeV\\\\cVjjjjfYfjHbXaDr@@\",\"coordinates\":\"!BNuSFPDlDTEHt_pHtP@H_TuOSU@HC~NKA`H_QLgSUAMT@a}_S_|BBpXKAaMT@\"},\"mass\":333.23485303196554,\"monoisotopicMass\":333.07258686438,\"unsaturation\":10,\"elements\":[{\"symbol\":\"C\",\"number\":11},{\"symbol\":\"H\",\"number\":16},{\"symbol\":\"N\",\"number\":3},{\"symbol\":\"O\",\"number\":7},{\"symbol\":\"P\",\"number\":1}]},{\"symbol\":\"Dmuf\",\"name\":\"5,2′-O-dimethyluridine monophosphate diradical 05U\",\"mf\":\"C11H15N2O8P\",\"kind\":\"NucleotideP\",\"oneLetter\":\"\\\\\",\"ocl\":{\"value\":\"fasPK@EAaSfoYKtGb{pRxyDhhhemDheIeV\\\\cVjjjjfYjZHbXaDr@@\",\"coordinates\":\"!BNuSFPDlDTEHt_pHtP@H_TuOSU@HC~NKA`H_QLgSUAMT@a}_S_|BBpXKAaMT@\"},\"mass\":334.2196139907822,\"monoisotopicMass\":334.05660244729,\"unsaturation\":10,\"elements\":[{\"symbol\":\"C\",\"number\":11},{\"symbol\":\"H\",\"number\":15},{\"symbol\":\"N\",\"number\":2},{\"symbol\":\"O\",\"number\":8},{\"symbol\":\"P\",\"number\":1}]},{\"symbol\":\"Tma\",\"name\":\"N6,N6,2′-O-trimethyladenosine monophosphate diradical 066A\",\"mf\":\"C13H18N5O6P\",\"kind\":\"NucleotideP\",\"oneLetter\":\"η\",\"ocl\":{\"value\":\"fmwhs@E^ct\\\\J\\\\udhOEw`eqrIQQQKZIQJQIkQg@q[vjjjj`Y`JjBHfHQL`@\",\"coordinates\":\"!BNuSFPDlDTEHt_pHtP@H_TuOSU@HEF@`H_R\\\\StPAKA@a}_S_|BD}RSuKQ@MD@SuHXK@\"},\"mass\":371.2862078321209,\"monoisotopicMass\":371.09947031813005,\"unsaturation\":14,\"elements\":[{\"symbol\":\"C\",\"number\":13},{\"symbol\":\"H\",\"number\":18},{\"symbol\":\"N\",\"number\":5},{\"symbol\":\"O\",\"number\":6},{\"symbol\":\"P\",\"number\":1}]},{\"symbol\":\"Nda\",\"name\":\"N6,2′-O-dimethyladenosine monophosphate diradical 06A\",\"mf\":\"C12H16N5O6P\",\"kind\":\"NucleotideP\",\"oneLetter\":\"χ\",\"ocl\":{\"value\":\"feghs@E^ct\\\\J\\\\udhOEw`eqrIQQQKZIQJQIkLxFK^uUUUTCLAUADSDHfP@\",\"coordinates\":\"!BNuSFPDlDTEHt_pHtP@H_TuOSU@HEF@`H_R\\\\StPAKA@a}_S_|BD}RSuKQ@MD@FBp\"},\"mass\":357.2595904272741,\"monoisotopicMass\":357.08382025367,\"unsaturation\":14,\"elements\":[{\"symbol\":\"C\",\"number\":12},{\"symbol\":\"H\",\"number\":16},{\"symbol\":\"N\",\"number\":5},{\"symbol\":\"O\",\"number\":6},{\"symbol\":\"P\",\"number\":1}]},{\"symbol\":\"Fmc\",\"name\":\"5-formyl-2′-O-methylcytidine monophosphate diradical 071C\",\"mf\":\"C10H12N3O8P\",\"kind\":\"NucleotideP\",\"oneLetter\":\"°\",\"ocl\":{\"value\":\"faspK@I^[BgENSghOFwaEqrIQQSYQJIRYULxDmUUUTsLttQDqBId@@\",\"coordinates\":\"!BNuSFPDlDTEHt_pHtP@H_TuPBOpcbpXBGtSItuPSU@H_Wtw@`lFBpXSU@@tP\"},\"mass\":333.1917590433254,\"monoisotopicMass\":333.03620135502996,\"unsaturation\":12,\"elements\":[{\"symbol\":\"C\",\"number\":10},{\"symbol\":\"H\",\"number\":12},{\"symbol\":\"N\",\"number\":3},{\"symbol\":\"O\",\"number\":8},{\"symbol\":\"P\",\"number\":1}]},{\"symbol\":\"Omi\",\"name\":\"2′-O-methylinosine monophosphate diradical 09A\",\"mf\":\"C11H13N4O7P\",\"kind\":\"NucleotideP\",\"oneLetter\":\"≤\",\"ocl\":{\"value\":\"fi{Is@E^cvENZrTXOEw`eqrIQQQKZIQJQIig@q[vjjjjiYffhbIbDSH@\",\"coordinates\":\"!BNuSFPDlDTEHt_pHtP@H_TuOSU@HEF@`H_R\\\\StPAKA@a}_S_|BD}RSuKQ@MD@\"},\"mass\":344.21773398124395,\"monoisotopicMass\":344.05218577211997,\"unsaturation\":14,\"elements\":[{\"symbol\":\"C\",\"number\":11},{\"symbol\":\"H\",\"number\":13},{\"symbol\":\"N\",\"number\":4},{\"symbol\":\"O\",\"number\":7},{\"symbol\":\"P\",\"number\":1}]},{\"symbol\":\"Opu\",\"name\":\"2′-O-methylpseudouridine monophosphate diradical 09U\",\"mf\":\"C10H13N2O8P\",\"kind\":\"NucleotideP\",\"oneLetter\":\"Z\",\"ocl\":{\"value\":\"fncPK@@qaSfoYJtGb{pRxyDhhhemDheIfsdZuUUULuMMDQLPbY@@\",\"coordinates\":\"!BNuSFPDlDTEHt_pHtP@H_TuOSU@HC~NKA`H_QLgSUAMT@a}_S_|BBpXSU@\"},\"mass\":320.1929965859354,\"monoisotopicMass\":320.04095238282997,\"unsaturation\":10,\"elements\":[{\"symbol\":\"C\",\"number\":10},{\"symbol\":\"H\",\"number\":13},{\"symbol\":\"N\",\"number\":2},{\"symbol\":\"O\",\"number\":8},{\"symbol\":\"P\",\"number\":1}]},{\"symbol\":\"Oma\",\"name\":\"2′-O-methyladenosine monophosphate diradical 0A\",\"mf\":\"C11H14N5O6P\",\"kind\":\"NucleotideP\",\"oneLetter\":\":\",\"ocl\":{\"value\":\"fi{hs@E^ct\\\\J\\\\udhOEw`eqrIQQQKZIQJQIig@q[vjjjj`Y`J`bIbDSH@\",\"coordinates\":\"!BNuSFPDlDTEHt_pHtP@H_TuOSU@HEF@`H_R\\\\StPAKA@a}_S_|BD}RSuKQ@MD@\"},\"mass\":343.2329730224273,\"monoisotopicMass\":343.06817018921,\"unsaturation\":14,\"elements\":[{\"symbol\":\"C\",\"number\":11},{\"symbol\":\"H\",\"number\":14},{\"symbol\":\"N\",\"number\":5},{\"symbol\":\"O\",\"number\":6},{\"symbol\":\"P\",\"number\":1}]},{\"symbol\":\"Omc\",\"name\":\"2′-O-methylcytidine monophosphate diradical 0C\",\"mf\":\"C10H14N3O7P\",\"kind\":\"NucleotideP\",\"oneLetter\":\"B\",\"ocl\":{\"value\":\"fncqs@EA[BgM^rTGb{pRxyDhhhemDheIfsdZuUUUTsLuDQLPbY@@\",\"coordinates\":\"!BNuSFPDlDTEHt_pHtP@H_TuOSU@HC~NKA`H_QLgSUAMT@a}_S_|BBpXSU@\"},\"mass\":319.2082356271187,\"monoisotopicMass\":319.05693679992004,\"unsaturation\":10,\"elements\":[{\"symbol\":\"C\",\"number\":10},{\"symbol\":\"H\",\"number\":14},{\"symbol\":\"N\",\"number\":3},{\"symbol\":\"O\",\"number\":7},{\"symbol\":\"P\",\"number\":1}]},{\"symbol\":\"Omg\",\"name\":\"2′-O-methylguanosine monophosphate diradical 0G\",\"mf\":\"C11H14N5O7P\",\"kind\":\"NucleotideP\",\"oneLetter\":\"#\",\"ocl\":{\"value\":\"fegis@E^ct\\\\J\\\\udlp^KoAKcdRbbbVtRbTbSbYpLV}jjjjjVYjZbHfHQL`@\",\"coordinates\":\"!BNuSFPDlDTEHt_pHtP@HoTuOSU@HEF@`H_R\\\\StPAKA@a}_S_|BD}RSuKQ@B\\\\StP@\"},\"mass\":359.23237794674554,\"monoisotopicMass\":359.06308480878,\"unsaturation\":14,\"elements\":[{\"symbol\":\"C\",\"number\":11},{\"symbol\":\"H\",\"number\":14},{\"symbol\":\"N\",\"number\":5},{\"symbol\":\"O\",\"number\":7},{\"symbol\":\"P\",\"number\":1}]},{\"symbol\":\"Omu\",\"name\":\"2′-O-methyluridinemonophosphate diradical 0U\",\"mf\":\"C10H13N2O8P\",\"kind\":\"NucleotideP\",\"oneLetter\":\"J\",\"ocl\":{\"value\":\"fncPK@EAaSfoYJtGb{pRxyDhhhemDheIfsdZuUUUTsMMDQLPbY@@\",\"coordinates\":\"!BNuSFPDlDTEHt_pHtP@H_TuOSU@HC~NKA`H_QLgSUAMT@a}_S_|BBpXSU@\"},\"mass\":320.1929965859354,\"monoisotopicMass\":320.04095238282997,\"unsaturation\":10,\"elements\":[{\"symbol\":\"C\",\"number\":10},{\"symbol\":\"H\",\"number\":13},{\"symbol\":\"N\",\"number\":2},{\"symbol\":\"O\",\"number\":8},{\"symbol\":\"P\",\"number\":1}]},{\"symbol\":\"Cdg\",\"name\":\"7-cyano-7-deazaguanosine monophosphate diradical 100G\",\"mf\":\"C12H12N5O7P\",\"kind\":\"NucleotideP\",\"oneLetter\":\"φ\",\"ocl\":{\"value\":\"fmwis@INzM\\\\J\\\\TgLp^MoBKcdRbbfrbTRdRUbSN^CWmUUUUKLuSuDQLPbY@@\",\"coordinates\":\"!BNuSFPDlDTEHt_pHtP@H_TuPBOq_qopHBGtgD}D@RpPH_Wtw@aOTd}RqdCQ@B\\\\StPCFP@\"},\"mass\":369.22723233536925,\"monoisotopicMass\":369.04743474432,\"unsaturation\":18,\"elements\":[{\"symbol\":\"C\",\"number\":12},{\"symbol\":\"H\",\"number\":12},{\"symbol\":\"N\",\"number\":5},{\"symbol\":\"O\",\"number\":7},{\"symbol\":\"P\",\"number\":1}]},{\"symbol\":\"Azg\",\"name\":\"7-aminomethyl-7-deazaguanosine monophosphate diradical 101G\",\"mf\":\"C12H16N5O7P\",\"kind\":\"NucleotideP\",\"oneLetter\":\"∉\",\"ocl\":{\"value\":\"fmwis@INzM\\\\J\\\\TgLp^MoBKcdRbbfrbTRdRUbSN^CWmUUUUKLuSUDQLPbY@@\",\"coordinates\":\"!BNuSFPDlDTEHt_pHtP@H_TuPBOq_qopHBGtgD}D@RpPH_Wtw@aOTd}RqdCQ@B\\\\StPA`z`\"},\"mass\":373.2589953515923,\"monoisotopicMass\":373.07873487324,\"unsaturation\":14,\"elements\":[{\"symbol\":\"C\",\"number\":12},{\"symbol\":\"H\",\"number\":16},{\"symbol\":\"N\",\"number\":5},{\"symbol\":\"O\",\"number\":7},{\"symbol\":\"P\",\"number\":1}]},{\"symbol\":\"Eqo\",\"name\":\"epoxyqueuosine monophosphate diradical 102G\",\"mf\":\"C17H22N5O10P\",\"kind\":\"NucleotideP\",\"oneLetter\":\"ς\",\"ocl\":{\"value\":\"el^ZEL@IGNaehJNEDlig`TPOFspb\\\\\\\\bTTTvTRbTbRlRjbbfXx|Bjz~aAajjjjiYfjZjjjjHPfDHSD@@\",\"coordinates\":\"!BNuSFPDlDTEHt_pHtP@H_TuPBOq_qopHBGtgD}D@RpPH_Wtw@aOTd}RqdCQ@B\\\\StP@{ULY@TEIKA@a}tPA}BOpHAEP\"},\"mass\":487.3585341325581,\"monoisotopicMass\":487.11042892533,\"unsaturation\":18,\"elements\":[{\"symbol\":\"C\",\"number\":17},{\"symbol\":\"H\",\"number\":22},{\"symbol\":\"N\",\"number\":5},{\"symbol\":\"O\",\"number\":10},{\"symbol\":\"P\",\"number\":1}]},{\"symbol\":\"Aes\",\"name\":\"archaeosine monophosphate diradical 103G\",\"mf\":\"C12H15N6O7P\",\"kind\":\"NucleotideP\",\"oneLetter\":\"(\",\"ocl\":{\"value\":\"fcoYs@INzM^xTxiNY`|[^DWGHeEEMeDheHdkDhsg`u{UUUURsMTmTQDqBId@@\",\"coordinates\":\"!BNuSFPDlDTEHt_pHtP@H_TuPBOq_qopHBGtgD}D@RpPH_Wtw@aOTd}RqdCQ@B\\\\StP@{UFCj\"},\"mass\":386.2577578089824,\"monoisotopicMass\":386.07398384544,\"unsaturation\":16,\"elements\":[{\"symbol\":\"C\",\"number\":12},{\"symbol\":\"H\",\"number\":15},{\"symbol\":\"N\",\"number\":6},{\"symbol\":\"O\",\"number\":7},{\"symbol\":\"P\",\"number\":1}]},{\"symbol\":\"Goq\",\"name\":\"galactosyl-queuosine monophosphate diradical 104G\",\"mf\":\"C23H32N5O14P\",\"kind\":\"NucleotideP\",\"oneLetter\":\"9\",\"ocl\":{\"value\":\"ekXzGL@IGNaehJNEDliod\\\\VU]SPOFspb\\\\\\\\bTTTvTRbTbRlRjbTrTrbfRXx|Bjz^AyEjjjjiYfjZijjjjjjbDIaBDq@@\",\"coordinates\":\"!BvuPfpDnDtEK_tPJHtXBH_TwPb@J_IorHbGtgD}F@RxRH_WwW@hbOTh}RIlCQ`B\\\\StXC[UB[@RxSPT`JHbGwQ`H`BaEQ~@Ha}bOq~Ox`BbGu~@Ha}bOrH@`\"},\"mass\":633.4999767508004,\"monoisotopicMass\":633.16833772591,\"unsaturation\":20,\"elements\":[{\"symbol\":\"C\",\"number\":23},{\"symbol\":\"H\",\"number\":32},{\"symbol\":\"N\",\"number\":5},{\"symbol\":\"O\",\"number\":14},{\"symbol\":\"P\",\"number\":1}]},{\"symbol\":\"Gaq\",\"name\":\"glutamyl-queuosine monophosphate diradical105G\",\"mf\":\"C22H29N6O12P\",\"kind\":\"NucleotideP\",\"oneLetter\":\"⊄\",\"ocl\":{\"value\":\"emWVCL@IGNaejXJNEDlioh\\\\YUPOFspb\\\\\\\\bTTTvTRbTbRlRjbTJTtrTXx|Bjz^AjjjjiYfjZijfjfjbDIaBDq@@\",\"coordinates\":\"!BTmB@c`JHUMmMtL@YtEHYgxQTaDoQ`L@YFY|gKMARH`Ygy|fpAfN`Hz@`H{PTb\\\\ltEIRtHBNHaTv|@YFYPTha}b@I~@Ha}_c~H@ha}bOq~@Ha}\"},\"mass\":600.473311954707,\"monoisotopicMass\":600.15810739451,\"unsaturation\":22,\"elements\":[{\"symbol\":\"C\",\"number\":22},{\"symbol\":\"H\",\"number\":29},{\"symbol\":\"N\",\"number\":6},{\"symbol\":\"O\",\"number\":12},{\"symbol\":\"P\",\"number\":1}]},{\"symbol\":\"Moq\",\"name\":\"mannosyl-queuosine monophosphate diradical 106G\",\"mf\":\"C23H32N5O14P\",\"kind\":\"NucleotideP\",\"oneLetter\":\"8\",\"ocl\":{\"value\":\"ekXzGL@IGNaehJNEDliod\\\\VU]SPOFspb\\\\\\\\bTTTvTRbTbRlRjbTrTrbfRXx|Bjz^AyEjjjjiYfjZijjjjjjbDIaBDq@@\",\"coordinates\":\"!BvuPfpDnDtEK_tPJHtXBH_TwPb@J_IorHbGtgD}F@RxRH_WwW@hbOTh}RIlCQ`B\\\\StXC[UB[@RxSPT`JHbGwQ`H`BaEQ~@Ha}bOq~Ox`BbGu~@Ha}bOrH@`\"},\"mass\":633.4999767508004,\"monoisotopicMass\":633.16833772591,\"unsaturation\":20,\"elements\":[{\"symbol\":\"C\",\"number\":23},{\"symbol\":\"H\",\"number\":32},{\"symbol\":\"N\",\"number\":5},{\"symbol\":\"O\",\"number\":14},{\"symbol\":\"P\",\"number\":1}]},{\"symbol\":\"Qus\",\"name\":\"queuosine monophosphate diradical 10G\",\"mf\":\"C17H22N5O9P\",\"kind\":\"NucleotideP\",\"oneLetter\":\"Q\",\"ocl\":{\"value\":\"edZZIL@IGNaehJNEDliohPOFspb\\\\\\\\bTTTvTRbTbRlRjbTKGG`UWSpMUUUUKLuSUMUTPaLHPfH@@\",\"coordinates\":\"!BNuSFPDlDTEHt_pHtP@H_TuPBOq_qopHBGtgD}D@RpPHoWtw@aOTd}RqdCQ@B\\\\StP@{ULY@RpQPTopHBGwQ@@QT\"},\"mass\":471.35912920823984,\"monoisotopicMass\":471.11551430576,\"unsaturation\":18,\"elements\":[{\"symbol\":\"C\",\"number\":17},{\"symbol\":\"H\",\"number\":22},{\"symbol\":\"N\",\"number\":5},{\"symbol\":\"O\",\"number\":9},{\"symbol\":\"P\",\"number\":1}]},{\"symbol\":\"Cpo\",\"name\":\"1-methyl-3-(3-amino-3-carboxypropyl)pseudouridine monophosphate diradical 1309U\",\"mf\":\"C14H20N3O10P\",\"kind\":\"NucleotideP\",\"oneLetter\":\"α\",\"ocl\":{\"value\":\"fgpk@OAWBgENSgi{`|[^DWGHeEEMeDheIhjbihs`RuUUTsTuSUMQDSDHfP@\",\"coordinates\":\"!BTh|SI~ioOwy`iR\\\\SiV|SFGxw}FH_]]}DqbH@gx_c|SFA`lIqOW_Xa}uwu~Ox`BbGu~Ox`B_`BH_P\"},\"mass\":421.2970385113492,\"monoisotopicMass\":421.08863085201,\"unsaturation\":12,\"elements\":[{\"symbol\":\"C\",\"number\":14},{\"symbol\":\"H\",\"number\":20},{\"symbol\":\"N\",\"number\":3},{\"symbol\":\"O\",\"number\":10},{\"symbol\":\"P\",\"number\":1}]},{\"symbol\":\"Mls\",\"name\":\"1-methylinosine monophosphate diradical 19A\",\"mf\":\"C11H13N4O7P\",\"kind\":\"NucleotideP\",\"oneLetter\":\"O\",\"ocl\":{\"value\":\"fi{Is@INBvENJSghOFwaEqrIQQSYQJIRIMIgOAjvjjjjefZZhbIbDSH@\",\"coordinates\":\"!BNuSFPDlDTEHt_pHtP@H_TuPBOq_qopHBGtgD}D@RpPH_Wtw@aOTd}RtPCQ@D}R\"},\"mass\":344.21773398124395,\"monoisotopicMass\":344.05218577211997,\"unsaturation\":14,\"elements\":[{\"symbol\":\"C\",\"number\":11},{\"symbol\":\"H\",\"number\":13},{\"symbol\":\"N\",\"number\":4},{\"symbol\":\"O\",\"number\":7},{\"symbol\":\"P\",\"number\":1}]},{\"symbol\":\"Mpu\",\"name\":\"1-methylpseudouridine monophosphate diradical 19U\",\"mf\":\"C10H13N2O8P\",\"kind\":\"NucleotideP\",\"oneLetter\":\"]\",\"ocl\":{\"value\":\"fncPK@OAaSbgIrtGc[pbxyDhhilheDiLjs`RuUUTsTuMDQLPbY@@\",\"coordinates\":\"!BNuSFPDlDTEHt_pHtP@H_TuPBOpcbpXBGtSItuPSU@H_Wtw@`lFBpXSU@\"},\"mass\":320.1929965859354,\"monoisotopicMass\":320.04095238282997,\"unsaturation\":10,\"elements\":[{\"symbol\":\"C\",\"number\":10},{\"symbol\":\"H\",\"number\":13},{\"symbol\":\"N\",\"number\":2},{\"symbol\":\"O\",\"number\":8},{\"symbol\":\"P\",\"number\":1}]},{\"symbol\":\"Mad\",\"name\":\"1-methyladenosine monophosphate diradical 1A\",\"mf\":\"C11H14N5O6P\",\"kind\":\"NucleotideP\",\"oneLetter\":\"\\\"\",\"ocl\":{\"value\":\"fi{hs@INBwlJ\\\\TgHOFwaEqrIQQSYQJIRIMIgOAjvjjjjefZZhbIbDSH@\",\"coordinates\":\"!BNuSFPDlDTEHt_pHtP@H_TuPBOq_qopHBGtgD}D@RpPH_Wtw@aOTd}RtPCQ@D}R\"},\"mass\":343.2329730224273,\"monoisotopicMass\":343.06817018921,\"unsaturation\":14,\"elements\":[{\"symbol\":\"C\",\"number\":11},{\"symbol\":\"H\",\"number\":14},{\"symbol\":\"N\",\"number\":5},{\"symbol\":\"O\",\"number\":6},{\"symbol\":\"P\",\"number\":1}]},{\"symbol\":\"Mgs\",\"name\":\"1-methylguanosine monophosphate diradical 1G\",\"mf\":\"C11H14N5O7P\",\"kind\":\"NucleotideP\",\"oneLetter\":\"K\",\"ocl\":{\"value\":\"fegis@INBwlJ\\\\TgHp^MoBKcdRbbfrbTRdR\\\\RYspZmjjjjiYfijbHfHQL`@\",\"coordinates\":\"!BNuSFPDlDTEHt_pHtP@H_TuPBOq_qopHBGtgD}D@RpPH_Wtw@aOTd}RtP@gD}D@SuH\"},\"mass\":359.23237794674554,\"monoisotopicMass\":359.06308480878,\"unsaturation\":14,\"elements\":[{\"symbol\":\"C\",\"number\":11},{\"symbol\":\"H\",\"number\":14},{\"symbol\":\"N\",\"number\":5},{\"symbol\":\"O\",\"number\":7},{\"symbol\":\"P\",\"number\":1}]},{\"symbol\":\"Msu\",\"name\":\"5-aminomethyl-2-selenouridine monophosphate diradical 20510U\",\"mf\":\"C10H14N3O7PSe\",\"kind\":\"NucleotideP\",\"oneLetter\":\"π\",\"ocl\":{\"value\":\"fasqp`I^{BgEIrtGc[p\\\\bQ\\\\\\\\bTTTvTRbTfUSNAKUUUULsTuDQLPbY@@\",\"coordinates\":\"!BNuSFPDlDTEHt_pHtP@H_TuPBOpcbpXBGtSItuPSU@H_Wtw@`lFBpXSU@@tP\"},\"mass\":398.1676241841323,\"monoisotopicMass\":398.97345859992004,\"unsaturation\":null,\"elements\":[{\"symbol\":\"C\",\"number\":10},{\"symbol\":\"H\",\"number\":14},{\"symbol\":\"N\",\"number\":3},{\"symbol\":\"O\",\"number\":7},{\"symbol\":\"P\",\"number\":1},{\"symbol\":\"Se\",\"number\":1}]},{\"symbol\":\"Mse\",\"name\":\"5-methylaminomethyl-2-selenouridine monophosphate diradical 20511U\",\"mf\":\"C11H16N3O7PSe\",\"kind\":\"NucleotideP\",\"oneLetter\":\"≅\",\"ocl\":{\"value\":\"fikqp`I^{BgEIrtGc[p\\\\bQ\\\\\\\\bTTTvTRbTfUVYpIZjjjifZfjHbXaDr@@\",\"coordinates\":\"!BNuSFPDlDTEHt_pHtP@H_TuPBOpcbpXBGtSItuPSU@H_Wtw@`lFBpXSUAMTBpX\"},\"mass\":412.19424158897914,\"monoisotopicMass\":412.98910866438,\"unsaturation\":null,\"elements\":[{\"symbol\":\"C\",\"number\":11},{\"symbol\":\"H\",\"number\":16},{\"symbol\":\"N\",\"number\":3},{\"symbol\":\"O\",\"number\":7},{\"symbol\":\"P\",\"number\":1},{\"symbol\":\"Se\",\"number\":1}]},{\"symbol\":\"Cse\",\"name\":\"5-carboxymethylaminomethyl-2-selenouridine monophosphate diradical 2051U\",\"mf\":\"C12H16N3O9PSe\",\"kind\":\"NucleotideP\",\"oneLetter\":\"⊥\",\"ocl\":{\"value\":\"fcwqH`I^{BgEIru^p^MoArIEqrIQQSYQJIRYUYJLxDmUUUTsMSTuDQLPbY@@\",\"coordinates\":\"!BKAb@tURDM\\\\YpMAMpBYMcx`BKB]~@Ha}SXW@h`Bb@IMcx}RtDvH_Xa}b@JH@ha}b@I~@Ha}\"},\"mass\":456.20378733435086,\"monoisotopicMass\":456.97893790352,\"unsaturation\":null,\"elements\":[{\"symbol\":\"C\",\"number\":12},{\"symbol\":\"H\",\"number\":16},{\"symbol\":\"N\",\"number\":3},{\"symbol\":\"O\",\"number\":9},{\"symbol\":\"P\",\"number\":1},{\"symbol\":\"Se\",\"number\":1}]},{\"symbol\":\"Agm\",\"name\":\"agmatidine monophosphate diradical 20C\",\"mf\":\"C14H26N7O6P\",\"kind\":\"NucleotideP\",\"oneLetter\":\"¿\",\"ocl\":{\"value\":\"fgxs@I^BuY{piqR\\\\`|[^DWGHeEEMeDeEHmUddhsgbuUUUSTuUUMIDSDHfP@\",\"coordinates\":\"!BDqc_tTnD_]\\\\fpH}MgrYRc}_|Dr_W_Wx@ThWM_|bOqRc}ARctu~@Gx@urH@gx@b@I~@H`BbGu~@@\"},\"mass\":419.373876184194,\"monoisotopicMass\":419.16821858483,\"unsaturation\":10,\"elements\":[{\"symbol\":\"C\",\"number\":14},{\"symbol\":\"H\",\"number\":26},{\"symbol\":\"N\",\"number\":7},{\"symbol\":\"O\",\"number\":6},{\"symbol\":\"P\",\"number\":1}]},{\"symbol\":\"Sou\",\"name\":\"2-selenouridine monophosphate diradical 20U\",\"mf\":\"C9H11N2O7PSe\",\"kind\":\"NucleotideP\",\"oneLetter\":\"ω\",\"ocl\":{\"value\":\"ff}Qp`I^aSbdyjCqmxNQHnNQJJJ[JIQJSMg@ejjjjfYihbIbDSH@\",\"coordinates\":\"!BNuSFPDlDTEHt_pHtP@H_TuPBOpcbpXBGtSItuPSU@H_Wtw@`lFDuP\"},\"mass\":369.1263628137839,\"monoisotopicMass\":369.9469094988,\"unsaturation\":null,\"elements\":[{\"symbol\":\"C\",\"number\":9},{\"symbol\":\"H\",\"number\":11},{\"symbol\":\"N\",\"number\":2},{\"symbol\":\"O\",\"number\":7},{\"symbol\":\"P\",\"number\":1},{\"symbol\":\"Se\",\"number\":1}]},{\"symbol\":\"Agu\",\"name\":\"5-aminomethyl-2-geranylthiouridine monophosphate diradical 21510U\",\"mf\":\"C20H30N3O7PS\",\"kind\":\"NucleotideP\",\"oneLetter\":\"Δ\",\"ocl\":{\"value\":\"ed\\\\\\\\NB@IOIhJNEDla`OFsp\\\\BHgGHeEEMeDheHdjdcEdhqpEUUUUURsUSMTuQBDpaBXdDt@\",\"coordinates\":\"!BDr__cdo[_X`fgx}RgqeRtM]}Dqa~O}\\\\BTmBH_]]}uwuRtMAMcuI~O}\\\\BupJH_]]}_`A~Oxa}uwu~Oxa}_cW_Xa}\"},\"mass\":487.5074340654907,\"monoisotopicMass\":487.15420849000003,\"unsaturation\":14,\"elements\":[{\"symbol\":\"C\",\"number\":20},{\"symbol\":\"H\",\"number\":30},{\"symbol\":\"N\",\"number\":3},{\"symbol\":\"O\",\"number\":7},{\"symbol\":\"P\",\"number\":1},{\"symbol\":\"S\",\"number\":1}]},{\"symbol\":\"Mgu\",\"name\":\"5-methylaminomethyl-2-geranylthiouridine monophosphate diradical 21511U\",\"mf\":\"C21H32N3O7PS\",\"kind\":\"NucleotideP\",\"oneLetter\":\"h\",\"ocl\":{\"value\":\"elR\\\\NB@IOIhJNEDla`OFsp\\\\BHgGHeEEMeDheHdjdlileFN@jjjjjjVZjYjijbDIaBDqHIh\",\"coordinates\":\"!BTv^cbn{__@fw|}RwqeRdK]}Tva~_{_|TiCp_[]}mwuRdIAMsuI~_{]|mwsp_[]}mwu~_{_||Gvw_Wy|Gu~_{]}|Gt\"},\"mass\":501.5340514703375,\"monoisotopicMass\":501.16985855446006,\"unsaturation\":14,\"elements\":[{\"symbol\":\"C\",\"number\":21},{\"symbol\":\"H\",\"number\":32},{\"symbol\":\"N\",\"number\":3},{\"symbol\":\"O\",\"number\":7},{\"symbol\":\"P\",\"number\":1},{\"symbol\":\"S\",\"number\":1}]},{\"symbol\":\"Cgu\",\"name\":\"5-carboxymethylaminomethyl-2-geranylthiouridine monophosphate diradical 2151U\",\"mf\":\"C22H32N3O9PS\",\"kind\":\"NucleotideP\",\"oneLetter\":\"f\",\"ocl\":{\"value\":\"ef^\\\\IB@IOIhJNEDla`XPOFsp\\\\BHgGHeEEMeDheHdjdlhehbhqpEUUUUURsUSMUMMTPaLHPfIAM@\",\"coordinates\":\"!BTv^cbn{_@fw|}RwqeRdK]}Tva~_{]|TiCp[_}muRdIAMsuI~_{]|mwsp_[]}mwu~_{]||Gvw_[_}_g}~_{]||Ou~_{]}|Gt\"},\"mass\":545.5435972157093,\"monoisotopicMass\":545.1596877935999,\"unsaturation\":16,\"elements\":[{\"symbol\":\"C\",\"number\":22},{\"symbol\":\"H\",\"number\":32},{\"symbol\":\"N\",\"number\":3},{\"symbol\":\"O\",\"number\":9},{\"symbol\":\"P\",\"number\":1},{\"symbol\":\"S\",\"number\":1}]},{\"symbol\":\"Mha\",\"name\":\"2-methylthio-N6-(cis-hydroxyisopentenyl) adenosine monophosphate diradical 2160A\",\"mf\":\"C16H22N5O7PS\",\"kind\":\"NucleotideP\",\"oneLetter\":\"≠\",\"ocl\":{\"value\":\"e`TZNB@IG@nahJNEDlo`OFspb\\\\V`cHeEEMeDheHdxeleDqqxEUuUUUU@sAETuTDHSBDIbP[P\",\"coordinates\":\"!BzfC@IeKPaDn}bHCQb@KQwuRDFALYpHCQt]WHc|TmCQw}~N`ME~@Gx@upJH@h`B_`BH_X`BbGvHGxbGt\"},\"mass\":459.41437086899504,\"monoisotopicMass\":459.09775624102,\"unsaturation\":16,\"elements\":[{\"symbol\":\"C\",\"number\":16},{\"symbol\":\"H\",\"number\":22},{\"symbol\":\"N\",\"number\":5},{\"symbol\":\"O\",\"number\":7},{\"symbol\":\"P\",\"number\":1},{\"symbol\":\"S\",\"number\":1}]},{\"symbol\":\"Mpa\",\"name\":\"2-methylthio-N6-isopentenyladenosine monophosphate diradical 2161A\",\"mf\":\"C16H22N5O6PS\",\"kind\":\"NucleotideP\",\"oneLetter\":\"*\",\"ocl\":{\"value\":\"eohZFB@IG@nahJNEDl`OFspb\\\\V`cHeEEMeDheHdxeleFNO@jnjjjjhFXHjfjBDIaBDq@@\",\"coordinates\":\"!BpBYTvxBNFY|bEJObGvOS\\\\@Yt]~DUEJOctu~@Ha}`HzOSTwPTh~H@h`B_`BH_Xa}bOrH@ha}b@I~@Ha}\"},\"mass\":443.4149659446768,\"monoisotopicMass\":443.10284162145,\"unsaturation\":16,\"elements\":[{\"symbol\":\"C\",\"number\":16},{\"symbol\":\"H\",\"number\":22},{\"symbol\":\"N\",\"number\":5},{\"symbol\":\"O\",\"number\":6},{\"symbol\":\"P\",\"number\":1},{\"symbol\":\"S\",\"number\":1}]},{\"symbol\":\"Mca\",\"name\":\"2-methylthio-N6-threonylcarbamoyladenosine monophosphate diradical 2162A\",\"mf\":\"C16H21N6O10PS\",\"kind\":\"NucleotideP\",\"oneLetter\":\"[\",\"ocl\":{\"value\":\"ebVVEB@IG@nachJNEDlm`XTPOFspb\\\\V`cHeEEMeDheHdxemLhhhqqxEUuUUUU@sAESUMUABDpaBX`@\",\"coordinates\":\"!BzfC@IeKPaDn}bHCQb@KQwuRDFALYpHCQt]W@h`BTmCQw}~N`ME~@Gx@upJH@h`B_`BH_Wxb@JH_WxbOrHo]^}_`BH_P\"},\"mass\":520.4113480993399,\"monoisotopicMass\":520.07774907193,\"unsaturation\":18,\"elements\":[{\"symbol\":\"C\",\"number\":16},{\"symbol\":\"H\",\"number\":21},{\"symbol\":\"N\",\"number\":6},{\"symbol\":\"O\",\"number\":10},{\"symbol\":\"P\",\"number\":1},{\"symbol\":\"S\",\"number\":1}]},{\"symbol\":\"Mva\",\"name\":\"2-methylthio-N6-hydroxynorvalylcarbamoyladenosine monophosphate diradical 2163A\",\"mf\":\"C17H23N6O10PS\",\"kind\":\"NucleotideP\",\"oneLetter\":\"≈\",\"ocl\":{\"value\":\"ej^VEB@IG@nachJNEDlm`XTPOFspb\\\\V`cHeEEMeDheHdxemLhhiVNO@jnjjjjhFXHjZijjBDIaBDq@@\",\"coordinates\":\"!BpBYTvxBNFY|BbEJObGvOS\\\\@Yt]~DUEJOctu~@Ha}`HzOSTwPTh~H@h`B_`BH_Xa}bOrH@gx@bGvHGx@bGwW@h`B_c~H@ha}\"},\"mass\":534.4379655041866,\"monoisotopicMass\":534.09339913639,\"unsaturation\":18,\"elements\":[{\"symbol\":\"C\",\"number\":17},{\"symbol\":\"H\",\"number\":23},{\"symbol\":\"N\",\"number\":6},{\"symbol\":\"O\",\"number\":10},{\"symbol\":\"P\",\"number\":1},{\"symbol\":\"S\",\"number\":1}]},{\"symbol\":\"Mya\",\"name\":\"2-methylthio cyclic N6-threonylcarbamoyladenosine monophosphate diradical 2164A\",\"mf\":\"C17H20N5O9PS\",\"kind\":\"NucleotideP\",\"oneLetter\":\"ÿ\",\"ocl\":{\"value\":\"elVZIB@IG@nkhJNEDlcghPOFspb\\\\V`cHeEEMeDheHdxeihiUFNO@jnkojjjjhFXHjfZjbHPfDHSD@@\",\"coordinates\":\"!BvuPfpDnDtEK_tPJHtXBH_TwPb@J_IorHbGtgD}F@RxRH_WwW@hbOTh}RIqOQ`MF@cuKW@hQTcttfpL@YS]@BbGvH@Gx\"},\"mass\":501.4080351062552,\"monoisotopicMass\":501.07193541570007,\"unsaturation\":20,\"elements\":[{\"symbol\":\"C\",\"number\":17},{\"symbol\":\"H\",\"number\":20},{\"symbol\":\"N\",\"number\":5},{\"symbol\":\"O\",\"number\":9},{\"symbol\":\"P\",\"number\":1},{\"symbol\":\"S\",\"number\":1}]},{\"symbol\":\"Hta\",\"name\":\"hydroxy-N6-threonylcarbamoyladenosine monophosphate diradical 2165A\",\"mf\":\"C15H19N6O11P\",\"kind\":\"NucleotideP\",\"oneLetter\":\"«\",\"ocl\":{\"value\":\"elZVML@IG@fnehJNEDligo`TPOFspb\\\\\\\\bTTTvTRbTbSVTrbbeXx|BjZjjjj`Y`JZijjBDIaBDq@@\",\"coordinates\":\"!BpBYTvxBNFY|bEJObGvOS\\\\@Yt]~DUEJOctu~@Ha}`HzOSTwPTh~HH`BbGvH_Xc|_`BH_Xc|_`BH_]_|bOq~Oxc|bGt\"},\"mass\":490.31934821268436,\"monoisotopicMass\":490.08494245264,\"unsaturation\":18,\"elements\":[{\"symbol\":\"C\",\"number\":15},{\"symbol\":\"H\",\"number\":19},{\"symbol\":\"N\",\"number\":6},{\"symbol\":\"O\",\"number\":11},{\"symbol\":\"P\",\"number\":1}]},{\"symbol\":\"Lyd\",\"name\":\"2-lysidine monophosphate diradical 21C\",\"mf\":\"C15H24N5O8P\",\"kind\":\"NucleotideP\",\"oneLetter\":\"}\",\"ocl\":{\"value\":\"eo`ZAL@IGOFmhJNEDlkg`OFspb\\\\\\\\bTTTvTRbTbSVRTtXxBJjjjjfYjZjfhaBXPaLP@\",\"coordinates\":\"!BTh|SI~ioOwy`iR\\\\SiV|SFGxw}FH_]]}Dqa~Oxc|_c|SFA`lIqOW_Xa}_c~HHa}bOrH_WxbOq~@Ha}\"},\"mass\":433.3541339985626,\"monoisotopicMass\":433.13624975064994,\"unsaturation\":12,\"elements\":[{\"symbol\":\"C\",\"number\":15},{\"symbol\":\"H\",\"number\":24},{\"symbol\":\"N\",\"number\":5},{\"symbol\":\"O\",\"number\":8},{\"symbol\":\"P\",\"number\":1}]},{\"symbol\":\"Gtu\",\"name\":\"2-geranylthiouridine monophosphate diradical 21U\",\"mf\":\"C19H27N2O7PS\",\"kind\":\"NucleotideP\",\"oneLetter\":\"Γ\",\"ocl\":{\"value\":\"e`XTNB@IOHJNEDln`OFsp\\\\BHgGHeEEMeDheHdtmEdhqpEUUUUURsUKUMTPaLHPfIAu@\",\"coordinates\":\"!BTv^cbn{__@fw|}RwqeRdK]}Tva~_{_|TiCp_[]}mwuRdIAMsuI~_{]||Gvw_Wy|Gvw_Wy|Gu~_{]}|Gt\"},\"mass\":458.46617269514235,\"monoisotopicMass\":458.12765938888003,\"unsaturation\":14,\"elements\":[{\"symbol\":\"C\",\"number\":19},{\"symbol\":\"H\",\"number\":27},{\"symbol\":\"N\",\"number\":2},{\"symbol\":\"O\",\"number\":7},{\"symbol\":\"P\",\"number\":1},{\"symbol\":\"S\",\"number\":1}]},{\"symbol\":\"Tyg\",\"name\":\"N2,N2,7-trimethylguanosine cap monophosphate diradical (cap TMG) 2279553N\",\"mf\":\"C13H20N5O10P2\",\"kind\":\"NucleotideP\",\"oneLetter\":\"¶\",\"ocl\":{\"value\":\"e`TZEBHIG@aihJNEHdleck`OFspz|MgDJTef[vVVe_gifNO@jijjjjjUijifjhaBXPaLP@\",\"coordinates\":\"!BvuPfpDnDtEK_t_rHtXBH_TwPbOr_I`JHbGtgD}F@RxS|uxc|_]^OTh}RIlA~@B\\\\StXCQ`Gx@Owx@_h{_cuH\"},\"mass\":468.2734710359255,\"monoisotopicMass\":468.06854085929,\"unsaturation\":13,\"elements\":[{\"symbol\":\"C\",\"number\":13},{\"symbol\":\"H\",\"number\":20},{\"symbol\":\"N\",\"number\":5},{\"symbol\":\"O\",\"number\":10},{\"symbol\":\"P\",\"number\":2}]},{\"symbol\":\"Tmg\",\"name\":\"N2,N2,7-trimethylguanosine monophosphate diradical 227G\",\"mf\":\"C13H20N5O7P\",\"kind\":\"NucleotideP\",\"oneLetter\":\"∠\",\"ocl\":{\"value\":\"fcoisBINCt\\\\J\\\\TgLp^MoBKbFY}dRbbfrbTRdRUbtYspZcjjjjiYfjjjHbXaDr@@\",\"coordinates\":\"!BNuSFPDlDTEHt_pHtP@H_TuPBOq_qopHBGtgD}D@RpPH_Wtw@aOTd}Rqd@gD}D@tPBNOt}R\"},\"mass\":389.30149426455074,\"monoisotopicMass\":389.11003500216,\"unsaturation\":12,\"elements\":[{\"symbol\":\"C\",\"number\":13},{\"symbol\":\"H\",\"number\":20},{\"symbol\":\"N\",\"number\":5},{\"symbol\":\"O\",\"number\":7},{\"symbol\":\"P\",\"number\":1}]},{\"symbol\":\"Dgu\",\"name\":\"N2,N2-dimethylguanosine monophosphate diradical 22G\",\"mf\":\"C12H16N5O7P\",\"kind\":\"NucleotideP\",\"oneLetter\":\"R\",\"ocl\":{\"value\":\"fmwis@INBwlJ\\\\TgHp^MoBKcdRbbfrbTRdR\\\\RcN^CWmUUUUKLuMUDQLPbY@@\",\"coordinates\":\"!BNuSFPDlDTEHt_pHtP@H_TuPBOq_qopHBGtgD}D@RpPH_Wtw@aOTd}RIqOQ@MD@cc}OT`\"},\"mass\":373.2589953515923,\"monoisotopicMass\":373.07873487324,\"unsaturation\":14,\"elements\":[{\"symbol\":\"C\",\"number\":12},{\"symbol\":\"H\",\"number\":16},{\"symbol\":\"N\",\"number\":5},{\"symbol\":\"O\",\"number\":7},{\"symbol\":\"P\",\"number\":1}]},{\"symbol\":\"Atu\",\"name\":\"5-aminomethyl-2-thiouridine monophosphate diradical 2510U\",\"mf\":\"C10H14N3O7PS\",\"kind\":\"NucleotideP\",\"oneLetter\":\"∫\",\"ocl\":{\"value\":\"fasqp`I^{BgEIrtGc[p\\\\DQ\\\\\\\\bTTTvTRbTfUSNAKUUUULsTuDQLPbY@@\",\"coordinates\":\"!BNuSFPDlDTEHt_pHtP@H_TuPBOpcbpXBGtSItuPSU@H_Wtw@`lFBpXSU@@tP\"},\"mass\":351.27302303324575,\"monoisotopicMass\":351.02900797432005,\"unsaturation\":10,\"elements\":[{\"symbol\":\"C\",\"number\":10},{\"symbol\":\"H\",\"number\":14},{\"symbol\":\"N\",\"number\":3},{\"symbol\":\"O\",\"number\":7},{\"symbol\":\"P\",\"number\":1},{\"symbol\":\"S\",\"number\":1}]},{\"symbol\":\"Mou\",\"name\":\"5-methylaminomethyl-2-thiouridine monophosphate diradical 2511U\",\"mf\":\"C11H16N3O7PS\",\"kind\":\"NucleotideP\",\"oneLetter\":\"S\",\"ocl\":{\"value\":\"fikqp`I^{BgEIrtGc[p\\\\DQ\\\\\\\\bTTTvTRbTfUVYpIZjjjifZfjHbXaDr@@\",\"coordinates\":\"!BNuSFPDlDTEHt_pHtP@H_TuPBOpcbpXBGtSItuPSU@H_Wtw@`lFBpXSUAMTBpX\"},\"mass\":365.2996404380926,\"monoisotopicMass\":365.04465803878,\"unsaturation\":10,\"elements\":[{\"symbol\":\"C\",\"number\":11},{\"symbol\":\"H\",\"number\":16},{\"symbol\":\"N\",\"number\":3},{\"symbol\":\"O\",\"number\":7},{\"symbol\":\"P\",\"number\":1},{\"symbol\":\"S\",\"number\":1}]},{\"symbol\":\"Ctu\",\"name\":\"5-carboxymethylaminomethyl-2-thiouridine monophosphate diradical 251U\",\"mf\":\"C12H16N3O9PS\",\"kind\":\"NucleotideP\",\"oneLetter\":\"$\",\"ocl\":{\"value\":\"fcwqH`I^{BgEIru^p^MoApQEqrIQQSYQJIRYUYJLxDmUUUTsMSTuDQLPbY@@\",\"coordinates\":\"!BKAb@tURDM\\\\YpMAMpBYMcx`BKB]~@Ha}SXW@h`Bb@IMcx}RtDvH_Xa}b@JH@ha}b@I~@Ha}\"},\"mass\":409.3091861834643,\"monoisotopicMass\":409.03448727792,\"unsaturation\":12,\"elements\":[{\"symbol\":\"C\",\"number\":12},{\"symbol\":\"H\",\"number\":16},{\"symbol\":\"N\",\"number\":3},{\"symbol\":\"O\",\"number\":9},{\"symbol\":\"P\",\"number\":1},{\"symbol\":\"S\",\"number\":1}]},{\"symbol\":\"Myu\",\"name\":\"5-methoxycarbonylmethyl-2-thiouridine monophosphate diradical 2521U\",\"mf\":\"C12H15N2O9PS\",\"kind\":\"NucleotideP\",\"oneLetter\":\"3\",\"ocl\":{\"value\":\"fmgQH`I^aSbdyZNXOFw`xHbxyDhhilheDiLjmLs`RuUUUSLuLuQDSDHfP@\",\"coordinates\":\"!BS]@lFJU`@Gyoza`lzf@lIwx@`H{WHc|KB_W_Wx@_`@lIr\\\\SFBrH@h`B_`BH_WxbOrH_P\"},\"mass\":394.2945422179627,\"monoisotopicMass\":394.02358824126003,\"unsaturation\":12,\"elements\":[{\"symbol\":\"C\",\"number\":12},{\"symbol\":\"H\",\"number\":15},{\"symbol\":\"N\",\"number\":2},{\"symbol\":\"O\",\"number\":9},{\"symbol\":\"P\",\"number\":1},{\"symbol\":\"S\",\"number\":1}]},{\"symbol\":\"Cou\",\"name\":\"5-carbamoylmethyl-2-thiouridine monophosphate diradical 253U\",\"mf\":\"C11H14N3O8PS\",\"kind\":\"NucleotideP\",\"oneLetter\":\"l\",\"ocl\":{\"value\":\"fe{pH`I^gBgEIrtXOFw`xHbxyDhhilheDiLjmF\\\\BVjjjjYfifhbIbDSH@\",\"coordinates\":\"!BNuSFPDlDTEHt_pHtP@H_TuPBOpcbpXBGtSItuPSU@H_Wtw@`lFBpXSUAMTC~NKA`\"},\"mass\":379.2831638542993,\"monoisotopicMass\":379.02392259389,\"unsaturation\":12,\"elements\":[{\"symbol\":\"C\",\"number\":11},{\"symbol\":\"H\",\"number\":14},{\"symbol\":\"N\",\"number\":3},{\"symbol\":\"O\",\"number\":8},{\"symbol\":\"P\",\"number\":1},{\"symbol\":\"S\",\"number\":1}]},{\"symbol\":\"Cau\",\"name\":\"5-carboxymethyl-2-thiouridine monophosphate diradical 2540U\",\"mf\":\"C11H13N2O9PS\",\"kind\":\"NucleotideP\",\"oneLetter\":\"℘\",\"ocl\":{\"value\":\"fe{QH`I^aSbdyZNXOFw`xHbxyDhhilheDiLjmF\\\\BVjjjjYfifhbIbDSH@\",\"coordinates\":\"!BNuSFPDlDTEHt_pHtP@H_TuPBOpcbpXBGtSItuPSU@H_Wtw@`lFBpXSUAMTC~NKA`\"},\"mass\":380.26792481311594,\"monoisotopicMass\":380.00793817680005,\"unsaturation\":12,\"elements\":[{\"symbol\":\"C\",\"number\":11},{\"symbol\":\"H\",\"number\":13},{\"symbol\":\"N\",\"number\":2},{\"symbol\":\"O\",\"number\":9},{\"symbol\":\"P\",\"number\":1},{\"symbol\":\"S\",\"number\":1}]},{\"symbol\":\"Tau\",\"name\":\"5-taurinomethyl-2-thiouridine monophosphate diradical 254U\",\"mf\":\"C12H18N3O10PS2\",\"kind\":\"NucleotideP\",\"oneLetter\":\"∃\",\"ocl\":{\"value\":\"fgpj`I^{BgEIrwY{`|[^C`bKblHrIQQSYQJIRYUYIRLxDmUUUTsMSUKTQDqBId@@\",\"coordinates\":\"!BKAb@tURD@m\\\\YpMAMpBYMcx`BKB]~@Ha}SXW@h`Bb@IMcx}RtDvH_Xa}b@JH@ha}b@JH__rH_]^H_P\"},\"mass\":459.3892600220213,\"monoisotopicMass\":459.01712313635005,\"unsaturation\":10,\"elements\":[{\"symbol\":\"C\",\"number\":12},{\"symbol\":\"H\",\"number\":18},{\"symbol\":\"N\",\"number\":3},{\"symbol\":\"O\",\"number\":10},{\"symbol\":\"P\",\"number\":1},{\"symbol\":\"S\",\"number\":2}]},{\"symbol\":\"Itu\",\"name\":\"5-(isopentenylaminomethyl)-2-thiouridine monophosphate diradical 2583U\",\"mf\":\"C15H22N3O7PS\",\"kind\":\"NucleotideP\",\"oneLetter\":\"½\",\"ocl\":{\"value\":\"fkoqp`I^{BgEIrtGc[p\\\\DQ\\\\\\\\bTTTvTRbTfUVRTYpIZjjjifZfijbHfHQL`@\",\"coordinates\":\"!BS]@lFJU`@Gyoza`lzf@lIwx@`H{W@h`BKB_W_Wx@_`@lIr\\\\SFBrH@h`B_`BH_Xc|bGvH@gx@bGt\"},\"mass\":419.3902285493682,\"monoisotopicMass\":419.09160823216,\"unsaturation\":12,\"elements\":[{\"symbol\":\"C\",\"number\":15},{\"symbol\":\"H\",\"number\":22},{\"symbol\":\"N\",\"number\":3},{\"symbol\":\"O\",\"number\":7},{\"symbol\":\"P\",\"number\":1},{\"symbol\":\"S\",\"number\":1}]},{\"symbol\":\"Mth\",\"name\":\"5-methyl-2-thiouridine monophosphate diradical 25U\",\"mf\":\"C10H13N2O7PS\",\"kind\":\"NucleotideP\",\"oneLetter\":\"F\",\"ocl\":{\"value\":\"fncQp`I^aSbdyZCqmxNBHnNQJJJ[JIQJSJlxDmUUUTsMSQDSDHfP@\",\"coordinates\":\"!BNuSFPDlDTEHt_pHtP@H_TuPBOpcbpXBGtSItuPSU@H_Wtw@`lFBpXSU@\"},\"mass\":336.25837906774416,\"monoisotopicMass\":336.01810893766003,\"unsaturation\":10,\"elements\":[{\"symbol\":\"C\",\"number\":10},{\"symbol\":\"H\",\"number\":13},{\"symbol\":\"N\",\"number\":2},{\"symbol\":\"O\",\"number\":7},{\"symbol\":\"P\",\"number\":1},{\"symbol\":\"S\",\"number\":1}]},{\"symbol\":\"Dmg\",\"name\":\"N2,7-dimethylguanosine monophosphate diradical 27G\",\"mf\":\"C12H18N5O7P\",\"kind\":\"NucleotideP\",\"oneLetter\":\"∨\",\"ocl\":{\"value\":\"fmwisBINCt\\\\J\\\\TgLp^MoBKbFY}dRbbfrbTRdRUbKN^CWmUUUUKLuUUDQLPbY@@\",\"coordinates\":\"!BNuSFPDlDTEHt_pHtP@H_TuPBOq_qopHBGtgD}D@RpPH_Wtw@aOTd}RqdCQ@B\\\\StPAOT`\"},\"mass\":375.27487685970397,\"monoisotopicMass\":375.0943849377,\"unsaturation\":12,\"elements\":[{\"symbol\":\"C\",\"number\":12},{\"symbol\":\"H\",\"number\":18},{\"symbol\":\"N\",\"number\":5},{\"symbol\":\"O\",\"number\":7},{\"symbol\":\"P\",\"number\":1}]},{\"symbol\":\"Dma\",\"name\":\"2,8-dimethyladenosine monophosphate diradical 28A\",\"mf\":\"C12H16N5O6P\",\"kind\":\"NucleotideP\",\"oneLetter\":\"±\",\"ocl\":{\"value\":\"feghs@INCv\\\\J\\\\UdhOFw`eqrIQQSYQJJJQKqLyxK^uUUUPMLAUADSDHfP@\",\"coordinates\":\"!BNuSFPDlDTEHt_pHtP@H_TuPBOq_qopHBGtgD}D@RpP@c`a}_S_|BD}RSuKQ@B\\\\StP@\"},\"mass\":357.2595904272741,\"monoisotopicMass\":357.08382025367,\"unsaturation\":14,\"elements\":[{\"symbol\":\"C\",\"number\":12},{\"symbol\":\"H\",\"number\":16},{\"symbol\":\"N\",\"number\":5},{\"symbol\":\"O\",\"number\":6},{\"symbol\":\"P\",\"number\":1}]},{\"symbol\":\"Mas\",\"name\":\"2-methyladenosine monophosphate diradical 2A\",\"mf\":\"C11H14N5O6P\",\"kind\":\"NucleotideP\",\"oneLetter\":\"/\",\"ocl\":{\"value\":\"fi{hs@INBt\\\\J\\\\TgHOFwaEqrIQQSYQJIRINIgOAjvjjjjAf@j`bIbDSH@\",\"coordinates\":\"!BNuSFPDlDTEHt_pHtP@H_TuPBOq_qopHBGtgD}D@RpPH_Wtw@aOTd}RtP@gD}D@\"},\"mass\":343.2329730224273,\"monoisotopicMass\":343.06817018921,\"unsaturation\":14,\"elements\":[{\"symbol\":\"C\",\"number\":11},{\"symbol\":\"H\",\"number\":14},{\"symbol\":\"N\",\"number\":5},{\"symbol\":\"O\",\"number\":6},{\"symbol\":\"P\",\"number\":1}]},{\"symbol\":\"Tcy\",\"name\":\"2-thiocytidine monophosphate diradical 2C\",\"mf\":\"C9H12N3O6PS\",\"kind\":\"NucleotideP\",\"oneLetter\":\"%\",\"ocl\":{\"value\":\"ff}pp`I^kBgEIrCqmxNBHnNQJJJ[JIQJSMg@ejjjjfYfhbIbDSH@\",\"coordinates\":\"!BNuSFPDlDTEHt_pHtP@H_TuPBOpcbpXBGtSItuPSU@H_Wtw@`lFDuP\"},\"mass\":321.2470007040807,\"monoisotopicMass\":321.01844329029,\"unsaturation\":10,\"elements\":[{\"symbol\":\"C\",\"number\":9},{\"symbol\":\"H\",\"number\":12},{\"symbol\":\"N\",\"number\":3},{\"symbol\":\"O\",\"number\":6},{\"symbol\":\"P\",\"number\":1},{\"symbol\":\"S\",\"number\":1}]},{\"symbol\":\"Nmg\",\"name\":\"N2-methylguanosine monophosphate diradical 2G\",\"mf\":\"C11H14N5O7P\",\"kind\":\"NucleotideP\",\"oneLetter\":\"L\",\"ocl\":{\"value\":\"fegis@INBwlJ\\\\TgHp^MoBKcdRbbfrbTRdR\\\\VYspZmjjjjiYfijbHfHQL`@\",\"coordinates\":\"!BNuSFPDlDTEHt_pHtP@H_TuPBOq_qopHBGtgD}D@RpPH_Wtw@aOTd}RtP@gD}D@SuH\"},\"mass\":359.23237794674554,\"monoisotopicMass\":359.06308480878,\"unsaturation\":14,\"elements\":[{\"symbol\":\"C\",\"number\":11},{\"symbol\":\"H\",\"number\":14},{\"symbol\":\"N\",\"number\":5},{\"symbol\":\"O\",\"number\":7},{\"symbol\":\"P\",\"number\":1}]},{\"symbol\":\"Thu\",\"name\":\"2-thiouridine monophosphate diradical 2U\",\"mf\":\"C9H11N2O7PS\",\"kind\":\"NucleotideP\",\"oneLetter\":\"2\",\"ocl\":{\"value\":\"ff}Qp`I^aSbdyjCqmxNBHnNQJJJ[JIQJSMg@ejjjjfYihbIbDSH@\",\"coordinates\":\"!BNuSFPDlDTEHt_pHtP@H_TuPBOpcbpXBGtSItuPSU@H_Wtw@`lFDuP\"},\"mass\":322.2317616628973,\"monoisotopicMass\":322.0024588732,\"unsaturation\":10,\"elements\":[{\"symbol\":\"C\",\"number\":9},{\"symbol\":\"H\",\"number\":11},{\"symbol\":\"N\",\"number\":2},{\"symbol\":\"O\",\"number\":7},{\"symbol\":\"P\",\"number\":1},{\"symbol\":\"S\",\"number\":1}]},{\"symbol\":\"Ahu\",\"name\":\"3-(3-amino-3-carboxypropyl)-5,6-dihydrouridine monophosphate diradical 308U\",\"mf\":\"C13H20N3O10P\",\"kind\":\"NucleotideP\",\"oneLetter\":\"Ð\",\"ocl\":{\"value\":\"fkopk@I^gBgENSens`|[^DWGHeEEMeDheIhueMF\\\\BVjjjjZfijfhbIbDSH@\",\"coordinates\":\"!BTh|SI~ioOwy`iR\\\\SiV|SFGxw}FH_]]}DqbH@gx_c|SFA`lIqOW_Xa}_c~HHa}_c~H@gx@bGt\"},\"mass\":409.28630261461393,\"monoisotopicMass\":409.08863085201,\"unsaturation\":10,\"elements\":[{\"symbol\":\"C\",\"number\":13},{\"symbol\":\"H\",\"number\":20},{\"symbol\":\"N\",\"number\":3},{\"symbol\":\"O\",\"number\":10},{\"symbol\":\"P\",\"number\":1}]},{\"symbol\":\"App\",\"name\":\"3-(3-amino-3-carboxypropyl)pseudouridine monophosphate diradical 309U\",\"mf\":\"C13H18N3O10P\",\"kind\":\"NucleotideP\",\"oneLetter\":\"Þ\",\"ocl\":{\"value\":\"fkopk@OAgBgENSens`|[^DWGHeEEMeDheIhueMF\\\\BVjjjfZfijfhbIbDSH@\",\"coordinates\":\"!BTh|SI~ioOwy`iR\\\\SiV|SFGxw}FH_]]}DqbH@gx_c|SFA`lIqOW_Xa}_c~HHa}_c~H@gx@bGt\"},\"mass\":407.2704211065024,\"monoisotopicMass\":407.07298078755,\"unsaturation\":12,\"elements\":[{\"symbol\":\"C\",\"number\":13},{\"symbol\":\"H\",\"number\":18},{\"symbol\":\"N\",\"number\":3},{\"symbol\":\"O\",\"number\":10},{\"symbol\":\"P\",\"number\":1}]},{\"symbol\":\"Apu\",\"name\":\"3-(3-amino-3-carboxypropyl)uridine monophosphate diradical 30U\",\"mf\":\"C13H18N3O10P\",\"kind\":\"NucleotideP\",\"oneLetter\":\"X\",\"ocl\":{\"value\":\"fkopk@I^gBgENSens`|[^DWGHeEEMeDheIhueMF\\\\BVjjjjYfijfhbIbDSH@\",\"coordinates\":\"!BTh|SI~ioOwy`iR\\\\SiV|SFGxw}FH_]]}DqbH@gx_c|SFA`lIqOW_Xa}_c~HHa}_c~H@gx@bGt\"},\"mass\":407.2704211065024,\"monoisotopicMass\":407.07298078755,\"unsaturation\":12,\"elements\":[{\"symbol\":\"C\",\"number\":13},{\"symbol\":\"H\",\"number\":18},{\"symbol\":\"N\",\"number\":3},{\"symbol\":\"O\",\"number\":10},{\"symbol\":\"P\",\"number\":1}]},{\"symbol\":\"Mws\",\"name\":\"methylwyosine monophosphate diradical 342G\",\"mf\":\"C15H18N5O7P\",\"kind\":\"NucleotideP\",\"oneLetter\":\"∑\",\"ocl\":{\"value\":\"eghZNL@IG@nahJNEDli`OFspb\\\\\\\\bTTTvTRbTbb\\\\rVSGG`SPrvuUUUUKMTsUUIBDpaBX`@\",\"coordinates\":\"!B_`CW@mF@ctvDUI|fRxPYgtwP[zV_IorHFY|gD}F@RxPYg|@YgrZOTh{_cuJOS]F@tXAKaI|fw}EMt@\"},\"mass\":411.3070845499097,\"monoisotopicMass\":411.0943849377,\"unsaturation\":18,\"elements\":[{\"symbol\":\"C\",\"number\":15},{\"symbol\":\"H\",\"number\":18},{\"symbol\":\"N\",\"number\":5},{\"symbol\":\"O\",\"number\":7},{\"symbol\":\"P\",\"number\":1}]},{\"symbol\":\"Uhw\",\"name\":\"undermodified hydroxywybutosine monophosphate diradical 3470G\",\"mf\":\"C18H23N6O10P\",\"kind\":\"NucleotideP\",\"oneLetter\":\"š\",\"ocl\":{\"value\":\"ejQVEL@IG@nahXJNEDliolRPOFspb\\\\\\\\bTTTvTRbTbb\\\\rVVTttXx|BZFVvjjjjiYjfZjjfjRDIaBDq@@\",\"coordinates\":\"!BKB^@ceS[H`Yg}ARpAeMtHa}KAcPTh{_S]CjXES[pAeMtH}MtEK@IdnDpBXBbES[UMo@F]ARaERH_X`B_`BH_WxbOq~@Ha}\"},\"mass\":514.3839139947949,\"monoisotopicMass\":514.12132796199,\"unsaturation\":20,\"elements\":[{\"symbol\":\"C\",\"number\":18},{\"symbol\":\"H\",\"number\":23},{\"symbol\":\"N\",\"number\":6},{\"symbol\":\"O\",\"number\":10},{\"symbol\":\"P\",\"number\":1}]},{\"symbol\":\"Acw\",\"name\":\"7-aminocarboxypropylwyosine monophosphate diradical 347G\",\"mf\":\"C18H23N6O9P\",\"kind\":\"NucleotideP\",\"oneLetter\":\"Ω\",\"ocl\":{\"value\":\"eb^VIL@IG@na`XJNEDlid\\\\POFspb\\\\\\\\bTTTvTRbTbb\\\\rVRrfcGG`SPrvuUUUUKMTsUUSUIBDpaBX`@\",\"coordinates\":\"!BDr]RcwwWpAg_tUS[cm~DUAf_XJUTvx}MaEP@_gwWcm~DUDnDUMo|urH@m_@FWwW_]^NwuS[bGtYgx`BbGu~Ox`B_`BH_P\"},\"mass\":498.38450907047655,\"monoisotopicMass\":498.12641334242,\"unsaturation\":20,\"elements\":[{\"symbol\":\"C\",\"number\":18},{\"symbol\":\"H\",\"number\":23},{\"symbol\":\"N\",\"number\":6},{\"symbol\":\"O\",\"number\":9},{\"symbol\":\"P\",\"number\":1}]},{\"symbol\":\"Hwy\",\"name\":\"methylated undermodified hydroxywybutosine monophosphate diradical 3480G\",\"mf\":\"C19H25N6O10P\",\"kind\":\"NucleotideP\",\"oneLetter\":\"y\",\"ocl\":{\"value\":\"efYVEL@IG@nahXJNEDliolRPOFspb\\\\\\\\bTTTvTRbTbb\\\\rVVTttsGG`SPrvuUUUUKMTsUUTuTdHSBDIb@@\",\"coordinates\":\"!B`HyRtL@f_XbDRxz@UHS_chc|S]BN`MAMwxyKaL@fUHS_cmG_chCjXI|YzfA}bL@fpBYTaHz@F\\\\BH@gx@upJH@ha}_`CWHc|_`@\"},\"mass\":528.4105313996416,\"monoisotopicMass\":528.1369780264499,\"unsaturation\":20,\"elements\":[{\"symbol\":\"C\",\"number\":19},{\"symbol\":\"H\",\"number\":25},{\"symbol\":\"N\",\"number\":6},{\"symbol\":\"O\",\"number\":10},{\"symbol\":\"P\",\"number\":1}]},{\"symbol\":\"Hwb\",\"name\":\"hydroxywybutosine monophosphate diradical 34830G\",\"mf\":\"C21H27N6O12P\",\"kind\":\"NucleotideP\",\"oneLetter\":\"⊆\",\"ocl\":{\"value\":\"ee[VCL@IG@nahXJNEDliobZV^POFspb\\\\\\\\bTTTvTRbTbb\\\\rVVTtRbfsGG`SPrvuUUUUKMTsUUULuUIBDpaBX`@\",\"coordinates\":\"!BKB^@ceS[@h`Yg}ARpAeMtHa}KAcPTh{_S]CjXES[pAeMtH}MtEK@IdnDpBXBbES[UMo@F]ARaERH_X`B_`BH_X`B_c~H_]]}bGu~Ox`B_c~H_P\"},\"mass\":586.4466945498602,\"monoisotopicMass\":586.14245733005,\"unsaturation\":22,\"elements\":[{\"symbol\":\"C\",\"number\":21},{\"symbol\":\"H\",\"number\":27},{\"symbol\":\"N\",\"number\":6},{\"symbol\":\"O\",\"number\":12},{\"symbol\":\"P\",\"number\":1}]},{\"symbol\":\"Pwb\",\"name\":\"peroxywybutosine monophosphate diradical 34832G\",\"mf\":\"C21H27N6O13P\",\"kind\":\"NucleotideP\",\"oneLetter\":\"W\",\"ocl\":{\"value\":\"emWVKL@IG@nadXJNEDliohZV^QPOFspb\\\\\\\\bTTTvTRbTbb\\\\rVVTRfTTvXx|BZFVvjjjjiYjfZjjjYjjRDIaBDq@@\",\"coordinates\":\"!BKB^@ceS[@h`Yg}ARpAeMtHa}KAcPTh{_S]CjXES[pAeMtH}MtEK@IdnDpB[|bES[UMo@F]ARaERH_X`B_`BH_X`Bb@I~Oxa}uwvH_Wxb@I~Oxa}\"},\"mass\":602.4460994741785,\"monoisotopicMass\":602.1373719496199,\"unsaturation\":22,\"elements\":[{\"symbol\":\"C\",\"number\":21},{\"symbol\":\"H\",\"number\":27},{\"symbol\":\"N\",\"number\":6},{\"symbol\":\"O\",\"number\":13},{\"symbol\":\"P\",\"number\":1}]},{\"symbol\":\"Wyb\",\"name\":\"wybutosine monophosphate diradical 3483G\",\"mf\":\"C21H27N6O11P\",\"kind\":\"NucleotideP\",\"oneLetter\":\"Y\",\"ocl\":{\"value\":\"eiSVML@IG@na`XJNEDlilRZVPOFspb\\\\\\\\bTTTvTRbTbb\\\\rVVRbTTvXx|BZFVvjjjjiYjfZjjfZjdaBXPaLP@\",\"coordinates\":\"!BsJ\\\\@ciP{@`YWuARPAeMT@a}sNaPThxSUCjhIP{PAeMTD}MTEI@IllDPB[|BIP{eCm@FUARAIPH_Pc|BGtHGzBGtw_Pa}_k|HGzBGt\"},\"mass\":570.4472896255419,\"monoisotopicMass\":570.14754271048,\"unsaturation\":22,\"elements\":[{\"symbol\":\"C\",\"number\":21},{\"symbol\":\"H\",\"number\":27},{\"symbol\":\"N\",\"number\":6},{\"symbol\":\"O\",\"number\":11},{\"symbol\":\"P\",\"number\":1}]},{\"symbol\":\"Awo\",\"name\":\"7-aminocarboxypropylwyosine methyl ester monophosphate diradical 348G\",\"mf\":\"C19H25N6O9P\",\"kind\":\"NucleotideP\",\"oneLetter\":\"⇑\",\"ocl\":{\"value\":\"ejQVIL@IG@na`XJNEDlid\\\\POFspb\\\\\\\\bTTTvTRbTbb\\\\rVVRffXx|BZFVvjjjjiYjfZjjZjRDIaBDq@@\",\"coordinates\":\"!B`HyRtL@f_XbDRxz@UHS_ch`BS]BN`MAMwxyKaL@fUHS_cmG_chCjXI|YzfA}bL@fpBYTaHz@F\\\\BHHa}bOq~@Ha}_c~H@ha}\"},\"mass\":512.4111264753234,\"monoisotopicMass\":512.14206340688,\"unsaturation\":20,\"elements\":[{\"symbol\":\"C\",\"number\":19},{\"symbol\":\"H\",\"number\":25},{\"symbol\":\"N\",\"number\":6},{\"symbol\":\"O\",\"number\":9},{\"symbol\":\"P\",\"number\":1}]},{\"symbol\":\"Wyo\",\"name\":\"wyosine monophosphate diradical 34G\",\"mf\":\"C14H16N5O7P\",\"kind\":\"NucleotideP\",\"oneLetter\":\"€\",\"ocl\":{\"value\":\"ek`ZNL@IG@nahJNEDli`OFspb\\\\\\\\bTTTvTRbTbb\\\\rVXx|BZFVvjjjjiYjfZjdaBXPaLP@\",\"coordinates\":\"!B_`CWMF@ctvDUI|fRxPYgtwP[zV_IorHFY|gD}F@RxPYg|@YgrZOTh{_cuJOS]F@tXAKaI|fw}D\"},\"mass\":397.2804671450629,\"monoisotopicMass\":397.07873487324,\"unsaturation\":18,\"elements\":[{\"symbol\":\"C\",\"number\":14},{\"symbol\":\"H\",\"number\":16},{\"symbol\":\"N\",\"number\":5},{\"symbol\":\"O\",\"number\":7},{\"symbol\":\"P\",\"number\":1}]},{\"symbol\":\"Pdu\",\"name\":\"3-methylpseudouridine monophosphate diradical 39U\",\"mf\":\"C10H13N2O8P\",\"kind\":\"NucleotideP\",\"oneLetter\":\"κ\",\"ocl\":{\"value\":\"fncPK@OAaSbgIrtGc[pbxyDhhilheDiMFs`RuUUTsTuMDQLPbY@@\",\"coordinates\":\"!BNuSFPDlDTEHt_pHtP@H_TuPBOpcbpXBGtSItuPSU@HoWtw@`lFC~NSU@\"},\"mass\":320.1929965859354,\"monoisotopicMass\":320.04095238282997,\"unsaturation\":10,\"elements\":[{\"symbol\":\"C\",\"number\":10},{\"symbol\":\"H\",\"number\":13},{\"symbol\":\"N\",\"number\":2},{\"symbol\":\"O\",\"number\":8},{\"symbol\":\"P\",\"number\":1}]},{\"symbol\":\"Mri\",\"name\":\"3-methyluridine monophosphate diradical 3U\",\"mf\":\"C10H13N2O8P\",\"kind\":\"NucleotideP\",\"oneLetter\":\"δ\",\"ocl\":{\"value\":\"fncPK@I^aSbgIrtGc[pbxyDhhilheDiMFs`RuUUUSLuMDQLPbY@@\",\"coordinates\":\"!BNuSFPDlDTEHt_pHtP@H_TuPBOpcbpXBGtSItuPSU@HoWtw@`lFC~NSU@\"},\"mass\":320.1929965859354,\"monoisotopicMass\":320.04095238282997,\"unsaturation\":10,\"elements\":[{\"symbol\":\"C\",\"number\":10},{\"symbol\":\"H\",\"number\":13},{\"symbol\":\"N\",\"number\":2},{\"symbol\":\"O\",\"number\":8},{\"symbol\":\"P\",\"number\":1}]},{\"symbol\":\"Acc\",\"name\":\"N4-acetylcytidine monophosphate diradical 42C\",\"mf\":\"C11H14N3O8P\",\"kind\":\"NucleotideP\",\"oneLetter\":\"M\",\"ocl\":{\"value\":\"fikpK@I^kBgENSghOFwaEqrIQQSYQJIRYiQg@ejjjjfYffhbIbDSH@\",\"coordinates\":\"!BNuSFPDlDTEHt_pHtP@H_TuPBOpcbpXBGtSItuPSU@H_Wtw@`lFDuPOxxSItuP\"},\"mass\":347.21837644817225,\"monoisotopicMass\":347.05185141949,\"unsaturation\":12,\"elements\":[{\"symbol\":\"C\",\"number\":11},{\"symbol\":\"H\",\"number\":14},{\"symbol\":\"N\",\"number\":3},{\"symbol\":\"O\",\"number\":8},{\"symbol\":\"P\",\"number\":1}]},{\"symbol\":\"Iws\",\"name\":\"isowyosine monophosphate diradical 42G\",\"mf\":\"C14H16N5O7P\",\"kind\":\"NucleotideP\",\"oneLetter\":\"⊇\",\"ocl\":{\"value\":\"ek`ZNL@IG@fnhJNEDla`OFspb\\\\\\\\bTTTvTRbTbSbRrXx|BjzfVjjjjiYjYjjdaBXPaLP@\",\"coordinates\":\"!BNuSFPDlDTEHt_pHtP@H_TuPBOq_qopHBGtgD}D@RpPH_Wtw@aOTd}RStwQ@MD@RpQ_qcQSU@\"},\"mass\":397.2804671450629,\"monoisotopicMass\":397.07873487324,\"unsaturation\":18,\"elements\":[{\"symbol\":\"C\",\"number\":14},{\"symbol\":\"H\",\"number\":16},{\"symbol\":\"N\",\"number\":5},{\"symbol\":\"O\",\"number\":7},{\"symbol\":\"P\",\"number\":1}]},{\"symbol\":\"Dmc\",\"name\":\"N4,N4-dimethylcytidine monophosphate diradical 44C\",\"mf\":\"C11H16N3O7P\",\"kind\":\"NucleotideP\",\"oneLetter\":\"μ\",\"ocl\":{\"value\":\"fasqs@I^kBgENSdGc[pbxyDhhilheDiLuF\\\\BVjjjjYfZjHbXaDr@@\",\"coordinates\":\"!BNuSFPDlDTEHt_pHtP@H_TuPBGpcbpXBGtSItuPSU@H_Wtw@`lFDuPOxxlF@\"},\"mass\":333.23485303196554,\"monoisotopicMass\":333.07258686438,\"unsaturation\":10,\"elements\":[{\"symbol\":\"C\",\"number\":11},{\"symbol\":\"H\",\"number\":16},{\"symbol\":\"N\",\"number\":3},{\"symbol\":\"O\",\"number\":7},{\"symbol\":\"P\",\"number\":1}]},{\"symbol\":\"Adw\",\"name\":\"7-aminocarboxypropyl-demethylwyosine monophosphate diradical 47G\",\"mf\":\"C17H21N6O9P\",\"kind\":\"NucleotideP\",\"oneLetter\":\"¥\",\"ocl\":{\"value\":\"elVVIL@IG@fnohJNEDlahTPOFspb\\\\\\\\bTTTvTRbTbSbRrrTtXx|BjzfVjjjjiYjYjjijdaBXPaLP@\",\"coordinates\":\"!B`MERc|@Y_]^DUH{_UMo_tXa}SXPTh{_w}GjXES[pAg_t]F@cm@Il@f@haTvuS[pAgPThQTbGvH@ha}_c~HGx@bGt\"},\"mass\":484.3578916656298,\"monoisotopicMass\":484.1107632779601,\"unsaturation\":20,\"elements\":[{\"symbol\":\"C\",\"number\":17},{\"symbol\":\"H\",\"number\":21},{\"symbol\":\"N\",\"number\":6},{\"symbol\":\"O\",\"number\":9},{\"symbol\":\"P\",\"number\":1}]},{\"symbol\":\"Ncd\",\"name\":\"N4-methylcytidine monophosphate diradical 4C\",\"mf\":\"C10H14N3O7P\",\"kind\":\"NucleotideP\",\"oneLetter\":\"ν\",\"ocl\":{\"value\":\"fncqs@I^kBgENSdGc[pbxyDhhilheDiLts`RuUUUSLsUDQLPbY@@\",\"coordinates\":\"!BNuSFPDlDTEHt_pHtP@H_TuPBOpcbpXBGtSItuPSU@H_Wtw@`lFDuPKA`\"},\"mass\":319.2082356271187,\"monoisotopicMass\":319.05693679992004,\"unsaturation\":10,\"elements\":[{\"symbol\":\"C\",\"number\":10},{\"symbol\":\"H\",\"number\":14},{\"symbol\":\"N\",\"number\":3},{\"symbol\":\"O\",\"number\":7},{\"symbol\":\"P\",\"number\":1}]},{\"symbol\":\"Dmw\",\"name\":\"4-demethylwyosine monophosphate diradical 4G\",\"mf\":\"C13H14N5O7P\",\"kind\":\"NucleotideP\",\"oneLetter\":\"†\",\"ocl\":{\"value\":\"fcis@INBwlJ\\\\TgHp^MoBKcdRbbfrbTRdR\\\\RVYspZ}fnjjjjefifjiHbXaDr@@\",\"coordinates\":\"!BNuSFPDlDTEHt_pHtP@H_TuPBOq_qopHBGtgD}D@RpPH_Wtw@aOTd}RStwQ@MD@RpQ_qcQ\"},\"mass\":383.253849740216,\"monoisotopicMass\":383.06308480878,\"unsaturation\":18,\"elements\":[{\"symbol\":\"C\",\"number\":13},{\"symbol\":\"H\",\"number\":14},{\"symbol\":\"N\",\"number\":5},{\"symbol\":\"O\",\"number\":7},{\"symbol\":\"P\",\"number\":1}]},{\"symbol\":\"Mtu\",\"name\":\"5-methoxyuridine monophosphate diradical 501U\",\"mf\":\"C10H13N2O9P\",\"kind\":\"NucleotideP\",\"oneLetter\":\"5\",\"ocl\":{\"value\":\"fasQK@I^aSbgIsUhOFwaEqrIQQSYQJIRYULxDmUUUTsMSTQDqBId@@\",\"coordinates\":\"!BNuSFPDlDTEHt_pHtP@H_TuPBOpcbpXBGtSItuPSU@H_Wtw@`lFBpXSU@@tP\"},\"mass\":336.19240151025366,\"monoisotopicMass\":336.03586700240004,\"unsaturation\":10,\"elements\":[{\"symbol\":\"C\",\"number\":10},{\"symbol\":\"H\",\"number\":13},{\"symbol\":\"N\",\"number\":2},{\"symbol\":\"O\",\"number\":9},{\"symbol\":\"P\",\"number\":1}]},{\"symbol\":\"Uoa\",\"name\":\"uridine 5-oxyacetic acid monophosphate diradical 502U\",\"mf\":\"C11H13N2O11P\",\"kind\":\"NucleotideP\",\"oneLetter\":\"V\",\"ocl\":{\"value\":\"fmgQk@I^aSbgIsUlu`|[^DWGHeEEMeDheIeUeF\\\\BVjjjjYfiijHbXaDr@@\",\"coordinates\":\"!BS]@lFJU`@Gyoza`lzf@lIwx@`H{WHc|KB_W_Wx@_`@lIr\\\\SFBrHHc|_`BH_Xc|_`BH_P\"},\"mass\":380.2019472556255,\"monoisotopicMass\":380.02569624154,\"unsaturation\":12,\"elements\":[{\"symbol\":\"C\",\"number\":11},{\"symbol\":\"H\",\"number\":13},{\"symbol\":\"N\",\"number\":2},{\"symbol\":\"O\",\"number\":11},{\"symbol\":\"P\",\"number\":1}]},{\"symbol\":\"Ume\",\"name\":\"uridine 5-oxyacetic acid methyl ester monophosphate diradical 503U\",\"mf\":\"C12H15N2O11P\",\"kind\":\"NucleotideP\",\"oneLetter\":\"υ\",\"ocl\":{\"value\":\"fcwQk@I^aSbgIsUlu`|[^DWGHeEEMeDheIeUeLs`RuUUUSLuMMTQDqBId@@\",\"coordinates\":\"!BKAb@tURDM\\\\YpMAMpBYMcxc|KB]~@Ha}SXWHc|bOqMcx}RtDvH_Xa}bOrH@ha}_c~HHa}\"},\"mass\":394.2285646604723,\"monoisotopicMass\":394.041346306,\"unsaturation\":12,\"elements\":[{\"symbol\":\"C\",\"number\":12},{\"symbol\":\"H\",\"number\":15},{\"symbol\":\"N\",\"number\":2},{\"symbol\":\"O\",\"number\":11},{\"symbol\":\"P\",\"number\":1}]},{\"symbol\":\"Hxc\",\"name\":\"5-hydroxycytidine monophosphate diradical 50C\",\"mf\":\"C9H12N3O8P\",\"kind\":\"NucleotideP\",\"oneLetter\":\"Ç\",\"ocl\":{\"value\":\"fncpK@I^[BgENSfhOFwaEqrIQQSYQJIRYUg@ejjjjfYfjHbXaDr@@\",\"coordinates\":\"!BNuSFPDlDTEHt_pHtP@H_TuPBOpcbpXBGtSItuPSU@H_Wtw@`lFBpXSU@\"},\"mass\":321.1810231465902,\"monoisotopicMass\":321.03620135502996,\"unsaturation\":10,\"elements\":[{\"symbol\":\"C\",\"number\":9},{\"symbol\":\"H\",\"number\":12},{\"symbol\":\"N\",\"number\":3},{\"symbol\":\"O\",\"number\":8},{\"symbol\":\"P\",\"number\":1}]},{\"symbol\":\"Hxu\",\"name\":\"5-hydroxyuridine monophosphate diradical 50U\",\"mf\":\"C9H11N2O9P\",\"kind\":\"NucleotideP\",\"oneLetter\":\"∝\",\"ocl\":{\"value\":\"fncQK@I^aSbgIsUhOFwaEqrIQQSYQJIRYUg@ejjjjfYjZHbXaDr@@\",\"coordinates\":\"!BNuSFPDlDTEHt_pHtP@H_TuPBOpcbpXBGtSItuPSU@H_Wtw@`lFBpXSU@\"},\"mass\":322.1657841054069,\"monoisotopicMass\":322.02021693794,\"unsaturation\":10,\"elements\":[{\"symbol\":\"C\",\"number\":9},{\"symbol\":\"H\",\"number\":11},{\"symbol\":\"N\",\"number\":2},{\"symbol\":\"O\",\"number\":9},{\"symbol\":\"P\",\"number\":1}]},{\"symbol\":\"Amu\",\"name\":\"5-aminomethyluridine monophosphate diradical 510U\",\"mf\":\"C10H14N3O8P\",\"kind\":\"NucleotideP\",\"oneLetter\":\"∪\",\"ocl\":{\"value\":\"faspK@I^{BgENSehOFwaEqrIQQSYQJIRYULxDmUUUTsMSTQDqBId@@\",\"coordinates\":\"!BNuSFPDlDTEHt_pHtP@H_TuPBOpcbpXBGtSItuPSU@H_Wtw@`lFBpXSU@@tP\"},\"mass\":335.207640551437,\"monoisotopicMass\":335.05185141949,\"unsaturation\":10,\"elements\":[{\"symbol\":\"C\",\"number\":10},{\"symbol\":\"H\",\"number\":14},{\"symbol\":\"N\",\"number\":3},{\"symbol\":\"O\",\"number\":8},{\"symbol\":\"P\",\"number\":1}]},{\"symbol\":\"Mau\",\"name\":\"5-methylaminomethyluridine monophosphate diradical 511U\",\"mf\":\"C11H16N3O8P\",\"kind\":\"NucleotideP\",\"oneLetter\":\"{\",\"ocl\":{\"value\":\"fikpK@I^{BgENSehOFwaEqrIQQSYQJIRYUYg@ejjjjfYjZhbIbDSH@\",\"coordinates\":\"!BNuSFPDlDTEHt_pHtP@H_TuPBOpcbpXBGtSItuPSU@H_Wtw@`lFBpXSUAMTBpX\"},\"mass\":349.2342579562838,\"monoisotopicMass\":349.06750148395,\"unsaturation\":10,\"elements\":[{\"symbol\":\"C\",\"number\":11},{\"symbol\":\"H\",\"number\":16},{\"symbol\":\"N\",\"number\":3},{\"symbol\":\"O\",\"number\":8},{\"symbol\":\"P\",\"number\":1}]},{\"symbol\":\"Hmc\",\"name\":\"5-hydroxymethylcytidine monophosphate diradical 51C\",\"mf\":\"C10H14N3O8P\",\"kind\":\"NucleotideP\",\"oneLetter\":\"∅\",\"ocl\":{\"value\":\"faspK@I^[BgENSghOFwaEqrIQQSYQJIRYULxDmUUUTsLuTQDqBId@@\",\"coordinates\":\"!BNuSFPDlDTEHt_pHtP@H_TuPBOpcbpXBGtSItuPSU@H_Wtw@`lFBpXSU@@tP\"},\"mass\":335.207640551437,\"monoisotopicMass\":335.05185141949,\"unsaturation\":10,\"elements\":[{\"symbol\":\"C\",\"number\":10},{\"symbol\":\"H\",\"number\":14},{\"symbol\":\"N\",\"number\":3},{\"symbol\":\"O\",\"number\":8},{\"symbol\":\"P\",\"number\":1}]},{\"symbol\":\"Cur\",\"name\":\"5-carboxymethylaminomethyluridine monophosphate diradical 51U\",\"mf\":\"C12H16N3O10P\",\"kind\":\"NucleotideP\",\"oneLetter\":\"!\",\"ocl\":{\"value\":\"fcwpk@I^{BgENSej}`|[^DWGHeEEMeDheIeUdhs`RuUUUSLuMSTQDqBId@@\",\"coordinates\":\"!BKAb@tURDM\\\\YpMAMpBYMcx`BKB]~@Ha}SXW@h`Bb@IMcx}RtDvH_Xa}b@JH@ha}b@I~@Ha}\"},\"mass\":393.24380370165557,\"monoisotopicMass\":393.05733072309,\"unsaturation\":12,\"elements\":[{\"symbol\":\"C\",\"number\":12},{\"symbol\":\"H\",\"number\":16},{\"symbol\":\"N\",\"number\":3},{\"symbol\":\"O\",\"number\":10},{\"symbol\":\"P\",\"number\":1}]},{\"symbol\":\"Chr\",\"name\":\"5-carboxyhydroxymethyluridine monophosphate diradical 520U\",\"mf\":\"C11H13N2O11P\",\"kind\":\"NucleotideP\",\"oneLetter\":\"≥\",\"ocl\":{\"value\":\"fmgQk@I^aSbgIrwlu`|[^DWGHeEEMeDheIeUCF\\\\BVjjjjYfiijHbXaDr@@\",\"coordinates\":\"!BNuSFPDlDTEHt_pHtP@H_TuPBOpcbpXBGtSItuPSU@H_Wtw@`lFBpXSU@@tTuPOxxlF@\"},\"mass\":380.2019472556255,\"monoisotopicMass\":380.02569624154,\"unsaturation\":12,\"elements\":[{\"symbol\":\"C\",\"number\":11},{\"symbol\":\"H\",\"number\":13},{\"symbol\":\"N\",\"number\":2},{\"symbol\":\"O\",\"number\":11},{\"symbol\":\"P\",\"number\":1}]},{\"symbol\":\"Mcu\",\"name\":\"5-methoxycarbonylmethyluridine monophosphate diradical 521U\",\"mf\":\"C12H15N2O10P\",\"kind\":\"NucleotideP\",\"oneLetter\":\"1\",\"ocl\":{\"value\":\"fmgPk@I^aSbgIrt\\\\p^MoBKcdRbbfrbTRdrjtsNAKUUUULsTsUDQLPbY@@\",\"coordinates\":\"!BS]@lFJU`@Gyoza`lzf@lIwx@`H{WHc|KB_W_Wx@_`@lIr\\\\SFBrH@h`B_`BH_WxbOrH_P\"},\"mass\":378.229159736154,\"monoisotopicMass\":378.04643168643,\"unsaturation\":12,\"elements\":[{\"symbol\":\"C\",\"number\":12},{\"symbol\":\"H\",\"number\":15},{\"symbol\":\"N\",\"number\":2},{\"symbol\":\"O\",\"number\":10},{\"symbol\":\"P\",\"number\":1}]},{\"symbol\":\"Hme\",\"name\":\"5-(carboxyhydroxymethyl)uridine methyl ester monophosphate diradical 522U\",\"mf\":\"C12H15N2O11P\",\"kind\":\"NucleotideP\",\"oneLetter\":\",\",\"ocl\":{\"value\":\"fcwQk@I^aSbgIrwlu`|[^DWGHeEEMeDheIeUCLs`RuUUUSLuMMTQDqBId@@\",\"coordinates\":\"!BS]@lFJU`@Gyoza`lzf@lIwx@`H{WHc|KB_W_Wx@_`@lIr\\\\SFBrHHc|_`A~@Ha}_c~H@ha}\"},\"mass\":394.2285646604723,\"monoisotopicMass\":394.041346306,\"unsaturation\":12,\"elements\":[{\"symbol\":\"C\",\"number\":12},{\"symbol\":\"H\",\"number\":15},{\"symbol\":\"N\",\"number\":2},{\"symbol\":\"O\",\"number\":11},{\"symbol\":\"P\",\"number\":1}]},{\"symbol\":\"Cxu\",\"name\":\"5-carboxymethyluridine monophosphate diradical 52U\",\"mf\":\"C11H13N2O10P\",\"kind\":\"NucleotideP\",\"oneLetter\":\"◊\",\"ocl\":{\"value\":\"fe{Pk@I^aSbgIrt\\\\p^MoBKcdRbbfrbTRdrjtYpIZjjjifZfZbHfHQL`@\",\"coordinates\":\"!BNuSFPDlDTEHt_pHtP@H_TuPBOpcbpXBGtSItuPSU@H_Wtw@`lFBpXSUAMTC~NKA`\"},\"mass\":364.2025423313072,\"monoisotopicMass\":364.03078162197,\"unsaturation\":12,\"elements\":[{\"symbol\":\"C\",\"number\":11},{\"symbol\":\"H\",\"number\":13},{\"symbol\":\"N\",\"number\":2},{\"symbol\":\"O\",\"number\":10},{\"symbol\":\"P\",\"number\":1}]},{\"symbol\":\"Hmu\",\"name\":\"5-carbamoylhydroxymethyluridine monophosphate diradical 531U\",\"mf\":\"C11H14N3O10P\",\"kind\":\"NucleotideP\",\"oneLetter\":\"r\",\"ocl\":{\"value\":\"fmgpk@I^WBgENSeoY`|[^DWGHeEEMeDheIeUCF\\\\BVjjjjYfiijHbXaDr@@\",\"coordinates\":\"!BNuSFPDlDTEHt_pHtP@H_TuPBOpcbpXBGtSItuPSU@H_Wtw@`lFBpXSU@@tTuPOxxlF@\"},\"mass\":379.21718629680873,\"monoisotopicMass\":379.04168065863,\"unsaturation\":12,\"elements\":[{\"symbol\":\"C\",\"number\":11},{\"symbol\":\"H\",\"number\":14},{\"symbol\":\"N\",\"number\":3},{\"symbol\":\"O\",\"number\":10},{\"symbol\":\"P\",\"number\":1}]},{\"symbol\":\"Ymu\",\"name\":\"5-carbamoylmethyluridine monophosphate diradical 53U\",\"mf\":\"C11H14N3O9P\",\"kind\":\"NucleotideP\",\"oneLetter\":\"&\",\"ocl\":{\"value\":\"fe{qK@I^gBgENSehp^MoBKcdRbbfrbTRdrjtYpIZjjjifZfZbHfHQL`@\",\"coordinates\":\"!BNuSFPDlDTEHt_pHtP@H_TuPBOpcbpXBGtSItuPSU@H_Wtw@`lFBpXSUAMTC~NKA`\"},\"mass\":363.2177813724905,\"monoisotopicMass\":363.04676603906006,\"unsaturation\":12,\"elements\":[{\"symbol\":\"C\",\"number\":11},{\"symbol\":\"H\",\"number\":14},{\"symbol\":\"N\",\"number\":3},{\"symbol\":\"O\",\"number\":9},{\"symbol\":\"P\",\"number\":1}]},{\"symbol\":\"Ttu\",\"name\":\"5-taurinomethyluridine monophosphate diradical 54U\",\"mf\":\"C12H18N3O11PS\",\"kind\":\"NucleotideP\",\"oneLetter\":\"Ê\",\"ocl\":{\"value\":\"fgqh`I^{BgENSenswAxv|HnJpcHeEEMeDheIeUdeHs`RuUUUSLuMTmQDSDHfP@\",\"coordinates\":\"!BKAb@tURD@m\\\\YpMAMpBYMcx`BKB]~@Ha}SXW@h`Bb@IMcx}RtDvH_Xa}b@JH@ha}b@JH__rH_]^H_P\"},\"mass\":443.32387754021244,\"monoisotopicMass\":443.03996658152005,\"unsaturation\":10,\"elements\":[{\"symbol\":\"C\",\"number\":12},{\"symbol\":\"H\",\"number\":18},{\"symbol\":\"N\",\"number\":3},{\"symbol\":\"O\",\"number\":11},{\"symbol\":\"P\",\"number\":1},{\"symbol\":\"S\",\"number\":1}]},{\"symbol\":\"Cmu\",\"name\":\"5-cyanomethyluridine monophosphate diradical 55U\",\"mf\":\"C11H12N3O8P\",\"kind\":\"NucleotideP\",\"oneLetter\":\"Ѷ\",\"ocl\":{\"value\":\"fikpK@I^GBgENSehOFwaEqrIQQSYQJIRYUYg@ejjjjfYj[hbIbDSH@\",\"coordinates\":\"!BNuSFPDlDTEHt_pHtP@H_TuPBOpcbpXBGtSItuPSU@H_Wtw@`lFBpXSU@@tPCQ\"},\"mass\":345.20249494006066,\"monoisotopicMass\":345.03620135502996,\"unsaturation\":14,\"elements\":[{\"symbol\":\"C\",\"number\":11},{\"symbol\":\"H\",\"number\":12},{\"symbol\":\"N\",\"number\":3},{\"symbol\":\"O\",\"number\":8},{\"symbol\":\"P\",\"number\":1}]},{\"symbol\":\"Pyu\",\"name\":\"5-(isopentenylaminomethyl)uridine monophosphate diradical 583U\",\"mf\":\"C15H22N3O8P\",\"kind\":\"NucleotideP\",\"oneLetter\":\"¾\",\"ocl\":{\"value\":\"fkopK@I^{BgENSehOFwaEqrIQQSYQJIRYUYIQg@ejjjjfYjZfjHbXaDr@@\",\"coordinates\":\"!BS]@lFJU`@Gyoza`lzf@lIwx@`H{W@h`BKB_W_Wx@_`@lIr\\\\SFBrH@h`B_`BH_Xc|bGvH@gx@bGt\"},\"mass\":403.32484606755946,\"monoisotopicMass\":403.11445167733,\"unsaturation\":12,\"elements\":[{\"symbol\":\"C\",\"number\":15},{\"symbol\":\"H\",\"number\":22},{\"symbol\":\"N\",\"number\":3},{\"symbol\":\"O\",\"number\":8},{\"symbol\":\"P\",\"number\":1}]},{\"symbol\":\"Mdu\",\"name\":\"5-methyldihydrouridine monophosphate diradical 58U\",\"mf\":\"C10H15N2O8P\",\"kind\":\"NucleotideP\",\"oneLetter\":\"ρ\",\"ocl\":{\"value\":\"fncPK@I^aSbgIrtGc[pbxyDhhilheDiLjs`RuUUUSTuMDQLPbY@@\",\"coordinates\":\"!BNuSFPDlDTEHt_pHtP@H_TuPBOpcbpXBGtSItuPSU@H_Wtw@`lFBpXSU@\"},\"mass\":322.20887809404695,\"monoisotopicMass\":322.05660244729,\"unsaturation\":8,\"elements\":[{\"symbol\":\"C\",\"number\":10},{\"symbol\":\"H\",\"number\":15},{\"symbol\":\"N\",\"number\":2},{\"symbol\":\"O\",\"number\":8},{\"symbol\":\"P\",\"number\":1}]},{\"symbol\":\"Mcd\",\"name\":\"5-methylcytidine monophosphate diradical 5C\",\"mf\":\"C10H14N3O7P\",\"kind\":\"NucleotideP\",\"oneLetter\":\"?\",\"ocl\":{\"value\":\"fncqs@I^[BgENSdGc[pbxyDhhilheDiLjs`RuUUUSLsUDQLPbY@@\",\"coordinates\":\"!BNuSFPDlDTEHt_pHtP@H_TuPBOpcbpXBGtSItuPSU@H_Wtw@`lFBpXSU@\"},\"mass\":319.2082356271187,\"monoisotopicMass\":319.05693679992004,\"unsaturation\":10,\"elements\":[{\"symbol\":\"C\",\"number\":10},{\"symbol\":\"H\",\"number\":14},{\"symbol\":\"N\",\"number\":3},{\"symbol\":\"O\",\"number\":7},{\"symbol\":\"P\",\"number\":1}]},{\"symbol\":\"Hia\",\"name\":\"N6-(cis-hydroxyisopentenyl)adenosine monophosphate diradical 60A\",\"mf\":\"C15H20N5O7P\",\"kind\":\"NucleotideP\",\"oneLetter\":\"`\",\"ocl\":{\"value\":\"eg`ZNL@IG@fnhJNEDlk`OFspb\\\\\\\\bTTTvTRbTbSVRTSGG`USUUUUTCLATuTDHSBDIbPSH\",\"coordinates\":\"!BzfC@IeKPaDn}bHCQb@KQwuRDFALYpHCQt]W@h`BTmCQw}~N`ME~@Gx@b@JH@ha}bOrH_Wxb@JH_P\"},\"mass\":413.3229660580212,\"monoisotopicMass\":413.11003500216003,\"unsaturation\":16,\"elements\":[{\"symbol\":\"C\",\"number\":15},{\"symbol\":\"H\",\"number\":20},{\"symbol\":\"N\",\"number\":5},{\"symbol\":\"O\",\"number\":7},{\"symbol\":\"P\",\"number\":1}]},{\"symbol\":\"Mta\",\"name\":\"2-methylthio-N6-methyladenosine monophosphate diradical 621A\",\"mf\":\"C12H16N5O6PS\",\"kind\":\"NucleotideP\",\"oneLetter\":\"∞\",\"ocl\":{\"value\":\"fmwhp`CQstZLDxipEfGa[qZDYEIlheDdhXdmDmKR\\\\u{MUUUU@aEUAFPTdmH@\",\"coordinates\":\"!BBGw|B@a}_S\\\\H@a}TEJNOuP{Ntm@fPBN[~iRSpHUCneXDBYTEITAEPDiVA@fTBYU@Sj[p\"},\"mass\":389.3243778334011,\"monoisotopicMass\":389.05589142807,\"unsaturation\":14,\"elements\":[{\"symbol\":\"C\",\"number\":12},{\"symbol\":\"H\",\"number\":16},{\"symbol\":\"N\",\"number\":5},{\"symbol\":\"O\",\"number\":6},{\"symbol\":\"P\",\"number\":1},{\"symbol\":\"S\",\"number\":1}]},{\"symbol\":\"Tca\",\"name\":\"N6-threonylcarbamoyladenosine monophosphate diradical 62A\",\"mf\":\"C15H19N6O10P\",\"kind\":\"NucleotideP\",\"oneLetter\":\"6\",\"ocl\":{\"value\":\"edRVEL@IG@fnehJNEDligo`POFspb\\\\\\\\bTTTvTRbTbSVTrbbcGG`USUUUUTCLASUMUABDpaBX`@\",\"coordinates\":\"!BzfC@IeKPaDn}bHCQbOsQwuRDFALYpHCQt]W@h`BTmCQw}~N`ME~@Gx@b@JH@ha}_c~H@ha}_c~H@ha}uwu~@Ha}\"},\"mass\":474.31994328836606,\"monoisotopicMass\":474.09002783307,\"unsaturation\":18,\"elements\":[{\"symbol\":\"C\",\"number\":15},{\"symbol\":\"H\",\"number\":19},{\"symbol\":\"N\",\"number\":6},{\"symbol\":\"O\",\"number\":10},{\"symbol\":\"P\",\"number\":1}]},{\"symbol\":\"Hva\",\"name\":\"N6-hydroxynorvalylcarbamoyladenosine monophosphate diradical 63A\",\"mf\":\"C16H21N6O10P\",\"kind\":\"NucleotideP\",\"oneLetter\":\"√\",\"ocl\":{\"value\":\"elZVIB@IG@fnehJNDligo`POEQql|HgGHeEEMeDheHdueLhhiVNO@jfjjjjhFXBfjZj`aBXPaLP@\",\"coordinates\":\"!BpBYTvxBNFY|bEJObGvOS\\\\@Yt]~DUEJOctu~@Ha}`HzOSTwPTh~HH`BbGvH_Xc|_`BH_Xc|_`BH_]_|bOq~Oxc|bGt\"},\"mass\":488.34656069321284,\"monoisotopicMass\":488.10567789753003,\"unsaturation\":18,\"elements\":[{\"symbol\":\"C\",\"number\":16},{\"symbol\":\"H\",\"number\":21},{\"symbol\":\"N\",\"number\":6},{\"symbol\":\"O\",\"number\":10},{\"symbol\":\"P\",\"number\":1}]},{\"symbol\":\"Aya\",\"name\":\"N6-acetyladenosine monophosphate diradical 64A\",\"mf\":\"C12H14N5O7P\",\"kind\":\"NucleotideP\",\"oneLetter\":\"⇓\",\"ocl\":{\"value\":\"fmwis@INBwlJ\\\\TgLp^MoBKcdRbbfrbTRdRZrcN^CUmUUUTCLASTDQLPbY@@\",\"coordinates\":\"!BNuSFPDlDTEHt_pHtP@H_TuPBOq_qopHBGtgD}D@RpPH_Wtw@aOTd}RtPCQ@D}RIqOQ@@\"},\"mass\":371.2431138434808,\"monoisotopicMass\":371.06308480878,\"unsaturation\":16,\"elements\":[{\"symbol\":\"C\",\"number\":12},{\"symbol\":\"H\",\"number\":14},{\"symbol\":\"N\",\"number\":5},{\"symbol\":\"O\",\"number\":7},{\"symbol\":\"P\",\"number\":1}]},{\"symbol\":\"Gca\",\"name\":\"N6-glycinylcarbamoyladenosine monophosphate diradical 65A\",\"mf\":\"C13H15N6O9P\",\"kind\":\"NucleotideP\",\"oneLetter\":\"≡\",\"ocl\":{\"value\":\"eohVIL@IG@fnehJNEDlikg`OFspb\\\\\\\\bTTTvTRbTbSVTrTXx|BjZjjjj`Y`JZfhHPfDHSD@@\",\"coordinates\":\"!BzfC@IeKPaDn}bHCQb@KQwuRDFALYpHCQt]W@h`BTmCQw}~N`ME~@Gx@bOrHHa}_c~H@ha}bOq~@Ha}\"},\"mass\":430.2673035543541,\"monoisotopicMass\":430.06381308458,\"unsaturation\":18,\"elements\":[{\"symbol\":\"C\",\"number\":13},{\"symbol\":\"H\",\"number\":15},{\"symbol\":\"N\",\"number\":6},{\"symbol\":\"O\",\"number\":9},{\"symbol\":\"P\",\"number\":1}]},{\"symbol\":\"Tya\",\"name\":\"N6-methyl-N6-threonylcarbamoyladenosinemonophosphate diradical 662A\",\"mf\":\"C16H21N6O10P\",\"kind\":\"NucleotideP\",\"oneLetter\":\"E\",\"ocl\":{\"value\":\"elZVEL@IG@fnmhJNEDleo`XPOFspb\\\\\\\\bTTTvTRbTbSVbaTTTXx|BjZjjjj`Y`JfjZjBDIaBDq@@\",\"coordinates\":\"!BzfC@IeKPaDn}bHCQb@KQwuRDFALYpHCQt]W@h`BTmCQw}~N`ME~@Gx@bOrHHa}_`A~Ox`BbGu~Ox`BbGwW_Wx@bGt\"},\"mass\":488.34656069321284,\"monoisotopicMass\":488.10567789753003,\"unsaturation\":18,\"elements\":[{\"symbol\":\"C\",\"number\":16},{\"symbol\":\"H\",\"number\":21},{\"symbol\":\"N\",\"number\":6},{\"symbol\":\"O\",\"number\":10},{\"symbol\":\"P\",\"number\":1}]},{\"symbol\":\"Nna\",\"name\":\"N6,N6-dimethyladenosine monophosphate diradical 66A\",\"mf\":\"C12H16N5O6P\",\"kind\":\"NucleotideP\",\"oneLetter\":\"ζ\",\"ocl\":{\"value\":\"feghs@INBwlJ\\\\TgHOFwaEqrIQQSYQJIRIMZLyxMVuUUUPLpEUADSDHfP@\",\"coordinates\":\"!BNuSFPDlDTEHt_pHtP@H_TuPBOq_qopHBGtgD}D@RpPH_Wtw@aOTd}RtPCQ@D}RFBp\"},\"mass\":357.2595904272741,\"monoisotopicMass\":357.08382025367,\"unsaturation\":14,\"elements\":[{\"symbol\":\"C\",\"number\":12},{\"symbol\":\"H\",\"number\":16},{\"symbol\":\"N\",\"number\":5},{\"symbol\":\"O\",\"number\":6},{\"symbol\":\"P\",\"number\":1}]},{\"symbol\":\"Fya\",\"name\":\"N6-formyladenosine monophosphate diradical 67A\",\"mf\":\"C11H12N5O7P\",\"kind\":\"NucleotideP\",\"oneLetter\":\"Ϩ\",\"ocl\":{\"value\":\"fegis@INBwlJ\\\\TgLp^MoBKcdRbbfrbTRdRZrYspZmjjjj`Y`JZBHfHQL`@\",\"coordinates\":\"!BNuSFPDlDTEHt_pHtP@H_TuPBOq_qopHBGtgD}D@RpPH_Wtw@aOTd}RtPCQ@D}RtP@\"},\"mass\":357.216496438634,\"monoisotopicMass\":357.04743474432,\"unsaturation\":16,\"elements\":[{\"symbol\":\"C\",\"number\":11},{\"symbol\":\"H\",\"number\":12},{\"symbol\":\"N\",\"number\":5},{\"symbol\":\"O\",\"number\":7},{\"symbol\":\"P\",\"number\":1}]},{\"symbol\":\"Hma\",\"name\":\"N6-hydroxymethyladenosine monophosphate diradical 68A\",\"mf\":\"C11H14N5O7P\",\"kind\":\"NucleotideP\",\"oneLetter\":\"Ϫ\",\"ocl\":{\"value\":\"fegis@INBwlJ\\\\TgLp^MoBKcdRbbfrbTRdRZrYspZmjjjj`Y`JjBHfHQL`@\",\"coordinates\":\"!BNuSFPDlDTEHt_pHtP@H_TuPBOq_qopHBGtgD}D@RpPH_Wtw@aOTd}RtPCQ@D}RtP@\"},\"mass\":359.23237794674554,\"monoisotopicMass\":359.06308480878,\"unsaturation\":14,\"elements\":[{\"symbol\":\"C\",\"number\":11},{\"symbol\":\"H\",\"number\":14},{\"symbol\":\"N\",\"number\":5},{\"symbol\":\"O\",\"number\":7},{\"symbol\":\"P\",\"number\":1}]},{\"symbol\":\"Cca\",\"name\":\"cyclic N6-threonylcarbamoyladenosine monophosphate diradical 69A\",\"mf\":\"C15H17N6O9P\",\"kind\":\"NucleotideP\",\"oneLetter\":\"e\",\"ocl\":{\"value\":\"ehRVIL@IG@fnehJNEDliko`OFspb\\\\\\\\bTTTvTRbTbSVTRRtXx|BjZvNjjjj`Y`IjfjbHPfDHSD`z`\",\"coordinates\":\"!BvuPfpDnDtEK_tPJHtXBH_TwPb@J_IorHbGtgD}F@RxRH_WwW@hbOTh}RtXCQ`A`l_`A`iVCjKAcjX@A~@h`Bup\"},\"mass\":456.30465685593623,\"monoisotopicMass\":456.07946314904,\"unsaturation\":20,\"elements\":[{\"symbol\":\"C\",\"number\":15},{\"symbol\":\"H\",\"number\":17},{\"symbol\":\"N\",\"number\":6},{\"symbol\":\"O\",\"number\":9},{\"symbol\":\"P\",\"number\":1}]},{\"symbol\":\"Fcy\",\"name\":\"5-formylcytidine monophosphate diradical71C\",\"mf\":\"C10H12N3O8P\",\"kind\":\"NucleotideP\",\"oneLetter\":\">\",\"ocl\":{\"value\":\"faspK@I^[BgENSghOFwaEqrIQQSYQJIRYULxDmUUUTsLttQDqBId@@\",\"coordinates\":\"!BNuSFPDlDTEHt_pHtP@H_TuPBOpcbpXBGtSItuPSU@H_Wtw@`lFBpXSU@@tP\"},\"mass\":333.1917590433254,\"monoisotopicMass\":333.03620135502996,\"unsaturation\":12,\"elements\":[{\"symbol\":\"C\",\"number\":10},{\"symbol\":\"H\",\"number\":12},{\"symbol\":\"N\",\"number\":3},{\"symbol\":\"O\",\"number\":8},{\"symbol\":\"P\",\"number\":1}]},{\"symbol\":\"Tur\",\"name\":\"4-thiouridine monophosphate diradical 74U\",\"mf\":\"C9H11N2O7PS\",\"kind\":\"NucleotideP\",\"oneLetter\":\"4\",\"ocl\":{\"value\":\"ff}Qp`I^aSbgIrCqmxQ\\\\ZaFQJJJ[JIQJSMg@ejjjjfYihbIbDSH@\",\"coordinates\":\"!BNuSFPDlDTEHt_pHtP@H_TuPBOpcbpXBGtSItuPSU@H_Wtw@`lFDuP\"},\"mass\":322.2317616628973,\"monoisotopicMass\":322.0024588732,\"unsaturation\":10,\"elements\":[{\"symbol\":\"C\",\"number\":9},{\"symbol\":\"H\",\"number\":11},{\"symbol\":\"N\",\"number\":2},{\"symbol\":\"O\",\"number\":7},{\"symbol\":\"P\",\"number\":1},{\"symbol\":\"S\",\"number\":1}]},{\"symbol\":\"Meg\",\"name\":\"7-methylguanosine monophosphate diradical 7G\",\"mf\":\"C11H15N5O7P\",\"kind\":\"NucleotideP\",\"oneLetter\":\"7\",\"ocl\":{\"value\":\"fegisDINCt\\\\J\\\\TgLp^MoBKbF\\\\bTTTvTRbTbRlSN^CWmUUUUKLuSTQDqBId@@\",\"coordinates\":\"!BNuSFPDlDTEHt_pHtP@H_TuPBOq_qopHBGtgD}D@RpPH_Wtw@aOTd}RqdCQ@B\\\\StP@\"},\"mass\":360.2403187008013,\"monoisotopicMass\":360.07090984101,\"unsaturation\":13,\"elements\":[{\"symbol\":\"C\",\"number\":11},{\"symbol\":\"H\",\"number\":15},{\"symbol\":\"N\",\"number\":5},{\"symbol\":\"O\",\"number\":7},{\"symbol\":\"P\",\"number\":1}]},{\"symbol\":\"Mea\",\"name\":\"8-methyladenosine monophosphate diradical 8A\",\"mf\":\"C11H14N5O6P\",\"kind\":\"NucleotideP\",\"oneLetter\":\"â\",\"ocl\":{\"value\":\"fi{hs@INCt\\\\J\\\\UdhOFw`eqrIQQSYQJJJQKigOA[vjjjjAi`J`bIbDSH@\",\"coordinates\":\"!BNuSFPDlDTEHt_pHtP@H_TuPBOq_qopHBGtgD}D@RpP@c`a}_S_|BD}RSuKQ@MD@\"},\"mass\":343.2329730224273,\"monoisotopicMass\":343.06817018921,\"unsaturation\":14,\"elements\":[{\"symbol\":\"C\",\"number\":11},{\"symbol\":\"H\",\"number\":14},{\"symbol\":\"N\",\"number\":5},{\"symbol\":\"O\",\"number\":6},{\"symbol\":\"P\",\"number\":1}]},{\"symbol\":\"Dhu\",\"name\":\"dihydrouridine monophosphate diradical 8U\",\"mf\":\"C9H13N2O8P\",\"kind\":\"NucleotideP\",\"oneLetter\":\"D\",\"ocl\":{\"value\":\"ff}PK@I^aSbgIsTGc[pbxyDhhilheDiLv\\\\BVjjjjZffbHfHQL`@\",\"coordinates\":\"!BNuSFPDlDTEHt_pHtP@H_TuPBOpcbpXBGtSItuPSU@H_Wtw@`lFDuP\"},\"mass\":308.1822606892002,\"monoisotopicMass\":308.04095238282997,\"unsaturation\":8,\"elements\":[{\"symbol\":\"C\",\"number\":9},{\"symbol\":\"H\",\"number\":13},{\"symbol\":\"N\",\"number\":2},{\"symbol\":\"O\",\"number\":8},{\"symbol\":\"P\",\"number\":1}]},{\"symbol\":\"Ins\",\"name\":\"inosine monophosphate diradical 9A\",\"mf\":\"C10H11N4O7P\",\"kind\":\"NucleotideP\",\"oneLetter\":\"I\",\"ocl\":{\"value\":\"fakIs@INBvENJSghOFwaEqrIQQSYQJIRIMLyxMVuUUUTlsSTQDqBId@@\",\"coordinates\":\"!BNuSFPDlDTEHt_pHtP@H_TuPBOq_qopHBGtgD}D@RpPH_Wtw@aOTd}RtPCQ@@\"},\"mass\":330.1911165763972,\"monoisotopicMass\":330.03653570766,\"unsaturation\":14,\"elements\":[{\"symbol\":\"C\",\"number\":10},{\"symbol\":\"H\",\"number\":11},{\"symbol\":\"N\",\"number\":4},{\"symbol\":\"O\",\"number\":7},{\"symbol\":\"P\",\"number\":1}]},{\"symbol\":\"Pis\",\"name\":\"pseudouridine monophosphate diradical 9U\",\"mf\":\"C9H11N2O8P\",\"kind\":\"NucleotideP\",\"oneLetter\":\"P\",\"ocl\":{\"value\":\"ff}PK@OAaSbgIsTGc[pbxyDhhilheDiLv\\\\BVjjjfZffbHfHQL`@\",\"coordinates\":\"!BNuSFPDlDTEHt_pHtP@H_TuPBOpcbpXBGtSItuPSU@H_Wtw@`lFDuP\"},\"mass\":306.1663791810886,\"monoisotopicMass\":306.02530231837,\"unsaturation\":10,\"elements\":[{\"symbol\":\"C\",\"number\":9},{\"symbol\":\"H\",\"number\":11},{\"symbol\":\"N\",\"number\":2},{\"symbol\":\"O\",\"number\":8},{\"symbol\":\"P\",\"number\":1}]},{\"symbol\":\"Pqb\",\"name\":\"preQ0base 100G diradical (base)\",\"mf\":\"C7H5N5O\",\"kind\":\"Nucleotide\",\"oneLetter\":\"ψ\",\"ocl\":{\"value\":\"dk^h@DxYLLbbTTRekiujYj^`@\",\"coordinates\":\"!B|Gwp_Gy|Gwp_[lk_gp_Ag_wrYRs}|f\"},\"mass\":175.1477760289729,\"monoisotopicMass\":175.04940980287,\"unsaturation\":14,\"elements\":[{\"symbol\":\"C\",\"number\":7},{\"symbol\":\"H\",\"number\":5},{\"symbol\":\"N\",\"number\":5},{\"symbol\":\"O\",\"number\":1}]},{\"symbol\":\"Pqg\",\"name\":\"preQ1base 101G diradical (base)\",\"mf\":\"C7H9N5O\",\"kind\":\"Nucleotide\",\"oneLetter\":\"∇\",\"ocl\":{\"value\":\"dk^h@DxYLLbbTTRckiUjYij`@\",\"coordinates\":\"!BWyfe[tlDWye_fXx@RpRe[wtHSuHH@a}\"},\"mass\":179.179539045196,\"monoisotopicMass\":179.08070993179,\"unsaturation\":10,\"elements\":[{\"symbol\":\"C\",\"number\":7},{\"symbol\":\"H\",\"number\":9},{\"symbol\":\"N\",\"number\":5},{\"symbol\":\"O\",\"number\":1}]},{\"symbol\":\"Qba\",\"name\":\"Qbase 10G diradical (base)\",\"mf\":\"C12H15N5O3\",\"kind\":\"Nucleotide\",\"oneLetter\":\"∴\",\"ocl\":{\"value\":\"fbmi`@D\\\\EHpHyrJIQQJMJIPtyIPTmSMMUMUP@@\",\"coordinates\":\"!BRpQ_f^i`RpQKAEARzfA_f_pHtP@H_Pc|BGuPThxUCl{RtBYTd|\"},\"mass\":277.27967290184347,\"monoisotopicMass\":277.11748936431,\"unsaturation\":14,\"elements\":[{\"symbol\":\"C\",\"number\":12},{\"symbol\":\"H\",\"number\":15},{\"symbol\":\"N\",\"number\":5},{\"symbol\":\"O\",\"number\":3}]},{\"symbol\":\"Dgc\",\"name\":\"N2,7-dimethylguanosine cap (cap DMG) diradical 279553N\",\"mf\":\"C12H18N5O11P2\",\"kind\":\"Nucleotide\",\"oneLetter\":\"®\",\"ocl\":{\"value\":\"e`TZMBHIG@aihJNEHdlemck`OFspz|OgDJ\\\\bTTTvTRbTbRvbtfKGG`UPuUUUUJtuTmUTPaLHPfH@@\",\"coordinates\":\"!BvuPfpDnDtEK_t_rHtXBH_TwPbOr_IorHbGtgD}F@RxS|uxc|_]^OTh}RIlBH_]F@IqOQ`@A~_c|bH}RbGt\"},\"mass\":470.24625855539705,\"monoisotopicMass\":470.04780541440005,\"unsaturation\":13,\"elements\":[{\"symbol\":\"C\",\"number\":12},{\"symbol\":\"H\",\"number\":18},{\"symbol\":\"N\",\"number\":5},{\"symbol\":\"O\",\"number\":11},{\"symbol\":\"P\",\"number\":2}]},{\"symbol\":\"Dpa\",\"name\":\"5′-(3′-dephosphoacetyl-CoA) diradical 4155N\",\"mf\":\"C23H35N7O16P3S\",\"kind\":\"Nucleotide\",\"oneLetter\":\"♣\",\"ocl\":{\"value\":\"elz~@jDCHlemnSTLBAEKBjfckgbV]XpEfCpB|IoCtHZy{lbdvbbfrbTRdRNRdnTbefRTrRTdTRrFVfjjjj`V`bZjjfjZjZ`bbLSaRP@\",\"coordinates\":\"!BvtmKaMmKUMlfgto[tDw_cosWt]~H@dvObGv_F_sWbOpgKMG_R}m}bHa}HbOSX}M_cQw}G_OwzH_[wW_c~H_Wx@G{|bM]}bGvHGxbGu~Oxa}bOq~Oxa}_c~H_WxuwvH_P\"},\"mass\":790.5483266874629,\"monoisotopicMass\":790.1073852418399,\"unsaturation\":21,\"elements\":[{\"symbol\":\"C\",\"number\":23},{\"symbol\":\"H\",\"number\":35},{\"symbol\":\"N\",\"number\":7},{\"symbol\":\"O\",\"number\":16},{\"symbol\":\"P\",\"number\":3},{\"symbol\":\"S\",\"number\":1}]},{\"symbol\":\"Dpm\",\"name\":\"5′-(3′-dephosphomalonyl-CoA) diradical 4255N\",\"mf\":\"C24H35N7O18P3S\",\"kind\":\"Nucleotide\",\"oneLetter\":\"♥\",\"ocl\":{\"value\":\"efq~DjDCHlemnSTLBAEKBjfckgbV]XrzpEfCpB|IoCtHZy{lbdvbbfrbTRdRNRdnTbefRTrRTrdbbVPrtuUUUTBtDSUUTuSUSSTDTQb\\\\JR@@\",\"coordinates\":\"!BIlB_Ib[@pAe`zni`FALSF@A~FBq~OrpXbGveX@A~_c~OTa`lzf@_ha}_]_Q`MF@bOpXKA`loXbH__rHb@JHoX`B@m]}uwx@bGu~Ox`BbKvH@ha}_c~H@hb}b@JH_Xc|_`BH_X`B_`BHoP\"},\"mass\":834.5578724328346,\"monoisotopicMass\":834.0972144809799,\"unsaturation\":23,\"elements\":[{\"symbol\":\"C\",\"number\":24},{\"symbol\":\"H\",\"number\":35},{\"symbol\":\"N\",\"number\":7},{\"symbol\":\"O\",\"number\":18},{\"symbol\":\"P\",\"number\":3},{\"symbol\":\"S\",\"number\":1}]},{\"symbol\":\"Dsc\",\"name\":\"5′-(3′-dephosphosuccinyl-CoA) radical 4355N\",\"mf\":\"C25H37N7O18P3S\",\"kind\":\"Nucleotide\",\"oneLetter\":\"♦\",\"ocl\":{\"value\":\"eny~DjDCHlemnSTLBAEKBjfckgbV]XzvpOFCpB|IoCtHZy{lbdvbbfrbTRdRNRdnTbefRTrRTrTdTRrFVfjjjj`V`bZjjfjZjZfhHhcDxTd@@\",\"coordinates\":\"!B[~kjXFjiV[Ry|fcm}MtGwWctvH_]Q_c}KaGwWbGvN`H}MgrX@_gx@h`gKB\\\\lbGvOSX}M@m^H@gwWbGvH@ha}_Xc|bGxb@I~@Ha}b@JH_X`B_`BH_X`BbGvH@ha}_c~H@ha}b@I~@Ha}\"},\"mass\":848.5844898376815,\"monoisotopicMass\":848.11286454544,\"unsaturation\":23,\"elements\":[{\"symbol\":\"C\",\"number\":25},{\"symbol\":\"H\",\"number\":37},{\"symbol\":\"N\",\"number\":7},{\"symbol\":\"O\",\"number\":18},{\"symbol\":\"P\",\"number\":3},{\"symbol\":\"S\",\"number\":1}]},{\"symbol\":\"Dpc\",\"name\":\"5′-(3′-dephospho-CoA) radical 455N\",\"mf\":\"C21H32N7O13P2S\",\"kind\":\"Nucleotide\",\"oneLetter\":\"♠\",\"ocl\":{\"value\":\"ek_^KBDIG@nabYXJNEHdliemh\\\\QPEfspZ|CPcKmnrIQQSYQJIRIGIRWJQRsIJYIccpJkjjjjjAZBIjjjZijjBDIaBDq@@\",\"coordinates\":\"!B[zW[UI|YchAMc{vHcuJH@m~NbGuKvwvHb@JNwx}Rgqe}bHa}@h`gDr\\\\Sb@JOTh}R@m]~@@A~b@I~@H`B_X`_hb}_`CW@h`B_`BH@gx@upJH@gx@b@I~@@\"},\"mass\":684.5310558604504,\"monoisotopicMass\":684.1254042880199,\"unsaturation\":19,\"elements\":[{\"symbol\":\"C\",\"number\":21},{\"symbol\":\"H\",\"number\":32},{\"symbol\":\"N\",\"number\":7},{\"symbol\":\"O\",\"number\":13},{\"symbol\":\"P\",\"number\":2},{\"symbol\":\"S\",\"number\":1}]},{\"symbol\":\"Dpe\",\"name\":\"5′-diphosphate end 552N\",\"mf\":\"O3P\",\"kind\":\"Nucleotide\",\"oneLetter\":\"ϒ\",\"ocl\":{\"value\":\"gJQdebGF^Dx|duK@@\",\"coordinates\":\"!BbOq~@GxbGt\"},\"mass\":78.97197677137483,\"monoisotopicMass\":78.95850585713,\"unsaturation\":1,\"elements\":[{\"symbol\":\"O\",\"number\":3},{\"symbol\":\"P\",\"number\":1}]},{\"symbol\":\"Mgc\",\"name\":\"7-methylguanosine cap (cap 0) diradical 79553N\",\"mf\":\"C11H16N5O11P2\",\"kind\":\"Nucleotide\",\"oneLetter\":\"©\",\"ocl\":{\"value\":\"eohZMBHIG@aihJNEHdlemck`OFspz|GgDJ\\\\bTTTvTRbTbRvbtcXx|BjFjjjjiVfjejjHPfDHSD@@\",\"coordinates\":\"!BvuPfpDnDtEK_tPJHtXBH_TwPb@J_I`JHbGtgD}F@RxPBux`B_]^OTh}RIlBH_]F@IqOQ`@A~_c|BbHa}\"},\"mass\":456.2196411505502,\"monoisotopicMass\":456.03215534994,\"unsaturation\":13,\"elements\":[{\"symbol\":\"C\",\"number\":11},{\"symbol\":\"H\",\"number\":16},{\"symbol\":\"N\",\"number\":5},{\"symbol\":\"O\",\"number\":11},{\"symbol\":\"P\",\"number\":2}]},{\"symbol\":\"Gyy\",\"name\":\"guanylylated 5′ end (cap G) diradical 9553N\",\"mf\":\"C10H13N5O10P2\",\"kind\":\"Nucleotide\",\"oneLetter\":\"ϑ\",\"ocl\":{\"value\":\"fkhh`INCt\\\\J\\\\UENY{NCqmxM|EnNQJJJ[JIQJQHzIRLyxM^uUUUTkSULuQDSDHfP@\",\"coordinates\":\"!BvuPfpDnDtEK_tPJHtXBH_TwPb@J_I`JHbGtgD}F@RxPBux`B_]^OTh}R_`CQ`B\\\\StXA~@C}~@Gx\"},\"mass\":425.1856780673293,\"monoisotopicMass\":425.01376563368,\"unsaturation\":14,\"elements\":[{\"symbol\":\"C\",\"number\":10},{\"symbol\":\"H\",\"number\":13},{\"symbol\":\"N\",\"number\":5},{\"symbol\":\"O\",\"number\":10},{\"symbol\":\"P\",\"number\":2}]},{\"symbol\":\"Furp\",\"name\":\"furan phosphate radical\",\"mf\":\"C5H6O4P\",\"kind\":\"RNAp\",\"oneLetter\":\"⬠\",\"ocl\":{\"value\":\"dmtBPDpnAYcpRZ}eeYjii@@\",\"coordinates\":\"!BNvw|Vso|kUl{[So|PPAGuU\\\\z`pP\"},\"mass\":161.072705703704,\"monoisotopicMass\":161.00037067008,\"unsaturation\":5,\"elements\":[{\"symbol\":\"C\",\"number\":5},{\"symbol\":\"H\",\"number\":6},{\"symbol\":\"O\",\"number\":4},{\"symbol\":\"P\",\"number\":1}]},{\"symbol\":\"Phg\",\"ocl\":{\"value\":\"dcNHPBPOEgEInVuWYj`@@@\"},\"name\":\"Phenyl glycine diradical\",\"mf\":\"C8H7NO\",\"kind\":\"aa\",\"mass\":133.1475805880365,\"monoisotopicMass\":133.05276384961002,\"unsaturation\":10,\"elements\":[{\"symbol\":\"C\",\"number\":8},{\"symbol\":\"H\",\"number\":7},{\"symbol\":\"N\",\"number\":1},{\"symbol\":\"O\",\"number\":1}]},{\"symbol\":\"Hpg\",\"ocl\":{\"value\":\"dknDPBPp|V\\\\Tfy[WWYj`@`@@\",\"coordinates\":\"!BbOq~@Ha}bOq~Oxa}bGwW_Wx?_c?W_Wx?\"},\"name\":\"4-hydroxyphenylglycine diradical\",\"mf\":\"C8H7NO2\",\"kind\":\"aa\",\"mass\":149.14698551235477,\"monoisotopicMass\":149.04767846918,\"unsaturation\":10,\"elements\":[{\"symbol\":\"C\",\"number\":8},{\"symbol\":\"H\",\"number\":7},{\"symbol\":\"N\",\"number\":1},{\"symbol\":\"O\",\"number\":2}]},{\"symbol\":\"Dpg\",\"ocl\":{\"value\":\"dg^LPBS[CqYqR[emYTyj`BH@@\",\"coordinates\":\"!BbOr~@Hb}bOr~Wxb}bKvWo[y_oe}HoY^}Uwt\"},\"name\":\"3,5-dihydroxyphenylglycine diradical\",\"mf\":\"C8H7NO3\",\"kind\":\"aa\",\"mass\":165.14639043667304,\"monoisotopicMass\":165.04259308875,\"unsaturation\":10,\"elements\":[{\"symbol\":\"C\",\"number\":8},{\"symbol\":\"H\",\"number\":7},{\"symbol\":\"N\",\"number\":1},{\"symbol\":\"O\",\"number\":3}]},{\"symbol\":\"T\",\"name\":\"Tritium\",\"mf\":\"[3H]\",\"ocl\":{\"value\":\"eFAAYhBLCEX@\",\"coordinates\":\"!B@@L\"},\"mass\":3.0160492779,\"monoisotopicMass\":3.0160492779,\"unsaturation\":-1,\"elements\":[{\"symbol\":\"H\",\"number\":1,\"isotope\":3}]}]","import { groups } from './groups';\n\nexport const groupsObject = {};\ngroups.forEach((e) => {\n  if (groupsObject[e.symbol]) {\n    console.log('The symbol ' + e.symbol + ' is used more than once');\n  }\n  groupsObject[e.symbol] = e;\n});\n","import { groupsObject } from './groupsObject.js';\n\n/**\n * Recreate a one letter sequence\n * @param {object} mf\n */\n\nexport function groupsToSequence(mf) {\n  mf = mf.replace(/\\([^(]*\\)/g, '');\n  let parts = mf.split(/(?=[A-Z ])/);\n  let usefulParts = [];\n  for (let part of parts) {\n    if (part === ' ') {\n      usefulParts.push(' ');\n      continue;\n    }\n    if (!part.match(/^[A-Z][a-z]{2,6}/)) continue;\n    if (groupsObject[part] && groupsObject[part].oneLetter) {\n      usefulParts.push(groupsObject[part].oneLetter);\n    } else {\n      usefulParts.push('?');\n    }\n  }\n  return usefulParts.join('').replace(/ +/g, ' ').trim();\n}\n","import { elementsAndStableIsotopesObject as elements } from 'chemical-elements';\n\nexport function getIsotopeRatioInfo(value) {\n  let result = { mass: 0, monoisotopicMass: 0 };\n  let element = elements[value.atom];\n  if (!element) throw new Error(`Element not found: ${value.atom}`);\n  let isotopesArray = element.isotopes;\n  let ratios = normalize(value.ratio);\n  let max = Math.max(...ratios);\n  if (ratios.length > isotopesArray.length) {\n    throw new Error(\n      `the number of specified ratios is bigger that the number of stable isotopes: ${value.atom}`,\n    );\n  }\n  for (let i = 0; i < ratios.length; i++) {\n    result.mass += ratios[i] * isotopesArray[i].mass;\n    if (max === ratios[i] && result.monoisotopicMass === 0) {\n      result.monoisotopicMass = isotopesArray[i].mass;\n    }\n  }\n  return result;\n}\n\nfunction normalize(array) {\n  let sum = array.reduce((prev, current) => prev + current, 0);\n  return array.map((a) => a / sum);\n}\n","import {\n  isotopesObject as isotopes,\n  elementsObject as elements,\n} from 'chemical-elements';\nimport { groupsObject as groups } from 'chemical-groups';\n\nimport { Kind } from '../Kind';\n\nimport { getIsotopeRatioInfo } from './getIsotopeRatioInfo';\n\n/**\n *\n * @param {*} parts\n * @param {*} [options={}]\n */\nexport function getEA(parts) {\n  let results = {};\n  for (let part of parts) {\n    for (let line of part) {\n      switch (line.kind) {\n        case Kind.ISOTOPE: {\n          let isotope = isotopes[line.value.isotope + line.value.atom];\n          if (!isotope) {\n            throw new Error(\n              `Unknown isotope: ${line.value.isotope}${line.value.atom}`,\n            );\n          }\n          addMass(results, line.value.atom, isotope.mass * line.multiplier);\n          break;\n        }\n\n        case Kind.ISOTOPE_RATIO: {\n          let isotopeRatioInfo = getIsotopeRatioInfo(line.value);\n          addMass(\n            results,\n            line.value.atom,\n            isotopeRatioInfo.mass * line.multiplier,\n          );\n          break;\n        }\n\n        case Kind.ATOM: {\n          let element = elements[line.value];\n          if (!element) {\n            element = groups[line.value];\n            if (!element) throw Error(`Unknown element: ${line.value}`);\n            // need to explode group ????\n          }\n          addMass(results, line.value, element.mass * line.multiplier);\n          break;\n        }\n\n        case Kind.CHARGE:\n          break;\n        default:\n          throw new Error('partToMF unhandled Kind: ', line.kind);\n      }\n    }\n  }\n\n  let eas = [];\n  let sum = 0;\n  for (let key in results) {\n    sum += results[key];\n    eas.push({\n      element: key,\n      mass: results[key],\n    });\n  }\n\n  eas.forEach((ea) => {\n    ea.ratio = ea.mass / sum;\n  });\n  return eas;\n}\n\nfunction addMass(results, atom, mass) {\n  if (!results[atom]) results[atom] = 0;\n  results[atom] += mass;\n}\n","import { elementsObject, elementsAndIsotopesObject } from 'chemical-elements';\n\nimport { Kind } from '../Kind';\n\n/**\n *\n * @param {*} parts\n * @param {*} [options={}]\n */\nexport function getElements(parts) {\n  const elements = [];\n  for (const part of parts) {\n    for (const line of part) {\n      let number = line.multiplier;\n      switch (line.kind) {\n        case Kind.ATOM: {\n          let symbol = line.value;\n          let element = elementsObject[symbol];\n          if (!element) {\n            throw new Error(`element unknown: ${symbol} - ${line}`);\n          }\n          addElement(elements, { symbol, number });\n          break;\n        }\n        case Kind.ISOTOPE: {\n          let element = elementsAndIsotopesObject[line.value.atom];\n          if (!element) {\n            throw new Error(`element unknown: ${part.value.atom} - ${line}`);\n          }\n          let isotope = element.isotopes.filter(\n            (a) => a.nominal === line.value.isotope,\n          )[0];\n          if (!isotope) {\n            throw new Error(`isotope unknown: ${line.value.isotope} - ${line}`);\n          }\n          addElement(elements, {\n            symbol: line.value.atom,\n            number,\n            isotope: line.value.isotope,\n          });\n          break;\n        }\n        default:\n          throw new Error(`unknown type: ${line.kind}`);\n      }\n    }\n  }\n  return elements;\n}\n\nfunction addElement(elements, newElement) {\n  for (let element of elements) {\n    if (\n      element.symbol === newElement.symbol &&\n      element.isotope === newElement.isotope\n    ) {\n      element.number += newElement.number;\n      return;\n    }\n  }\n  elements.push(newElement);\n}\n","import { Kind } from '../Kind.js';\n\n/**\n * Convert a MF part to an array of atoms\n * This procedure will suppress the isotopes !\n * This is mainly used to make queries\n */\n\nexport function partToAtoms(part) {\n  let atoms = {};\n  for (let line of part) {\n    switch (line.kind) {\n      case Kind.ISOTOPE:\n        if (!atoms[line.value.atom]) atoms[line.value.atom] = 0;\n        atoms[line.value.atom] += line.multiplier;\n        break;\n      case Kind.ISOTOPE_RATIO:\n        if (!atoms[line.value.atom]) atoms[line.value.atom] = 0;\n        atoms[line.value.atom] += line.multiplier;\n        break;\n      case Kind.ATOM:\n        if (!atoms[line.value]) atoms[line.value] = 0;\n        atoms[line.value] += line.multiplier;\n        break;\n      case Kind.CHARGE:\n        break;\n      case Kind.ANCHOR:\n        break;\n      default:\n        throw new Error('partToMF unhandled Kind: ', line.kind);\n    }\n  }\n  return atoms;\n}\n","import { Kind } from '../Kind';\n\nexport function partToMF(part, options = {}) {\n  let mf = [];\n  for (let line of part) {\n    switch (line.kind) {\n      case Kind.ISOTOPE:\n        if (line.multiplier !== 0) {\n          mf.push(\n            `[${line.value.isotope}${line.value.atom}]${\n              line.multiplier !== 1 ? line.multiplier : ''\n            }`,\n          );\n        }\n        break;\n      case Kind.ISOTOPE_RATIO:\n        if (line.multiplier !== 0) {\n          mf.push(\n            `${line.value.atom}{${line.value.ratio.join(',')}}${\n              line.multiplier !== 1 ? line.multiplier : ''\n            }`,\n          );\n        }\n        break;\n      case Kind.ATOM:\n        if (line.multiplier !== 0) {\n          mf.push(line.value + (line.multiplier !== 1 ? line.multiplier : ''));\n        }\n        break;\n      case Kind.CHARGE:\n        if (line.value === 0 || options.neutral) break;\n        mf.push(`(${line.value > 0 ? `+${line.value}` : line.value})`);\n        break;\n      default:\n    }\n  }\n  return mf.join('');\n}\n","import {\n  ELECTRON_MASS,\n  unsaturationsObject as unsaturations,\n  elementsAndIsotopesObject as elements,\n  isotopesObject as isotopes,\n} from 'chemical-elements';\nimport { groupsObject as groups } from 'chemical-groups';\n\nimport { Kind } from '../Kind';\n\nimport { getIsotopeRatioInfo } from './getIsotopeRatioInfo';\nimport { partToAtoms } from './partToAtoms';\nimport { partToMF } from './partToMF';\n\n/**\n *\n * @param {*} parts\n * @param {*} [options={}]\n */\nexport function getInfo(parts, options = {}) {\n  let { customUnsaturations = {} } = options;\n  if (parts.length === 0) return {};\n  if (parts.length === 1) {\n    return getProcessedPart(parts[0], customUnsaturations);\n  }\n\n  let result = { parts: [] };\n  for (let part of parts) {\n    result.parts.push(getProcessedPart(part, customUnsaturations));\n  }\n\n  result.monoisotopicMass = 0;\n  result.mass = 0;\n  result.charge = 0;\n  result.mf = result.parts.map((a) => a.mf).join('.');\n  result.parts.forEach((a) => {\n    result.mass += a.mass;\n    result.monoisotopicMass += a.monoisotopicMass;\n    result.charge += a.charge;\n  });\n  return result;\n}\n\nfunction getProcessedPart(part, customUnsaturations) {\n  let currentPart = {\n    mass: 0,\n    monoisotopicMass: 0,\n    charge: 0,\n    mf: '',\n    atoms: partToAtoms(part),\n  };\n  let unsaturation = 0;\n  let validUnsaturation = true;\n  currentPart.mf = partToMF(part);\n\n  for (let line of part) {\n    let currentElement = '';\n    switch (line.kind) {\n      case Kind.ATOM: {\n        currentElement = line.value;\n        let element = elements[line.value];\n\n        // todo should we have a kind GROUP ?\n        if (!element) {\n          element = groups[line.value];\n          if (!element) throw Error(`Unknown element: ${line.value}`);\n          if (!customUnsaturations[line.value]) {\n            customUnsaturations[line.value] = element.unsaturation;\n          }\n        }\n        if (!element) throw new Error(`Unknown element: ${line.value}`);\n        currentPart.monoisotopicMass +=\n          element.monoisotopicMass * line.multiplier;\n        currentPart.mass += element.mass * line.multiplier;\n        break;\n      }\n      case Kind.ISOTOPE: {\n        currentElement = line.value.atom;\n        let isotope = isotopes[line.value.isotope + line.value.atom];\n        if (!isotope) {\n          throw new Error(\n            `Unknown isotope: ${line.value.isotope}${line.value.atom}`,\n          );\n        }\n        currentPart.monoisotopicMass += isotope.mass * line.multiplier;\n        currentPart.mass += isotope.mass * line.multiplier;\n        break;\n      }\n      case Kind.ISOTOPE_RATIO: {\n        currentElement = line.value.atom;\n        let isotopeRatioInfo = getIsotopeRatioInfo(line.value);\n        currentPart.monoisotopicMass +=\n          isotopeRatioInfo.monoisotopicMass * line.multiplier;\n        currentPart.mass += isotopeRatioInfo.mass * line.multiplier;\n        break;\n      }\n      case Kind.CHARGE:\n        currentPart.charge = line.value;\n        if (validUnsaturation) {\n          unsaturation -= line.value;\n        }\n        break;\n      default:\n        throw new Error('Unimplemented Kind in getInfo', line.kind);\n    }\n    if (currentElement) {\n      if (customUnsaturations[currentElement] !== undefined) {\n        unsaturation += customUnsaturations[currentElement] * line.multiplier;\n      } else if (unsaturations[currentElement] !== undefined) {\n        unsaturation += unsaturations[currentElement] * line.multiplier;\n      } else {\n        validUnsaturation = false;\n      }\n    }\n  }\n\n  // need to calculate the observedMonoisotopicMass\n  if (currentPart.charge) {\n    currentPart.observedMonoisotopicMass =\n      (currentPart.monoisotopicMass - currentPart.charge * ELECTRON_MASS) /\n      Math.abs(currentPart.charge);\n  }\n  if (validUnsaturation) {\n    currentPart.unsaturation = unsaturation / 2 + 1;\n  }\n  return currentPart;\n}\n","import {\n  elementsAndStableIsotopesObject as elements,\n  isotopesObject as isotopes,\n} from 'chemical-elements';\n\nimport { Kind } from '../Kind';\n\n/**\n *\n * @param {*} parts\n * @param {*} options\n */\nexport function getIsotopesInfo(parts) {\n  if (parts.length === 0) return [];\n  if (parts.length > 1) {\n    throw new Error('getIsotopesInfo can not be applied on multipart MF');\n  }\n\n  return getProcessedPart(parts[0]);\n}\n\nfunction getProcessedPart(part) {\n  let result = {\n    charge: 0,\n    isotopes: [],\n  };\n  for (let line of part) {\n    switch (line.kind) {\n      case Kind.ISOTOPE: {\n        let isotope = isotopes[line.value.isotope + line.value.atom];\n        if (!isotope) {\n          throw Error('unknown isotope:', line.value.atom, line.value.isotope);\n        }\n        result.isotopes.push({\n          atom: `[${line.value.isotope}${line.value.atom}]`,\n          number: line.multiplier,\n          distribution: [{ x: isotope.mass, y: 1 }],\n        });\n        break;\n      }\n      case Kind.ISOTOPE_RATIO:\n        {\n          let element = elements[line.value.atom];\n          if (!element) throw new Error('unknown element:', line.value);\n\n          let distribution = getDistribution(\n            element.isotopes,\n            line.value.ratio,\n          );\n          result.isotopes.push({\n            atom: `${line.value.atom}{${line.value.ratio.join(',')}}`,\n            number: line.multiplier,\n            distribution,\n          });\n        }\n        break;\n      case Kind.ATOM: {\n        let element = elements[line.value];\n        if (!element) throw new Error('unknown element:', line.value);\n        result.isotopes.push({\n          atom: line.value,\n          number: line.multiplier,\n          distribution: element.isotopes.map((e) => ({\n            x: e.mass,\n            y: e.abundance,\n          })),\n        });\n        break;\n      }\n      case Kind.CHARGE:\n        result.charge += line.value;\n        break;\n      default:\n        throw new Error('partToMF unhandled Kind: ', line.kind);\n    }\n  }\n  return result;\n}\n\nfunction getDistribution(isotopesArray, ratio) {\n  let ratios = normalize(ratio);\n  let result = [];\n  if (ratios.length > isotopesArray.length) {\n    throw new Error(\n      `the number of specified ratios is bigger that the number of stable isotopes: ${isotopes}`,\n    );\n  }\n  for (let i = 0; i < ratios.length; i++) {\n    result.push({\n      x: isotopesArray[i].mass,\n      y: ratios[i],\n    });\n  }\n  return result;\n}\n\nfunction normalize(array) {\n  let sum = array.reduce((prev, current) => prev + current, 0);\n  return array.map((a) => a / sum);\n}\n","import { Kind } from '../Kind';\n\nimport { toDisplay } from './toDisplay.js';\n/**\n * Converts an array of mf elements to an array of formatting information\n * @param {Array<Object>} result of the parse method\n */\n\nexport function partsToDisplay(parts) {\n  let lines = [];\n  for (let part of parts) {\n    if (lines.length > 0) lines.push({ kind: Kind.SALT, value: '•' });\n    for (let partLine of part) {\n      lines.push(partLine);\n      if (partLine.multiplier) {\n        lines.push({\n          kind: Kind.MULTIPLIER,\n          value: partLine.multiplier,\n        });\n      }\n    }\n  }\n\n  return toDisplay(lines);\n}\n","import { partToMF } from './partToMF';\n\nexport function partsToMF(parts, options) {\n  let mf = [];\n  for (let part of parts) {\n    mf.push(partToMF(part, options));\n  }\n  return mf.join(' . ');\n}\n","/**\n * Implementation of the Hill system for sorting atoms\n * https://en.wikipedia.org/wiki/Chemical_formula#Hill_system\n * @param {string} a - first atom to compare\n * @param {string} b - second atom to compare\n * @returns\n */\n\nexport function atomSorter(a, b) {\n  if (a === b) return 0;\n  if (a === 'C') return -1;\n  if (b === 'C') return 1;\n  if (a === 'H') return -1;\n  if (b === 'H') return 1;\n  if (a < b) return -1;\n  return 1;\n}\n","import { atomSorter } from 'atom-sorter';\nimport { groupsObject } from 'chemical-groups';\n\nimport { Kind } from '../Kind';\n\n/**\n *\n * @param {*} lines\n * @param {object} [options={}]\n * @param {boolean} [options.expand=true] - Should we expand the groupsObject\n */\n\nexport function toParts(lines, options = {}) {\n  const { expand: shouldExpandgroupsObject = true } = options;\n  let parts = [];\n  let currentPart = createNewPart();\n  let previousKind = Kind.BEGIN;\n  parts.push(currentPart);\n  for (let line of lines) {\n    switch (line.kind) {\n      case Kind.ATOM:\n      case Kind.ISOTOPE_RATIO:\n      case Kind.ISOTOPE:\n      case Kind.CHARGE:\n        currentPart.lines.push({ ...line, multiplier: 1 });\n        break;\n      case Kind.OPENING_PARENTHESIS:\n        openingParenthesis(currentPart);\n        break;\n      case Kind.CLOSING_PARENTHESIS:\n        closingParenthesis(currentPart);\n        break;\n      case Kind.PRE_MULTIPLIER:\n        preMultiplier(currentPart, line);\n        break;\n      case Kind.MULTIPLIER:\n        postMultiplier(currentPart, line.value, previousKind);\n        break;\n      case Kind.SALT:\n        globalPartMultiplier(currentPart);\n        currentPart = createNewPart();\n        parts.push(currentPart);\n        break;\n      case Kind.ANCHOR: // we ignore anchors to create the parts and canonized MF\n        break;\n      case Kind.COMMENT: // we ignore comments to create the parts and canonized MF\n        break;\n      case Kind.TEXT:\n        break;\n      default:\n        throw new Error(`Can not process mf having: ${line.kind}`);\n    }\n    previousKind = line.kind;\n  }\n  globalPartMultiplier(currentPart);\n  if (shouldExpandgroupsObject) expandgroupsObject(parts);\n  return combineAtomsIsotopesCharges(parts);\n}\n\nfunction createNewPart() {\n  let currentMultiplier = { value: 1, fromIndex: 0 };\n  return { lines: [], multipliers: [currentMultiplier], currentMultiplier };\n}\n\nfunction openingParenthesis(currentPart) {\n  currentPart.currentMultiplier = {\n    value: 1,\n    fromIndex: currentPart.lines.length,\n  };\n  currentPart.multipliers.push(currentPart.currentMultiplier);\n}\n\nfunction closingParenthesis(currentPart) {\n  currentPart.currentMultiplier = currentPart.multipliers.pop();\n  if (currentPart.currentMultiplier !== 1) {\n    for (\n      let i = currentPart.currentMultiplier.fromIndex;\n      i < currentPart.lines.length;\n      i++\n    ) {\n      currentPart.lines[i].multiplier *= currentPart.currentMultiplier.value;\n    }\n  }\n}\n\nfunction preMultiplier(currentPart, line) {\n  currentPart.currentMultiplier.value *= line.value;\n}\n\nfunction globalPartMultiplier(currentPart) {\n  for (\n    let i = currentPart.multipliers[0].fromIndex;\n    i < currentPart.lines.length;\n    i++\n  ) {\n    currentPart.lines[i].multiplier *= currentPart.multipliers[0].value;\n  }\n}\n\nfunction postMultiplier(currentPart, value, previousKind) {\n  if (previousKind === Kind.CLOSING_PARENTHESIS) {\n    // need to apply to everything till the previous parenthesis\n    for (\n      let i = currentPart.currentMultiplier.fromIndex;\n      i < currentPart.lines.length;\n      i++\n    ) {\n      currentPart.lines[i].multiplier *= value;\n    }\n  } else {\n    // just applies to the previous element\n    currentPart.lines[currentPart.lines.length - 1].multiplier *= value;\n  }\n}\n\nfunction expandgroupsObject(parts) {\n  for (let part of parts) {\n    let expanded = false;\n    for (let i = 0; i < part.lines.length; i++) {\n      let line = part.lines[i];\n      if (line.kind === Kind.ATOM) {\n        let group = groupsObject[line.value];\n\n        if (group) {\n          expanded = true;\n          for (let element of group.elements) {\n            if (element.isotope) {\n              part.lines.push({\n                kind: 'isotope',\n                value: { atom: element.symbol, isotope: element.isotope },\n                multiplier: line.multiplier * element.number,\n              });\n            } else {\n              part.lines.push({\n                kind: 'atom',\n                value: element.symbol,\n                multiplier: line.multiplier * element.number,\n              });\n            }\n          }\n          part.lines[i] = undefined;\n        }\n      }\n    }\n    if (expanded) part.lines = part.lines.filter((a) => a);\n  }\n}\n\nfunction combineAtomsIsotopesCharges(parts) {\n  let results = [];\n  for (let part of parts) {\n    let result = [];\n    results.push(result);\n    calculateAndSortKeys(part);\n\n    let currentKey = '';\n    for (let key of part.keys) {\n      if (key.key === Kind.CHARGE) {\n        if (currentKey !== key.key) {\n          result.push({\n            kind: Kind.CHARGE,\n            value: key.value.value * key.value.multiplier,\n          });\n        } else {\n          result[result.length - 1].value +=\n            key.value.value * key.value.multiplier;\n        }\n      } else if (currentKey !== key.key) {\n        result.push(key.value);\n      } else {\n        result[result.length - 1].multiplier += key.value.multiplier;\n      }\n      currentKey = key.key;\n    }\n\n    result.sort((a, b) => {\n      if (a.kind === Kind.CHARGE) return 1;\n      if (b.kind === Kind.CHARGE) return -1;\n\n      let atomA = a.kind === Kind.ATOM ? a.value : a.value.atom;\n      let atomB = b.kind === Kind.ATOM ? b.value : b.value.atom;\n      if (atomA !== atomB) return atomSorter(atomA, atomB);\n      // same atome but some isotopes ...\n      if (a.kind === Kind.ATOM) return -1;\n      if (b.kind === Kind.ATOM) return 1;\n      if (a.kind === Kind.ISOTOPE) return -1;\n      if (b.kind === Kind.ISOTOPE) return 1;\n      if (a.kind === Kind.ISOTOPE_RATIO) return -1;\n      if (b.kind === Kind.ISOTOPE_RATIO) return 1;\n      return 0;\n    });\n  }\n  return results;\n}\n\nfunction calculateAndSortKeys(part) {\n  part.keys = [];\n  for (let line of part.lines) {\n    part.keys.push({ key: getKey(line), value: line });\n  }\n  part.keys.sort((a, b) => stringComparator(a.key, b.key));\n}\n\nfunction getKey(line) {\n  let key = [line.kind];\n\n  switch (line.kind) {\n    case Kind.CHARGE:\n      break;\n    default:\n      if (typeof line.value === 'string') {\n        key.push(line.value);\n      } else {\n        for (let prop of Object.keys(line.value).sort()) {\n          key.push(line.value[prop]);\n        }\n      }\n  }\n  return key.join('-');\n}\n\nfunction stringComparator(a, b) {\n  if (a < b) return -1;\n  if (a > b) return 1;\n  return 0;\n}\n","import { Format } from '../Format';\n\nimport { superscript, subscript } from './subSuperscript';\n\nexport function toText(lines) {\n  let text = [];\n  for (let line of lines) {\n    switch (line.kind) {\n      case Format.SUBSCRIPT:\n        {\n          const value = String(line.value);\n          for (let i = 0; i < value.length; i++) {\n            const char = value[i];\n            if (subscript[char]) {\n              text.push(subscript[char]);\n            } else {\n              throw new Error(`Subscript problem with: ${char}`);\n            }\n          }\n        }\n        break;\n      case Format.SUPERSCRIPT: {\n        const value = String(line.value);\n        for (let i = 0; i < value.length; i++) {\n          const char = value[i];\n          if (superscript[char]) {\n            text.push(superscript[char]);\n          } else {\n            throw new Error(`Superscript problem with: ${char}`);\n          }\n        }\n        break;\n      }\n      case Format.SUPERIMPOSE: {\n        const under = String(line.under);\n        for (let i = 0; i < under.length; i++) {\n          const char = under[i];\n          if (subscript[char]) {\n            text.push(subscript[char]);\n          } else {\n            throw new Error(`Subscript problem with: ${char}`);\n          }\n        }\n        const over = String(line.over);\n        for (let i = 0; i < over.length; i++) {\n          const char = over[i];\n          if (superscript[char]) {\n            text.push(superscript[char]);\n          } else {\n            throw new Error(`Superscript problem with: ${char}`);\n          }\n        }\n        break;\n      }\n      default:\n        text.push(line.value);\n    }\n  }\n  return text.join('');\n}\n","import { ensureCase } from './ensureCase';\nimport { parse } from './parse';\nimport { getEA } from './util/getEA';\nimport { getElements } from './util/getElements';\nimport { getInfo } from './util/getInfo';\nimport { getIsotopesInfo } from './util/getIsotopesInfo';\nimport { partsToDisplay } from './util/partsToDisplay';\nimport { partsToMF } from './util/partsToMF';\nimport { toDisplay } from './util/toDisplay';\nimport { toHtml } from './util/toHtml';\nimport { toParts } from './util/toParts';\nimport { toText } from './util/toText';\n\n/**\n * Class allowing to deal with molecular formula and derived information\n */\nexport class MF {\n  constructor(mf, options = {}) {\n    if (options.ensureCase) {\n      mf = ensureCase(mf);\n    }\n    this.parsed = parse(mf);\n    this.cache = {};\n  }\n\n  toDisplay() {\n    if (!this.cache.displayed) this.cache.displayed = toDisplay(this.parsed);\n    return this.cache.displayed;\n  }\n\n  toHtml() {\n    if (!this.cache.html) {\n      this.toDisplay();\n      this.cache.html = toHtml(this.cache.displayed);\n    }\n    return this.cache.html;\n  }\n\n  toText() {\n    if (!this.cache.text) {\n      this.toDisplay();\n      this.cache.text = toText(this.cache.displayed);\n    }\n    return this.cache.text;\n  }\n\n  toCanonicText() {\n    if (!this.cache.canonicText) {\n      this.cache.canonicText = new MF(this.toMF()).toText(this.cache.displayed);\n    }\n    return this.cache.canonicText;\n  }\n\n  toParts(options) {\n    if (!this.cache.parts) {\n      this.cache.parts = toParts(this.parsed, options);\n    }\n    return this.cache.parts;\n  }\n\n  /**\n   * Returns an object with the global MF, global charge, monoisotopic mass and mass\n   * as well as the same information for all the parts\n   * @param {object} [options={}] options\n   */\n  getInfo(options = {}) {\n    if (!this.cache.info) {\n      this.toParts();\n      this.cache.info = getInfo(this.cache.parts, options);\n    }\n    return this.cache.info;\n  }\n\n  /**\n   * Returns an object with the elemental analysis\n   */\n  getEA(options = {}) {\n    if (!this.cache.ea) {\n      this.toParts();\n      this.cache.ea = getEA(this.cache.parts, options);\n    }\n    return this.cache.ea;\n  }\n\n  /**\n   * Get the different elements for each part\n   * @returns an array\n   */\n  getElements() {\n    if (!this.cache.elements) {\n      this.toParts();\n      this.cache.elements = getElements(this.cache.parts);\n    }\n    return this.cache.elements;\n  }\n\n  /**\n   * Returns an array with each atom and isotopic composition\n   */\n  getIsotopesInfo(options = {}) {\n    if (!this.cache.isotopesInfo) {\n      this.toParts();\n      this.cache.isotopesInfo = getIsotopesInfo(this.cache.parts, options);\n    }\n    return this.cache.isotopesInfo;\n  }\n\n  /**\n   * Get a canonized MF\n   */\n  toMF() {\n    if (!this.cache.mf) {\n      this.toParts();\n      this.cache.mf = partsToMF(this.cache.parts);\n    }\n    return this.cache.mf;\n  }\n\n  /**\n   * Get a canonized MF\n   */\n  toNeutralMF() {\n    if (!this.cache.neutralMF) {\n      this.toParts();\n      this.cache.neutralMF = partsToMF(this.cache.parts, { neutral: true });\n    }\n    return this.cache.neutralMF;\n  }\n\n  canonize() {\n    this.toParts();\n    this.cache.displayed = partsToDisplay(this.cache.parts);\n    this.cache.html = undefined;\n  }\n}\n","import {\n  xyObjectMaxXPoint,\n  xyObjectMinXPoint,\n  xyObjectSumY,\n} from 'ml-spectra-processing';\n\n/**\n * Filter the array of peaks\n * @param {array} peaks - array of all the peaks\n * @param {object} [options={}]\n * @param {number} [options.from] - min X value of the window to consider\n * @param {number} [options.to] - max X value of the window to consider\n * @param {number} [options.threshold=0.01] - minimal intensity compare to base peak\n * @param {number} [options.limit=undefined] - maximal number of peaks (based on intensity)\n * @param {number} [options.sumValue] // if sumValue is defined, maxValue is ignored\n * @returns {array} - copy of peaks with 'close' annotation\n */\n\nexport function getPeaks(peaks, options = {}) {\n  const {\n    from = xyObjectMinXPoint(peaks).x,\n    to = xyObjectMaxXPoint(peaks).x,\n    threshold = 0.01,\n    limit,\n    sumValue,\n  } = options;\n\n  let maxY = Number.MIN_SAFE_INTEGER;\n  for (let peak of peaks) {\n    if (peak.y > maxY) maxY = peak.y;\n  }\n  let minY = maxY * threshold;\n\n  peaks = peaks.filter(\n    (peak) => peak.x >= from && peak.x <= to && peak.y >= minY,\n  );\n\n  if (limit && peaks.length > limit) {\n    peaks.sort((a, b) => b.y - a.y);\n    peaks = peaks.slice(0, limit);\n  }\n  if (sumValue) {\n    peaks = JSON.parse(JSON.stringify(peaks));\n    const currentSum = xyObjectSumY(peaks);\n    const ratio = sumValue / currentSum;\n    peaks.forEach((peak) => (peak.y *= ratio));\n  }\n\n  return peaks.sort((a, b) => a.x - b.x);\n}\n","import { EMDB } from 'emdb';\nimport { MF } from 'mf-parser';\n\nimport { getPeaks } from './getPeaks.js';\n\n/**\n * Filter the array of peaks\n * @param {array} peaks - array of all the peaks\n * @param {string} mf - Molecular formula of the parent molecule\n * @param {object} [options={}]\n * @param {number} [options.from] - min X value of the window to consider\n * @param {number} [options.to] - max X value of the window to consider\n * @param {number} [options.threshold=0.01] - minimal intensity compare to base peak\n * @param {number} [options.limit=undefined] - maximal number of peaks (based on intensity)\n * @param {string} [options.ionizations]\n * @param {number} [options.precision]\n * @returns {array} - copy of peaks with 'close' annotation\n */\n\nexport async function getFragmentPeaks(peaks, mf, options = {}) {\n  const emdb = new EMDB();\n\n  const { ionizations = '', precision } = options;\n\n  const mfInfo = new MF(mf).getInfo();\n  const ranges = Object.keys(mfInfo.atoms)\n    .map((key) => `${key}0-${mfInfo.atoms[key]}`)\n    .join(' ');\n  peaks = getPeaks(peaks, options);\n  for (let peak of peaks) {\n    peak.mfs = (\n      await emdb.fromMonoisotopicMass(peak.x, {\n        precision,\n        ranges,\n        ionizations,\n      })\n    ).mfs;\n  }\n  peaks = peaks.filter((peak) => peak.mfs.length > 0);\n  return peaks;\n}\n","import { xySortX, xyJoinX } from 'ml-spectra-processing';\n\n/**\n * Remove an integer number of time the specifiedd monoisotopic mass\n * Mass remainder analysis (MARA): https://doi.org/10.1021/acs.analchem.7b04730\n * @param {object} spectrum\n * @param {number} mass\n * @param {object} [options={}\n * @param {number} [options.delta=0.001]\n */\nexport function getMassRemainder(spectrum, mass, options = {}) {\n  const { delta = 0.001 } = options;\n  const x = spectrum.x.slice();\n  const y = spectrum.y;\n  for (let i = 0; i < x.length; i++) {\n    const factor = Math.floor(x[i] / mass);\n    x[i] = x[i] - factor * mass;\n  }\n  // we sort and join\n  return xyJoinX(xySortX({ x, y }), { delta });\n}\n","export function calculateOverlapFromDiff(diffs) {\n    if (diffs[1].length === 0)\n        return 0;\n    let sumPos = 0;\n    for (let i = 0; i < diffs[1].length; i++) {\n        sumPos += Math.abs(diffs[1][i]);\n    }\n    return 1 - sumPos;\n}\n//# sourceMappingURL=calculateOverlapFromDiff.js.map","/**\n * This code requires the use of an array like  [[x1,y1],[x2,y2], ...]\n * If it is not the right format, we will just convert it\n * Otherwise we return the correct format\n * @param {Peaks} peaks\n * @returns [number[], number[]]\n */\nexport function checkPeaks(peaks) {\n    // if it is already a 2D array of points, we just return them\n    if (Array.isArray(peaks) && Array.isArray(peaks[0]) && peaks.length === 2) {\n        return peaks;\n    }\n    if (Array.isArray(peaks.x) && Array.isArray(peaks.y)) {\n        return [peaks.x, peaks.y];\n    }\n    const x = new Array(peaks.length);\n    const y = new Array(peaks.length);\n    for (let i = 0; i < peaks.length; i++) {\n        x[i] = peaks[i][0];\n        y[i] = peaks[i][1];\n    }\n    return [x, y];\n}\n//# sourceMappingURL=checkPeaks.js.map","export function extract(array, from, to) {\n    const newArray = [[], []];\n    let j = 0;\n    const length = array[0] ? array[0].length : 0;\n    for (let i = 0; i < length; i++) {\n        if ((!from || array[0][i] >= from) && (!to || array[0][i] <= to)) {\n            newArray[0][j] = array[0][i];\n            newArray[1][j] = array[1][i];\n            j++;\n        }\n    }\n    return newArray;\n}\n//# sourceMappingURL=extract.js.map","// returns an new array based on array1 where there is a peak of array2 at a distance under width/2\nexport function getCommonArray(array1, array2, width) {\n    const newArray = [[], []];\n    let pos2 = 0;\n    width /= 2;\n    let j = 0;\n    const array1Length = array1[0] ? array1[0].length : 0;\n    const array2Length = array2[0] ? array2[0].length : 0;\n    for (let i = 0; i < array1Length; i++) {\n        while (pos2 < array2Length && array1[0][i] > array2[0][pos2] + width) {\n            pos2++;\n        }\n        if (pos2 < array2Length && array1[0][i] > array2[0][pos2] - width) {\n            newArray[0][j] = array1[0][i];\n            newArray[1][j] = array1[1][i];\n            j++;\n        }\n    }\n    return newArray;\n}\n//# sourceMappingURL=getCommonArray.js.map","'use strict';\n\nfunction compareNumbers(a, b) {\n    return a - b;\n}\n\n/**\n * Computes the sum of the given values\n * @param {Array} values\n * @returns {number}\n */\nexports.sum = function sum(values) {\n    var sum = 0;\n    for (var i = 0; i < values.length; i++) {\n        sum += values[i];\n    }\n    return sum;\n};\n\n/**\n * Computes the maximum of the given values\n * @param {Array} values\n * @returns {number}\n */\nexports.max = function max(values) {\n    var max = values[0];\n    var l = values.length;\n    for (var i = 1; i < l; i++) {\n        if (values[i] > max) max = values[i];\n    }\n    return max;\n};\n\n/**\n * Computes the minimum of the given values\n * @param {Array} values\n * @returns {number}\n */\nexports.min = function min(values) {\n    var min = values[0];\n    var l = values.length;\n    for (var i = 1; i < l; i++) {\n        if (values[i] < min) min = values[i];\n    }\n    return min;\n};\n\n/**\n * Computes the min and max of the given values\n * @param {Array} values\n * @returns {{min: number, max: number}}\n */\nexports.minMax = function minMax(values) {\n    var min = values[0];\n    var max = values[0];\n    var l = values.length;\n    for (var i = 1; i < l; i++) {\n        if (values[i] < min) min = values[i];\n        if (values[i] > max) max = values[i];\n    }\n    return {\n        min: min,\n        max: max\n    };\n};\n\n/**\n * Computes the arithmetic mean of the given values\n * @param {Array} values\n * @returns {number}\n */\nexports.arithmeticMean = function arithmeticMean(values) {\n    var sum = 0;\n    var l = values.length;\n    for (var i = 0; i < l; i++) {\n        sum += values[i];\n    }\n    return sum / l;\n};\n\n/**\n * {@link arithmeticMean}\n */\nexports.mean = exports.arithmeticMean;\n\n/**\n * Computes the geometric mean of the given values\n * @param {Array} values\n * @returns {number}\n */\nexports.geometricMean = function geometricMean(values) {\n    var mul = 1;\n    var l = values.length;\n    for (var i = 0; i < l; i++) {\n        mul *= values[i];\n    }\n    return Math.pow(mul, 1 / l);\n};\n\n/**\n * Computes the mean of the log of the given values\n * If the return value is exponentiated, it gives the same result as the\n * geometric mean.\n * @param {Array} values\n * @returns {number}\n */\nexports.logMean = function logMean(values) {\n    var lnsum = 0;\n    var l = values.length;\n    for (var i = 0; i < l; i++) {\n        lnsum += Math.log(values[i]);\n    }\n    return lnsum / l;\n};\n\n/**\n * Computes the weighted grand mean for a list of means and sample sizes\n * @param {Array} means - Mean values for each set of samples\n * @param {Array} samples - Number of original values for each set of samples\n * @returns {number}\n */\nexports.grandMean = function grandMean(means, samples) {\n    var sum = 0;\n    var n = 0;\n    var l = means.length;\n    for (var i = 0; i < l; i++) {\n        sum += samples[i] * means[i];\n        n += samples[i];\n    }\n    return sum / n;\n};\n\n/**\n * Computes the truncated mean of the given values using a given percentage\n * @param {Array} values\n * @param {number} percent - The percentage of values to keep (range: [0,1])\n * @param {boolean} [alreadySorted=false]\n * @returns {number}\n */\nexports.truncatedMean = function truncatedMean(values, percent, alreadySorted) {\n    if (alreadySorted === undefined) alreadySorted = false;\n    if (!alreadySorted) {\n        values = [].concat(values).sort(compareNumbers);\n    }\n    var l = values.length;\n    var k = Math.floor(l * percent);\n    var sum = 0;\n    for (var i = k; i < (l - k); i++) {\n        sum += values[i];\n    }\n    return sum / (l - 2 * k);\n};\n\n/**\n * Computes the harmonic mean of the given values\n * @param {Array} values\n * @returns {number}\n */\nexports.harmonicMean = function harmonicMean(values) {\n    var sum = 0;\n    var l = values.length;\n    for (var i = 0; i < l; i++) {\n        if (values[i] === 0) {\n            throw new RangeError('value at index ' + i + 'is zero');\n        }\n        sum += 1 / values[i];\n    }\n    return l / sum;\n};\n\n/**\n * Computes the contraharmonic mean of the given values\n * @param {Array} values\n * @returns {number}\n */\nexports.contraHarmonicMean = function contraHarmonicMean(values) {\n    var r1 = 0;\n    var r2 = 0;\n    var l = values.length;\n    for (var i = 0; i < l; i++) {\n        r1 += values[i] * values[i];\n        r2 += values[i];\n    }\n    if (r2 < 0) {\n        throw new RangeError('sum of values is negative');\n    }\n    return r1 / r2;\n};\n\n/**\n * Computes the median of the given values\n * @param {Array} values\n * @param {boolean} [alreadySorted=false]\n * @returns {number}\n */\nexports.median = function median(values, alreadySorted) {\n    if (alreadySorted === undefined) alreadySorted = false;\n    if (!alreadySorted) {\n        values = [].concat(values).sort(compareNumbers);\n    }\n    var l = values.length;\n    var half = Math.floor(l / 2);\n    if (l % 2 === 0) {\n        return (values[half - 1] + values[half]) * 0.5;\n    } else {\n        return values[half];\n    }\n};\n\n/**\n * Computes the variance of the given values\n * @param {Array} values\n * @param {boolean} [unbiased=true] - if true, divide by (n-1); if false, divide by n.\n * @returns {number}\n */\nexports.variance = function variance(values, unbiased) {\n    if (unbiased === undefined) unbiased = true;\n    var theMean = exports.mean(values);\n    var theVariance = 0;\n    var l = values.length;\n\n    for (var i = 0; i < l; i++) {\n        var x = values[i] - theMean;\n        theVariance += x * x;\n    }\n\n    if (unbiased) {\n        return theVariance / (l - 1);\n    } else {\n        return theVariance / l;\n    }\n};\n\n/**\n * Computes the standard deviation of the given values\n * @param {Array} values\n * @param {boolean} [unbiased=true] - if true, divide by (n-1); if false, divide by n.\n * @returns {number}\n */\nexports.standardDeviation = function standardDeviation(values, unbiased) {\n    return Math.sqrt(exports.variance(values, unbiased));\n};\n\nexports.standardError = function standardError(values) {\n    return exports.standardDeviation(values) / Math.sqrt(values.length);\n};\n\n/**\n * IEEE Transactions on biomedical engineering, vol. 52, no. 1, january 2005, p. 76-\n * Calculate the standard deviation via the Median of the absolute deviation\n *  The formula for the standard deviation only holds for Gaussian random variables.\n * @returns {{mean: number, stdev: number}}\n */\nexports.robustMeanAndStdev = function robustMeanAndStdev(y) {\n    var mean = 0, stdev = 0;\n    var length = y.length, i = 0;\n    for (i = 0; i < length; i++) {\n        mean += y[i];\n    }\n    mean /= length;\n    var averageDeviations = new Array(length);\n    for (i = 0; i < length; i++)\n        averageDeviations[i] = Math.abs(y[i] - mean);\n    averageDeviations.sort(compareNumbers);\n    if (length % 2 === 1) {\n        stdev = averageDeviations[(length - 1) / 2] / 0.6745;\n    } else {\n        stdev = 0.5 * (averageDeviations[length / 2] + averageDeviations[length / 2 - 1]) / 0.6745;\n    }\n\n    return {\n        mean: mean,\n        stdev: stdev\n    };\n};\n\nexports.quartiles = function quartiles(values, alreadySorted) {\n    if (typeof (alreadySorted) === 'undefined') alreadySorted = false;\n    if (!alreadySorted) {\n        values = [].concat(values).sort(compareNumbers);\n    }\n\n    var quart = values.length / 4;\n    var q1 = values[Math.ceil(quart) - 1];\n    var q2 = exports.median(values, true);\n    var q3 = values[Math.ceil(quart * 3) - 1];\n\n    return {q1: q1, q2: q2, q3: q3};\n};\n\nexports.pooledStandardDeviation = function pooledStandardDeviation(samples, unbiased) {\n    return Math.sqrt(exports.pooledVariance(samples, unbiased));\n};\n\nexports.pooledVariance = function pooledVariance(samples, unbiased) {\n    if (typeof (unbiased) === 'undefined') unbiased = true;\n    var sum = 0;\n    var length = 0, l = samples.length;\n    for (var i = 0; i < l; i++) {\n        var values = samples[i];\n        var vari = exports.variance(values);\n\n        sum += (values.length - 1) * vari;\n\n        if (unbiased)\n            length += values.length - 1;\n        else\n            length += values.length;\n    }\n    return sum / length;\n};\n\nexports.mode = function mode(values) {\n    var l = values.length,\n        itemCount = new Array(l),\n        i;\n    for (i = 0; i < l; i++) {\n        itemCount[i] = 0;\n    }\n    var itemArray = new Array(l);\n    var count = 0;\n\n    for (i = 0; i < l; i++) {\n        var index = itemArray.indexOf(values[i]);\n        if (index >= 0)\n            itemCount[index]++;\n        else {\n            itemArray[count] = values[i];\n            itemCount[count] = 1;\n            count++;\n        }\n    }\n\n    var maxValue = 0, maxIndex = 0;\n    for (i = 0; i < count; i++) {\n        if (itemCount[i] > maxValue) {\n            maxValue = itemCount[i];\n            maxIndex = i;\n        }\n    }\n\n    return itemArray[maxIndex];\n};\n\nexports.covariance = function covariance(vector1, vector2, unbiased) {\n    if (typeof (unbiased) === 'undefined') unbiased = true;\n    var mean1 = exports.mean(vector1);\n    var mean2 = exports.mean(vector2);\n\n    if (vector1.length !== vector2.length)\n        throw 'Vectors do not have the same dimensions';\n\n    var cov = 0, l = vector1.length;\n    for (var i = 0; i < l; i++) {\n        var x = vector1[i] - mean1;\n        var y = vector2[i] - mean2;\n        cov += x * y;\n    }\n\n    if (unbiased)\n        return cov / (l - 1);\n    else\n        return cov / l;\n};\n\nexports.skewness = function skewness(values, unbiased) {\n    if (typeof (unbiased) === 'undefined') unbiased = true;\n    var theMean = exports.mean(values);\n\n    var s2 = 0, s3 = 0, l = values.length;\n    for (var i = 0; i < l; i++) {\n        var dev = values[i] - theMean;\n        s2 += dev * dev;\n        s3 += dev * dev * dev;\n    }\n    var m2 = s2 / l;\n    var m3 = s3 / l;\n\n    var g = m3 / (Math.pow(m2, 3 / 2.0));\n    if (unbiased) {\n        var a = Math.sqrt(l * (l - 1));\n        var b = l - 2;\n        return (a / b) * g;\n    } else {\n        return g;\n    }\n};\n\nexports.kurtosis = function kurtosis(values, unbiased) {\n    if (typeof (unbiased) === 'undefined') unbiased = true;\n    var theMean = exports.mean(values);\n    var n = values.length, s2 = 0, s4 = 0;\n\n    for (var i = 0; i < n; i++) {\n        var dev = values[i] - theMean;\n        s2 += dev * dev;\n        s4 += dev * dev * dev * dev;\n    }\n    var m2 = s2 / n;\n    var m4 = s4 / n;\n\n    if (unbiased) {\n        var v = s2 / (n - 1);\n        var a = (n * (n + 1)) / ((n - 1) * (n - 2) * (n - 3));\n        var b = s4 / (v * v);\n        var c = ((n - 1) * (n - 1)) / ((n - 2) * (n - 3));\n\n        return a * b - 3 * c;\n    } else {\n        return m4 / (m2 * m2) - 3;\n    }\n};\n\nexports.entropy = function entropy(values, eps) {\n    if (typeof (eps) === 'undefined') eps = 0;\n    var sum = 0, l = values.length;\n    for (var i = 0; i < l; i++)\n        sum += values[i] * Math.log(values[i] + eps);\n    return -sum;\n};\n\nexports.weightedMean = function weightedMean(values, weights) {\n    var sum = 0, l = values.length;\n    for (var i = 0; i < l; i++)\n        sum += values[i] * weights[i];\n    return sum;\n};\n\nexports.weightedStandardDeviation = function weightedStandardDeviation(values, weights) {\n    return Math.sqrt(exports.weightedVariance(values, weights));\n};\n\nexports.weightedVariance = function weightedVariance(values, weights) {\n    var theMean = exports.weightedMean(values, weights);\n    var vari = 0, l = values.length;\n    var a = 0, b = 0;\n\n    for (var i = 0; i < l; i++) {\n        var z = values[i] - theMean;\n        var w = weights[i];\n\n        vari += w * (z * z);\n        b += w;\n        a += w * w;\n    }\n\n    return vari * (b / (b * b - a));\n};\n\nexports.center = function center(values, inPlace) {\n    if (typeof (inPlace) === 'undefined') inPlace = false;\n\n    var result = values;\n    if (!inPlace)\n        result = [].concat(values);\n\n    var theMean = exports.mean(result), l = result.length;\n    for (var i = 0; i < l; i++)\n        result[i] -= theMean;\n};\n\nexports.standardize = function standardize(values, standardDev, inPlace) {\n    if (typeof (standardDev) === 'undefined') standardDev = exports.standardDeviation(values);\n    if (typeof (inPlace) === 'undefined') inPlace = false;\n    var l = values.length;\n    var result = inPlace ? values : new Array(l);\n    for (var i = 0; i < l; i++)\n        result[i] = values[i] / standardDev;\n    return result;\n};\n\nexports.cumulativeSum = function cumulativeSum(array) {\n    var l = array.length;\n    var result = new Array(l);\n    result[0] = array[0];\n    for (var i = 1; i < l; i++)\n        result[i] = result[i - 1] + array[i];\n    return result;\n};\n","'use strict';\n\nvar arrayStat = require('./array');\n\nfunction compareNumbers(a, b) {\n    return a - b;\n}\n\nexports.max = function max(matrix) {\n    var max = -Infinity;\n    for (var i = 0; i < matrix.length; i++) {\n        for (var j = 0; j < matrix[i].length; j++) {\n            if (matrix[i][j] > max) max = matrix[i][j];\n        }\n    }\n    return max;\n};\n\nexports.min = function min(matrix) {\n    var min = Infinity;\n    for (var i = 0; i < matrix.length; i++) {\n        for (var j = 0; j < matrix[i].length; j++) {\n            if (matrix[i][j] < min) min = matrix[i][j];\n        }\n    }\n    return min;\n};\n\nexports.minMax = function minMax(matrix) {\n    var min = Infinity;\n    var max = -Infinity;\n    for (var i = 0; i < matrix.length; i++) {\n        for (var j = 0; j < matrix[i].length; j++) {\n            if (matrix[i][j] < min) min = matrix[i][j];\n            if (matrix[i][j] > max) max = matrix[i][j];\n        }\n    }\n    return {\n        min:min,\n        max:max\n    };\n};\n\nexports.entropy = function entropy(matrix, eps) {\n    if (typeof (eps) === 'undefined') {\n        eps = 0;\n    }\n    var sum = 0,\n        l1 = matrix.length,\n        l2 = matrix[0].length;\n    for (var i = 0; i < l1; i++) {\n        for (var j = 0; j < l2; j++) {\n            sum += matrix[i][j] * Math.log(matrix[i][j] + eps);\n        }\n    }\n    return -sum;\n};\n\nexports.mean = function mean(matrix, dimension) {\n    if (typeof (dimension) === 'undefined') {\n        dimension = 0;\n    }\n    var rows = matrix.length,\n        cols = matrix[0].length,\n        theMean, N, i, j;\n\n    if (dimension === -1) {\n        theMean = [0];\n        N = rows * cols;\n        for (i = 0; i < rows; i++) {\n            for (j = 0; j < cols; j++) {\n                theMean[0] += matrix[i][j];\n            }\n        }\n        theMean[0] /= N;\n    } else if (dimension === 0) {\n        theMean = new Array(cols);\n        N = rows;\n        for (j = 0; j < cols; j++) {\n            theMean[j] = 0;\n            for (i = 0; i < rows; i++) {\n                theMean[j] += matrix[i][j];\n            }\n            theMean[j] /= N;\n        }\n    } else if (dimension === 1) {\n        theMean = new Array(rows);\n        N = cols;\n        for (j = 0; j < rows; j++) {\n            theMean[j] = 0;\n            for (i = 0; i < cols; i++) {\n                theMean[j] += matrix[j][i];\n            }\n            theMean[j] /= N;\n        }\n    } else {\n        throw new Error('Invalid dimension');\n    }\n    return theMean;\n};\n\nexports.sum = function sum(matrix, dimension) {\n    if (typeof (dimension) === 'undefined') {\n        dimension = 0;\n    }\n    var rows = matrix.length,\n        cols = matrix[0].length,\n        theSum, i, j;\n\n    if (dimension === -1) {\n        theSum = [0];\n        for (i = 0; i < rows; i++) {\n            for (j = 0; j < cols; j++) {\n                theSum[0] += matrix[i][j];\n            }\n        }\n    } else if (dimension === 0) {\n        theSum = new Array(cols);\n        for (j = 0; j < cols; j++) {\n            theSum[j] = 0;\n            for (i = 0; i < rows; i++) {\n                theSum[j] += matrix[i][j];\n            }\n        }\n    } else if (dimension === 1) {\n        theSum = new Array(rows);\n        for (j = 0; j < rows; j++) {\n            theSum[j] = 0;\n            for (i = 0; i < cols; i++) {\n                theSum[j] += matrix[j][i];\n            }\n        }\n    } else {\n        throw new Error('Invalid dimension');\n    }\n    return theSum;\n};\n\nexports.product = function product(matrix, dimension) {\n    if (typeof (dimension) === 'undefined') {\n        dimension = 0;\n    }\n    var rows = matrix.length,\n        cols = matrix[0].length,\n        theProduct, i, j;\n\n    if (dimension === -1) {\n        theProduct = [1];\n        for (i = 0; i < rows; i++) {\n            for (j = 0; j < cols; j++) {\n                theProduct[0] *= matrix[i][j];\n            }\n        }\n    } else if (dimension === 0) {\n        theProduct = new Array(cols);\n        for (j = 0; j < cols; j++) {\n            theProduct[j] = 1;\n            for (i = 0; i < rows; i++) {\n                theProduct[j] *= matrix[i][j];\n            }\n        }\n    } else if (dimension === 1) {\n        theProduct = new Array(rows);\n        for (j = 0; j < rows; j++) {\n            theProduct[j] = 1;\n            for (i = 0; i < cols; i++) {\n                theProduct[j] *= matrix[j][i];\n            }\n        }\n    } else {\n        throw new Error('Invalid dimension');\n    }\n    return theProduct;\n};\n\nexports.standardDeviation = function standardDeviation(matrix, means, unbiased) {\n    var vari = exports.variance(matrix, means, unbiased), l = vari.length;\n    for (var i = 0; i < l; i++) {\n        vari[i] = Math.sqrt(vari[i]);\n    }\n    return vari;\n};\n\nexports.variance = function variance(matrix, means, unbiased) {\n    if (typeof (unbiased) === 'undefined') {\n        unbiased = true;\n    }\n    means = means || exports.mean(matrix);\n    var rows = matrix.length;\n    if (rows === 0) return [];\n    var cols = matrix[0].length;\n    var vari = new Array(cols);\n\n    for (var j = 0; j < cols; j++) {\n        var sum1 = 0, sum2 = 0, x = 0;\n        for (var i = 0; i < rows; i++) {\n            x = matrix[i][j] - means[j];\n            sum1 += x;\n            sum2 += x * x;\n        }\n        if (unbiased) {\n            vari[j] = (sum2 - ((sum1 * sum1) / rows)) / (rows - 1);\n        } else {\n            vari[j] = (sum2 - ((sum1 * sum1) / rows)) / rows;\n        }\n    }\n    return vari;\n};\n\nexports.median = function median(matrix) {\n    var rows = matrix.length, cols = matrix[0].length;\n    var medians = new Array(cols);\n\n    for (var i = 0; i < cols; i++) {\n        var data = new Array(rows);\n        for (var j = 0; j < rows; j++) {\n            data[j] = matrix[j][i];\n        }\n        data.sort(compareNumbers);\n        var N = data.length;\n        if (N % 2 === 0) {\n            medians[i] = (data[N / 2] + data[(N / 2) - 1]) * 0.5;\n        } else {\n            medians[i] = data[Math.floor(N / 2)];\n        }\n    }\n    return medians;\n};\n\nexports.mode = function mode(matrix) {\n    var rows = matrix.length,\n        cols = matrix[0].length,\n        modes = new Array(cols),\n        i, j;\n    for (i = 0; i < cols; i++) {\n        var itemCount = new Array(rows);\n        for (var k = 0; k < rows; k++) {\n            itemCount[k] = 0;\n        }\n        var itemArray = new Array(rows);\n        var count = 0;\n\n        for (j = 0; j < rows; j++) {\n            var index = itemArray.indexOf(matrix[j][i]);\n            if (index >= 0) {\n                itemCount[index]++;\n            } else {\n                itemArray[count] = matrix[j][i];\n                itemCount[count] = 1;\n                count++;\n            }\n        }\n\n        var maxValue = 0, maxIndex = 0;\n        for (j = 0; j < count; j++) {\n            if (itemCount[j] > maxValue) {\n                maxValue = itemCount[j];\n                maxIndex = j;\n            }\n        }\n\n        modes[i] = itemArray[maxIndex];\n    }\n    return modes;\n};\n\nexports.skewness = function skewness(matrix, unbiased) {\n    if (typeof (unbiased) === 'undefined') unbiased = true;\n    var means = exports.mean(matrix);\n    var n = matrix.length, l = means.length;\n    var skew = new Array(l);\n\n    for (var j = 0; j < l; j++) {\n        var s2 = 0, s3 = 0;\n        for (var i = 0; i < n; i++) {\n            var dev = matrix[i][j] - means[j];\n            s2 += dev * dev;\n            s3 += dev * dev * dev;\n        }\n\n        var m2 = s2 / n;\n        var m3 = s3 / n;\n        var g = m3 / Math.pow(m2, 3 / 2);\n\n        if (unbiased) {\n            var a = Math.sqrt(n * (n - 1));\n            var b = n - 2;\n            skew[j] = (a / b) * g;\n        } else {\n            skew[j] = g;\n        }\n    }\n    return skew;\n};\n\nexports.kurtosis = function kurtosis(matrix, unbiased) {\n    if (typeof (unbiased) === 'undefined') unbiased = true;\n    var means = exports.mean(matrix);\n    var n = matrix.length, m = matrix[0].length;\n    var kurt = new Array(m);\n\n    for (var j = 0; j < m; j++) {\n        var s2 = 0, s4 = 0;\n        for (var i = 0; i < n; i++) {\n            var dev = matrix[i][j] - means[j];\n            s2 += dev * dev;\n            s4 += dev * dev * dev * dev;\n        }\n        var m2 = s2 / n;\n        var m4 = s4 / n;\n\n        if (unbiased) {\n            var v = s2 / (n - 1);\n            var a = (n * (n + 1)) / ((n - 1) * (n - 2) * (n - 3));\n            var b = s4 / (v * v);\n            var c = ((n - 1) * (n - 1)) / ((n - 2) * (n - 3));\n            kurt[j] = a * b - 3 * c;\n        } else {\n            kurt[j] = m4 / (m2 * m2) - 3;\n        }\n    }\n    return kurt;\n};\n\nexports.standardError = function standardError(matrix) {\n    var samples = matrix.length;\n    var standardDeviations = exports.standardDeviation(matrix);\n    var l = standardDeviations.length;\n    var standardErrors = new Array(l);\n    var sqrtN = Math.sqrt(samples);\n\n    for (var i = 0; i < l; i++) {\n        standardErrors[i] = standardDeviations[i] / sqrtN;\n    }\n    return standardErrors;\n};\n\nexports.covariance = function covariance(matrix, dimension) {\n    return exports.scatter(matrix, undefined, dimension);\n};\n\nexports.scatter = function scatter(matrix, divisor, dimension) {\n    if (typeof (dimension) === 'undefined') {\n        dimension = 0;\n    }\n    if (typeof (divisor) === 'undefined') {\n        if (dimension === 0) {\n            divisor = matrix.length - 1;\n        } else if (dimension === 1) {\n            divisor = matrix[0].length - 1;\n        }\n    }\n    var means = exports.mean(matrix, dimension);\n    var rows = matrix.length;\n    if (rows === 0) {\n        return [[]];\n    }\n    var cols = matrix[0].length,\n        cov, i, j, s, k;\n\n    if (dimension === 0) {\n        cov = new Array(cols);\n        for (i = 0; i < cols; i++) {\n            cov[i] = new Array(cols);\n        }\n        for (i = 0; i < cols; i++) {\n            for (j = i; j < cols; j++) {\n                s = 0;\n                for (k = 0; k < rows; k++) {\n                    s += (matrix[k][j] - means[j]) * (matrix[k][i] - means[i]);\n                }\n                s /= divisor;\n                cov[i][j] = s;\n                cov[j][i] = s;\n            }\n        }\n    } else if (dimension === 1) {\n        cov = new Array(rows);\n        for (i = 0; i < rows; i++) {\n            cov[i] = new Array(rows);\n        }\n        for (i = 0; i < rows; i++) {\n            for (j = i; j < rows; j++) {\n                s = 0;\n                for (k = 0; k < cols; k++) {\n                    s += (matrix[j][k] - means[j]) * (matrix[i][k] - means[i]);\n                }\n                s /= divisor;\n                cov[i][j] = s;\n                cov[j][i] = s;\n            }\n        }\n    } else {\n        throw new Error('Invalid dimension');\n    }\n\n    return cov;\n};\n\nexports.correlation = function correlation(matrix) {\n    var means = exports.mean(matrix),\n        standardDeviations = exports.standardDeviation(matrix, true, means),\n        scores = exports.zScores(matrix, means, standardDeviations),\n        rows = matrix.length,\n        cols = matrix[0].length,\n        i, j;\n\n    var cor = new Array(cols);\n    for (i = 0; i < cols; i++) {\n        cor[i] = new Array(cols);\n    }\n    for (i = 0; i < cols; i++) {\n        for (j = i; j < cols; j++) {\n            var c = 0;\n            for (var k = 0, l = scores.length; k < l; k++) {\n                c += scores[k][j] * scores[k][i];\n            }\n            c /= rows - 1;\n            cor[i][j] = c;\n            cor[j][i] = c;\n        }\n    }\n    return cor;\n};\n\nexports.zScores = function zScores(matrix, means, standardDeviations) {\n    means = means || exports.mean(matrix);\n    if (typeof (standardDeviations) === 'undefined') standardDeviations = exports.standardDeviation(matrix, true, means);\n    return exports.standardize(exports.center(matrix, means, false), standardDeviations, true);\n};\n\nexports.center = function center(matrix, means, inPlace) {\n    means = means || exports.mean(matrix);\n    var result = matrix,\n        l = matrix.length,\n        i, j, jj;\n\n    if (!inPlace) {\n        result = new Array(l);\n        for (i = 0; i < l; i++) {\n            result[i] = new Array(matrix[i].length);\n        }\n    }\n\n    for (i = 0; i < l; i++) {\n        var row = result[i];\n        for (j = 0, jj = row.length; j < jj; j++) {\n            row[j] = matrix[i][j] - means[j];\n        }\n    }\n    return result;\n};\n\nexports.standardize = function standardize(matrix, standardDeviations, inPlace) {\n    if (typeof (standardDeviations) === 'undefined') standardDeviations = exports.standardDeviation(matrix);\n    var result = matrix,\n        l = matrix.length,\n        i, j, jj;\n\n    if (!inPlace) {\n        result = new Array(l);\n        for (i = 0; i < l; i++) {\n            result[i] = new Array(matrix[i].length);\n        }\n    }\n\n    for (i = 0; i < l; i++) {\n        var resultRow = result[i];\n        var sourceRow = matrix[i];\n        for (j = 0, jj = resultRow.length; j < jj; j++) {\n            if (standardDeviations[j] !== 0 && !isNaN(standardDeviations[j])) {\n                resultRow[j] = sourceRow[j] / standardDeviations[j];\n            }\n        }\n    }\n    return result;\n};\n\nexports.weightedVariance = function weightedVariance(matrix, weights) {\n    var means = exports.mean(matrix);\n    var rows = matrix.length;\n    if (rows === 0) return [];\n    var cols = matrix[0].length;\n    var vari = new Array(cols);\n\n    for (var j = 0; j < cols; j++) {\n        var sum = 0;\n        var a = 0, b = 0;\n\n        for (var i = 0; i < rows; i++) {\n            var z = matrix[i][j] - means[j];\n            var w = weights[i];\n\n            sum += w * (z * z);\n            b += w;\n            a += w * w;\n        }\n\n        vari[j] = sum * (b / (b * b - a));\n    }\n\n    return vari;\n};\n\nexports.weightedMean = function weightedMean(matrix, weights, dimension) {\n    if (typeof (dimension) === 'undefined') {\n        dimension = 0;\n    }\n    var rows = matrix.length;\n    if (rows === 0) return [];\n    var cols = matrix[0].length,\n        means, i, ii, j, w, row;\n\n    if (dimension === 0) {\n        means = new Array(cols);\n        for (i = 0; i < cols; i++) {\n            means[i] = 0;\n        }\n        for (i = 0; i < rows; i++) {\n            row = matrix[i];\n            w = weights[i];\n            for (j = 0; j < cols; j++) {\n                means[j] += row[j] * w;\n            }\n        }\n    } else if (dimension === 1) {\n        means = new Array(rows);\n        for (i = 0; i < rows; i++) {\n            means[i] = 0;\n        }\n        for (j = 0; j < rows; j++) {\n            row = matrix[j];\n            w = weights[j];\n            for (i = 0; i < cols; i++) {\n                means[j] += row[i] * w;\n            }\n        }\n    } else {\n        throw new Error('Invalid dimension');\n    }\n\n    var weightSum = arrayStat.sum(weights);\n    if (weightSum !== 0) {\n        for (i = 0, ii = means.length; i < ii; i++) {\n            means[i] /= weightSum;\n        }\n    }\n    return means;\n};\n\nexports.weightedCovariance = function weightedCovariance(matrix, weights, means, dimension) {\n    dimension = dimension || 0;\n    means = means || exports.weightedMean(matrix, weights, dimension);\n    var s1 = 0, s2 = 0;\n    for (var i = 0, ii = weights.length; i < ii; i++) {\n        s1 += weights[i];\n        s2 += weights[i] * weights[i];\n    }\n    var factor = s1 / (s1 * s1 - s2);\n    return exports.weightedScatter(matrix, weights, means, factor, dimension);\n};\n\nexports.weightedScatter = function weightedScatter(matrix, weights, means, factor, dimension) {\n    dimension = dimension || 0;\n    means = means || exports.weightedMean(matrix, weights, dimension);\n    if (typeof (factor) === 'undefined') {\n        factor = 1;\n    }\n    var rows = matrix.length;\n    if (rows === 0) {\n        return [[]];\n    }\n    var cols = matrix[0].length,\n        cov, i, j, k, s;\n\n    if (dimension === 0) {\n        cov = new Array(cols);\n        for (i = 0; i < cols; i++) {\n            cov[i] = new Array(cols);\n        }\n        for (i = 0; i < cols; i++) {\n            for (j = i; j < cols; j++) {\n                s = 0;\n                for (k = 0; k < rows; k++) {\n                    s += weights[k] * (matrix[k][j] - means[j]) * (matrix[k][i] - means[i]);\n                }\n                cov[i][j] = s * factor;\n                cov[j][i] = s * factor;\n            }\n        }\n    } else if (dimension === 1) {\n        cov = new Array(rows);\n        for (i = 0; i < rows; i++) {\n            cov[i] = new Array(rows);\n        }\n        for (i = 0; i < rows; i++) {\n            for (j = i; j < rows; j++) {\n                s = 0;\n                for (k = 0; k < cols; k++) {\n                    s += weights[k] * (matrix[j][k] - means[j]) * (matrix[i][k] - means[i]);\n                }\n                cov[i][j] = s * factor;\n                cov[j][i] = s * factor;\n            }\n        }\n    } else {\n        throw new Error('Invalid dimension');\n    }\n\n    return cov;\n};\n","'use strict';\n\nexports.array = require('./array');\nexports.matrix = require('./matrix');\n","import { array as StatArray } from 'ml-stat';\nexport function normalize(array) {\n    const min = StatArray.min(array[1]);\n    const max = StatArray.max(array[1]);\n    const sum = StatArray.sum(array[1]);\n    const length = array[1] ? array[1].length : 0;\n    if (sum !== 0) {\n        for (let i = 0; i < length; i++) {\n            array[1][i] /= sum;\n        }\n    }\n    return {\n        sum,\n        min,\n        max,\n    };\n}\n//# sourceMappingURL=normalize.js.map","import { extract } from './extract';\nimport { getCommonArray } from './getCommonArray';\nimport { normalize } from './normalize';\nimport { COMMON_SECOND, COMMON_FIRST } from './index';\n// this method will systematically take care of both array\nexport function commonExtractAndNormalize(array1, array2, width, from, to, common) {\n    if (!Array.isArray(array1) || !Array.isArray(array2)) {\n        return {\n            info: undefined,\n            data: undefined,\n        };\n    }\n    const extract1 = extract(array1, from, to);\n    const extract2 = extract(array2, from, to);\n    let common1, common2, info1, info2;\n    if (common & COMMON_SECOND) {\n        common1 = getCommonArray(extract1, extract2, width);\n        info1 = normalize(common1);\n    }\n    else {\n        common1 = extract1;\n        info1 = normalize(common1);\n    }\n    if (common & COMMON_FIRST) {\n        common2 = getCommonArray(extract2, extract1, width);\n        info2 = normalize(common2);\n    }\n    else {\n        common2 = extract2;\n        info2 = normalize(common2);\n    }\n    return {\n        info1,\n        info2,\n        data1: common1,\n        data2: common2,\n    };\n}\n//# sourceMappingURL=commonExtractAndNormalize.js.map","import { extract } from './extract';\nimport { normalize } from './normalize';\nexport function extractAndNormalize(array, from, to) {\n    if (!Array.isArray(array)) {\n        return {\n            info: undefined,\n            data: undefined,\n        };\n    }\n    const newArray = extract(array, from, to);\n    const info = normalize(newArray);\n    return {\n        info,\n        data: newArray,\n    };\n}\n//# sourceMappingURL=extractAndNormalize.js.map","// Adapted from: http://stackoverflow.com/questions/563198/how-do-you-detect-where-two-line-segments-intersect/1968345#1968345\nexport function getIntersection(segment1, segment2) {\n    const p0X = segment1[0][0];\n    const p0Y = segment1[0][1];\n    const p1X = segment1[1][0];\n    const p1Y = segment1[1][1];\n    const p2X = segment2[0][0];\n    const p2Y = segment2[0][1];\n    const p3X = segment2[1][0];\n    const p3Y = segment2[1][1];\n    const s1X = p1X - p0X;\n    const s1Y = p1Y - p0Y;\n    const s2X = p3X - p2X;\n    const s2Y = p3Y - p2Y;\n    const s = (-s1Y * (p0X - p2X) + s1X * (p0Y - p2Y)) / (-s2X * s1Y + s1X * s2Y);\n    const t = (s2X * (p0Y - p2Y) - s2Y * (p0X - p2X)) / (-s2X * s1Y + s1X * s2Y);\n    if (s >= 0 && s <= 1 && t >= 0 && t <= 1) {\n        return {\n            x: p0X + t * s1X,\n            y: p0Y + t * s1Y,\n        };\n    }\n    return null; // No collision\n}\n//# sourceMappingURL=getIntersection.js.map","import { calculateOverlapFromDiff } from './calculateOverlapFromDiff';\nimport { checkPeaks } from './checkPeaks';\nimport { commonExtractAndNormalize } from './commonExtractAndNormalize';\nimport { extract } from './extract';\nimport { extractAndNormalize } from './extractAndNormalize';\nimport { getCommonArray } from './getCommonArray.js';\nimport { getIntersection } from './getIntersection';\nimport { normalize } from './normalize';\nexport const COMMON_NO = 0;\nexport const COMMON_FIRST = 1;\nexport const COMMON_SECOND = 2;\nexport const COMMON_BOTH = 3;\n/**\n * A number, or a string containing a number.\n * @typedef {([number[],number[]]|[number,number][]|{x:number[],y:number[]})} Peaks\n */\n/**\n * Create a comparator class\n * {object} [options={}]\n * {string} [options.common=''] should we take only common peaks 'first', 'second', 'both', ''\n * {number} [options.widthBottom=2] bottom trapezoid width for similarity evaluation\n * {number} [options.widthTop=1] top trapezoid width for similarity evaluation\n * {number} [options.from] from region used for similarity calculation\n * {number} [options.to] to region used for similarity calculation\n */\nexport class Comparator {\n    constructor(options = {}) {\n        this.array1 = [];\n        this.array2 = [];\n        this.setOptions(options);\n    }\n    /*\n       2 formats are allowed:\n       [[x1,x2,...],[y1,y2,...]] or [[x1,y1],[x2,y2], ...]\n      */\n    setOptions(options = {}) {\n        if (typeof options.common === 'string') {\n            if (options.common.toLowerCase() === 'first') {\n                this.common = COMMON_FIRST;\n            }\n            else if (options.common.toLowerCase() === 'second') {\n                this.common = COMMON_SECOND;\n            }\n            else if (options.common.toLowerCase() === 'both') {\n                this.common = COMMON_BOTH;\n            }\n            else {\n                this.common = COMMON_NO;\n            }\n        }\n        else if (options.common === true) {\n            this.common = COMMON_BOTH;\n        }\n        else {\n            this.common = COMMON_NO;\n        }\n        this.trapezoid = options.trapezoid;\n        this.commonFactor = options.commonFactor || this.commonFactor || 4;\n        const { widthBottom = this.widthBottom || 2, widthTop = this.widthTop || 1, from = this.from, to = this.to, } = options;\n        this.setTrapezoid(widthBottom, widthTop);\n        this.setFromTo(from, to);\n    }\n    /**\n     *\n     * @param {Peaks} peaks\n     */\n    setPeaks1(peaks) {\n        this.array1 = checkPeaks(peaks);\n        if (this.common) {\n            const extracts = commonExtractAndNormalize(this.array1, this.array2, this.widthBottom, this.from, this.to, this.common);\n            this.array1Extract = extracts.data1;\n            this.array1ExtractInfo = extracts.info1;\n            this.array2Extract = extracts.data2;\n            this.array2ExtractInfo = extracts.info2;\n        }\n        else {\n            const extract = extractAndNormalize(this.array1, this.from, this.to);\n            this.array1Extract = extract.data;\n            this.array1ExtractInfo = extract.info;\n        }\n    }\n    /**\n     *\n     * @param {Peaks} peaks\n     */\n    setPeaks2(peaks) {\n        this.array2 = checkPeaks(peaks);\n        if (this.common) {\n            const extracts = commonExtractAndNormalize(this.array1, this.array2, this.widthBottom, this.from, this.to, this.common);\n            this.array1Extract = extracts.data1;\n            this.array1ExtractInfo = extracts.info1;\n            this.array2Extract = extracts.data2;\n            this.array2ExtractInfo = extracts.info2;\n        }\n        else {\n            const extract = extractAndNormalize(this.array2, this.from, this.to);\n            this.array2Extract = extract.data;\n            this.array2ExtractInfo = extract.info;\n        }\n    }\n    getExtract1() {\n        return this.array1Extract;\n    }\n    getExtract2() {\n        return this.array2Extract;\n    }\n    getExtractInfo1() {\n        return this.array1ExtractInfo;\n    }\n    getExtractInfo2() {\n        return this.array2ExtractInfo;\n    }\n    /**\n     * Set the new bottom and top width of the trapezoid\n     * @param {number} newWidthBottom\n     * @param {number} newWidthTop\n     */\n    setTrapezoid(newWidthBottom, newWidthTop) {\n        this.widthTop = newWidthTop;\n        this.widthBottom = newWidthBottom;\n        this.widthSlope = (this.widthBottom - this.widthTop) / 2;\n        if (this.widthBottom < this.widthTop) {\n            throw new Error('widthBottom has to be larger than widthTop');\n        }\n    }\n    /**\n     * Set the from / to for comparison\n     * @param {number} newFrom - set the new from value\n     * @param {number} newTo - set the new to value\n     * @returns\n     */\n    setFromTo(newFrom, newTo) {\n        if (newFrom === this.from && newTo === this.to)\n            return;\n        this.from = newFrom;\n        this.to = newTo;\n        if (this.common) {\n            const extracts = commonExtractAndNormalize(this.array1, this.array2, this.widthBottom, this.from, this.to, this.common, this.commonFactor);\n            this.array1Extract = extracts.data1;\n            this.array1ExtractInfo = extracts.info1;\n            this.array2Extract = extracts.data2;\n            this.array2ExtractInfo = extracts.info2;\n        }\n        else {\n            let extract1 = extractAndNormalize(this.array1, this.from, this.to);\n            this.array1Extract = extract1.data;\n            this.array1ExtractInfo = extract1.info;\n            let extract2 = extractAndNormalize(this.array2, this.from, this.to);\n            this.array2Extract = extract2.data;\n            this.array2ExtractInfo = extract2.info;\n        }\n    }\n    /**\n     *\n     * @param {number} x1\n     * @param {number} y1\n     * @param {number} x2\n     * @param {number} y2\n     * @returns\n     */\n    getOverlap(x1, y1, x2, y2) {\n        if (y1 === 0 || y2 === 0)\n            return 0;\n        // TAKE CARE !!! We multiply the diff by 2 !!!\n        const diff = Math.abs(x1 - x2) * 2;\n        if (diff > this.widthBottom)\n            return 0;\n        if (diff <= this.widthTop) {\n            return Math.min(y1, y2);\n        }\n        const maxValue = (Math.max(y1, y2) * (this.widthBottom - diff)) /\n            (this.widthBottom - this.widthTop);\n        return Math.min(y1, y2, maxValue);\n    }\n    /**\n     * This is the old trapezoid similarity\n     * @param {number} x1\n     * @param {number} y1\n     * @param {number} x2\n     * @param {number} y2\n     * @param {number} widthTop\n     * @param {number} widthBottom\n     * @returns\n     */\n    getOverlapTrapezoid(x1, y1, x2, y2, widthTop, widthBottom) {\n        // eslint-disable-next-line no-console\n        console.error('getOverlapTrapezoid should not be used anymore');\n        const factor = 2 / (widthTop + widthBottom); // correction for surface=1\n        if (y1 === 0 || y2 === 0)\n            return 0;\n        if (x1 === x2) {\n            // they have the same position\n            return Math.min(y1, y2);\n        }\n        const diff = Math.abs(x1 - x2);\n        if (diff >= widthBottom)\n            return 0;\n        if (y1 === y2) {\n            // do they have the same height ???\n            // we need to find the common length\n            if (diff <= widthTop) {\n                return ((widthTop + widthBottom) / 2 - diff) * y1 * factor;\n            }\n            else if (diff <= widthBottom) {\n                return ((((((widthBottom - diff) * y1) / 2) * (diff - widthTop)) /\n                    (widthBottom - widthTop)) *\n                    factor);\n            }\n            return 0;\n        }\n        else {\n            // the height are different and not the same position ...\n            // we need to consider only one segment to find its intersection\n            const small = Math.min(y1, y2);\n            const big = Math.max(y1, y2);\n            const targets = [\n                [\n                    [0, 0],\n                    [this.widthSlope, small],\n                ],\n                [\n                    [this.widthSlope, small],\n                    [this.widthSlope + widthTop, small],\n                ],\n                [\n                    [widthTop + this.widthSlope, small],\n                    [widthBottom, 0],\n                ],\n            ];\n            let segment;\n            if ((x1 > x2 && y1 > y2) || (x1 < x2 && y1 < y2)) {\n                segment = [\n                    [diff, 0],\n                    [diff + this.widthSlope, big],\n                ];\n            }\n            else {\n                segment = [\n                    [diff + this.widthSlope, big],\n                    [diff, 0],\n                ];\n            }\n            for (let i = 0; i < 3; i++) {\n                const intersection = getIntersection(targets[i], segment);\n                if (intersection) {\n                    switch (i) {\n                        case 0:\n                            return small - ((diff * intersection.y) / 2) * factor;\n                        case 1: // to simplify ...\n                            //     console.log(\"           \",widthSlope,small,big,intersection.x)\n                            return ((((this.widthSlope * small) / (2 * big)) * small +\n                                (widthTop + this.widthSlope - intersection.x) * small +\n                                (this.widthSlope * small) / 2) *\n                                factor);\n                        case 2:\n                            return (((widthBottom - diff) * intersection.y) / 2) * factor;\n                        default:\n                            throw new Error(`unexpected intersection value: ${i}`);\n                    }\n                }\n            }\n        }\n        return NaN;\n    }\n    /**\n     * This method calculates the total diff. The sum of positive value will yield to overlap\n     * @returns\n     */\n    calculateDiff() {\n        // we need to take 2 pointers\n        // and travel progressively between them ...\n        const newFirst = [\n            this.array1Extract[0].slice(),\n            this.array1Extract[1].slice(),\n        ];\n        const newSecond = [\n            this.array2Extract[0].slice(),\n            this.array2Extract[1].slice(),\n        ];\n        const array1Length = this.array1Extract[0]\n            ? this.array1Extract[0].length\n            : 0;\n        const array2Length = this.array2Extract[0]\n            ? this.array2Extract[0].length\n            : 0;\n        let pos1 = 0;\n        let pos2 = 0;\n        let previous2 = 0;\n        while (pos1 < array1Length) {\n            const diff = newFirst[0][pos1] - this.array2Extract[0][pos2];\n            if (Math.abs(diff) < this.widthBottom) {\n                // there is some overlap\n                let overlap;\n                if (this.trapezoid) {\n                    // old trapezoid overlap similarity\n                    overlap = this.getOverlapTrapezoid(newFirst[0][pos1], newFirst[1][pos1], newSecond[0][pos2], newSecond[1][pos2], this.widthTop, this.widthBottom);\n                }\n                else {\n                    overlap = this.getOverlap(newFirst[0][pos1], newFirst[1][pos1], newSecond[0][pos2], newSecond[1][pos2], this.widthTop, this.widthBottom);\n                }\n                newFirst[1][pos1] -= overlap;\n                newSecond[1][pos2] -= overlap;\n                if (pos2 < array2Length - 1) {\n                    pos2++;\n                }\n                else {\n                    pos1++;\n                    pos2 = previous2;\n                }\n            }\n            else if (diff > 0 && pos2 < array2Length - 1) {\n                pos2++;\n                previous2 = pos2;\n            }\n            else {\n                pos1++;\n                pos2 = previous2;\n            }\n        }\n        return newSecond;\n    }\n    /**\n     * Set the new peaks and return info\n     * @param {Peaks} newPeaks1\n     * @param {Peaks} newPeaks2\n     * @returns\n     */\n    getSimilarity(newPeaks1, newPeaks2) {\n        if (newPeaks1)\n            this.setPeaks1(newPeaks1);\n        if (newPeaks2)\n            this.setPeaks2(newPeaks2);\n        const diff = this.calculateDiff();\n        return {\n            diff,\n            extract1: this.getExtract1(),\n            extract2: this.getExtract2(),\n            extractInfo1: this.getExtractInfo1(),\n            extractInfo2: this.getExtractInfo2(),\n            similarity: calculateOverlapFromDiff(diff),\n            widthBottom: this.widthBottom,\n            widthTop: this.widthTop,\n        };\n    }\n    /**\n     * This works mainly when you have a array1 that is fixed\n     * newPeaks2 have to be normalized ! (sum to 1)\n     * @param {Peaks} newPeaks2\n     * @param {number} from\n     * @param {number} to\n     * @returns\n     */\n    fastSimilarity(newPeaks2, from, to) {\n        this.array1Extract = extract(this.array1, from, to);\n        this.array2Extract = newPeaks2;\n        if (this.common & COMMON_SECOND) {\n            this.array1Extract = getCommonArray(this.array1Extract, this.array2Extract, this.widthBottom);\n        }\n        normalize(this.array1Extract);\n        const diff = this.calculateDiff();\n        return calculateOverlapFromDiff(diff);\n    }\n}\n//# sourceMappingURL=index.js.map","import { Comparator } from 'peaks-similarity';\n\n/**\n * @param {object}   [options={}]\n * @param {object}   [options.minCharge=1]\n * @param {object}   [options.maxCharge=10]\n * @param {object}   [options.similarity={}]\n * @param {object}   [options.similarity.widthBottom]\n * @param {object}   [options.similarity.widthTop]\n * @param {object}   [options.similarity.widthFunction] - function called with mass that should return an object width containing top and bottom\n * @param {object}   [options.similarity.zone={}]\n * @param {object}   [options.similarity.zone.low=-0.5] - window shift based on observed monoisotopic mass\n * @param {object}   [options.similarity.zone.high=2.5] - to value for the comparison window\n * @param {object}   [options.similarity.common]\n */\n\nconst NEUTRON_MASS = 1;\n\nexport function getPeakChargeBySimilarity(spectrum, targetMass, options = {}) {\n  let { similarity = {}, minCharge = 1, maxCharge = 10 } = options;\n  let { zone = {}, widthFunction } = similarity;\n  let { low = -0.5, high = 2.5 } = zone;\n\n  if (!spectrum || !spectrum.data.x.length > 0) {\n    throw Error(\n      'You need to add an experimental spectrum first using setMassSpectrum',\n    );\n  }\n\n  let width = {\n    bottom: similarity.widthBottom,\n    top: similarity.widthTop,\n  };\n\n  similarity = JSON.parse(JSON.stringify(similarity));\n  similarity.common = 'second';\n\n  let experimentalData = spectrum.data;\n  let similarityProcessor = new Comparator(similarity);\n  similarityProcessor.setPeaks1([experimentalData.x, experimentalData.y]);\n\n  if (widthFunction && typeof widthFunction === 'string') {\n    // eslint-disable-next-line no-new-func\n    widthFunction = new Function('mass', widthFunction);\n    let checkTopBottom = widthFunction(123);\n    if (!checkTopBottom.bottom || !checkTopBottom.top) {\n      throw Error(\n        'widthFunction should return an object with bottom and top properties',\n      );\n    }\n  }\n\n  let fromCharge =\n    minCharge * maxCharge > 0\n      ? Math.round(Math.min(Math.abs(minCharge), Math.abs(maxCharge)))\n      : 1;\n  let toCharge = Math.round(Math.max(Math.abs(minCharge), Math.abs(maxCharge)));\n\n  let fromIsotope = Math.ceil(low);\n  let toIsotope = Math.floor(high);\n  let isotopeHeight = 1 / (toIsotope - fromIsotope + 1);\n\n  let results = [];\n\n  for (let charge = fromCharge; charge < toCharge + 1; charge++) {\n    let isotopePositions = { x: [], y: [] };\n    for (\n      let isotopePosition = fromIsotope;\n      isotopePosition < toIsotope + 1;\n      isotopePosition++\n    ) {\n      isotopePositions.x.push(\n        targetMass + (isotopePosition * NEUTRON_MASS) / charge,\n      );\n      isotopePositions.y.push(isotopeHeight);\n    }\n    let from = targetMass + low / Math.abs(charge);\n    let to = targetMass + high / Math.abs(charge);\n    similarityProcessor.setFromTo(from, to);\n    if (widthFunction) {\n      width = widthFunction(targetMass);\n      similarityProcessor.setTrapezoid(width.bottom, width.top);\n    }\n\n    similarityProcessor.setPeaks2([isotopePositions.x, isotopePositions.y]);\n    let result = similarityProcessor.getSimilarity();\n\n    results.push({ charge, similarity: result.similarity });\n  }\n\n  return results.sort((a, b) => b.similarity - a.similarity)[0].charge;\n}\n","import { xMaxValue } from 'ml-spectra-processing';\n/**\n * When a spectrum is continous ?\n * - has more than 100 points\n * - deltaX change can not be more than a factor 2\n * - deltaX may not be larger than 0.1\n * - if y is zero it does not count\n * @param {object} spectrum\n * @param {object} [options={}]\n * @param {number} [options.minLength=100]\n * @param {number} [options.relativeHeightThreshold=0.001] // Under this value the\n * @param {number} [options.maxDeltaRatio=3]\n */\n\nexport function isContinuous(spectrum, options = {}) {\n  const {\n    minLength = 100,\n    maxDeltaRatio = 3,\n    relativeHeightThreshold = 0.001,\n  } = options;\n  const minHeight = xMaxValue(spectrum.data.y) * relativeHeightThreshold;\n  const minRadio = 1 / maxDeltaRatio;\n  const maxRatio = 1 * maxDeltaRatio;\n  if (spectrum.continuous === undefined) {\n    let xs = spectrum.data.x;\n    let ys = spectrum.data.y;\n    if (xs.length < minLength) {\n      spectrum.continuous = false;\n    } else {\n      let previousDelta = xs[1] - xs[0];\n      spectrum.continuous = true;\n      let success = 0;\n      let failed = 0;\n      for (let i = 0; i < xs.length - 1; i++) {\n        if (ys[i] < minHeight || ys[i + 1] < minHeight) {\n          previousDelta = 0;\n          continue;\n        }\n        let delta = xs[i + 1] - xs[i];\n        if (previousDelta) {\n          let ratio = delta / previousDelta;\n          if (\n            (Math.abs(delta) > 0.1 || ratio < minRadio || ratio > maxRatio) &&\n            ys[i] !== 0 &&\n            ys[i + 1] !== 0\n          ) {\n            failed++;\n            break;\n          } else {\n            success++;\n          }\n        }\n        previousDelta = delta;\n      }\n      if (success / failed < 10) {\n        spectrum.continuous = false;\n      }\n    }\n  }\n  return spectrum.continuous;\n}\n","var IDX=256, HEX=[], BUFFER;\nwhile (IDX--) HEX[IDX] = (IDX + 256).toString(16).substring(1);\n\nexport function v4() {\n\tvar i=0, num, out='';\n\n\tif (!BUFFER || ((IDX + 16) > 256)) {\n\t\tBUFFER = Array(i=256);\n\t\twhile (i--) BUFFER[i] = 256 * Math.random() | 0;\n\t\ti = IDX = 0;\n\t}\n\n\tfor (; i < 16; i++) {\n\t\tnum = BUFFER[IDX + i];\n\t\tif (i==6) out += HEX[num & 15 | 64];\n\t\telse if (i==8) out += HEX[num & 63 | 128];\n\t\telse out += HEX[num];\n\n\t\tif (i & 1 && i > 1 && i < 11) out += '-';\n\t}\n\n\tIDX++;\n\treturn out;\n}\n","import { isAnyArray } from 'is-any-array';\n/**\n * Apply Savitzky Golay algorithm\n * @param [ys] Array of y values\n * @param [xs] Array of X or deltaX\n * @return  Array containing the new ys (same length)\n */\nexport function sgg(ys, xs, options = {}) {\n    let { windowSize = 9, derivative = 0, polynomial = 3 } = options;\n    if (windowSize % 2 === 0 || windowSize < 5 || !Number.isInteger(windowSize)) {\n        throw new RangeError('Invalid window size (should be odd and at least 5 integer number)');\n    }\n    if (!isAnyArray(ys)) {\n        throw new TypeError('Y values must be an array');\n    }\n    if (typeof xs === 'undefined') {\n        throw new TypeError('X must be defined');\n    }\n    if (windowSize > ys.length) {\n        throw new RangeError(`Window size is higher than the data length ${windowSize}>${ys.length}`);\n    }\n    if (derivative < 0 || !Number.isInteger(derivative)) {\n        throw new RangeError('Derivative should be a positive integer');\n    }\n    if (polynomial < 1 || !Number.isInteger(polynomial)) {\n        throw new RangeError('Polynomial should be a positive integer');\n    }\n    if (polynomial >= 6) {\n        // eslint-disable-next-line no-console\n        console.warn('You should not use polynomial grade higher than 5 if you are' +\n            ' not sure that your data arises from such a model. Possible polynomial oscillation problems');\n    }\n    let half = Math.floor(windowSize / 2);\n    let np = ys.length;\n    let ans = new Float64Array(np);\n    let weights = fullWeights(windowSize, polynomial, derivative);\n    let hs = 0;\n    let constantH = true;\n    if (isAnyArray(xs)) {\n        constantH = false;\n    }\n    else {\n        hs = Math.pow(xs, derivative);\n    }\n    //For the borders\n    for (let i = 0; i < half; i++) {\n        let wg1 = weights[half - i - 1];\n        let wg2 = weights[half + i + 1];\n        let d1 = 0;\n        let d2 = 0;\n        for (let l = 0; l < windowSize; l++) {\n            d1 += wg1[l] * ys[l];\n            d2 += wg2[l] * ys[np - windowSize + l];\n        }\n        if (constantH) {\n            ans[half - i - 1] = d1 / hs;\n            ans[np - half + i] = d2 / hs;\n        }\n        else {\n            hs = getHs(xs, half - i - 1, half, derivative);\n            ans[half - i - 1] = d1 / hs;\n            hs = getHs(xs, np - half + i, half, derivative);\n            ans[np - half + i] = d2 / hs;\n        }\n    }\n    //For the internal points\n    let wg = weights[half];\n    for (let i = windowSize; i <= np; i++) {\n        let d = 0;\n        for (let l = 0; l < windowSize; l++)\n            d += wg[l] * ys[l + i - windowSize];\n        if (!constantH) {\n            hs = getHs(xs, i - half - 1, half, derivative);\n        }\n        ans[i - half - 1] = d / hs;\n    }\n    return ans;\n}\nfunction getHs(h, center, half, derivative) {\n    let hs = 0;\n    let count = 0;\n    for (let i = center - half; i < center + half; i++) {\n        if (i >= 0 && i < h.length - 1) {\n            hs += h[i + 1] - h[i];\n            count++;\n        }\n    }\n    return Math.pow(hs / count, derivative);\n}\nfunction gramPoly(i, m, k, s) {\n    let Grampoly = 0;\n    if (k > 0) {\n        Grampoly =\n            ((4 * k - 2) / (k * (2 * m - k + 1))) *\n                (i * gramPoly(i, m, k - 1, s) + s * gramPoly(i, m, k - 1, s - 1)) -\n                (((k - 1) * (2 * m + k)) / (k * (2 * m - k + 1))) *\n                    gramPoly(i, m, k - 2, s);\n    }\n    else {\n        if (k === 0 && s === 0) {\n            Grampoly = 1;\n        }\n        else {\n            Grampoly = 0;\n        }\n    }\n    return Grampoly;\n}\nfunction genFact(a, b) {\n    let gf = 1;\n    if (a >= b) {\n        for (let j = a - b + 1; j <= a; j++) {\n            gf *= j;\n        }\n    }\n    return gf;\n}\nfunction weight(i, t, m, n, s) {\n    let sum = 0;\n    for (let k = 0; k <= n; k++) {\n        sum +=\n            (2 * k + 1) *\n                (genFact(2 * m, k) / genFact(2 * m + k + 1, k + 1)) *\n                gramPoly(i, m, k, 0) *\n                gramPoly(t, m, k, s);\n    }\n    return sum;\n}\n/**\n * @private\n * @param m  Number of points\n * @param n  Polynomial grade\n * @param s  Derivative\n */\nfunction fullWeights(m, n, s) {\n    let weights = new Array(m);\n    let np = Math.floor(m / 2);\n    for (let t = -np; t <= np; t++) {\n        weights[t + np] = new Float64Array(m);\n        for (let j = -np; j <= np; j++) {\n            weights[t + np][j + np] = weight(j, t, np, n, s);\n        }\n    }\n    return weights;\n}\n//# sourceMappingURL=index.js.map","/**\n * Correction of the x and y coordinates using a quadratic optimizations with the peak and its 3 closest neighbors to determine the true x,y values of the peak.\n * This process is done in place and is very fast.\n * @param data\n * @param peaks\n */\nexport function optimizeTop(data, peaks) {\n    const { x, y } = data;\n    for (const peak of peaks) {\n        let currentIndex = peak.index;\n        // The detected peak could be moved 1 or 2 units to left or right.\n        if (y[currentIndex - 1] >= y[currentIndex - 2] &&\n            y[currentIndex - 1] >= y[currentIndex]) {\n            currentIndex--;\n        }\n        else {\n            if (y[currentIndex + 1] >= y[currentIndex] &&\n                y[currentIndex + 1] >= y[currentIndex + 2]) {\n                currentIndex++;\n            }\n            else {\n                if (y[currentIndex - 2] >= y[currentIndex - 3] &&\n                    y[currentIndex - 2] >= y[currentIndex - 1]) {\n                    currentIndex -= 2;\n                }\n                else {\n                    if (y[currentIndex + 2] >= y[currentIndex + 1] &&\n                        y[currentIndex + 2] >= y[currentIndex + 3]) {\n                        currentIndex += 2;\n                    }\n                }\n            }\n        }\n        // interpolation to a sin() function\n        if (y[currentIndex - 1] > 0 &&\n            y[currentIndex + 1] > 0 &&\n            y[currentIndex] >= y[currentIndex - 1] &&\n            y[currentIndex] >= y[currentIndex + 1] &&\n            (y[currentIndex] !== y[currentIndex - 1] ||\n                y[currentIndex] !== y[currentIndex + 1])) {\n            let alpha = 20 * Math.log10(y[currentIndex - 1]);\n            let beta = 20 * Math.log10(y[currentIndex]);\n            let gamma = 20 * Math.log10(y[currentIndex + 1]);\n            let p = (0.5 * (alpha - gamma)) / (alpha - 2 * beta + gamma);\n            peak.x = x[currentIndex] + (x[currentIndex] - x[currentIndex - 1]) * p;\n            peak.y =\n                y[currentIndex] -\n                    0.25 * (y[currentIndex - 1] - y[currentIndex + 1]) * p;\n        }\n    }\n}\n//# sourceMappingURL=optimizeTop.js.map","import { v4 as generateID } from '@lukeed/uuid';\nimport { sgg } from 'ml-savitzky-golay-generalized';\nimport { xIsEquallySpaced, xIsMonotoneIncreasing, xMinValue, xMaxValue, xNoiseStandardDeviation, } from 'ml-spectra-processing';\nimport { optimizeTop } from './utils/optimizeTop';\n/**\n * Global spectra deconvolution\n * @param  data - Object data with x and y arrays. Values in x has to be growing\n * @param {number} [options.broadRatio = 0.00] - If `broadRatio` is higher than 0, then all the peaks which second derivative\n * smaller than `broadRatio * maxAbsSecondDerivative` will be marked with the soft mask equal to true.\n\n */\nexport function gsd(data, options = {}) {\n    let { sgOptions = {\n        windowSize: 9,\n        polynomial: 3,\n    }, noiseLevel, smoothY = false, maxCriteria = true, minMaxRatio = 0.00025, realTopDetection = false, } = options;\n    let { x, y } = data;\n    if (!xIsMonotoneIncreasing(x)) {\n        throw new Error('GSD only accepts monotone increasing x values');\n    }\n    //rescale;\n    y = y.slice();\n    // If the max difference between delta x is less than 5%, then,\n    // we can assume it to be equally spaced variable\n    let equallySpaced = xIsEquallySpaced(x);\n    if (noiseLevel === undefined) {\n        if (equallySpaced) {\n            const noiseInfo = xNoiseStandardDeviation(y);\n            if (maxCriteria) {\n                noiseLevel = noiseInfo.median + 1.5 * noiseInfo.sd;\n            }\n            else {\n                noiseLevel = -noiseInfo.median + 1.5 * noiseInfo.sd;\n            }\n        }\n        else {\n            noiseLevel = 0;\n        }\n    }\n    else {\n        if (maxCriteria === false) {\n            noiseLevel *= -1;\n        }\n    }\n    if (maxCriteria === false) {\n        for (let i = 0; i < y.length; i++) {\n            y[i] *= -1;\n        }\n    }\n    if (noiseLevel !== undefined) {\n        for (let i = 0; i < y.length; i++) {\n            if (y[i] < noiseLevel) {\n                y[i] = noiseLevel;\n            }\n        }\n    }\n    let yData = y;\n    let dY, ddY;\n    const { windowSize, polynomial } = sgOptions;\n    if (equallySpaced) {\n        if (smoothY) {\n            yData = sgg(y, x[1] - x[0], {\n                windowSize,\n                polynomial,\n                derivative: 0,\n            });\n        }\n        dY = sgg(y, x[1] - x[0], {\n            windowSize,\n            polynomial,\n            derivative: 1,\n        });\n        ddY = sgg(y, x[1] - x[0], {\n            windowSize,\n            polynomial,\n            derivative: 2,\n        });\n    }\n    else {\n        if (smoothY) {\n            yData = sgg(y, x, {\n                windowSize,\n                polynomial,\n                derivative: 0,\n            });\n        }\n        dY = sgg(y, x, {\n            windowSize,\n            polynomial,\n            derivative: 1,\n        });\n        ddY = sgg(y, x, {\n            windowSize,\n            polynomial,\n            derivative: 2,\n        });\n    }\n    const minY = xMinValue(yData);\n    const maxY = xMaxValue(yData);\n    if (minY > maxY || minY === maxY)\n        return [];\n    const yThreshold = minY + (maxY - minY) * minMaxRatio;\n    const dX = x[1] - x[0];\n    let lastMax = null;\n    let lastMin = null;\n    let minddY = [];\n    let intervalL = [];\n    let intervalR = [];\n    // By the intermediate value theorem We cannot find 2 consecutive maximum or minimum\n    for (let i = 1; i < yData.length - 1; ++i) {\n        if ((dY[i] < dY[i - 1] && dY[i] <= dY[i + 1]) ||\n            (dY[i] <= dY[i - 1] && dY[i] < dY[i + 1])) {\n            lastMin = {\n                x: x[i],\n                index: i,\n            };\n            if (dX > 0 && lastMax !== null) {\n                intervalL.push(lastMax);\n                intervalR.push(lastMin);\n            }\n        }\n        // Maximum in first derivative\n        if ((dY[i] >= dY[i - 1] && dY[i] > dY[i + 1]) ||\n            (dY[i] > dY[i - 1] && dY[i] >= dY[i + 1])) {\n            lastMax = {\n                x: x[i],\n                index: i,\n            };\n            if (dX < 0 && lastMin !== null) {\n                intervalL.push(lastMax);\n                intervalR.push(lastMin);\n            }\n        }\n        // Minimum in second derivative\n        if (ddY[i] < ddY[i - 1] && ddY[i] < ddY[i + 1]) {\n            minddY.push(i);\n        }\n    }\n    let lastK = -1;\n    const peaks = [];\n    for (const minddYIndex of minddY) {\n        let deltaX = x[minddYIndex];\n        let possible = -1;\n        let k = lastK + 1;\n        let minDistance = Number.POSITIVE_INFINITY;\n        let currentDistance = 0;\n        while (possible === -1 && k < intervalL.length) {\n            currentDistance = Math.abs(deltaX - (intervalL[k].x + intervalR[k].x) / 2);\n            if (currentDistance < (intervalR[k].x - intervalL[k].x) / 2) {\n                possible = k;\n                lastK = k;\n            }\n            ++k;\n            // Not getting closer?\n            if (currentDistance >= minDistance) {\n                break;\n            }\n            minDistance = currentDistance;\n        }\n        if (possible !== -1) {\n            if (yData[minddYIndex] > yThreshold) {\n                let width = Math.abs(intervalR[possible].x - intervalL[possible].x);\n                peaks.push({\n                    id: generateID(),\n                    x: deltaX,\n                    y: yData[minddYIndex],\n                    width,\n                    index: minddYIndex,\n                    ddY: ddY[minddYIndex],\n                    inflectionPoints: {\n                        from: intervalL[possible],\n                        to: intervalR[possible],\n                    },\n                });\n            }\n        }\n    }\n    if (realTopDetection) {\n        optimizeTop({ x, y: yData }, peaks);\n    }\n    peaks.forEach((peak) => {\n        if (!maxCriteria) {\n            peak.y *= -1;\n            peak.ddY = peak.ddY * -1;\n        }\n    });\n    peaks.sort((a, b) => {\n        return a.x - b.x;\n    });\n    return peaks;\n}\n//# sourceMappingURL=gsd.js.map","/**\n * @param {object}   [options={}]\n * @param {object}   [options.min=1]\n * @param {object}   [options.max=10]\n * @param {object}   [options.low=-1]\n * @param {object}   [options.high=1]\n * @param {object}   [options.precision=100]\n */\n\nconst NEUTRON_MASS = 1;\n\nexport function appendPeaksCharge(peaks, options = {}) {\n  let {\n    precision = 100,\n    low = -1,\n    high = 1,\n    min: minCharge = 1,\n    max: maxCharge = 10,\n  } = options;\n  let fromCharge =\n    minCharge * maxCharge > 0\n      ? Math.round(Math.min(Math.abs(minCharge), Math.abs(maxCharge)))\n      : 1;\n  let toCharge = Math.round(Math.max(Math.abs(minCharge), Math.abs(maxCharge)));\n\n  let fromIsotope = Math.ceil(low);\n  let toIsotope = Math.floor(high);\n  let numberIsotopes = toIsotope - fromIsotope + 1;\n  let isotopeIntensity = 1 / numberIsotopes;\n  let fromIndex = 0;\n  let localFromIndex = 0;\n  let localToIndex = 0;\n  for (let peakIndex = 0; peakIndex < peaks.length; peakIndex++) {\n    let peak = peaks[peakIndex];\n\n    let targetMass = peak.x;\n    localFromIndex = fromIndex;\n    let bestCharge = fromCharge;\n    let bestChargeMatch = 0;\n    for (let charge = fromCharge; charge < toCharge + 1; charge++) {\n      let theoreticalPositions = {\n        x: [],\n        y: new Array(numberIsotopes).fill(isotopeIntensity),\n      };\n\n      let massRange = precision * 1e-6 * targetMass;\n      for (\n        let isotopePosition = fromIsotope;\n        isotopePosition < toIsotope + 1;\n        isotopePosition++\n      ) {\n        theoreticalPositions.x.push(\n          targetMass + (isotopePosition * NEUTRON_MASS) / charge,\n        );\n      }\n      let fromMass = targetMass + low / Math.abs(charge) - massRange;\n      let toMass = targetMass + high / Math.abs(charge) + massRange;\n\n      if (charge === 1) {\n        // we may move the fromIndex\n        while (peaks[fromIndex].x < fromMass) {\n          fromIndex++;\n        }\n      }\n\n      /*\n       * Find the from / to index for the specific peak and specific charge\n       */\n      while (peaks[localFromIndex].x < fromMass) {\n        localFromIndex++;\n      }\n      localToIndex = localFromIndex;\n      let localHeightSum = 0;\n      while (localToIndex < peaks.length && peaks[localToIndex].x < toMass) {\n        localHeightSum += peaks[localToIndex].y;\n        localToIndex++;\n      }\n      localToIndex--;\n\n      //  console.log({ localFromIndex, localToIndex });\n      /*\n        Calculate the overlap for a specific peak and specific charge\n      */\n      let currentTheoreticalPosition = 0;\n      let theoreticalMaxValue = 1 / numberIsotopes;\n      let totalMatch = 0;\n\n      for (let index = localFromIndex; index <= localToIndex; index++) {\n        let minMass =\n          theoreticalPositions.x[currentTheoreticalPosition] -\n          massRange / charge;\n        let maxMass =\n          theoreticalPositions.x[currentTheoreticalPosition] +\n          massRange / charge;\n\n        while (maxMass < peaks[index].x) {\n          currentTheoreticalPosition++;\n          theoreticalMaxValue = 1 / numberIsotopes;\n          minMass =\n            theoreticalPositions.x[currentTheoreticalPosition] -\n            massRange / charge;\n          maxMass =\n            theoreticalPositions.x[currentTheoreticalPosition] +\n            massRange / charge;\n        }\n\n        while (index < peaks.length && peaks[index].x < minMass) {\n          index++;\n        }\n\n        //    console.log({ index, minMass, maxMass, massRange, localHeightSum });\n        if (index < peaks.length && peaks[index].x <= maxMass) {\n          while (index < peaks.length && peaks[index].x <= maxMass) {\n            if (peaks[index].x >= minMass && peaks[index].x <= maxMass) {\n              let value = peaks[index].y / localHeightSum;\n              //      console.log({ theoreticalMaxValue, value });\n              value = Math.min(theoreticalMaxValue, value);\n\n              theoreticalMaxValue -= value;\n              totalMatch += value;\n            }\n            index++;\n          }\n          index--;\n        }\n\n        if (totalMatch > bestChargeMatch) {\n          bestCharge = charge;\n          bestChargeMatch = totalMatch;\n        }\n      }\n    }\n    peak.charge = bestCharge;\n  }\n  return peaks;\n}\n","import { gsd } from 'ml-gsd';\nimport { xyEnsureGrowingX } from 'ml-spectra-processing';\n\nimport { appendPeaksCharge } from './appendPeaksCharge';\n\n/**\n * Filter the array of peaks\n * @param {Spectrum} spectrum - array of all the peaks\n * @param {object} [options={}]\n * @param {object} [options.charge={}]\n * @param {number} [options.charge.min=1]\n * @param {number} [options.charge.max=10]\n * @param {number} [options.charge.low=-1]\n * @param {number} [options.charge.high=1]\n * @param {number} [options.charge.precision=30]\n * @returns {array} - copy of peaks with 'close' annotation\n */\n\nexport function peakPicking(spectrum, options = {}) {\n  const { charge: chargeOptions = {} } = options;\n  if (!spectrum.peaks || spectrum.peaks.length === 0) {\n    spectrum.peaks = [];\n    const keys = Object.keys(spectrum.data).filter(\n      (key) => key !== 'x' && key !== 'y',\n    );\n    if (spectrum.isContinuous()) {\n      // some experimental data are really problematic and we need to add this line\n      const data = xyEnsureGrowingX(spectrum.data);\n      const gsdPeaks = gsd(data, {\n        minMaxRatio: 0.00025, // Threshold to determine if a given peak should be considered as a noise\n        realTopDetection: true,\n        smoothY: false,\n        sgOptions: { windowSize: 7, polynomial: 3 },\n      });\n      for (let gsdPeak of gsdPeaks) {\n        const peak = { x: gsdPeak.x, y: gsdPeak.y, width: gsdPeak.width };\n        for (let key of keys) {\n          peak[key] = spectrum.data[key][gsdPeak.index];\n        }\n        spectrum.peaks.push(peak);\n      }\n    } else {\n      spectrum.peaks = [];\n      let data = spectrum.data;\n      for (let i = 0; i < data.x.length; i++) {\n        const peak = {\n          x: data.x[i],\n          y: data.y[i],\n          width: 0,\n        };\n        for (let key of keys) {\n          peak[key] = spectrum.data[key][i];\n        }\n        spectrum.peaks.push(peak);\n      }\n    }\n    // required and linked to https://github.com/mljs/global-spectral-deconvolution/issues/17\n    spectrum.peaks = spectrum.peaks.filter(\n      (peak) => !isNaN(peak.x) && !isNaN(peak.y),\n    );\n    appendPeaksCharge(spectrum.peaks, chargeOptions);\n  }\n\n  return spectrum.peaks;\n}\n","export default function maybeToPrecision(value, digits) {\n  if (value < 0) {\n    value = 0 - value;\n    if (typeof digits === 'number') {\n      return `- ${value.toPrecision(digits)}`;\n    } else {\n      return `- ${value.toString()}`;\n    }\n  } else {\n    if (typeof digits === 'number') {\n      return value.toPrecision(digits);\n    } else {\n      return value.toString();\n    }\n  }\n}\n","import { isAnyArray } from 'is-any-array';\n\nexport default function checkArraySize(x, y) {\n  if (!isAnyArray(x) || !isAnyArray(y)) {\n    throw new TypeError('x and y must be arrays');\n  }\n  if (x.length !== y.length) {\n    throw new RangeError('x and y arrays must have the same length');\n  }\n}\n","import { isAnyArray } from 'is-any-array';\n\nexport { default as maybeToPrecision } from './maybeToPrecision';\nexport { default as checkArrayLength } from './checkArrayLength';\n\nexport default class BaseRegression {\n  constructor() {\n    if (new.target === BaseRegression) {\n      throw new Error('BaseRegression must be subclassed');\n    }\n  }\n\n  predict(x) {\n    if (typeof x === 'number') {\n      return this._predict(x);\n    } else if (isAnyArray(x)) {\n      const y = [];\n      for (let i = 0; i < x.length; i++) {\n        y.push(this._predict(x[i]));\n      }\n      return y;\n    } else {\n      throw new TypeError('x must be a number or array');\n    }\n  }\n\n  _predict() {\n    throw new Error('_predict must be implemented');\n  }\n\n  train() {\n    // Do nothing for this package\n  }\n\n  toString() {\n    return '';\n  }\n\n  toLaTeX() {\n    return '';\n  }\n\n  /**\n   * Return the correlation coefficient of determination (r) and chi-square.\n   * @param {Array<number>} x\n   * @param {Array<number>} y\n   * @return {object}\n   */\n  score(x, y) {\n    if (!isAnyArray(x) || !isAnyArray(y) || x.length !== y.length) {\n      throw new Error('x and y must be arrays of the same length');\n    }\n\n    const n = x.length;\n    const y2 = new Array(n);\n    for (let i = 0; i < n; i++) {\n      y2[i] = this._predict(x[i]);\n    }\n\n    let xSum = 0;\n    let ySum = 0;\n    let chi2 = 0;\n    let rmsd = 0;\n    let xSquared = 0;\n    let ySquared = 0;\n    let xY = 0;\n    for (let i = 0; i < n; i++) {\n      xSum += y2[i];\n      ySum += y[i];\n      xSquared += y2[i] * y2[i];\n      ySquared += y[i] * y[i];\n      xY += y2[i] * y[i];\n      if (y[i] !== 0) {\n        chi2 += ((y[i] - y2[i]) * (y[i] - y2[i])) / y[i];\n      }\n      rmsd += (y[i] - y2[i]) * (y[i] - y2[i]);\n    }\n\n    const r =\n      (n * xY - xSum * ySum) /\n      Math.sqrt((n * xSquared - xSum * xSum) * (n * ySquared - ySum * ySum));\n\n    return {\n      r: r,\n      r2: r * r,\n      chi2: chi2,\n      rmsd: Math.sqrt(rmsd / n),\n    };\n  }\n}\n","import BaseRegression, {\r\n  checkArrayLength,\r\n  maybeToPrecision,\r\n} from 'ml-regression-base';\r\n\r\nexport default class SimpleLinearRegression extends BaseRegression {\r\n  constructor(x, y) {\r\n    super();\r\n    if (x === true) {\r\n      this.slope = y.slope;\r\n      this.intercept = y.intercept;\r\n      this.coefficients = [y.intercept, y.slope];\r\n    } else {\r\n      checkArrayLength(x, y);\r\n      regress(this, x, y);\r\n    }\r\n  }\r\n\r\n  toJSON() {\r\n    return {\r\n      name: 'simpleLinearRegression',\r\n      slope: this.slope,\r\n      intercept: this.intercept,\r\n    };\r\n  }\r\n\r\n  _predict(x) {\r\n    return this.slope * x + this.intercept;\r\n  }\r\n\r\n  computeX(y) {\r\n    return (y - this.intercept) / this.slope;\r\n  }\r\n\r\n  toString(precision) {\r\n    let result = 'f(x) = ';\r\n    if (this.slope !== 0) {\r\n      const xFactor = maybeToPrecision(this.slope, precision);\r\n      result += `${xFactor === '1' ? '' : `${xFactor} * `}x`;\r\n      if (this.intercept !== 0) {\r\n        const absIntercept = Math.abs(this.intercept);\r\n        const operator = absIntercept === this.intercept ? '+' : '-';\r\n        result += ` ${operator} ${maybeToPrecision(absIntercept, precision)}`;\r\n      }\r\n    } else {\r\n      result += maybeToPrecision(this.intercept, precision);\r\n    }\r\n    return result;\r\n  }\r\n\r\n  toLaTeX(precision) {\r\n    return this.toString(precision);\r\n  }\r\n\r\n  static load(json) {\r\n    if (json.name !== 'simpleLinearRegression') {\r\n      throw new TypeError('not a SLR model');\r\n    }\r\n    return new SimpleLinearRegression(true, json);\r\n  }\r\n}\r\n\r\nfunction regress(slr, x, y) {\r\n  const n = x.length;\r\n  let xSum = 0;\r\n  let ySum = 0;\r\n\r\n  let xSquared = 0;\r\n  let xY = 0;\r\n\r\n  for (let i = 0; i < n; i++) {\r\n    xSum += x[i];\r\n    ySum += y[i];\r\n    xSquared += x[i] * x[i];\r\n    xY += x[i] * y[i];\r\n  }\r\n\r\n  const numerator = n * xY - xSum * ySum;\r\n  slr.slope = numerator / (n * xSquared - xSum * xSum);\r\n  slr.intercept = (1 / n) * ySum - slr.slope * (1 / n) * xSum;\r\n  slr.coefficients = [slr.intercept, slr.slope];\r\n}\r\n","import BaseRegression, {\n  checkArrayLength,\n  maybeToPrecision\n} from 'ml-regression-base';\nimport SimpleLinearRegression from 'ml-regression-simple-linear';\n\nexport default class PowerRegression extends BaseRegression {\n  constructor(x, y) {\n    super();\n    if (x === true) {\n      // reloading model\n      this.A = y.A;\n      this.B = y.B;\n    } else {\n      checkArrayLength(x, y);\n      regress(this, x, y);\n    }\n  }\n\n  _predict(newInputs) {\n    return this.A * Math.pow(newInputs, this.B);\n  }\n\n  toJSON() {\n    return {\n      name: 'powerRegression',\n      A: this.A,\n      B: this.B\n    };\n  }\n\n  toString(precision) {\n    return `f(x) = ${maybeToPrecision(\n      this.A,\n      precision\n    )} * x^${maybeToPrecision(this.B, precision)}`;\n  }\n\n  toLaTeX(precision) {\n    let latex = '';\n    if (this.B >= 0) {\n      latex = `f(x) = ${maybeToPrecision(\n        this.A,\n        precision\n      )}x^{${maybeToPrecision(this.B, precision)}}`;\n    } else {\n      latex = `f(x) = \\\\frac{${maybeToPrecision(\n        this.A,\n        precision\n      )}}{x^{${maybeToPrecision(-this.B, precision)}}}`;\n    }\n    latex = latex.replace(/e([+-]?[0-9]+)/g, 'e^{$1}');\n    return latex;\n  }\n\n  static load(json) {\n    if (json.name !== 'powerRegression') {\n      throw new TypeError('not a power regression model');\n    }\n    return new PowerRegression(true, json);\n  }\n}\n\nfunction regress(pr, x, y) {\n  const n = x.length;\n  const xl = new Array(n);\n  const yl = new Array(n);\n  for (let i = 0; i < n; i++) {\n    xl[i] = Math.log(x[i]);\n    yl[i] = Math.log(y[i]);\n  }\n\n  const linear = new SimpleLinearRegression(xl, yl);\n  pr.A = Math.exp(linear.intercept);\n  pr.B = linear.slope;\n}\n","import Regression from 'ml-regression-power';\nimport { xMaxValue, xMinValue } from 'ml-spectra-processing';\n\nexport function peaksWidth(peaks) {\n  let xs = peaks.map((peak) => peak.x);\n  let widths = peaks.map((peak) => peak.width);\n\n  if (xs.length < 2) {\n    throw new Error(\n      `peaksWidth: not enough peaks (less than 2) for automatic width calculation: ${xs.length}`,\n    );\n  }\n  let regression = new Regression(xs, widths, {\n    computeQuality: true,\n    computeCoefficient: true,\n  });\n\n  if (isNaN(regression.A) || isNaN(regression.B)) {\n    throw new Error('peaksWidth: can not calculate regression');\n  }\n\n  let from = xMinValue(xs);\n  let to = xMaxValue(xs);\n\n  let regressionChart = { x: [], y: [] };\n  for (let x = from; x <= to; x += (to - from) / 1000) {\n    regressionChart.x.push(x);\n    regressionChart.y.push(regression.predict(x));\n  }\n  return {\n    widths: {\n      x: xs,\n      y: widths,\n    },\n    fit: regressionChart,\n    score: regression.score(xs, widths),\n\n    predictFct: regression.predict.bind(regression),\n    tex: regression.toLaTeX(3),\n    A: regression.A,\n    B: regression.B,\n    predictFctString: `${regression.A} * mass ** ${regression.B}`,\n  };\n}\n","import { isAnyArray } from 'is-any-array';\nimport { xMaxValue, xNormed } from 'ml-spectra-processing';\nimport { parseXY } from 'xy-parser';\n\nimport { getBestPeaks } from './getBestPeaks';\nimport { getFragmentPeaks } from './getFragmentPeaks';\nimport { getMassRemainder } from './getMassRemainder';\nimport { getPeakChargeBySimilarity } from './getPeakChargeBySimilarity';\nimport { getPeaks } from './getPeaks';\nimport { isContinuous } from './isContinuous';\nimport { peakPicking } from './peakPicking';\nimport { peaksWidth } from './peaksWidth';\n\n/**\n * Class dealing with mass spectra and peak picking\n */\nexport class Spectrum {\n  constructor(data = { x: [], y: [] }) {\n    if (\n      typeof data !== 'object' ||\n      !isAnyArray(data.x) ||\n      !isAnyArray(data.y)\n    ) {\n      throw new TypeError('Spectrum data must be an object with x:[], y:[]');\n    }\n    this.data = { ...data };\n    Object.defineProperty(this.data, 'xOriginal', {\n      enumerable: false,\n      writable: true,\n    });\n    this.cache = {};\n    this.peaks = [];\n  }\n\n  maxY() {\n    return xMaxValue(this.data.y);\n  }\n\n  sumY() {\n    if (!this.cache.sumY) {\n      this.cache.sumY = this.data.y.reduce(\n        (previous, current) => previous + current,\n        0,\n      );\n    }\n    return this.cache.sumY;\n  }\n\n  scaleY(intensity = 1) {\n    let basePeak = this.maxY() / intensity;\n    this.data.y = this.data.y.map((y) => y / basePeak);\n    return this;\n  }\n\n  rescaleX(callback) {\n    this.ensureOriginalX();\n\n    for (let i = 0; i < this.data.x.length; i++) {\n      this.data.x[i] = callback(this.data.xOriginal[i]);\n    }\n\n    return this;\n  }\n\n  ensureOriginalX() {\n    if (!this.data.xOriginal) {\n      this.data.xOriginal = this.data.x.slice(0);\n    }\n  }\n\n  normedY(total = 1) {\n    this.data.y = xNormed(this.data.y);\n    if (total !== 1) {\n      this.data.y = this.data.y.map((y) => y * total);\n    }\n    return this;\n  }\n\n  peakPicking() {\n    peakPicking(this);\n    return this.peaks;\n  }\n\n  peaksWidth() {\n    peakPicking(this);\n    return peaksWidth(this.peaks);\n  }\n\n  getBestPeaks(options) {\n    peakPicking(this);\n    return getBestPeaks(this.peaks, options);\n  }\n\n  getPeakChargeBySimilarity(targetMass, options) {\n    return getPeakChargeBySimilarity(this, targetMass, options);\n  }\n\n  getPeaks(options) {\n    peakPicking(this);\n    return getPeaks(this.peaks, options);\n  }\n\n  getPeaksAsDataXY(options) {\n    peakPicking(this);\n    const peaks = getPeaks(this.peaks, options);\n    return {\n      x: peaks.map((peak) => peak.x),\n      y: peaks.map((peak) => peak.y),\n    };\n  }\n\n  /**\n   * Returns also peaks possible for a specific molecular formula\n   * @example\n   *  const spectrum = new Spectrum({x:[], y:[]})\n   *  await spectrum.getFragmentPeaks();\n   * @param {string} mf\n   * @param {object} options\n   * @returns\n   */\n  getFragmentPeaksFct(mf, options) {\n    peakPicking(this);\n    return getFragmentPeaks(this.peaks, mf, options);\n  }\n\n  isContinuous() {\n    return isContinuous(this);\n  }\n\n  /**\n   * Remove an integer number of time the specifiedd monoisotopic mass\n   * Mass remainder analysis (MARA): https://doi.org/10.1021/acs.analchem.7b04730\n   */\n  getMassRemainderFct(mass, options = {}) {\n    return getMassRemainder(this.data, mass, options);\n  }\n}\n\nexport function fromPeaks(peaks) {\n  if (peaks.length === 0) return new Spectrum();\n  const data = {};\n  for (let key of Object.keys(peaks[0])) {\n    data[key] = peaks.map((peak) => peak[key]);\n  }\n  return new Spectrum(data);\n}\n\nexport function fromText(text) {\n  const data = parseXY(text);\n  return new Spectrum(data);\n}\n","export default function cosine(a, b) {\n  var ii = a.length;\n  var p = 0;\n  var p2 = 0;\n  var q2 = 0;\n  for (var i = 0; i < ii; i++) {\n    p += a[i] * b[i];\n    p2 += a[i] * a[i];\n    q2 += b[i] * b[i];\n  }\n  return p / (Math.sqrt(p2) * Math.sqrt(q2));\n}\n","import { similarity } from 'ml-distance';\nimport {\n  xIsMonotoneIncreasing,\n  xyArrayAlign,\n  xyFilterMinYValue,\n  xyFilterTopYValues,\n  xySortX,\n} from 'ml-spectra-processing';\n\nexport class MSComparator {\n  /**\n   * Create a class that will be able to get the similarity between 2 spectra\n   * @param {object} [options={}]\n   * @param {number} [options.nbPeaks]\n   * @param {number} [options.minIntensity]\n   * @param {number} [options.massPower=3]\n   * @param {number} [options.intensityPower=0.6]\n   * @param {number|Function} [options.delta=0.1]\n   */\n  constructor(options = {}) {\n    this.options = {\n      massPower: 3,\n      intensityPower: 0.6,\n      delta: 0.1,\n      ...options,\n    };\n    this.cache = new WeakMap();\n  }\n\n  /**\n   *\n   * @param {import('cheminfo-types').DataXY} dataXY1\n   * @param {import('cheminfo-types').DataXY} dataXY2\n   */\n  getSimilarity(dataXY1, dataXY2) {\n    const data1 = normalizeAndCacheData(this.cache, dataXY1, this.options);\n    const data2 = normalizeAndCacheData(this.cache, dataXY2, this.options);\n    const aligned = xyArrayAlign([data1, data2], this.options);\n\n    const vector1 = new Float64Array(aligned.x.length);\n    const vector2 = new Float64Array(aligned.x.length);\n    for (let i = 0; i < aligned.x.length; i++) {\n      vector1[i] =\n        aligned.x[i] ** this.options.massPower *\n        aligned.ys[0][i] ** this.options.intensityPower;\n      vector2[i] =\n        aligned.x[i] ** this.options.massPower *\n        aligned.ys[1][i] ** this.options.intensityPower;\n    }\n\n    return similarity.cosine(vector1, vector2);\n  }\n}\n\n/**\n *\n * @param {WeakMap} cache\n * @param {import('cheminfo-types').DataXY} dataXY\n * @param {object} [options={}]\n * @param {number} [options.nbPeaks]\n * @param {number} [options.minIntensity]\n */\nfunction normalizeAndCacheData(cache, dataXY, options = {}) {\n  const { nbPeaks, minIntensity } = options;\n  if (cache.has(dataXY)) return cache.get(dataXY);\n\n  let data = { ...dataXY };\n  if (!xIsMonotoneIncreasing(data.x)) {\n    data = xySortX(data);\n  }\n\n  if (minIntensity !== undefined) {\n    data = xyFilterMinYValue(data, minIntensity);\n  }\n\n  if (nbPeaks !== undefined) {\n    data = xyFilterTopYValues(data, nbPeaks);\n  }\n\n  cache.set(dataXY, data);\n  return data;\n}\n","import { EMDB } from 'emdb';\n\n/**\n *\n * @param {array} bestPeaks\n * @param {object} [options={}]\n * @param {array} [options.mfColors={}]\n * @param {number} [options.numberDigits=5]\n * @param {number} [options.numberMFs=0]\n * @param {boolean} [options.showMF=false]\n * @param {array} [options.mfColors={}]\n * @param {number} [options.charge=1]\n * @param {number} [options.shift=0]\n * @param {object} [options.mfPrefs]\n * @param {number} [options.displayCharge=true]\n * @param {number} [options.displayProperties=[]] Array of properties name to display\n * @returns {Promise}\n */\n\nexport async function getPeaksAnnotation(bestPeaks, options = {}) {\n  const emdb = new EMDB();\n\n  options = { limit: 5, precision: 100, ...options };\n\n  let {\n    numberDigits = 5,\n    shift = 0,\n    showMF = false,\n    numberMFs = 0,\n    charge = 1,\n    mfPrefs = {},\n    displayCharge = true,\n    displayProperties = [],\n    mfColors = [\n      { limit: 3, color: 'green' },\n      { limit: 20, color: 'lightgreen' },\n      { limit: 50, color: 'lightorange' },\n    ],\n  } = options;\n  if (showMF && !numberMFs) numberMFs = 1;\n  let annotations = [];\n  bestPeaks.sort((a, b) => (a.close ? -1 : b.close ? 1 : 0));\n\n  for (let peak of bestPeaks) {\n    let textLine = 0;\n    let annotation;\n    if (peak.close) {\n      annotation = {\n        type: 'line',\n        _highlight: peak._highlight,\n        info: peak,\n        position: [\n          {\n            y: peak.y,\n            dy: '-5px',\n            x: peak.x,\n          },\n          {\n            y: peak.y,\n            dy: '-15px',\n            x: peak.x,\n          },\n        ],\n      };\n      annotations.push(annotation);\n      annotation = {\n        type: 'ellipse',\n        _highlight: peak._highlight,\n        info: peak,\n        position: [\n          {\n            y: peak.y,\n            dy: '-15px',\n            x: peak.x,\n          },\n        ],\n        props: {\n          rx: '3px',\n          ry: '3px',\n          fillOpacity: 0.3,\n        },\n      };\n    } else {\n      annotation = {\n        type: 'line',\n        _highlight: peak._highlight,\n        position: [\n          {\n            y: peak.y,\n            dy: '-5px',\n            x: peak.x,\n          },\n          {\n            y: peak.y,\n            dy: '-25px',\n            x: peak.x,\n          },\n        ],\n        labels: [\n          {\n            text: (peak.x + shift).toFixed(numberDigits),\n            color: 'red',\n            position: {\n              x: peak.x,\n              y: peak.y,\n              dy: `${textLine++ * -13 - 17}px`,\n              dx: '2px',\n            },\n          },\n        ],\n      };\n\n      if (displayCharge) {\n        annotation.labels.push({\n          text: `Z:${peak.charge}`,\n          color: 'grey',\n          position: {\n            x: peak.x,\n            y: peak.y,\n            dy: '-4px',\n            dx: '2px',\n          },\n        });\n      }\n\n      if (numberMFs) {\n        // we have 2 cases. Either there is a shift and we deal with differences\n        // otherwise it is absolute\n        // if there is a shift we consider only a neutral loss and the parameter charge is important\n        if (shift) {\n          // neutral loss\n          let currentMfPrefs = {\n            ...mfPrefs,\n            allowNeutral: true,\n            ionizations: '',\n          };\n          // we need to deal with the precision and increase it\n          currentMfPrefs.precision =\n            (currentMfPrefs.precision / Math.max(Math.abs(peak.x + shift), 1)) *\n            peak.x;\n          await emdb.fromMonoisotopicMass(\n            Math.abs((peak.x + shift) * charge),\n            currentMfPrefs,\n          );\n        } else {\n          await emdb.fromMonoisotopicMass(Math.abs(peak.x * charge), mfPrefs);\n        }\n\n        let mfs = emdb.get('monoisotopic');\n        let numberOfMFS = Math.min(mfs.length, numberMFs);\n\n        for (let i = 0; i < numberOfMFS; i++) {\n          let mf = mfs[i];\n\n          let ppm = shift ? (mf.ms.ppm / shift) * mfs[0].ms.em : mf.ms.ppm;\n          annotation.labels.push({\n            text: mf.mf,\n            color: getColor(mfColors, Math.abs(ppm)),\n            position: {\n              x: peak.x,\n              y: peak.y,\n              dy: `${textLine++ * -13 - 17}px`,\n              dx: '2px',\n            },\n          });\n        }\n      }\n\n      if (displayProperties.length > 0) {\n        for (let property of displayProperties) {\n          annotation.labels.push({\n            text: peak[property],\n            color: 'red',\n            position: {\n              x: peak.x,\n              y: peak.y,\n              dy: `${textLine++ * -13 - 17}px`,\n              dx: '2px',\n            },\n          });\n        }\n      }\n    }\n    annotations.push(annotation);\n  }\n  return annotations;\n}\n\nfunction getColor(colors, value) {\n  for (let color of colors) {\n    if (value < color.limit) return color.color;\n  }\n  return 'lightgrey';\n}\n","import { getPeaksAnnotation } from './getPeaksAnnotation.js';\n\nexport const JsGraph = {\n  getPeaksAnnotation,\n};\n","/**\n * @param {object}   [entry={}]\n * @param {object}   [options={}]\n * @param {number}   [options.min=-Infinity] - Minimal unsaturation\n * @param {number}   [options.max=+Infinity] - Maximal unsaturation\n * @param {boolean}   [options.onlyInteger=false] - Integer unsaturation\n * @param {boolean}   [options.onlyNonInteger=false] - Non integer unsaturation\n * @return {boolean}\n */\n\nexport function unsaturationMatcher(entry, options = {}) {\n  const {\n    min = Number.MIN_SAFE_INTEGER,\n    max = Number.MAX_SAFE_INTEGER,\n    onlyInteger,\n    onlyNonInteger,\n  } = options;\n\n  if (entry.unsaturation !== undefined) {\n    if (entry.unsaturation < min || entry.unsaturation > max) return false;\n    if (onlyInteger && !Number.isInteger(entry.unsaturation)) return false;\n    if (onlyNonInteger && Number.isInteger(entry.unsaturation)) return false;\n  }\n  return true;\n}\n","import { unsaturationMatcher } from './unsaturationMatcher.js';\n\n/**\n * Returns true if the entry containing MF information match\n * @param {object}   [entry={}] - object containing mw, ...\n * @param {object}   [options={}]\n * @param {number}   [options.minMW=0] - Minimal molecular weight\n * @param {number}   [options.maxMW=+Infinity] - Maximal molecular weight\n * @param {number}   [options.minEM=0] - Minimal monoisotopic mass\n * @param {number}   [options.maxEM=+Infinity] - Maximal monoisotopic mass\n * @param {number}   [options.minCharge=-Infinity] - Minimal charge\n * @param {number}   [options.maxCharge=+Infinity] - Maximal charge\n * @param {object}   [options.unsaturation={}]\n * @param {number}   [options.unsaturation.min=-Infinity] - Minimal unsaturation\n * @param {number}   [options.unsaturation.max=+Infinity] - Maximal unsaturation\n * @param {boolean}   [options.unsaturation.onlyInteger=false] - Integer unsaturation\n * @param {boolean}   [options.unsaturation.onlyNonInteger=false] - Non integer unsaturation\n * @param {object}   [options.atoms] - object of atom:{min, max}\n * @return {boolean}\n */\n\nexport function generalMatcher(entry, options = {}) {\n  const {\n    minMW = 0,\n    maxMW = +Infinity,\n    minEM = 0,\n    maxEM = +Infinity,\n    minCharge = Number.MIN_SAFE_INTEGER,\n    maxCharge = Number.MAX_SAFE_INTEGER,\n    unsaturation = {},\n    atoms,\n  } = options;\n\n  if (entry.mw !== undefined) {\n    if (entry.mw < minMW || entry.mw > maxMW) return false;\n  }\n\n  if (entry.em !== undefined) {\n    if (entry.em < minEM || entry.em > maxEM) return false;\n  }\n\n  if (entry.charge !== undefined) {\n    if (entry.charge < minCharge || entry.charge > maxCharge) return false;\n  }\n\n  if (unsaturation !== undefined && entry.unsaturation !== undefined) {\n    if (!unsaturationMatcher(entry, unsaturation)) return false;\n  }\n\n  if (entry.atoms !== undefined && atoms) {\n    // all the atoms of the entry must fit in the range\n    for (let atom in entry.atoms) {\n      if (!atoms[atom]) return false;\n      if (entry.atoms[atom] < atoms[atom].min) return false;\n      if (entry.atoms[atom] > atoms[atom].max) return false;\n    }\n  }\n  return true;\n}\n","import { ELECTRON_MASS } from 'chemical-elements';\n\nexport function getMsem(em, charge) {\n  if (charge > 0) {\n    return em / charge - ELECTRON_MASS;\n  } else if (charge < 0) {\n    return em / (charge * -1) + ELECTRON_MASS;\n  } else {\n    return 0;\n  }\n}\n","import { getMsem } from './getMsem';\n\n/**\n * Returns an object containing:\n * {ms: {em, charge, ionization}, ionization: {}}\n * We return the ionization in order to know which one has been selected\n */\n\nexport function getMsInfo(entry, options = {}) {\n  const {\n    allowNeutralMolecules,\n    ionization = { mf: '', em: 0, charge: 0 },\n    forceIonization = false,\n    targetMass,\n  } = options;\n\n  let realIonization = ionization;\n  if (!forceIonization && entry.ionization && entry.ionization.mf !== '') {\n    realIonization = entry.ionization;\n  }\n  let ms = {\n    ionization: realIonization.mf,\n    em: 0,\n    charge: entry.charge + realIonization.charge,\n  };\n\n  if (ms.charge !== 0) {\n    ms.em = getMsem(entry.em + realIonization.em, ms.charge);\n  } else if (allowNeutralMolecules) {\n    ms.em = entry.em + realIonization.em;\n  }\n  if (targetMass) {\n    ms.delta = targetMass - ms.em;\n    ms.ppm = ((targetMass - ms.em) / ms.em) * 1e6;\n  }\n  return {\n    ms,\n    ionization: realIonization,\n  };\n}\n","import { MF } from 'mf-parser';\n\nexport function mfDiff(mfString1, mfString2) {\n  let mf1 = new MF(mfString1).getInfo().atoms;\n  let mf2 = new MF(mfString2).getInfo().atoms;\n  let atoms = Object.keys(mf1);\n  Object.keys(mf2).forEach((atom) => {\n    if (!atoms.includes(atom)) atoms.push(atom);\n  });\n  let mf = '';\n  for (let atom of atoms) {\n    let diff = (mf1[atom] || 0) - (mf2[atom] || 0);\n    if (diff) mf += atom + diff;\n  }\n  return new MF(mf).toMF();\n}\n","export function processRange(string, comment, options = {}) {\n  const { limit } = options;\n  let results = [];\n  let parts = string.split(/(-?[0-9]+--?[0-9]+)/).filter((v) => v); // remove empty parts\n\n  let position = -1;\n  let mfs = [];\n  for (let i = 0; i < parts.length; i++) {\n    let part = parts[i];\n    if (!~part.search(/-?[0-9]--?[0-9]/)) {\n      position++;\n      mfs[position] = {\n        mf: part,\n        min: 1,\n        max: 1,\n      };\n    } else {\n      let min = part.replace(/^(-?[0-9]*)-(-?[0-9]*)/, '$1') >> 0;\n      let max = part.replace(/^(-?[0-9]*)-(-?[0-9]*)/, '$2') >> 0;\n      mfs[position].min = Math.min(min, max);\n      mfs[position].max = Math.max(min, max);\n    }\n  }\n\n  let currents = new Array(mfs.length);\n  for (let i = 0; i < currents.length; i++) {\n    currents[i] = mfs[i].min;\n  }\n\n  position = 0;\n  while (position < currents.length) {\n    if (currents[position] < mfs[position].max) {\n      results.push(getMF(mfs, currents, comment));\n      currents[position]++;\n      for (let i = 0; i < position; i++) {\n        currents[i] = mfs[i].min;\n      }\n      position = 0;\n    } else {\n      position++;\n    }\n    if (results.length > limit) {\n      throw Error(`processRange generates to many fragments (over ${limit})`);\n    }\n  }\n\n  results.push(getMF(mfs, currents, comment));\n  return results;\n}\n\nfunction getMF(mfs, currents, comment) {\n  let mf = '';\n  for (let i = 0; i < mfs.length; i++) {\n    if (currents[i] === 0) {\n      // TODO we need to remove from currents[i] till we reach another part of the MF\n      mf += removeMFLastPart(mfs[i].mf);\n    } else {\n      mf += mfs[i].mf;\n      if (currents[i] !== 1) {\n        mf += currents[i];\n      }\n    }\n  }\n  if (comment) mf += `$${comment}`;\n  return mf;\n}\n\n/*\n   Allows to remove the last part of a MF. Useful when you have something with '0' times.\n   C10H -> C10\n   C10((Me)N) -> C10\n   C10Ala -> C10\n   C10Ala((Me)N) -> C10Ala\n   */\nfunction removeMFLastPart(mf) {\n  let parenthesis = 0;\n  let start = true;\n  for (let i = mf.length - 1; i >= 0; i--) {\n    let ascii = mf.charCodeAt(i);\n\n    if (ascii > 96 && ascii < 123) {\n      // lowercase\n      if (!start && !parenthesis) {\n        return mf.substr(0, i + 1);\n      }\n    } else if (ascii > 64 && ascii < 91) {\n      // uppercase\n      if (!start && !parenthesis) {\n        return mf.substr(0, i + 1);\n      }\n      start = false;\n    } else if (ascii === 40) {\n      // (\n      parenthesis--;\n      if (!parenthesis) return mf.substr(0, i);\n    } else if (ascii === 41) {\n      // )\n      parenthesis++;\n    } else {\n      start = false;\n      if (!parenthesis) return mf.substr(0, i + 1);\n    }\n  }\n  return '';\n}\n","import { MF } from 'mf-parser';\n\nimport { processRange } from './processRange';\n\nexport function preprocessIonizations(ionizationsString = '') {\n  if (Array.isArray(ionizationsString)) return ionizationsString;\n  let ionizations = ionizationsString.split(/ *[.,;\\t\\r\\n]+ */);\n\n  // it is allowed to have ranges in Ionizations. We need to explode them.\n\n  let results = [];\n\n  for (let ionization of ionizations) {\n    let parts = processRange(ionization);\n    for (let part of parts) {\n      let info = new MF(part).getInfo();\n      results.push({\n        mf: part,\n        em: info.monoisotopicMass,\n        charge: info.charge,\n        atoms: info.atoms,\n      });\n    }\n  }\n\n  return results;\n}\n","import { Kind, MF, parse } from 'mf-parser';\n\nexport function preprocessRanges(ranges) {\n  ranges = JSON.parse(JSON.stringify(ranges));\n  if (typeof ranges === 'string') {\n    // need to convert to ranges\n    let parsed = parse(ranges.replace(/[\\r\\n\\t ]/g, ''));\n    let newRanges = [];\n    let current = {\n      mf: '',\n      min: 1,\n      max: 1,\n    };\n\n    // example ClBr2(CH2)0-2NO\n    // the idea is that has long as we don't have a range we don't really care\n    // there is a limitation is that the range has to be first level of parenthesis\n    let parenthesisLevel = 0;\n    let currentMF = ''; // start at an atom first level or a parenthesis\n    for (let item of parsed) {\n      switch (item.kind) {\n        case Kind.ATOM:\n          if (parenthesisLevel === 0 && currentMF) {\n            current.mf += currentMF;\n            currentMF = '';\n          }\n          currentMF += item.value;\n          break;\n        case Kind.ISOTOPE:\n          if (parenthesisLevel === 0 && currentMF) {\n            current.mf += currentMF;\n            currentMF = '';\n          }\n          currentMF += `[${item.value.isotope}${item.value.atom}]`;\n          break;\n        case Kind.MULTIPLIER:\n          if (parenthesisLevel === 0 && currentMF) {\n            current.mf += currentMF + item.value;\n            currentMF = '';\n          } else {\n            currentMF += item.value;\n          }\n          break;\n        case Kind.MULTIPLIER_RANGE:\n          if (parenthesisLevel !== 0) {\n            throw new Error('Range multiplier can only be at the first level');\n          }\n          newRanges.push({\n            mf: currentMF,\n            min: item.value.from,\n            max: item.value.to,\n          });\n          currentMF = '';\n          break;\n        case Kind.OPENING_PARENTHESIS:\n          parenthesisLevel++;\n          currentMF += '(';\n          break;\n        case Kind.CLOSING_PARENTHESIS:\n          parenthesisLevel--;\n          currentMF += ')';\n          break;\n        default:\n          throw Error(`can not preprocess ${ranges}`);\n      }\n    }\n    if (currentMF) {\n      current.mf += currentMF;\n    }\n    if (current.mf) {\n      newRanges.push(current);\n    }\n    ranges = newRanges;\n  }\n  let possibilities = [];\n  for (let i = 0; i < ranges.length; i++) {\n    let range = ranges[i];\n    let min = range.min === undefined ? 0 : range.min;\n    let max = range.max === undefined ? 1 : range.max;\n    let possibility = {\n      mf: range.mf,\n      originalMinCount: min, // value defined by the user\n      originalMaxCount: max, // value defined by the user\n      currentMinCount: min,\n      currentMaxCount: max,\n      currentCount: min,\n      currentMonoisotopicMass: 0,\n      currentCharge: 0,\n      currentUnsaturation: 0,\n      initialOrder: i,\n      minInnerMass: 0,\n      maxInnerMass: 0,\n      minInnerCharge: 0,\n      maxInnerCharge: 0,\n      minCharge: 0,\n      maxCharge: 0,\n      minMass: 0,\n      maxMass: 0,\n      innerCharge: false,\n    };\n    possibilities.push(possibility);\n    let info = new MF(range.mf).getInfo();\n    possibility.em = range.em || info.monoisotopicMass;\n    possibility.charge = range.charge || info.charge;\n    possibility.unsaturation =\n      range.unsaturation === undefined\n        ? (info.unsaturation - 1) * 2\n        : range.unsaturation;\n    possibility.atoms = info.atoms;\n    if (possibility.mf !== info.mf) {\n      possibility.isGroup = true;\n    }\n  }\n  possibilities = possibilities.filter(\n    (r) => r.originalMinCount !== 0 || r.originalMaxCount !== 0,\n  );\n\n  // we will sort the way we analyse the data\n  // 1. The one possibility parameter\n  // 2. The charged part\n  // 3. Decreasing em\n  possibilities.sort((a, b) => {\n    if (a.originalMinCount === a.originalMaxCount) return -1; // should be in front, they are 'static'\n    if (b.originalMinCount === b.originalMaxCount) return 1;\n    if (a.charge && b.charge) {\n      if (Math.abs(a.charge) > Math.abs(b.charge)) return -1;\n      if (Math.abs(a.charge) < Math.abs(b.charge)) return 1;\n      return b.em - a.em;\n    }\n    if (a.charge) return -1;\n    if (b.charge) return 1;\n    return b.em - a.em;\n  });\n\n  // we calculate couple of fixed values\n\n  for (let i = 0; i < possibilities.length; i++) {\n    for (let j = i; j < possibilities.length; j++) {\n      let possibility = possibilities[j];\n      if (possibility.em > 0) {\n        possibilities[i].minMass +=\n          possibility.em * possibility.originalMinCount;\n        possibilities[i].maxMass +=\n          possibility.em * possibility.originalMaxCount;\n      } else {\n        possibilities[i].minMass +=\n          possibility.em * possibility.originalMaxCount;\n        possibilities[i].maxMass +=\n          possibility.em * possibility.originalMinCount;\n      }\n      if (possibility.charge > 0) {\n        possibilities[i].minCharge +=\n          possibility.charge * possibility.originalMinCount;\n        possibilities[i].maxCharge +=\n          possibility.charge * possibility.originalMaxCount;\n      } else {\n        possibilities[i].minCharge +=\n          possibility.charge * possibility.originalMaxCount;\n        possibilities[i].maxCharge +=\n          possibility.charge * possibility.originalMinCount;\n      }\n    }\n  }\n\n  for (let i = 0; i < possibilities.length; i++) {\n    if (i < possibilities.length - 1) {\n      let possibility = possibilities[i];\n      let innerPossibility = possibilities[i + 1];\n      possibility.minInnerMass = innerPossibility.minMass;\n      possibility.maxInnerMass = innerPossibility.maxMass;\n      possibility.minInnerCharge = innerPossibility.minCharge;\n      possibility.maxInnerCharge = innerPossibility.maxCharge;\n      if (possibility.minInnerCharge || possibility.maxInnerCharge) {\n        possibility.innerCharge = true;\n      }\n    }\n  }\n\n  return possibilities;\n}\n","import { Kind, parse } from 'mf-parser';\n\nexport function getRangesForFragment(ranges) {\n  ranges = JSON.parse(JSON.stringify(ranges));\n  if (typeof ranges === 'string') {\n    // need to convert to ranges\n    let parsed = parse(ranges.replace(/[\\r\\n\\t ]/g, ''));\n    let newRanges = [];\n\n    // example ClBr2(CH2)0-2NO\n    // the idea is that has long as we don't have a range we don't really care\n    // there is a limitation is that the range has to be first level of parenthesis\n    let parenthesisLevel = 0;\n    let currentMF = ''; // start at an atom first level or a parenthesis\n    for (let item of parsed) {\n      switch (item.kind) {\n        case Kind.ATOM:\n          if (parenthesisLevel === 0 && currentMF) {\n            newRanges.push({\n              mf: currentMF,\n            });\n            currentMF = '';\n          }\n          currentMF += item.value;\n          break;\n        case Kind.ISOTOPE:\n          if (parenthesisLevel === 0 && currentMF) {\n            newRanges.push({\n              mf: currentMF,\n            });\n            currentMF = '';\n          }\n          currentMF += `[${item.value.isotope}${item.value.atom}]`;\n          break;\n        case Kind.MULTIPLIER:\n          if (parenthesisLevel === 0 && currentMF) {\n            newRanges.push({\n              mf: currentMF,\n              max: item.value,\n            });\n            currentMF = '';\n          } else {\n            currentMF += item.value;\n          }\n          break;\n        case Kind.MULTIPLIER_RANGE:\n          if (parenthesisLevel !== 0) {\n            throw new Error('Range multiplier can only be at the first level');\n          }\n          newRanges.push({\n            mf: currentMF,\n            min: item.value.from,\n            max: item.value.to,\n          });\n          currentMF = '';\n          break;\n        case Kind.OPENING_PARENTHESIS:\n          if (parenthesisLevel === 0 && currentMF) {\n            newRanges.push({\n              mf: currentMF,\n            });\n            currentMF = '';\n          }\n          parenthesisLevel++;\n          currentMF += '(';\n          break;\n        case Kind.CLOSING_PARENTHESIS:\n          parenthesisLevel--;\n          currentMF += ')';\n          break;\n        default:\n          throw Error(`can not preprocess ${ranges}`);\n      }\n    }\n    if (currentMF) {\n      newRanges.push({ mf: currentMF });\n    }\n    ranges = newRanges;\n  }\n  let possibilities = [];\n  for (const range of ranges) {\n    if (range.max === 0) continue;\n    let max = range.max === undefined ? 1 : range.max;\n    possibilities.push(`${range.mf}0-${max}`);\n  }\n\n  return possibilities.join(' ');\n}\n","/**\n * Returns the closest index of a `target`\n *\n * @param array - array of numbers\n * @param target - target\n * @returns - closest index\n */\nexport function xFindClosestIndex(array, target, options = {}) {\n    const { sorted = true } = options;\n    if (sorted) {\n        let low = 0;\n        let high = array.length - 1;\n        let middle = 0;\n        while (high - low > 1) {\n            middle = low + ((high - low) >> 1);\n            if (array[middle] < target) {\n                low = middle;\n            }\n            else if (array[middle] > target) {\n                high = middle;\n            }\n            else {\n                return middle;\n            }\n        }\n        if (low < array.length - 1) {\n            if (Math.abs(target - array[low]) < Math.abs(array[low + 1] - target)) {\n                return low;\n            }\n            else {\n                return low + 1;\n            }\n        }\n        else {\n            return low;\n        }\n    }\n    else {\n        let index = 0;\n        let diff = Number.POSITIVE_INFINITY;\n        for (let i = 0; i < array.length; i++) {\n            const currentDiff = Math.abs(array[i] - target);\n            if (currentDiff < diff) {\n                diff = currentDiff;\n                index = i;\n            }\n        }\n        return index;\n    }\n}\n//# sourceMappingURL=xFindClosestIndex.js.map","import { getMsInfo } from 'mf-utilities';\nimport { xFindClosestIndex } from 'ml-spectra-processing';\n\nimport { unsaturationMatcher } from './unsaturationMatcher.js';\n/**\n * @param {object}         [entry={}]\n * @param {object}         [options={}]\n * @param {object}         [options.ionization={ mf: '', em: 0, charge: 0 }] - ionization method\n * @param {boolean}        [options.forceIonization=false] - If true ignore existing ionizations\n * @param {number}         [options.precision=1000] - The precision on the experimental mass\n * @param {number}         [options.targetMass] - Target mass, allows to calculate error and filter results\n * @param {number[]}       [options.targetMasses] - Target masses: SORTED array of numbers\n * @param {number[]}       [options.targetIntensities] - Target intensities: SORTED array of numbers\n * @param {number}         [options.minEM=0] - Minimal monoisotopic mass\n * @param {number}         [options.maxEM=+Infinity] - Maximal monoisotopic mass\n * @param {number}         [options.minMSEM=0] - Minimal monoisotopic mass observed by mass\n * @param {number}         [options.maxMSEM=+Infinity] - Maximal monoisotopic mass observed by mass\n * @param {number}         [options.minCharge=-Infinity] - Minimal charge\n * @param {number}         [options.maxCharge=+Infinity] - Maximal charge\n * @param {boolean}        [options.allowNegativeAtoms=false] - Allow to have negative number of atoms\n * @param {object}         [options.unsaturation={}]\n * @param {number}         [options.unsaturation.min=-Infinity] - Minimal unsaturation\n * @param {number}         [options.unsaturation.max=+Infinity] - Maximal unsaturation\n * @param {boolean}        [options.unsaturation.onlyInteger=false] - Integer unsaturation\n * @param {boolean}        [options.unsaturation.onlyNonInteger=false] - Non integer unsaturation\n * @param {boolean}        [options.atoms] - object of atom:{min, max}\n * @param {Function}       [options.callback] - a function that contains information about the current MF\n * @return {boolean}\n */\n\n/**\n * We always recalculate msem\n */\n\nexport function msemMatcher(entry, options = {}) {\n  const {\n    ionization = { mf: '', em: 0, charge: 0, atoms: {} },\n    forceIonization = false,\n    precision = 1000,\n    minCharge = Number.MIN_SAFE_INTEGER,\n    maxCharge = Number.MAX_SAFE_INTEGER,\n    unsaturation = {},\n    targetMass, // if present we will calculate the errors\n    targetMasses, // if present we will calculate the smallest error\n    targetIntensities, // if present it will be added in the report\n    minEM = -Infinity,\n    maxEM = +Infinity,\n    minMSEM = -Infinity,\n    maxMSEM = +Infinity,\n    allowNegativeAtoms = false,\n    atoms,\n    callback,\n  } = options;\n\n  let msInfo = getMsInfo(entry, {\n    ionization,\n    forceIonization,\n    targetMass,\n  });\n  let ms = msInfo.ms;\n\n  if (entry.em !== undefined) {\n    if (entry.em < minEM || entry.em > maxEM) return false;\n  }\n\n  if (ms.em !== undefined) {\n    if (ms.em < minMSEM || ms.em > maxMSEM) return false;\n  }\n\n  if (targetMass && Math.abs(ms.ppm) > precision) return false;\n\n  if (entry.charge !== undefined) {\n    if (ms.charge < minCharge || ms.charge > maxCharge) return false;\n  }\n  if (unsaturation !== undefined && entry.unsaturation !== undefined) {\n    if (!unsaturationMatcher(entry, unsaturation)) {\n      return false;\n    }\n  }\n  if (entry.atoms !== undefined && atoms) {\n    // all the atoms of the entry must fit in the range\n    for (let atom in entry.atoms) {\n      if (!atoms[atom]) return false;\n      if (entry.atoms[atom] < atoms[atom].min) return false;\n      if (entry.atoms[atom] > atoms[atom].max) return false;\n    }\n  }\n\n  if (entry.atoms !== undefined && !allowNegativeAtoms) {\n    const ionizationAtoms =\n      (msInfo.ionization && msInfo.ionization.atoms) || {};\n    const atomKeys = new Set(\n      Object.keys(ionizationAtoms).concat(Object.keys(entry.atoms)),\n    );\n    for (let atom of atomKeys) {\n      if ((entry.atoms[atom] || 0) + (ionizationAtoms[atom] || 0) < 0) {\n        return false;\n      }\n    }\n  }\n\n  if (targetMasses && targetMasses.length > 0) {\n    let index = xFindClosestIndex(targetMasses, ms.em);\n    let closestMass = targetMasses[index];\n    msInfo = getMsInfo(entry, {\n      ionization,\n      forceIonization,\n      targetMass: closestMass,\n    });\n    msInfo.ms.target = { mass: closestMass };\n    if (targetIntensities) {\n      msInfo.ms.target.intensity = targetIntensities[index];\n    }\n    // need to find the closest targetMasses\n    if (Math.abs(msInfo.ms.ppm) > precision) return false;\n  }\n\n  if (callback) {\n    if (!callback(entry)) return false;\n  }\n\n  return msInfo;\n}\n","import { ELECTRON_MASS } from 'chemical-elements';\n\n/**\n * returns all the possible neutral mass for a defined experimental (targetMass) mass\n */\n\nexport class TargetMassCache {\n  constructor(targetMass, possibilities, options = {}) {\n    const {\n      allowNeutral = false, // msem because em in this case !\n      minCharge = Number.MIN_SAFE_INTEGER,\n      maxCharge = Number.MAX_SAFE_INTEGER,\n      charge = 0,\n      precision = 100,\n    } = options;\n\n    if (!possibilities || possibilities.length === 0) return;\n\n    let firstPossibility = possibilities[0];\n    let currentMinCharge = Math.max(\n      minCharge,\n      firstPossibility.minCharge + charge,\n    );\n    let currentMaxCharge = Math.min(\n      maxCharge,\n      firstPossibility.maxCharge + charge,\n    );\n\n    this.minCharge = currentMinCharge;\n    this.maxCharge = currentMaxCharge;\n\n    let size = this.maxCharge - this.minCharge + 1;\n    this.data = [];\n    let minMass = 0;\n    let maxMass = 0;\n    let range = (targetMass * precision) / 1e6;\n    for (let i = 0; i < size; i++) {\n      let currentCharge = i + this.minCharge;\n      if (currentCharge === 0) {\n        if (allowNeutral) {\n          minMass = targetMass - range;\n          maxMass = targetMass + range;\n        } else {\n          minMass = Number.MAX_SAFE_INTEGER;\n          maxMass = Number.MIN_SAFE_INTEGER;\n        }\n      } else {\n        minMass =\n          (targetMass - range) * Math.abs(currentCharge) +\n          ELECTRON_MASS * currentCharge;\n        maxMass =\n          (targetMass + range) * Math.abs(currentCharge) +\n          ELECTRON_MASS * currentCharge;\n      }\n\n      this.data.push({\n        charge: currentCharge,\n        minMass,\n        maxMass,\n      });\n    }\n  }\n\n  getMinMass(charge) {\n    return this.data[charge - this.minCharge]\n      ? this.data[charge - this.minCharge].minMass\n      : Number.MAX_SAFE_INTEGER;\n  }\n\n  getMaxMass(charge) {\n    return this.data[charge - this.minCharge]\n      ? this.data[charge - this.minCharge].maxMass\n      : Number.MIN_SAFE_INTEGER;\n  }\n}\n","import { atomSorter } from 'atom-sorter';\nimport { msemMatcher } from 'mf-matcher';\nimport {\n  preprocessIonizations,\n  preprocessRanges,\n  getMsInfo,\n} from 'mf-utilities';\n\nimport { TargetMassCache } from './TargetMassCache';\n\n/**\n * @param {number}        targetMass - Monoisotopic mass\n * @param {object}        [options={}]\n * @param {number}        [options.maxIterations=10000000] - Maximum number of iterations\n * @param {boolean}       [options.allowNeutral=true]\n * @param {boolean}       [options.uniqueMFs=true]\n * @param {number}        [options.limit=1000] - Maximum number of results\n * @param {string}        [options.ionizations=''] - string containing a comma separated list of modifications\n * @param {string}        [options.ranges='C0-100 H0-100 O0-100 N0-100'] - range of mfs to search\n * @param {number}        [options.precision=100] - Allowed mass range based on precision\n * @param {object}        [options.filter={}]\n * @param {number}        [options.filter.minCharge=-Infinity] - Minimal charge\n * @param {number}        [options.filter.maxCharge=+Infinity] - Maximal charge\n * @param {object}        [options.filter.unsaturation={}]\n * @param {number}        [options.filter.unsaturation.min=-Infinity] - Minimal unsaturation\n * @param {number}        [options.filter.unsaturation.max=+Infinity] - Maximal unsaturation\n * @param {boolean}        [options.filter.unsaturation.onlyInteger=false] - Integer unsaturation\n * @param {boolean}        [options.filter.unsaturation.onlyNonInteger=false] - Non integer unsaturation\n * @param {object}        [options.filter.atoms] - object of atom:{min, max}\n * @param {function}      [options.filter.callback] - a function to filter the MF\n * @returns {object}\n */\n\nexport function findMFsSync(targetMass, options = {}) {\n  const {\n    filter = {},\n    maxIterations = 1e8,\n    limit = 1000,\n    allowNeutral = true, // if there is no msem we use em !\n    uniqueMFs = false, // if there is no msem we use em !\n    ranges = [\n      { mf: 'C', min: 0, max: 100 },\n      { mf: 'H', min: 0, max: 100 },\n      { mf: 'O', min: 0, max: 100 },\n      { mf: 'N', min: 0, max: 100 },\n    ],\n  } = options;\n  let targetMassCache;\n  const {\n    minCharge = Number.MIN_SAFE_INTEGER,\n    maxCharge = Number.MAX_SAFE_INTEGER,\n    unsaturation = {},\n  } = filter;\n\n  let filterUnsaturation = !!unsaturation;\n  // we calculate not the real unsaturation but the one before dividing by 2 + 1\n  let fakeMinUnsaturation =\n    unsaturation.min === undefined\n      ? Number.MIN_SAFE_INTEGER\n      : (unsaturation.min - 1) * 2;\n  let fakeMaxUnsaturation =\n    unsaturation.max === undefined\n      ? Number.MAX_SAFE_INTEGER\n      : (unsaturation.max - 1) * 2;\n\n  let filterCharge =\n    minCharge !== Number.MIN_SAFE_INTEGER ||\n    maxCharge !== Number.MAX_SAFE_INTEGER;\n\n  let advancedFilter;\n  if (filter.atoms || filter.callback) {\n    advancedFilter = {\n      atoms: filter.atoms,\n      callback: filter.callback,\n    };\n  }\n\n  let result = {\n    mfs: [],\n    info: {\n      numberMFEvaluated: 0,\n      numberResults: 0,\n    },\n  };\n  let orderMapping = []; // used to sort the atoms\n\n  // we need to make the processing for all the ionizations\n  let ionizations = preprocessIonizations(options.ionizations);\n  for (let ionization of ionizations) {\n    let currentIonization = {\n      currentMonoisotopicMass: ionization.em || 0,\n      currentCharge: ionization.charge,\n      currentUnsaturation: 0, // we don't take into account the unsaturation of the ionization agent\n    };\n    // if (DEBUG) console.log('new ionization', ionization.mf, ionization.em, ionization.charge);\n    // ionization em and charge will be used to set the first atom value\n    let possibilities = preprocessRanges(ranges);\n    orderMapping = getOrderMapping(possibilities);\n\n    if (possibilities.length === 0) return { mfs: [] };\n    targetMassCache = new TargetMassCache(targetMass, possibilities, {\n      ...options,\n      ...{ charge: ionization.charge },\n    });\n\n    let theEnd = false;\n    let maxPosition = possibilities.length;\n    let lastPosition = possibilities.length - 1;\n    let currentPosition = 0;\n    let currentAtom;\n    let previousAtom;\n    let lastPossibility = possibilities[lastPosition];\n\n    initializePossibilities(possibilities, currentIonization, targetMassCache);\n\n    //  if (DEBUG) console.log('possibilities', possibilities.map((a) => `${a.mf + a.originalMinCount}-${a.originalMaxCount}`));\n\n    let isValid = false; // designed so that the first time it is not a valid solution\n    while (!theEnd) {\n      if (result.info.numberMFEvaluated++ > maxIterations) {\n        throw Error(\n          `Iteration number is over the current maximum of: ${maxIterations}`,\n        );\n      }\n      if (filterUnsaturation) {\n        let unsaturationValue = lastPossibility.currentUnsaturation;\n        let isOdd = Math.abs(unsaturationValue % 2);\n        if (\n          (unsaturation.onlyInteger && isOdd === 1) ||\n          (unsaturation.onlyNonInteger && isOdd === 0) ||\n          fakeMinUnsaturation > unsaturationValue ||\n          fakeMaxUnsaturation < unsaturationValue\n        ) {\n          isValid = false;\n        }\n      }\n      if (\n        filterCharge &&\n        (lastPossibility.currentCharge < minCharge ||\n          lastPossibility.currentCharge > maxCharge)\n      ) {\n        isValid = false;\n      }\n\n      if (isValid) {\n        let minMass = targetMassCache.getMinMass(lastPossibility.currentCharge);\n        let maxMass = targetMassCache.getMaxMass(lastPossibility.currentCharge);\n        if (\n          lastPossibility.currentMonoisotopicMass < minMass ||\n          lastPossibility.currentMonoisotopicMass > maxMass\n        ) {\n          isValid = false;\n        }\n      }\n\n      if (isValid) {\n        result.info.numberResults++;\n        let newResult = getResult(\n          possibilities,\n          targetMass,\n          allowNeutral,\n          ionization,\n          orderMapping,\n        );\n        if (advancedFilter) {\n          isValid = msemMatcher(newResult, advancedFilter) !== false;\n        }\n        if (isValid) {\n          result.mfs.push(newResult);\n          if (result.mfs.length > 2 * limit) {\n            if (uniqueMFs) ensureUniqueMF(result);\n            result.mfs.sort((a, b) => Math.abs(a.ms.ppm) - Math.abs(b.ms.ppm));\n            result.mfs.length = limit;\n          }\n        }\n      }\n\n      isValid = true;\n      // we need to setup all the arrays if possible\n      while (currentPosition < maxPosition && currentPosition >= 0) {\n        currentAtom = possibilities[currentPosition];\n        previousAtom =\n          currentPosition === 0\n            ? currentIonization\n            : possibilities[currentPosition - 1];\n        if (currentAtom.currentCount < currentAtom.currentMaxCount) {\n          currentAtom.currentCount++;\n          updateCurrentAtom(currentAtom, previousAtom);\n          if (currentPosition < lastPosition) {\n            currentPosition++;\n            setCurrentMinMax(\n              possibilities[currentPosition],\n              possibilities[currentPosition - 1],\n              targetMassCache,\n            );\n          } else {\n            break;\n          }\n        } else {\n          currentPosition--;\n        }\n      }\n\n      if (currentPosition < 0) {\n        theEnd = true;\n      }\n    }\n  }\n\n  if (uniqueMFs) ensureUniqueMF(result);\n  result.mfs.sort((a, b) => Math.abs(a.ms.ppm) - Math.abs(b.ms.ppm));\n  if (result.mfs.length > limit) {\n    result.mfs.length = limit;\n  }\n  result.mfs.forEach((mf) => delete mf.currentCounts);\n  return result;\n}\n\n/**\n * Ensure that we have only once the same MF\n * In order to improve the speed we just consider the em\n * @param {object} result\n */\nfunction ensureUniqueMF(result) {\n  result.mfs.sort((a, b) => a.em - b.em);\n  let previousEM = 0;\n  let bestCounts = [];\n  const mfs = [];\n  next: for (let current of result.mfs) {\n    if (current.em - previousEM > 1e-8) {\n      previousEM = current.em;\n      bestCounts = current.currentCounts;\n      mfs.push(current);\n    } else {\n      for (let i = 0; i < current.currentCounts.length; i++) {\n        // better priority ???\n        if (current.currentCounts[i] > bestCounts[i]) {\n          mfs.pop();\n          mfs.push(current);\n          bestCounts = current.currentCounts;\n          continue;\n        } else if (current.currentCounts[i] < bestCounts[i]) {\n          continue next;\n        }\n      }\n    }\n  }\n  result.mfs = mfs;\n}\n\nfunction updateCurrentAtom(currentAtom, previousAtom) {\n  currentAtom.currentMonoisotopicMass =\n    previousAtom.currentMonoisotopicMass +\n    currentAtom.em * currentAtom.currentCount;\n  currentAtom.currentCharge =\n    previousAtom.currentCharge + currentAtom.charge * currentAtom.currentCount;\n  currentAtom.currentUnsaturation =\n    previousAtom.currentUnsaturation +\n    currentAtom.unsaturation * currentAtom.currentCount;\n}\n\nfunction getResult(\n  possibilities,\n  targetMass,\n  allowNeutralMolecules,\n  ionization,\n  orderMapping,\n) {\n  let lastPossibility = possibilities[possibilities.length - 1];\n\n  let result = {\n    em: lastPossibility.currentMonoisotopicMass - ionization.em,\n    unsaturation: lastPossibility.currentUnsaturation,\n    mf: '',\n    charge: lastPossibility.currentCharge - ionization.charge,\n    ionization,\n    atoms: {},\n    groups: {},\n    currentCounts: possibilities.map((possibility) => possibility.currentCount),\n  };\n\n  // we check that the first time we meet the ionization group it does not end\n  // in the final result\n\n  for (let i = 0; i < possibilities.length; i++) {\n    let possibility = possibilities[orderMapping[i]];\n    if (possibility.currentCount !== 0) {\n      if (possibility.isGroup) {\n        if (possibility.currentCount === 1) {\n          result.mf += `${possibility.mf}`;\n        } else if (possibility.mf.match(/^\\([^()]*\\)$/)) {\n          result.mf += `${possibility.mf}${possibility.currentCount}`;\n        } else {\n          result.mf += `(${possibility.mf})${possibility.currentCount}`;\n        }\n        if (result.groups[possibility.mf]) {\n          result.groups[possibility.mf] += possibility.currentCount;\n        } else {\n          result.groups[possibility.mf] = possibility.currentCount;\n        }\n      } else {\n        result.mf += possibility.mf;\n        if (possibility.currentCount !== 1) {\n          result.mf += possibility.currentCount;\n        }\n      }\n      for (let atom in possibility.atoms) {\n        if (result.atoms[atom]) {\n          result.atoms[atom] +=\n            possibility.atoms[atom] * possibility.currentCount;\n        } else {\n          result.atoms[atom] =\n            possibility.atoms[atom] * possibility.currentCount;\n        }\n      }\n    }\n  }\n  result.unsaturation = (result.unsaturation + Math.abs(result.charge)) / 2 + 1;\n  result.ms = getMsInfo(result, { targetMass, allowNeutralMolecules }).ms;\n  return result;\n}\n\nfunction setCurrentMinMax(currentAtom, previousAtom, targetMassCache) {\n  // the current min max can only be optimize if the charge will not change anymore\n  if (currentAtom.innerCharge === true || currentAtom.charge !== 0) {\n    currentAtom.currentMinCount = currentAtom.originalMinCount;\n    currentAtom.currentMaxCount = currentAtom.originalMaxCount;\n    currentAtom.currentCount = currentAtom.currentMinCount - 1;\n  } else {\n    // no more change of charge, we can optimize\n    let currentMass =\n      previousAtom !== undefined ? previousAtom.currentMonoisotopicMass : 0;\n    let currentCharge =\n      previousAtom !== undefined ? previousAtom.currentCharge : 0;\n    currentAtom.currentMinCount = Math.max(\n      Math.floor(\n        (targetMassCache.getMinMass(currentCharge) -\n          currentMass -\n          currentAtom.maxInnerMass) /\n          currentAtom.em,\n      ),\n      currentAtom.originalMinCount,\n    );\n    currentAtom.currentMaxCount = Math.min(\n      Math.floor(\n        (targetMassCache.getMaxMass(currentCharge) -\n          currentMass -\n          currentAtom.minInnerMass) /\n          currentAtom.em,\n      ),\n      currentAtom.originalMaxCount,\n    );\n    currentAtom.currentCount = currentAtom.currentMinCount - 1;\n  }\n}\n\nfunction initializePossibilities(\n  possibilities,\n  currentIonization,\n  targetMassCache,\n) {\n  for (let i = 0; i < possibilities.length; i++) {\n    if (i === 0) {\n      updateCurrentAtom(possibilities[i], currentIonization);\n      setCurrentMinMax(possibilities[i], currentIonization, targetMassCache);\n    } else {\n      updateCurrentAtom(possibilities[i], possibilities[i - 1]);\n    }\n  }\n}\n\n// eslint-disable-next-line no-unused-vars\nfunction possibilitiesToString(possibilities) {\n  return possibilities.map((a) => [\n    `mf:${a.mf}`,\n    `current:${a.currentCount}`,\n    `min:${a.currentMinCount}`,\n    `max:${a.currentMaxCount}`,\n    `charge:${a.currentCharge}`,\n  ]);\n}\n\nfunction getOrderMapping(possibilities) {\n  let mapping = possibilities.map((p, i) => ({ atom: p.mf, index: i }));\n  mapping.sort((a, b) => {\n    return atomSorter(a.atom, b.atom);\n  });\n  return mapping.map((a) => a.index);\n}\n","import { findMFsSync } from './findMFsSync';\n\n/**\n * @param {number}        targetMass - Monoisotopic mass\n * @param {object}        [options={}]\n * @param {number}        [options.maxIterations=10000000] - Maximum number of iterations\n * @param {boolean}       [options.allowNeutral=true]\n * @param {boolean}       [options.uniqueMFs=true]\n * @param {number}        [options.limit=1000] - Maximum number of results\n * @param {string}        [options.ionizations=''] - string containing a comma separated list of modifications\n * @param {string}        [options.ranges='C0-100 H0-100 O0-100 N0-100'] - range of mfs to search\n * @param {number}        [options.precision=100] - Allowed mass range based on precision\n * @param {object}        [options.filter={}]\n * @param {number}        [options.filter.minCharge=-Infinity] - Minimal charge\n * @param {number}        [options.filter.maxCharge=+Infinity] - Maximal charge\n * @param {object}        [options.filter.unsaturation={}]\n * @param {number}        [options.filter.unsaturation.min=-Infinity] - Minimal unsaturation\n * @param {number}        [options.filter.unsaturation.max=+Infinity] - Maximal unsaturation\n * @param {boolean}        [options.filter.unsaturation.onlyInteger=false] - Integer unsaturation\n * @param {boolean}        [options.filter.unsaturation.onlyNonInteger=false] - Non integer unsaturation\n * @param {object}        [options.filter.atoms] - object of atom:{min, max}\n * @param {function}      [options.filter.callback] - a function to filter the MF\n * @returns {Promise}\n */\n\nexport async function findMFs(targetMass, options = {}) {\n  return findMFsSync(targetMass, options);\n}\n","import { findMFs } from 'mf-finder';\n\n/**\n *\n * @param {object}   experimentalSpectrum\n * @param {object}   database\n * @param {object}   [options={}]\n * @param {function} [options.onStep] - Callback to do after each step\n * @param {string}   [options.ionizations=''] - string containing a comma separated list of modifications\n * @param {number}   [options.precision=100] - Allowed mass range based on precision\n */\nexport async function appendFragmentsInfo(\n  experimentalSpectrum,\n  database,\n  options = {},\n) {\n  const { ionizations, onStep, precision } = options;\n  if (!experimentalSpectrum) {\n    throw new Error('Experimental spectrum is not defined');\n  }\n  if (!database) {\n    throw new Error('Database is not defined');\n  }\n\n  const peaks = experimentalSpectrum.getPeaks({ sumValue: 1 });\n  for (let entry of database) {\n    const ranges = Object.keys(entry.atoms)\n      .map((atom) => `${atom}0-${entry.atoms[atom]}`)\n      .join(' ');\n\n    entry.fragments = {\n      nbFound: 0,\n      intensityFound: 0,\n      assignments: [],\n    };\n    for (let i = 0; i < peaks.length; i++) {\n      if (onStep) await onStep(i);\n      const peak = peaks[i];\n      const possibleMFs = await findMFs(peak.x, {\n        ionizations,\n        precision,\n        ranges,\n      });\n      if (possibleMFs.mfs.length > 0) {\n        entry.fragments.nbFound++;\n        entry.fragments.intensityFound += peak.y;\n        entry.fragments.assignments.push({\n          peak,\n          bestMF: possibleMFs.mfs[0],\n        });\n      }\n    }\n  }\n}\n","'use strict';\n\n/**\n * Modify object a to join it with b and make the sum of each of the keys\n * @param {*} a\n * @param {*} source1\n *\n * @return {object}\n */\n\nfunction sum(target) {\n    for (var i = 1; i < arguments.length; i++) {\n        let toSum = arguments[i];\n        for (var key of Object.keys(toSum)) {\n            if (target[key]) {\n                target[key] += toSum[key];\n            } else {\n                target[key] = toSum[key];\n            }\n        }\n    }\n    return target;\n}\n\nmodule.exports = sum;\n","import { ELECTRON_MASS } from 'chemical-elements';\nimport { msemMatcher } from 'mf-matcher';\nimport { MF } from 'mf-parser';\nimport { processRange, preprocessIonizations } from 'mf-utilities';\nimport sum from 'sum-object-keys';\n/**\n * Generate all the possible combinations of molecular formula and calculate\n * for each of them the monoisotopic mass and observed moniisotopic mass (m/z)\n * In the molecular formula there may be a comment after the '$' symbol\n *\n * @param keys\n * @param {object}        [options={}]\n * @param {number}        [options.limit=10000000] - Maximum number of results\n * @param {boolean}       [options.estimate=false] - estimate the number of MF without filters\n * @param {boolean}       [options.canonizeMF=true] - Canonize molecular formula\n * @param {boolean}       [options.uniqueMFs=true] - Force canonization and make MF unique\n * @param {string}        [options.ionizations=''] - Comma separated list of ionizations (to charge the molecule)\n * @param {function}      [options.onStep] - Callback to do after each step\n * @param {object}        [options.filter={}] - Minimal monoisotopic mass\n * @param {number}        [options.filter.minMass=0] - Minimal monoisotopic mass\n * @param {number}        [options.filter.maxMass=+Infinity] - Maximal monoisotopic mass\n * @param {number}        [options.filter.minEM=0] - Minimal neutral monoisotopic mass\n * @param {number}        [options.filter.maxEM=+Infinity] - Maximal neutral monoisotopic mass\n * @param {number}        [options.filter.precision=1000] - The precision on the experimental mass\n * @param {number}        [options.filter.targetMass] - Target mass, allows to calculate error and filter results\n * @param {number[]}      [options.filter.targetMasses] - Target masses: SORTED array of numbers\n * @param {number}        [options.filter.precision=1000] - Precision\n * @param {number}        [options.filter.minCharge=-Infinity] - Minimal charge\n * @param {number}        [options.filter.maxCharge=+Infinity] - Maximal charge\n * @param {boolean}       [options.filter.allowNegativeAtoms=false] - Allow to have negative number of atoms\n * @param {object}        [options.filter.unsaturation={}]\n * @param {number}        [options.filter.unsaturation.min=-Infinity] - Minimal unsaturation\n * @param {number}        [options.filter.unsaturation.max=+Infinity] - Maximal unsaturation\n * @param {boolean}       [options.filter.unsaturation.onlyInteger=false] - Integer unsaturation\n * @param {boolean}       [options.filter.unsaturation.onlyNonInteger=false] - Non integer unsaturation\n * @param {object}        [options.filter.atoms] - object of atom:{min, max}\n * @param {function}      [options.filter.callback] - a function to filter the MF\n * @param {string}        [options.filterFct]\n * @param {object}        [options.links]\n * @param {boolean}       [options.links.filter] We filter all the MF that do not match the '*X'\n * @returns {Promise}\n */\n\nexport async function generateMFs(keys, options = {}) {\n  options = { ...options };\n\n  let { limit = 100000, uniqueMFs = true, estimate = false, onStep } = options;\n\n  options.filterFctVariables = {};\n  for (let i = 0; i < keys.length; i++) {\n    const key = keys[i];\n    if (typeof key === 'object' && key.name) {\n      options.filterFctVariables[key.name] = i;\n      keys[i] = key.value;\n    }\n  }\n\n  if (options.filterFct) {\n    // we create a real javascript function\n    let variables = Object.keys(options.filterFctVariables);\n    variables.push('mm', 'mz', 'charge', 'unsaturation', 'atoms');\n    // eslint-disable-next-line no-new-func\n    options.filterFct = new Function(\n      ...variables,\n      `return ${options.filterFct}`,\n    );\n  }\n\n  if (uniqueMFs === true) options.canonizeMF = true;\n  if (options.canonizeMF === undefined) options.canonizeMF = true;\n  options.ionizations = preprocessIonizations(options.ionizations);\n\n  if (!Array.isArray(keys)) {\n    throw new Error('You need to specify an array of strings or arrays');\n  }\n\n  // we allow String delimited by \". or ;\" instead of an array\n  for (let i = 0; i < keys.length; i++) {\n    if (!Array.isArray(keys[i])) {\n      keys[i] = keys[i].split(/[.,]/);\n    }\n  }\n\n  // we allow ranges in a string ...\n  // problem with ranges is that we need to know to what the range applies\n  for (let i = 0; i < keys.length; i++) {\n    let parts = keys[i];\n    let newParts = [];\n    for (let j = 0; j < parts.length; j++) {\n      let part = parts[j];\n      let comment = part.replace(/^([^$]*\\$|.*)/, '');\n      part = part.replace(/\\$.*/, '').replace(/\\s/g, '');\n      if (part.match(/[0-9]-[0-9-]/)) {\n        // deal with negative numbers\n        // there are ranges ... we are in trouble !\n        newParts = newParts.concat(processRange(part, comment, { limit }));\n      } else {\n        newParts.push(parts[j]); // the part with the comments !\n      }\n    }\n    keys[i] = newParts;\n  }\n\n  if (estimate) {\n    let total = keys.reduce(\n      (previous, current) => previous * current.length,\n      1,\n    );\n    return total * options.ionizations.length;\n  }\n\n  let results = [];\n  let sizes = [];\n  let currents = [];\n  for (let i = 0; i < keys.length; i++) {\n    sizes.push(keys[i].length - 1);\n    currents.push(0);\n  }\n  let position = 0;\n  let evolution = 0;\n  while (position < currents.length) {\n    if (currents[position] < sizes[position]) {\n      if (onStep) await onStep(evolution);\n      evolution++;\n      appendResult(results, currents, keys, options);\n      currents[position]++;\n      for (let i = 0; i < position; i++) {\n        currents[i] = 0;\n      }\n      position = 0;\n    } else {\n      position++;\n    }\n    if (evolution > limit) {\n      throw new Error(\n        `You have reached the limit of ${limit}. You could still change this value using the limit option but it is likely to crash.`,\n      );\n    }\n  }\n\n  appendResult(results, currents, keys, options);\n  if (uniqueMFs) {\n    let uniqueMFsObject = {};\n    results.forEach((result) => {\n      uniqueMFsObject[result.mf + result.ionization.mf] = result;\n    });\n    results = Object.keys(uniqueMFsObject).map((k) => uniqueMFsObject[k]);\n  }\n  results.sort((a, b) => a.em - b.em);\n  return results;\n}\n\nlet ems = {};\n\n// internal method used as a cache\nfunction getMonoisotopicMass(mfString) {\n  if (!ems[mfString]) {\n    // we need to calculate based on the mf but not very often ...\n    let mf = new MF(mfString);\n    let info = mf.getInfo();\n    ems[mfString] = {\n      em: info.monoisotopicMass,\n      charge: info.charge,\n      mw: info.mass,\n      unsaturation: (info.unsaturation - 1) * 2,\n      atoms: info.atoms,\n    };\n  }\n  return ems[mfString];\n}\n\nfunction getEMFromParts(parts, currents, ionization) {\n  let charge = 0;\n  let em = 0;\n  let mw = 0;\n  let unsaturation = 0;\n  let validUnsaturation = true;\n  let atoms = {};\n\n  for (let i = 0; i < parts.length; i++) {\n    let part = parts[i][currents[i]];\n    if (part) {\n      let info = getMonoisotopicMass(part);\n      charge += info.charge;\n      em += info.em;\n      mw += info.mw;\n      sum(atoms, info.atoms);\n      if (info.unsaturation && validUnsaturation) {\n        unsaturation += info.unsaturation;\n      }\n    }\n  }\n\n  return {\n    charge,\n    em,\n    mw,\n    ionization,\n    unsaturation: validUnsaturation ? unsaturation / 2 + 1 : undefined,\n    atoms,\n  };\n}\n\nfunction appendResult(results, currents, keys, options = {}) {\n  const { canonizeMF, filter, ionizations, links = {} } = options;\n  // this script is designed to combine molecular formula\n  // that may contain comments after a \"$\" sign\n  // therefore we should put all the comments at the ned\n\n  if (links.filter) {\n    let stars = [];\n    for (let i = 0; i < keys.length; i++) {\n      let anchors = keys[i][currents[i]].match(/#[0-9]+/g);\n      if (anchors) stars.push(...anchors);\n    }\n    if (stars.length % 2 === 1) return;\n    stars = stars.sort();\n    for (let i = 0; i < stars.length; i += 2) {\n      if (stars[i] !== stars[i + 1]) return;\n    }\n  }\n\n  for (let ionization of ionizations) {\n    let result = getEMFromParts(keys, currents, ionization);\n    if (options.filterFct) {\n      let variables = [];\n      for (let key in options.filterFctVariables) {\n        variables.push(currents[options.filterFctVariables[key]]);\n      }\n\n      variables.push(\n        result.em,\n        (result.em + ionization.em - ionization.charge * ELECTRON_MASS) /\n          Math.abs(ionization.charge),\n        result.charge + result.ionization.charge,\n        result.unsaturation,\n        result.atoms,\n      );\n      if (!options.filterFct.apply(null, variables)) continue;\n    }\n\n    result.parts = [];\n    result.mf = '';\n\n    let comments = [];\n    for (let i = 0; i < keys.length; i++) {\n      let key = keys[i][currents[i]];\n      if (key) {\n        if (key.indexOf('$') > -1) {\n          comments.push(key.replace(/^[^$]*\\$/, ''));\n          key = key.replace(/\\$.*/, '');\n        }\n        result.parts[i] = key;\n        result.mf += key;\n      }\n    }\n\n    if (comments.length > 0) {\n      result.comment = comments.join(' ');\n    }\n\n    let match = msemMatcher(result, filter);\n    if (!match) continue;\n    result.ms = match.ms;\n    result.ionization = match.ionization;\n\n    if (canonizeMF) {\n      result.mf = new MF(result.mf).toMF();\n    }\n\n    results.push(result);\n  }\n}\n","import { generateMFs } from 'mf-generator';\n\n/**\n * Generates a database 'generated' from an array of molecular formula\n * @param {array} mfsArray - Array of string or Array of array containing the parts to combine\n * @param {object} [options={}]\n * @param {boolean} [options.estimate=false] - estimate the number of MF without filters\n * @param {string} [options.databaseName='generated']\n * @param {function} [options.onStep] - Callback to do after each step\n * @param {number} [options.limit=10000000] - Maximum number of results\n * @param {boolean} [options.canonizeMF=true] - Canonize molecular formula\n * @param {boolean} [options.uniqueMFs=true] - Force canonization and make MF unique\n * @param {string} [options.ionizations=''] - Comma separated list of ionizations (to charge the molecule)\n * @param {object} [options.filter={}]\n * @param {number} [options.filter.minMass=0] - Minimal monoisotopic mass\n * @param {number} [options.filter.maxMass=+Infinity] - Maximal monoisotopic mass\n * @param {number} [options.filter.minEM=0] - Minimal neutral monoisotopic mass\n * @param {number} [options.filter.maxEM=+Infinity] - Maximal neutral monoisotopic mass\n * @param {number} [options.filter.minMSEM=0] - Minimal observed monoisotopic mass\n * @param {number} [options.filter.maxMSEM=+Infinity] - Maximal observed monoisotopic mass\n * @param {number} [options.filter.minCharge=-Infinity] - Minimal charge\n * @param {number} [options.filter.maxCharge=+Infinity] - Maximal charge\n *\n * @param {object} [options.filter.unsaturation={}]\n * @param {number} [options.filter.unsaturation.min=-Infinity] - Minimal unsaturation\n * @param {number} [options.filter.unsaturation.max=+Infinity] - Maximal unsaturation\n * @param {boolean} [options.filter.unsaturation.onlyInteger=false] - Integer unsaturation\n * @param {boolean} [options.filter.unsaturation.onlyNonInteger=false] - Non integer unsaturation\n * @param {object} [options.filter.atoms] - object of atom:{min, max}\n * @param {function} [options.filter.callback] - a function to filter the MF\n * @param {string}  [options.filterFct] - A string representing a function\n *\n * @example\n *\n * const {EMDB} = require('emdb');\n * let emdb = new EMDB();\n * let array = ['C1-10', 'H1-10'];\n * emdb.fromArray(array); // create a database 'generated' combining all possibilies\n * console.log(emdb.get('generated').length); // 100\n *\n * @example\n * const {EMDB} = require('emdb');\n * let emdb = new EMDB();\n * let array = ['C1-10 H1-10'];\n * emdb.fromArray(array); // create a database 'generated' combining all possibilies\n * console.log(emdb.get('generated').length); // 100\n *\n * @example\n * const {EMDB} = require('emdb');\n * let emdb = new EMDB();\n * // in case of an array of array, one of the group is allwed\n * let array = [['C1-10','H1-10'],'Cl0-1 Br0-1'];\n * emdb.fromArray(array); // create a database 'generated' combining all possibilies\n * console.log(emdb.get('generated').length); // 80\n *\n * @example\n * <script src=\"https://www.lactame.com/lib/mass-tools/HEAD/mass-tools.js\" />\n * <script>\n *   let emdb = new MassTools.EMDB();\n *   let array = ['C1-10', 'H1-10'];\n *   emdb.fromArray(array); // create a database 'generated' combining all possibilities\n *   console.log(emdb.get('generated').length); // 100\n * </script>\n *\n * // from the browser\n */\n\nexport async function fromArray(mfsArray, options = {}) {\n  return generateMFs(mfsArray, options);\n}\n","let xAtomicNumber = 0;\n\n/**\n * Tag an atom to be able to visualize it\n * @param {import('openchemlib').Molecule} molecule\n * @param {number} iAtom\n */\nexport function tagAtom(molecule, iAtom) {\n  let customLabel = `${molecule.getAtomLabel(iAtom)}*`;\n  molecule.setAtomCustomLabel(iAtom, customLabel);\n  if (molecule.getAtomicNo(iAtom) === 1) {\n    molecule.setAtomicNo(iAtom, getXAtomicNumber(molecule));\n  } else {\n    // we can not use X because we would have problems with valencies if it is\n    // expanded hydrogens or not\n    // we can not only use a custom label because it does not count for the canonisation\n    molecule.setAtomMass(iAtom, molecule.getAtomMass(iAtom) + 5);\n  }\n  return customLabel;\n}\n\nfunction getXAtomicNumber(molecule) {\n  if (!xAtomicNumber) {\n    const OCL = molecule.getOCL();\n    xAtomicNumber = OCL.Molecule.getAtomicNoFromLabel('X');\n  }\n  return xAtomicNumber;\n}\n","/**\n *\n * @param {import('openchemlib').Molecule} [molecule] An instance of a molecule\n * @param {object} [options={}]\n * @param {object} [options.OCL] openchemlib library\n */\nexport function makeRacemic(molecule) {\n  const { Molecule } = molecule.getOCL();\n\n  // if we don't calculate this we have 2 epimers\n  molecule.ensureHelperArrays(Molecule.cHelperCIP);\n\n  // we need to make one group \"AND\" for chiral (to force to racemic, this means diastereotopic and not enantiotopic)\n  for (let i = 0; i < molecule.getAllAtoms(); i++) {\n    if (molecule.getAtomParity(i) !== Molecule.cAtomParityNone) {\n      molecule.setAtomESR(i, Molecule.cESRTypeAnd, 0); // changed to group 0; TLS 9.Nov.2015\n    }\n  }\n}\n","/**\n * Check if a specific atom is a sp3 carbon\n * @param {import('openchemlib').Molecule} molecule\n * @param {number} atomID\n */\n\nexport function isCsp3(molecule, atomID) {\n  if (molecule.getAtomicNo(atomID) !== 6) return false;\n  if (molecule.getAtomCharge(atomID) !== 0) return false;\n  if (\n    molecule.getImplicitHydrogens(atomID) + molecule.getConnAtoms(atomID) !==\n    4\n  ) {\n    return false;\n  }\n  return true;\n}\n","import { isCsp3 } from '../util/isCsp3';\nimport { makeRacemic } from '../util/makeRacemic';\nimport { tagAtom } from '../util/tagAtom';\n\nexport const FULL_HOSE_CODE = 1;\nexport const HOSE_CODE_CUT_C_SP3_SP3 = 2;\n\n/**\n * Returns the hose code for a specific atom number\n * @param {import('openchemlib').Molecule} originalMolecule - The OCL molecule to be fragmented\n * @param {number[]} rootAtoms\n * @param {object} [options={}]\n * @param {boolean} [options.isTagged] Specify is the atoms are already tagged\n * @param {number} [options.minSphereSize=0] Smallest hose code sphere\n * @param {number} [options.maxSphereSize=4] Largest hose code sphere\n * @param {number} [options.kind=FULL_HOSE_CODE] Kind of hose code, default usual sphere\n */\nexport function getHoseCodesForAtoms(\n  originalMolecule,\n  rootAtoms = [],\n  options = {},\n) {\n  const OCL = originalMolecule.getOCL();\n  const {\n    minSphereSize = 0,\n    maxSphereSize = 4,\n    kind = FULL_HOSE_CODE,\n    isTagged = false,\n  } = options;\n\n  const molecule = originalMolecule.getCompactCopy();\n\n  if (!isTagged) {\n    const tags = [];\n    for (let i = 0; i < rootAtoms.length; i++) {\n      let rootAtom = rootAtoms[i];\n      tags.push(tagAtom(molecule, rootAtom));\n      molecule.addImplicitHydrogens();\n      molecule.addMissingChirality();\n      molecule.ensureHelperArrays(OCL.Molecule.cHelperNeighbours);\n      // because ensuring helper reorder atoms we need to look again for it\n    }\n    rootAtoms.length = 0;\n    for (let j = 0; j < molecule.getAllAtoms(); j++) {\n      if (tags.includes(molecule.getAtomCustomLabel(j))) {\n        rootAtoms.push(j);\n      }\n    }\n  }\n\n  let fragment = new OCL.Molecule(0, 0);\n  let results = [];\n  let min = 0;\n  let max = 0;\n  let atomMask = new Array(molecule.getAllAtoms());\n  let atomList = new Array(molecule.getAllAtoms());\n\n  for (let sphere = 0; sphere <= maxSphereSize; sphere++) {\n    if (max === 0) {\n      for (let rootAtom of rootAtoms) {\n        atomList[max] = rootAtom;\n        atomMask[rootAtom] = true;\n        max++;\n      }\n    } else {\n      let newMax = max;\n      for (let i = min; i < max; i++) {\n        let atom = atomList[i];\n        for (let j = 0; j < molecule.getAllConnAtoms(atom); j++) {\n          let connAtom = molecule.getConnAtom(atom, j);\n          if (!atomMask[connAtom]) {\n            switch (kind) {\n              case FULL_HOSE_CODE:\n                atomMask[connAtom] = true;\n                atomList[newMax++] = connAtom;\n                break;\n              case HOSE_CODE_CUT_C_SP3_SP3:\n                if (!(isCsp3(molecule, atom) && isCsp3(molecule, connAtom))) {\n                  atomMask[connAtom] = true;\n                  atomList[newMax++] = connAtom;\n                }\n                break;\n              default:\n                throw new Error('getHoseCoesForAtom unknown kind');\n            }\n          }\n        }\n      }\n      min = max;\n      max = newMax;\n    }\n    molecule.copyMoleculeByAtoms(fragment, atomMask, true, null);\n    if (sphere >= minSphereSize) {\n      makeRacemic(fragment);\n      results.push(\n        fragment.getCanonizedIDCode(\n          OCL.Molecule.CANONIZER_ENCODE_ATOM_CUSTOM_LABELS,\n        ),\n      );\n    }\n  }\n  return results;\n}\n","/**\n * Implementation of the Hill system for sorting atoms\n * https://en.wikipedia.org/wiki/Chemical_formula#Hill_system\n * @param {string} a - first atom to compare\n * @param {string} b - second atom to compare\n * @returns\n */\n\nexport function atomSorter(a, b) {\n  if (a === b) return 0;\n  if (a === 'C') return -1;\n  if (b === 'C') return 1;\n  if (a === 'H') return -1;\n  if (b === 'H') return 1;\n  if (a < b) return -1;\n  return 1;\n}\n","import { atomSorter } from 'atom-sorter';\n\n/**\n * Calculate the molecular formula in 'chemcalc' notation taking into account fragments, isotopes and charges\n * {OCL.Molecule} [molecule] an instance of OCL.Molecule\n * @returns {object}\n */\n\nexport function getMF(molecule) {\n  let entries = molecule.getFragments();\n  let result = {};\n  let parts = [];\n  let allAtoms = [];\n\n  entries.forEach((entry) => {\n    let mf = getFragmentMF(entry, allAtoms);\n    parts.push(mf);\n  });\n\n  let counts = {};\n  for (let part of parts) {\n    if (!counts[part]) counts[part] = 0;\n    counts[part]++;\n  }\n  parts = [];\n  for (let key of Object.keys(counts).sort()) {\n    if (counts[key] > 1) {\n      parts.push(counts[key] + key);\n    } else {\n      parts.push(key);\n    }\n  }\n\n  result.parts = parts;\n  result.mf = toMFString(allAtoms);\n  return result;\n}\n\nfunction getFragmentMF(molecule, allAtoms) {\n  let atoms = [];\n  for (let i = 0; i < molecule.getAllAtoms(); i++) {\n    let atom = {};\n    atom.charge = molecule.getAtomCharge(i);\n    atom.label = molecule.getAtomLabel(i);\n    atom.mass = molecule.getAtomMass(i);\n    atom.implicitHydrogens = molecule.getImplicitHydrogens(i);\n    atoms.push(atom);\n    allAtoms.push(atom);\n  }\n  return toMFString(atoms);\n}\n\nfunction toMFString(atoms) {\n  let charge = 0;\n  let mfs = {};\n  for (let atom of atoms) {\n    let label = atom.label;\n    charge += atom.charge;\n    if (atom.mass) {\n      label = `[${atom.mass}${label}]`;\n    }\n    let mfAtom = mfs[label];\n    if (!mfAtom) {\n      mfs[label] = 0;\n    }\n    mfs[label] += 1;\n    if (atom.implicitHydrogens) {\n      if (!mfs.H) mfs.H = 0;\n      mfs.H += atom.implicitHydrogens;\n    }\n  }\n\n  let mf = '';\n  let keys = Object.keys(mfs).sort(atomSorter);\n  for (let key of keys) {\n    mf += key;\n    if (mfs[key] > 1) mf += mfs[key];\n  }\n\n  if (charge > 0) {\n    mf += `(+${charge > 1 ? charge : ''})`;\n  } else if (charge < 0) {\n    mf += `(${charge < -1 ? charge : '-'})`;\n  }\n  return mf;\n}\n","import { MF } from 'mf-parser';\nimport { getMF, getHoseCodesForAtoms } from 'openchemlib-utils';\n\n/**\n * The function performs the fragmentation of all single linear bonds\n * @param {import('openchemlib').Molecule} molecule - The OCL molecule to be fragmented\n * @param {object} [options={}]\n * @param {boolean} [options.calculateHoseCodes=false] - calculating hose code for bonds is quite time consuming\n * @param {string} [options.parentIDCode=molecule.getIDCode()]\n * @returns Results fragmentation of acyclic bonds\n */\n\nexport function fragmentAcyclicBonds(molecule, options = {}) {\n  const { Molecule } = molecule.getOCL();\n  const { calculateHoseCodes, parentIDCode = molecule.getIDCode() } = options;\n  let atoms = [];\n  // Prepare object with lenght equal to number of atoms\n  for (let i = 0; i < molecule.getAllAtoms(); i++) {\n    let atom = {};\n    atoms.push(atom);\n    atom.i = i;\n    atom.links = [];\n  }\n  let bonds = [];\n  for (let i = 0; i < molecule.getAllBonds(); i++) {\n    let bond = {};\n    // get informations of bonds\n    bond.index = i;\n    bond.order = molecule.getBondOrder(i); // dative, single , double, triple\n    bond.atom1 = molecule.getBondAtom(0, i); // atom 1 index\n    bond.atom2 = molecule.getBondAtom(1, i); // atom 2 index\n\n    bond.type = molecule.getBondType(i); // cBondTypeSingle,cBondTypeDouble,cBondTypeTriple,cBondTypeDelocalized\n    bond.isAromatic = molecule.isAromaticBond(i);\n    bond.isRingBond = molecule.isRingBond(i);\n\n    // Mapping of bonds to be fragmented, only if they are single bond not aromatic and cyclic the mapping occurs\n    if (\n      bond.isAromatic ||\n      bond.type > 1 ||\n      bond.isRingBond ||\n      bond.order !== 1\n    ) {\n      continue;\n    } else {\n      bond.selected = true;\n      atoms[bond.atom1].links.push(bond.atom2);\n      atoms[bond.atom2].links.push(bond.atom1);\n    }\n    bonds.push(bond);\n  }\n\n  let brokenMolecule = {};\n  let fragmentMap = [];\n  let nbFragments = [];\n  let results = [];\n\n  for (let bond of bonds) {\n    if (bond.selected) {\n      // if bond.selected is true (line 46) the molecule will be fragmented\n      brokenMolecule[bond.index] = molecule.getCompactCopy(); // get a copy of the molecule\n      brokenMolecule[bond.index].setAtomCustomLabel(bond.atom1, '*');\n      brokenMolecule[bond.index].setAtomCustomLabel(bond.atom2, '*');\n      brokenMolecule[bond.index].markBondForDeletion(bond.index); //mark bond to be deleted\n      // the function returns an array of map\n\n      brokenMolecule[bond.index].deleteMarkedAtomsAndBonds(); // delete marked bonds\n    }\n    nbFragments = brokenMolecule[bond.index].getFragmentNumbers(fragmentMap);\n    // only if there are 2 fragments code can continue\n    if (nbFragments === 2) {\n      for (let i = 0; i < nbFragments; i++) {\n        const result = {};\n        if (calculateHoseCodes) {\n          result.hoses = getHoseCodesForAtoms(molecule, [\n            bond.atom1,\n            bond.atom2,\n          ]);\n        }\n\n        result.atomMap = [];\n\n        // assign fragment id to index of for loop\n        let includeAtom = fragmentMap.map((id) => {\n          return id === i;\n        });\n\n        let fragment = new Molecule(100, 100);\n\n        let atomMap = [];\n\n        brokenMolecule[bond.index].copyMoleculeByAtoms(\n          fragment,\n          includeAtom,\n          false,\n          atomMap,\n        );\n\n        for (let j = 0; j < atomMap.length; j++) {\n          if (fragment.getAtomCustomLabel(atomMap[j]) === '*') {\n            result.atomMap.push(j);\n            if (atoms[j].links.length > 0) {\n              fragment.addBond(atomMap[j], fragment.addAtom(154));\n            }\n          }\n        }\n        fragment.removeAtomCustomLabels();\n        fragment.setFragment(false);\n        result.idCode = fragment.getIDCode();\n        result.parentIDCode = parentIDCode;\n        result.cleavedBonds = [\n          {\n            index: bond.index,\n            order: bond.order,\n            atom1: bond.atom1,\n            atom2: bond.atom2,\n          },\n        ];\n        result.mfInfo = new MF(\n          getMF(fragment).mf.replace(/R[1-9]?/, ''),\n        ).getInfo();\n        result.fragmentType = 'acyclic';\n\n        results.push(result);\n      }\n    }\n  }\n  // sort result in order fragment 1-2; 3-4; ...\n  results = results.sort((a, b) => {\n    return a.mfInfo.monoisotopicMass - b.mfInfo.monoisotopicMass;\n  });\n\n  return results;\n}\n","/**\n * This function returns ringBond, and object that contains information about the bonds of each ring\n * @param {import('openchemlib').Molecule} molecule - The OCL molecule to be fragmented\n * @returns Information of ring bonds for each ring in the molecule\n */\n\nexport function getRingsInfo(molecule) {\n  const ringSet = molecule.getRingSet();\n  let ringBonds = [];\n  // create a new array with the length of the number of bonds in the molecule and fills it with 0\n  let nbRingForBonds = new Array(molecule.getAllBonds()).fill(0);\n\n  for (let i = 0; i < ringSet.getSize(); i++) {\n    for (let bond of ringSet.getRingBonds(i)) {\n      nbRingForBonds[bond]++;\n    }\n  }\n\n  for (let i = 0; i < ringSet.getSize(); i++) {\n    ringBonds.push({\n      bonds: ringSet.getRingBonds(i).map((bondIndex) => ({\n        index: bondIndex,\n        ringIndex: i,\n        nbRings: nbRingForBonds[bondIndex], // in how many rings this bond is included\n        order: molecule.getBondOrder(bondIndex),\n        isAromatic: ringSet.isAromatic(i),\n        atom1: molecule.getBondAtom(0, bondIndex),\n        atom2: molecule.getBondAtom(1, bondIndex),\n      })),\n    });\n  }\n\n  return ringBonds;\n}\n","import { getRingsInfo } from './getRingsInfo.js';\n/**\n * This function returns an array of objects with all combination of 2 bonds who can be fragmented in the same ring\n * @param {import('openchemlib').Molecule} molecule - The OCL molecule to be fragmented\n * @returns All combination of 2 bonds who can be fragmented in the same ring\n */\nexport function getFragmentableRings(molecule) {\n  let ringsInfo = getRingsInfo(molecule);\n  let fragmentableRingBonds = [];\n  for (let ring = 0; ring < ringsInfo.length; ring++) {\n    let bonds = ringsInfo[ring].bonds;\n    // we prevent to consecutive bonds to be cleaved\n    for (let first = 0; first < bonds.length; first++) {\n      let end = first === 0 ? bonds.length - 1 : bonds.length;\n      for (let second = first + 2; second < end; second++) {\n        if (\n          bonds[first].order === 1 &&\n          bonds[second].order === 1 &&\n          !bonds[first].isAromatic &&\n          !bonds[second].isAromatic &&\n          bonds[first].nbRings < 2 &&\n          bonds[second].nbRings < 2\n        ) {\n          fragmentableRingBonds.push({ bonds: [bonds[first], bonds[second]] });\n        }\n      }\n    }\n  }\n\n  return fragmentableRingBonds;\n}\n","import { MF } from 'mf-parser';\nimport { getMF, getHoseCodesForAtoms } from 'openchemlib-utils';\n\nimport { getFragmentableRings } from './utils/getFragmentableRings.js';\n\n/**\n * The function performs the fragmentation of all single ring bonds not belonging to aromatic rings\n * @param {import('openchemlib').Molecule} molecule - The OCL molecule to be fragmented\n * @param {object} [options={}]\n * @param {boolean} [options.calculateHoseCodes=false] - calculating hose code for bonds is quite time consuming\n * @param {string} [options.parentIDCode=molecule.getIDCode()]\n * @returns  Array with results for the fragmentation of ring bonds\n */\n\nexport function fragmentRings(molecule, options = {}) {\n  const { Molecule } = molecule.getOCL();\n  const { calculateHoseCodes, parentIDCode = molecule.getIDCode() } = options;\n\n  const fragmentableRingBonds = getFragmentableRings(molecule);\n\n  let fragmentationResults = [];\n\n  for (let ringBonds of fragmentableRingBonds) {\n    const brokenMolecule = molecule.getCompactCopy();\n    let fragmentMap = [];\n    let atoms = [];\n    let rLinks = {};\n    for (let bond of ringBonds.bonds) {\n      brokenMolecule.markBondForDeletion(bond.index);\n      brokenMolecule.setAtomCustomLabel(bond.atom1, '*');\n      brokenMolecule.setAtomCustomLabel(bond.atom2, '*');\n      atoms.push(bond.atom1);\n      atoms.push(bond.atom2);\n      rLinks[bond.atom1] = bond.atom2;\n      rLinks[bond.atom2] = bond.atom1;\n    }\n    brokenMolecule.deleteMarkedAtomsAndBonds();\n\n    const nbFragments = brokenMolecule.getFragmentNumbers(fragmentMap);\n\n    for (let i = 0; i < nbFragments; i++) {\n      const result = {};\n      if (calculateHoseCodes) {\n        result.hoses = getHoseCodesForAtoms(molecule, atoms);\n      }\n\n      result.atomMap = [];\n      let includeAtom = fragmentMap.map((id) => {\n        return id === i;\n      });\n      let fragment = new Molecule(0, 0);\n      let atomMap = [];\n\n      brokenMolecule.copyMoleculeByAtoms(fragment, includeAtom, false, atomMap);\n      // if includeAtom has more then 3 true all true should become false and all false should become true\n\n      for (let j = 0; j < atomMap.length; j++) {\n        if (fragment.getAtomCustomLabel(atomMap[j]) === '*') {\n          result.atomMap.push(j);\n          if (rLinks[j] !== undefined) {\n            fragment.addBond(atomMap[j], fragment.addAtom(154));\n          }\n        }\n      }\n      fragment.removeAtomCustomLabels();\n\n      fragment.setFragment(false);\n      //      console.log(fragment.getIDCode(), getMF(fragment).mf);\n      result.idCode = fragment.getIDCode();\n      result.parentIDCode = parentIDCode;\n      result.cleavedBonds = ringBonds.bonds;\n      result.mfInfo = new MF(\n        getMF(fragment).mf.replace(/R[1-9]?/, ''),\n      ).getInfo();\n      result.fragmentType = 'cyclic';\n      fragmentationResults.push(result);\n    }\n  }\n\n  fragmentationResults = fragmentationResults.sort((a, b) => {\n    return a.mfInfo.monoisotopicMass - b.mfInfo.monoisotopicMass;\n  });\n  return fragmentationResults;\n}\n","import { MF } from 'mf-parser';\nimport { getMF } from 'openchemlib-utils';\n\nimport { fragmentAcyclicBonds } from './fragmentAcyclicBonds.js';\nimport { fragmentRings } from './fragmentRings.js';\n\n/**\n * This function fragment both acyclic and cyclic bonds of the molecule\n * @param {import('openchemlib').Molecule} molecule - The OCL molecule to be fragmented\n * @param {object} [options={}]\n * @param {boolean} [options.calculateHoseCodes=false] - calculating hose code for bonds is quite time consuming\n * @param {boolean} [options.cyclic=true] - calculate cyclic fragmentation\n * @param {boolean} [options.acyclic=true] - calculate acyclic fragmentation\n * @param {boolean} [options.full=true] - calculate the molecular formula of the full molecule\n * @returns {object} In-Silico fragmentation results\n */\nexport function fragment(molecule, options = {}) {\n  const {\n    cyclic = true,\n    acyclic = true,\n    full = true,\n    calculateHoseCodes = false,\n  } = options;\n\n  const parentIDCode = molecule.getIDCode();\n\n  let molecularIon = full\n    ? [\n        {\n          idCode: parentIDCode,\n          parentIDCode,\n          mfInfo: new MF(getMF(molecule).mf).getInfo(),\n          fragmentType: 'molecule',\n        },\n      ]\n    : [];\n  let acyclicBonds = acyclic\n    ? fragmentAcyclicBonds(molecule, { calculateHoseCodes, parentIDCode })\n    : [];\n  let cyclicBonds = cyclic\n    ? fragmentRings(molecule, { calculateHoseCodes, parentIDCode })\n    : [];\n  let result = [...molecularIon, ...acyclicBonds, ...cyclicBonds];\n\n  return result.sort(\n    (a, b) => a.mfInfo.monoisotopicMass - b.mfInfo.monoisotopicMass,\n  );\n}\n","import { fragment } from 'mass-fragmentation';\nimport { msemMatcher } from 'mf-matcher';\nimport { MF } from 'mf-parser';\nimport { preprocessIonizations } from 'mf-utilities';\n/** * Generates a database 'monoisotopic' from a monoisotopic mass and various options\n * @param {{smiles?:string,molecule?:string,idCode?:string}[]}    entries - Array of object containing a property to recreate the molecule\n * @param {import('openchemlib')} ocl - The OCL library\n * @param {object}    [options={}]\n * @param {function}  [options.onStep] - Callback to do after each step\n * @param {boolean}   [options.allowNeutral=true]\n * @param {string}    [options.ionizations=''] - string containing a comma separated list of modifications\n * @param {number}    [options.precision=100] - Allowed mass range based on precision\n * @param {boolean}   [options.groupResults=false] - Should we group the results if they have the same monoisotopic mass and experimental mass\n * @param {object}    [options.fragmentation={}]\n * @param {object}    [options.fragmentation.acyclic=false]\n * @param {object}    [options.fragmentation.cyclic=false]\n * @param {object}    [options.fragmentation.full=true]\n * @param {object}    [options.filter={}]\n * @param {number}    [options.filter.minCharge=-Infinity] - Minimal charge\n * @param {number}    [options.filter.maxCharge=+Infinity] - Maximal charge\n * @param {object}    [options.filter.unsaturation={}]\n * @param {number}    [options.filter.unsaturation.min=-Infinity] - Minimal unsaturation\n * @param {number}    [options.filter.unsaturation.max=+Infinity] - Maximal unsaturation\n * @param {boolean}    [options.filter.unsaturation.onlyInteger=false] - Integer unsaturation\n * @param {boolean}    [options.filter.unsaturation.onlyNonInteger=false] - Non integer unsaturation\n * @param {number}    [options.filter.targetMass] - Target mass, allows to calculate error and filter results\n * @param {number[]}  [options.filter.targetMasses] - Target masses: SORTED array of numbers\n * @param {number[]}  [options.filter.targetIntensities] - Target intensities: SORTED array of numbers\n * @param {object}    [options.filter.atoms] - object of atom:{min, max}\n * @param {function}  [options.filter.callback] - a function to filter the MF\n * @returns {Promise}\n */\n\nexport async function fromMolecules(entries, ocl, options = {}) {\n  let {\n    onStep,\n    ionizations,\n    filter,\n    fragmentation = { acyclic: false, cyclic: false, full: true },\n    groupResults = false,\n  } = options;\n\n  ionizations = preprocessIonizations(ionizations);\n  let results = [];\n  for (let i = 0; i < entries.length; i++) {\n    const entry = entries[i];\n    const molecule = getMolecule(entry, ocl);\n    if (!molecule) continue;\n\n    const { acyclic = false, cyclic = false, full = true } = fragmentation;\n\n    const allFragments = fragment(molecule, {\n      acyclic,\n      cyclic,\n      full,\n    });\n\n    for (const oneFragment of allFragments) {\n      appendResults(results, oneFragment, {\n        entry,\n        ionizations,\n        filter,\n      });\n      if (onStep) onStep(i);\n    }\n  }\n\n  if (groupResults) {\n    results = groupFragmentationResults(results);\n  }\n  return results.sort((a, b) => a.em - b.em);\n}\n\n/**\n * We could group the results and replace the property 'fragment' by 'fragments'\n * @param {*} results\n */\nfunction groupFragmentationResults(results) {\n  const sortedResults = results.slice().sort((a, b) => {\n    if (a.em !== b.em) {\n      return a.em - b.em;\n    }\n    if (a.ms.em !== b.ms.em) {\n      return a.ms.em - b.ms.em;\n    }\n    if (a.fragment.idCode < b.fragment.idCode) return -1;\n    return 1;\n  });\n  const groupedResults = [];\n  let currentResult = {};\n  for (let result of sortedResults) {\n    if (\n      result.em !== currentResult.em ||\n      result.ms.em !== currentResult.ms.em\n    ) {\n      currentResult = { ...result };\n      currentResult.fragments = [\n        {\n          idCode: result.fragment.idCode,\n          type: result.fragment.type,\n          count: 1,\n          parents: [{ ...result.fragment.parent, count: 1 }],\n        },\n      ];\n      delete currentResult.fragment;\n      groupedResults.push(currentResult);\n    } else {\n      const lastFragment =\n        currentResult.fragments[currentResult.fragments.length - 1];\n      if (lastFragment.idCode === result.fragment.idCode) {\n        lastFragment.count++;\n        if (\n          lastFragment.parents[lastFragment.parents.length - 1].idCode ===\n          result.fragment.parent.idCode\n        ) {\n          lastFragment.parents[lastFragment.parents.length - 1].count++;\n        } else {\n          lastFragment.parents.push({ ...result.fragment.parent, count: 1 });\n        }\n      } else {\n        currentResult.fragments.push({\n          idCode: result.fragment.idCode,\n          type: result.fragment.type,\n          count: 1,\n          parents: [{ ...result.fragment.parent, count: 1 }],\n        });\n      }\n    }\n  }\n  for (let group of groupedResults) {\n    group.fragments = group.fragments.sort((a, b) => b.count - a.count);\n  }\n  return groupedResults;\n}\n\n/**\n *\n * @param {object} entry\n * @param {import('openchemlib')} ocl - The OCL library\n */\nfunction getMolecule(entry, ocl) {\n  if (entry.idCode) {\n    return ocl.Molecule.fromIDCode(entry.idCode);\n  }\n  if (entry.ocl && entry.ocl.idCode) {\n    return ocl.Molecule.fromIDCode(entry.ocl.idCode);\n  }\n  if (entry.smiles) {\n    return ocl.Molecule.fromSmiles(entry.smiles);\n  }\n  if (entry.molfile) {\n    return ocl.Molecule.fromMolfile(entry.molfile);\n  }\n  return undefined;\n}\n\nfunction appendResults(results, oneFragment, options) {\n  const mf = oneFragment.mfInfo.mf;\n  const { ionizations, filter, entry } = options;\n  const mfInfo = new MF(mf).getInfo();\n  for (let ionization of ionizations) {\n    const result = {\n      charge: mfInfo.charge,\n      em: mfInfo.monoisotopicMass,\n      mw: mfInfo.mass,\n      mf: mfInfo.mf,\n      ionization,\n      unsaturation: mfInfo.unsaturation,\n      atoms: mfInfo.atoms,\n      fragment: {\n        parent: {\n          ...entry,\n          idCode: oneFragment.parentIDCode,\n        },\n        idCode: oneFragment.idCode,\n        type: oneFragment.fragmentType,\n      },\n    };\n\n    let match = msemMatcher(result, filter);\n    if (!match) continue;\n    result.ms = match.ms;\n    result.ionization = match.ionization;\n\n    results.push(result);\n  }\n}\n","import { findMFs } from 'mf-finder';\n\n/**\n * Generates a database 'monoisotopic' from a monoisotopic mass and various options\n * @param {number|string|array}    masses - Monoisotopic mass\n * @param {object}    [options={}]\n * @param {number}    [options.maxIterations=10000000] - Maximum number of iterations\n * @param {function}  [options.onStep] - Callback to do after each step\n * @param {boolean}   [options.allowNeutral=true]\n * @param {boolean}   [options.uniqueMFs=true]\n * @param {number}    [options.limit=1000] - Maximum number of results\n * @param {string}    [options.ionizations=''] - string containing a comma separated list of modifications\n * @param {string}    [options.ranges='C0-100 H0-100 O0-100 N0-100'] - range of mfs to search\n * @param {number}    [options.precision=100] - Allowed mass range based on precision\n * @param {object}    [options.filter={}]\n * @param {number}    [options.filter.minCharge=-Infinity] - Minimal charge\n * @param {number}    [options.filter.maxCharge=+Infinity] - Maximal charge\n * @param {object}    [options.filter.unsaturation={}]\n * @param {number}    [options.filter.unsaturation.min=-Infinity] - Minimal unsaturation\n * @param {number}    [options.filter.unsaturation.max=+Infinity] - Maximal unsaturation\n * @param {boolean}    [options.filter.unsaturation.onlyInteger=false] - Integer unsaturation\n * @param {boolean}    [options.filter.unsaturation.onlyNonInteger=false] - Non integer unsaturation\n * @param {object}    [options.filter.atoms] - object of atom:{min, max}\n * @param {function}  [options.filter.callback] - a function to filter the MF\n * @returns {Promise}\n */\n\nexport async function fromMonoisotopicMass(masses, options = {}) {\n  if (typeof masses === 'string') {\n    masses = masses.split(/[ ,;\\r\\n\\t]/).map(Number);\n  }\n  if (typeof masses === 'number') {\n    masses = [masses];\n  }\n  let results = [];\n  for (let mass of masses) {\n    results.push(await findMFs(mass, options));\n  }\n  return {\n    mfs: results.map((entry) => entry.mfs).flat(),\n    info: {\n      numberMFEvaluated: results.reduce(\n        (sum, current) => (sum += current.info.numberMFEvaluated),\n        0,\n      ),\n      numberResults: results.reduce(\n        (sum, current) => (sum += current.info.numberResults),\n        0,\n      ),\n    },\n  };\n}\n","/**\n * Ensure that the sequence is in uppercase taking into account possible modifications\n * @param {string} [options.circular=false]\n */\n\nexport function ensureUppercaseSequence(sequence) {\n  let parenthesisCounter = 0;\n  let parts = [];\n  let part = '';\n  for (let i = 0; i < sequence.length; i++) {\n    let currentSymbol = sequence[i];\n\n    if (currentSymbol === '(' && parenthesisCounter === 0 && part) {\n      parts.push(part);\n      part = currentSymbol;\n    } else if (currentSymbol === ')' && parenthesisCounter === 0) {\n      part += currentSymbol;\n      parts.push(part);\n      part = '';\n    } else {\n      part += currentSymbol;\n    }\n  }\n  if (part) parts.push(part);\n  for (let i = 0; i < parts.length; i++) {\n    if (!parts[i].startsWith('(') && parts[i].match(/^[a-z]+$/)) {\n      parts[i] = parts[i].toUpperCase();\n    }\n  }\n  return parts.join('');\n}\n","import { groups } from 'chemical-groups';\n\nimport { ensureUppercaseSequence } from './ensureUppercaseSequence';\n\n/**\n * Convert a nucleic sequence to a MF\n * @param {String} sequence\n * @param {object} [options={}]\n * @param {string} [options.kind] - rna, ds-dna or dna. Default if contains U: rna, otherwise ds-dna\n * @param {string} [options.fivePrime=monophosphate] - alcohol, monophosphate, diphosphate, triphosphate\n * @param {string} [options.circular=false]\n */\n\nexport function sequenceToMF(sequence, options = {}) {\n  let fivePrimeTerminal = 'HO';\n  let threePrimeTerminal = 'H';\n  sequence = sequence.replace(/^HO/, '');\n  sequence = sequence.replace(/H$/, '');\n  sequence = sequence.trim();\n\n  if (sequence === '') return '';\n\n  sequence = ensureUppercaseSequence(sequence);\n\n  // if the sequence is in lowercase but the parenthesis we should convert it to uppercase\n\n  if (sequence.match(/^[a-z]+$/)) {\n    sequence = sequence.toUpperCase();\n  }\n\n  let { kind, circular, fivePrime = 'monophosphate' } = options;\n  fivePrime = fivePrime.replace(/[^a-zA-Z]/g, '').toLowerCase();\n\n  if (!kind) {\n    if (sequence.includes('U')) {\n      kind = 'rna';\n    } else {\n      kind = 'ds-dna';\n    }\n  }\n\n  kind = kind.replace(/[^A-Za-z]/g, '').toLowerCase();\n\n  if (sequence.includes('(') && kind === 'dsdna') {\n    throw new Error(\n      'Nucleotide sequenceToMF: modifications not allowed for ds-DNA',\n    );\n  }\n\n  let results = [[]];\n  if (kind === 'dsdna') results.push([]);\n\n  let parenthesisCounter = 0;\n\n  for (let i = 0; i < sequence.length; i++) {\n    let currentSymbol = sequence[i];\n    while (sequence[i + 1] && sequence[i + 1].match(/[a-z]/)) {\n      i++;\n      currentSymbol += sequence[i];\n    }\n\n    if (currentSymbol.length > 1) {\n      results[0].push(currentSymbol);\n      continue;\n    }\n\n    if (\n      currentSymbol === '(' ||\n      currentSymbol === ')' ||\n      parenthesisCounter > 0\n    ) {\n      if (currentSymbol === '(') {\n        parenthesisCounter++;\n        if (i === 0) fivePrimeTerminal = '';\n      }\n      if (currentSymbol === ')') {\n        parenthesisCounter--;\n        if (i === sequence.length - 1) threePrimeTerminal = '';\n      }\n      switch (kind) {\n        case 'dna':\n        case 'rna':\n          results[0].push(currentSymbol);\n          break;\n        default:\n          // eslint-disable-next-line no-console\n          console.warn(\n            `Nucleotide sequenceToMF with modification: unknown kind: ${kind}`,\n          );\n      }\n      continue;\n    }\n\n    let nucleotideType = i === 0 ? fivePrime : 'monophosphate';\n\n    currentSymbol = currentSymbol.replace(/[ \\t\\r\\n]/, '');\n    if (!currentSymbol) continue;\n\n    switch (kind) {\n      case 'dna':\n        results[0].push(desoxyNucleotides[nucleotideType][currentSymbol]);\n        break;\n      case 'rna':\n        results[0].push(oxyNucleotides[nucleotideType][currentSymbol]);\n        break;\n      case 'dsdna':\n        results[0].push(desoxyNucleotides[nucleotideType][currentSymbol]);\n        results[1].unshift(\n          desoxyNucleotides[nucleotideType][complementary[currentSymbol]],\n        );\n        break;\n      default:\n        // eslint-disable-next-line no-console\n        console.warn(`Nucleotide sequenceToMF: unknown kind: ${kind}`);\n    }\n  }\n\n  if (!circular) {\n    results.forEach((result) => result.unshift(fivePrimeTerminal));\n    results.forEach((result) => result.push(threePrimeTerminal));\n  }\n\n  return results.map((result) => result.join('')).join('.');\n}\n\nconst complementary = {\n  A: 'T',\n  T: 'A',\n  C: 'G',\n  G: 'C',\n};\n\nconst desoxyNucleotides = {\n  alcohol: {},\n  monophosphate: {},\n  diphosphate: {},\n  triphosphate: {},\n};\n\ngroups\n  .filter((group) => group.kind === 'DNA')\n  .forEach((group) => {\n    if (group.oneLetter) {\n      desoxyNucleotides.alcohol[group.oneLetter] = group.symbol;\n    }\n  });\n\ngroups\n  .filter((group) => group.kind === 'DNAp')\n  .forEach((group) => {\n    if (group.oneLetter) {\n      desoxyNucleotides.monophosphate[group.oneLetter] = group.symbol;\n    }\n  });\n\ngroups\n  .filter((group) => group.kind === 'NucleotideP')\n  .forEach((group) => {\n    if (group.oneLetter) {\n      desoxyNucleotides.monophosphate[group.oneLetter] = group.symbol;\n    }\n  });\n\ngroups\n  .filter((group) => group.kind === 'DNApp')\n  .forEach((group) => {\n    if (group.oneLetter) {\n      desoxyNucleotides.diphosphate[group.oneLetter] = group.symbol;\n    }\n  });\n\ngroups\n  .filter((group) => group.kind === 'DNAppp')\n  .forEach((group) => {\n    if (group.oneLetter) {\n      desoxyNucleotides.triphosphate[group.oneLetter] = group.symbol;\n    }\n  });\n\nconst oxyNucleotides = {\n  alcohol: {},\n  monophosphate: {},\n  diphosphate: {},\n  triphosphate: {},\n};\n\ngroups\n  .filter((group) => group.kind === 'RNA')\n  .forEach((group) => {\n    if (group.oneLetter) {\n      oxyNucleotides.alcohol[group.oneLetter] = group.symbol;\n    }\n  });\n\ngroups\n  .filter((group) => group.kind === 'RNAp')\n  .forEach((group) => {\n    if (group.oneLetter) {\n      oxyNucleotides.monophosphate[group.oneLetter] = group.symbol;\n    }\n  });\n\ngroups\n  .filter((group) => group.kind === 'NucleotideP')\n  .forEach((group) => {\n    if (group.oneLetter) {\n      oxyNucleotides.monophosphate[group.oneLetter] = group.symbol;\n    }\n  });\n\ngroups\n  .filter((group) => group.kind === 'RNApp')\n  .forEach((group) => {\n    if (group.oneLetter) {\n      oxyNucleotides.diphosphate[group.oneLetter] = group.symbol;\n    }\n  });\n\ngroups\n  .filter((group) => group.kind === 'RNAppp')\n  .forEach((group) => {\n    if (group.oneLetter) {\n      oxyNucleotides.triphosphate[group.oneLetter] = group.symbol;\n    }\n  });\n","export function furanThreeTerm(nucleotide) {\n  // last residue should become a furan\n  let parts = nucleotide\n    .replace(/ /g, '')\n    .replace(/([a-z)0-9])([A-Z][a-z](?=[a-z]))/g, '$1 $2')\n    .split(/ /);\n  let last = parts.pop();\n  if (!last.match(/D[atcg]mp(.*)$/)) {\n    // eslint-disable-next-line no-console\n    console.warn(\n      `furanThreeTerm can not remove a non monophosphate nucleic acid: ${last}`,\n    );\n    return parts.join('') + last;\n  }\n  return parts.join('') + last.replace(/D[atcg]mp(.*)$/, 'Furp');\n}\n","import { furanThreeTerm } from './furanThreeTerm.js';\n\nexport function addFiveTerm(mfs, fiveTerm, i, options) {\n  if (options.a) mfs.push(`${fiveTerm}O-1H-1$a${i}`); // neutral ok\n  if (options.ab && i > 1) mfs.push(`${furanThreeTerm(fiveTerm)}$a${i}-B`); // A minus base\n  if (options.b) mfs.push(`${fiveTerm}H$b${i}`); // need to add an hydrogen, see: https://books.google.ch/books?id=B57e37bJjqAC&pg=PA172&lpg=PA172&dq=oligonucleotide+b+fragmentation&source=bl&ots=mRr29Pexx2&sig=1NUQcWV-wuj6o9q81my86AVoRto&hl=fr&sa=X&ved=2ahUKEwjI5M3yn-7fAhUJMewKHQR6Bcs4ChDoATADegQIBhAB#v=onepage&q&f=true\n  if (options.c) mfs.push(`${fiveTerm}PO2$c${i}`); // neutral ok\n  if (options.d) mfs.push(`${fiveTerm}PO3H2$d${i}`);\n  if (options.dh2o) mfs.push(`${fiveTerm}PO2$d${i}-H2O`);\n}\n","import { mfDiff } from 'mf-utilities';\n\nconst mfLosses = {};\n['Amp', 'Tmp', 'Cmp', 'Gmp', 'Ump'].forEach((nucleotide) => {\n  mfLosses[nucleotide] = {\n    code: nucleotide.charAt(0),\n    diff: mfDiff('Rmp', nucleotide),\n  };\n});\n\n['Damp', 'Dtmp', 'Dcmp', 'Dgmp', 'Dump'].forEach((nucleotide) => {\n  mfLosses[nucleotide] = {\n    code: nucleotide.charAt(1).toUpperCase(),\n    diff: mfDiff('Drmp', nucleotide),\n  };\n});\n\nexport function baseLoss(nucleotide) {\n  // any residue can loose a base\n  let results = [];\n\n  for (let key in mfLosses) {\n    const base = mfLosses[key];\n    if (nucleotide.includes(key)) {\n      results.push(`${nucleotide}(${base.diff})$${base.code}*`);\n    }\n  }\n\n  return results;\n}\n","import { baseLoss } from './baseLoss.js';\n\nexport function addFiveTermBaseLoss(mfs, fiveTerm, i, options) {\n  if (!options.abcdBaseLoss) return;\n  let loss = baseLoss(fiveTerm);\n\n  loss.forEach((mf) => {\n    if (options.a) {\n      mfs.push(`${mf}`.replace('$', `O-1H-1$a${i} `));\n    }\n    if (options.b) {\n      mfs.push(`${mf}`.replace('$', `H-1$b${i} `));\n    }\n    if (options.c) {\n      mfs.push(`${mf}`.replace('$', `PO2$c${i} `));\n    }\n    if (options.d) {\n      mfs.push(`${mf}`.replace('$', `PO3H2$d${i} `));\n    }\n  });\n}\n","import { furanThreeTerm } from './furanThreeTerm.js';\n\n// https://books.google.ch/books?id=B57e37bJjqAC&pg=PA172&lpg=PA172&dq=oligonucleotide+b+fragmentation&source=bl&ots=mRr29Pexx2&sig=1NUQcWV-wuj6o9q81my86AVoRto&hl=fr&sa=X&ved=2ahUKEwjI5M3yn-7fAhUJMewKHQR6Bcs4ChDoATADegQIBhAB#v=onepage&q=oligonucleotide%20b%20fragmentation&f=false\n\nexport function addInternalTerm(mfs, internal, ter3, ter5, options = {}) {\n  if (options.aw) {\n    // without base loss\n    mfs.push(`HO${internal}O-1H-1$w${ter3}:a${ter5}`); // A W\n  }\n\n  if (options.bw) {\n    // without base loss\n    mfs.push(`HO${internal}H$w${ter3}:b${ter5}`); // B W\n  }\n\n  if (options.abw) {\n    // with base loss\n    let fragment = furanThreeTerm(internal);\n    mfs.push(`HO${fragment}$w${ter3}:a${ter5}-B`); // A minus base - W\n  }\n\n  if (options.aby) {\n    // with base loss\n    let fragment = furanThreeTerm(internal);\n    mfs.push(`O-2P-1${fragment}$y${ter3}:a${ter5}-B`); // A minus base - Y\n  }\n}\n","export function addThreeTerm(mfs, threeTerm, i, options) {\n  if (options.w) mfs.push(`HO${threeTerm}$w${i}`); // neutral ok\n  if (options.x) mfs.push(`H-1${threeTerm}$x${i}`); // neutral ok\n  if (options.y) mfs.push(`O-2P-1${threeTerm}$y${i}`); // neutral ok\n  if (options.z) mfs.push(`O-3H-2P-1${threeTerm}$z${i}`); // neutral ok\n  if (options.zch2) mfs.push(`O-3H-4C-1P-1${threeTerm}$z${i}-CH2`); // TODO to confirm\n}\n","import { baseLoss } from './baseLoss.js';\n\nexport function addThreeTermBaseLoss(mfs, threeTerm, i, options) {\n  if (!options.wxyzBaseLoss) return;\n  let loss = baseLoss(threeTerm);\n\n  loss.forEach((mf) => {\n    if (options.w) {\n      mfs.push(`HO${mf}`.replace('$', `$w${i} `));\n    }\n    if (options.x) {\n      mfs.push(`H-1${mf}`.replace('$', `$x${i} `));\n    }\n    if (options.y) {\n      mfs.push(`O-2P-1${mf}`.replace('$', `$y${i} `));\n    }\n    if (options.z) {\n      mfs.push(`O-3H-1P-1(+)${mf}`.replace('$', `$z${i} `));\n    }\n  });\n}\n","import { addFiveTerm } from './addFiveTerm';\nimport { addFiveTermBaseLoss } from './addFiveTermBaseLoss';\nimport { addInternalTerm } from './addInternalTerm';\nimport { addThreeTerm } from './addThreeTerm';\nimport { addThreeTermBaseLoss } from './addThreeTermBaseLoss';\n\nexport function generateFragments(mf, options) {\n  if (options === undefined) {\n    options = {\n      a: false,\n      ab: false,\n      b: false,\n      c: false,\n      d: false,\n      dh2o: false,\n      w: false,\n      x: false,\n      y: false,\n      z: false,\n      zch2: false,\n      aw: false,\n      bw: false,\n      abw: false,\n      aby: false,\n      abcdBaseLoss: false,\n      wxyzBaseLoss: false,\n    };\n  }\n\n  let mfs = [];\n  // need to allow 0-9 to deal with neutral loss\n  let mfparts = mf\n    .replace(/([a-z)0-9])([A-Z][a-z](?=[a-z]))/g, '$1 $2')\n    .split(/ /);\n\n  let fiveTerm = '';\n  let threeTerm = '';\n\n  if (mfparts[0].startsWith('(')) {\n    fiveTerm += mfparts[0];\n    mfparts = mfparts.splice(1);\n  }\n\n  if (mfparts[mfparts.length - 1].includes('(')) {\n    threeTerm += mfparts[mfparts.length - 1].replace(/^[^()]*/, '');\n    mfparts[mfparts.length - 1] = mfparts[mfparts.length - 1].replace(\n      /\\(.*/,\n      '',\n    );\n  }\n\n  for (let ter5 = 1; ter5 < mfparts.length; ter5++) {\n    fiveTerm += mfparts[ter5 - 1];\n    threeTerm = mfparts[mfparts.length - ter5] + threeTerm;\n\n    addFiveTerm(mfs, fiveTerm, ter5, options);\n    addFiveTermBaseLoss(mfs, fiveTerm, ter5, options);\n    addThreeTerm(mfs, threeTerm, ter5, options);\n    addThreeTermBaseLoss(mfs, threeTerm, ter5, options);\n  }\n\n  for (let i = 1; i < mfparts.length - 1; i++) {\n    let internal = '';\n    for (let j = i; j < mfparts.length - 1; j++) {\n      internal += mfparts[j];\n      if (j > i) {\n        addInternalTerm(mfs, internal, mfparts.length - i, j + 1, options);\n      }\n    }\n  }\n\n  return mfs;\n}\n","import { groupsToSequence } from 'chemical-groups';\nimport { generateMFs } from 'mf-generator';\nimport { sequenceToMF, generateFragments, baseLoss } from 'nucleotide';\n\n/**\n * Add a database starting from a peptidic sequence\n *\n * @param {string} [sequencesString] Sequence as a string of 1 letter or 3 letters code. Could also be a correct molecular formula respecting uppercase, lowercase\n * @param {object} [options={}]\n * @param {boolean} [options.estimate=false] - estimate the number of MF without filters\n * @param {function} [options.onStep] - Callback to do after each step\n * @param {number} [options.limit=100000]\n * @param {string} [options.ionizations='']\n * @param {object} [options.info={}]\n * @param {string} [options.info.kind] - rna, ds-dna or dna. Default if contains U: rna, otherwise ds-dna\n * @param {string} [options.info.fivePrime=monophosphate] - alcohol, monophosphate, diphosphate, triphosphate\n * @param {string} [options.info.circular=false]\n * @param {array}   [options.mfsArray=[]]\n * @param {object}  [options.fragmentation={}] Object defining options for fragmentation\n * @param {boolean} [options.fragmentation.a=false] If true allow fragments of type 'a'\n * @param {boolean} [options.fragmentation.ab=false] If true allow fragments of type 'a' minus base\n * @param {boolean} [options.fragmentation.b=false] If true allow fragments of type 'b'\n * @param {boolean} [options.fragmentation.c=false] If true allow fragments of type 'c'\n * @param {boolean} [options.fragmentation.d=false] If true allow fragments of type 'd'\n * @param {boolean} [options.fragmentation.dh2o=false] If true allow fragments of type 'd' with water loss\n * @param {boolean} [options.fragmentation.w=false] If true allow fragments of type 'w'\n * @param {boolean} [options.fragmentation.x=false] If true allow fragments of type 'x'\n * @param {boolean} [options.fragmentation.y=false] If true allow fragments of type 'y'\n * @param {boolean} [options.fragmentation.z=false] If true allow fragments of type 'z'\n * @param {boolean} [options.baseLoss=false] If true allow base loss at all the positions\n *\n * @param {object} [options.filter={}] Object defining options for molecular formula filter\n * @param {number} [options.filter.minMass=0] - Minimal monoisotopic mass\n * @param {number} [options.filter.maxMass=+Infinity] - Maximal monoisotopic mass\n * @param {number} [options.filter.minEM=0] - Minimal neutral monoisotopic mass\n * @param {number} [options.filter.maxEM=+Infinity] - Maximal neutral monoisotopic mass\n * @param {number} [options.filter.minMSEM=0] - Minimal observed monoisotopic mass\n * @param {number} [options.filter.maxMSEM=+Infinity] - Maximal observed monoisotopic mass\n * @param {number} [options.filter.minCharge=-Infinity] - Minimal charge\n * @param {number} [options.filter.maxCharge=+Infinity] - Maximal charge\n * @param {object} [options.filter.unsaturation={}]\n * @param {number} [options.filter.unsaturation.min=-Infinity] - Minimal unsaturation\n * @param {number} [options.filter.unsaturation.max=+Infinity] - Maximal unsaturation\n * @param {boolean} [options.filter.unsaturation.onlyInteger=false] - Integer unsaturation\n * @param {boolean} [options.filter.unsaturation.onlyNonInteger=false] - Non integer unsaturation\n * @returns {Promise}\n */\n\nexport async function fromNucleicSequence(sequencesString, options = {}) {\n  const {\n    mfsArray = [],\n    fragmentation = {},\n    filter = {},\n    ionizations = '',\n    info = {},\n    estimate = false,\n    limit = 100000,\n    onStep,\n  } = options;\n\n  let sequences = sequenceToMF(sequencesString, info).split('.');\n  let fragmentsArray = sequences.slice();\n\n  // calculate fragmentation\n  for (let i = 0; i < sequences.length; i++) {\n    let sequence = sequences[i];\n    let fragments = generateFragments(sequence, fragmentation);\n    if (i === 1) {\n      // complementary sequence\n      fragments = fragments.map((fragment) => fragment.replace(/\\$/g, '$cmp-'));\n    }\n    fragmentsArray = fragmentsArray.concat(fragments);\n    if (fragmentation.baseLoss) {\n      fragmentsArray = fragmentsArray.concat(baseLoss(sequence));\n    }\n  }\n\n  mfsArray.push(fragmentsArray);\n\n  let combined = await generateMFs(mfsArray, {\n    ionizations,\n    filter,\n    uniqueMFs: false,\n    estimate,\n    onStep,\n    limit,\n  });\n\n  if (Array.isArray(combined)) {\n    // not an estimation\n    combined.forEach((result) => {\n      result.sequence = groupsToSequence(\n        result.parts.filter((part) => part).join(' '),\n      );\n    });\n  }\n\n  return combined;\n}\n","// SOURCE: https://en.wikipedia.org/wiki/Amino_acid\n// Link for UTF8 code for modified: https://codepoints.net/search?sc=Grek\nexport const aminoAcids = [\n  // Standard amino acids\n  {\n    name: 'Alanine',\n    aa3: 'Ala',\n    aa1: 'A',\n    sc: {\n      type: 'hydrophobic',\n    },\n    pKaC: 2.33,\n    pKaN: 9.71,\n  },\n  {\n    name: 'Arginine',\n    aa3: 'Arg',\n    aa1: 'R',\n    sc: {\n      type: 'positive',\n      pKa: 12.1,\n    },\n    pKaC: 2.03,\n    pKaN: 9.0,\n  },\n  {\n    name: 'Asparagine',\n    aa3: 'Asn',\n    aa1: 'N',\n    sc: {\n      type: 'polar',\n    },\n    pKaC: 2.13,\n    pKaN: 9.05,\n  },\n  {\n    name: 'Aspartic acid',\n    aa3: 'Asp',\n    aa1: 'D',\n    sc: {\n      type: 'negative',\n      pKa: 3.71,\n    },\n    pKaC: 1.95,\n    pKaN: 9.66,\n  },\n  {\n    name: 'Cysteine',\n    aa3: 'Cys',\n    aa1: 'C',\n    sc: {\n      type: 'special',\n      pKa: 8.14,\n    },\n    pKaC: 1.91,\n    pKaN: 10.28,\n  },\n  {\n    name: 'Glutamic acid',\n    aa3: 'Glu',\n    aa1: 'E',\n    sc: {\n      type: 'negative',\n      pKa: 4.15,\n    },\n    pKaC: 2.16,\n    pKaN: 9.58,\n  },\n  {\n    name: 'Glutamine',\n    aa3: 'Gln',\n    aa1: 'Q',\n    sc: {\n      type: 'polar',\n    },\n    pKaC: 2.18,\n    pKaN: 9.0,\n  },\n  {\n    name: 'Glycine',\n    aa3: 'Gly',\n    aa1: 'G',\n    sc: {\n      type: 'special',\n    },\n    pKaC: 2.34,\n    pKaN: 9.58,\n  },\n  {\n    name: 'Histidine',\n    aa3: 'His',\n    aa1: 'H',\n    sc: {\n      type: 'positive',\n      pKa: 6.04,\n    },\n    pKaC: 1.7,\n    pKaN: 9.09,\n  },\n  {\n    name: 'Isoleucine',\n    aa3: 'Ile',\n    aa1: 'I',\n    sc: {\n      type: 'hydrophobic',\n    },\n    pKaC: 2.26,\n    pKaN: 9.6,\n  },\n  {\n    name: 'Leucine',\n    aa3: 'Leu',\n    aa1: 'L',\n    sc: {\n      type: 'hydrophobic',\n    },\n    pKaC: 2.32,\n    pKaN: 9.58,\n  },\n  {\n    name: 'Lysine',\n    aa3: 'Lys',\n    aa1: 'K',\n    sc: {\n      type: 'positive',\n      pKa: 10.67,\n    },\n    pKaC: 2.15,\n    pKaN: 9.16,\n  },\n  {\n    name: 'Methionine',\n    aa3: 'Met',\n    aa1: 'M',\n    sc: {\n      type: 'hydrophobic',\n    },\n    pKaC: 2.16,\n    pKaN: 9.08,\n  },\n  {\n    name: 'Phenylalanine',\n    aa3: 'Phe',\n    aa1: 'F',\n    sc: {\n      type: 'hydrophobic',\n    },\n    pKaC: 2.18,\n    pKaN: 9.09,\n  },\n  {\n    name: 'Proline',\n    aa3: 'Pro',\n    aa1: 'P',\n    sc: {\n      type: 'special',\n    },\n    pKaC: 1.95,\n    pKaN: 10.47,\n  },\n  {\n    name: 'Serine',\n    aa3: 'Ser',\n    aa1: 'S',\n    sc: {\n      type: 'polar',\n    },\n    pKaC: 2.13,\n    pKaN: 9.05,\n  },\n  {\n    name: 'Threonine',\n    aa3: 'Thr',\n    aa1: 'T',\n    sc: {\n      type: 'polar',\n    },\n    pKaC: 2.2,\n    pKaN: 8.96,\n  },\n  {\n    name: 'Tryptophan',\n    aa3: 'Trp',\n    aa1: 'W',\n    sc: {\n      type: 'hydrophobic',\n    },\n    pKaC: 2.38,\n    pKaN: 9.34,\n  },\n  {\n    name: 'Tyrosine',\n    aa3: 'Tyr',\n    aa1: 'Y',\n    sc: {\n      type: 'hydrophobic',\n    },\n    pKaC: 2.24,\n    pKaN: 9.04,\n  },\n  {\n    name: 'Valine',\n    aa3: 'Val',\n    aa1: 'V',\n    sc: {\n      type: 'hydrophobic',\n    },\n    pKaC: 2.27,\n    pKaN: 9.52,\n  },\n  // Additional\n  {\n    name: 'Selenocysteine',\n    aa3: 'Sec',\n    aa1: 'U',\n  },\n  {\n    name: 'Pyrrolysine',\n    aa3: 'Pyl',\n    aa1: 'O',\n  },\n  // Ambiguous\n  {\n    name: 'Asparagine or aspartic acid',\n    aa3: 'Asx',\n    aa1: 'B',\n  },\n  {\n    name: 'Glutamine or glutamic acid',\n    aa3: 'Glx',\n    aa1: 'Z',\n  },\n  {\n    name: 'Leucine or isoleucine',\n    aa3: 'Xle',\n    aa1: 'J',\n  },\n  {\n    name: 'Unspecified or unknown',\n    aa3: 'Xaa',\n    aa1: 'X',\n  },\n];\n","import { aminoAcids } from './aminoAcids';\n\n// we will convert the data to an object to be much faster\nlet aaObject = {};\nfor (let i = 0; i < aminoAcids.length; i++) {\n  aaObject[aminoAcids[i].aa3] = aminoAcids[i];\n}\n\nexport function calculateCharge(aas, pH = 7.0) {\n  let combined = combine(aas);\n  if (!combined) return;\n  let charge = calculateForPh(combined, pH);\n  return Math.round(charge * 1000) / 1000;\n}\n\n// this methods required an array of aas\n\nexport function calculateIEP(aas) {\n  let combined = combine(aas);\n  if (!combined) return;\n  let first = 0;\n  let last = 14;\n  let current = 14;\n  let previous = 0;\n  let currentCharge;\n  while (Math.abs(current - previous) > 0.0001) {\n    previous = current;\n    current = (last + first) / 2;\n    currentCharge = calculateForPh(combined, current);\n    if (currentCharge > 0) {\n      first = current;\n    } else if (currentCharge < 0) {\n      last = current;\n    } else {\n      previous = current;\n    }\n  }\n  return Math.round(current * 1000) / 1000;\n}\n\nexport function calculateChart(aas) {\n  let combined = combine(aas);\n  if (!combined) return;\n  let y = [];\n  let x = [];\n  let yAbs = [];\n  for (let i = 0; i <= 14; i = i + 0.01) {\n    let charge = calculateForPh(combined, i);\n    x.push(i);\n    y.push(charge);\n    yAbs.push(Math.abs(charge));\n  }\n  combined.x = x;\n  combined.y = y;\n  combined.yAbs = yAbs;\n\n  return combined;\n}\n\nfunction calculateForPh(combined, pH) {\n  let total = 0;\n  total += 1 / (1 + 10 ** (pH - combined.first));\n  total += -1 / (1 + 10 ** (combined.last - pH));\n  for (let key in combined.acid) {\n    total += -combined.acid[key] / (1 + 10 ** (aaObject[key].sc.pKa - pH));\n  }\n  for (let key in combined.basic) {\n    total += combined.basic[key] / (1 + 10 ** (pH - aaObject[key].sc.pKa));\n  }\n  return total;\n}\n\n// we will combine the amino acids\nfunction combine(aas) {\n  let combined = {};\n  if (aaObject[aas[0]]) {\n    combined.first = aaObject[aas[0]].pKaN;\n  } else {\n    return;\n  }\n  if (aaObject[aas[aas.length - 1]]) {\n    combined.last = aaObject[aas[aas.length - 1]].pKaC;\n  } else {\n    return;\n  }\n  combined.basic = {};\n  combined.acid = {};\n  for (let i = 0; i < aas.length; i++) {\n    let currentAA = aas[i];\n    if (!aaObject[currentAA]) return;\n    if (aaObject[currentAA].sc && aaObject[currentAA].sc.type) {\n      if (aaObject[currentAA].sc.type === 'positive') {\n        if (!combined.basic[currentAA]) {\n          combined.basic[currentAA] = 0;\n        }\n        combined.basic[currentAA]++;\n      } else if (aaObject[currentAA].sc.type === 'negative') {\n        if (!combined.acid[currentAA]) {\n          combined.acid[currentAA] = 0;\n        }\n        combined.acid[currentAA]++;\n      }\n    }\n  }\n  return combined;\n}\n\n/*\n We can generate a color based on iep\n 0 -> 7 means that at pH 7 it is charged negatively (blue)\n 7 -> 14 means that at pH7 it is charged positively (red)\n */\nexport function getColor(iep) {\n  if (iep < 7) {\n    if (iep < 3) iep = 3;\n    let white = Math.round(255 - (7 - iep) * (200 / 4));\n    return `rgb(${white},${white},255)`;\n  } else if (iep > 7) {\n    if (iep > 11) iep = 11;\n    let white = Math.round(255 - (iep - 7) * (200 / 4));\n    return `rgb(255,${white},${white})`;\n  }\n  return 'rgb(255,255,255)';\n}\n","export function splitPeptide(sequence) {\n  let aas = sequence.replace(/([A-Z])/g, ' $1').split(/ /);\n  let begin = 0;\n  while (aas[begin] === '' || aas[begin] === 'H') {\n    begin++;\n  }\n  let end = aas.length - 1;\n  while (aas[end] === 'O' || aas[end] === 'H') {\n    end--;\n  }\n  aas = aas.slice(begin, end + 1);\n  return aas;\n}\n","/**\n * Add neutral loss on natural amino acids\n * @param {*} mf\n * @returns\n */\nexport function allowNeutralLoss(mf) {\n  if (Array.isArray(mf)) {\n    for (let i = 0; i < mf.length; i++) {\n      mf[i] = allowOneNeutralLoss(mf[i]);\n    }\n    return mf;\n  } else {\n    return allowOneNeutralLoss(mf);\n  }\n}\n\nfunction allowOneNeutralLoss(mf) {\n  mf = mf.replace(/(Ser|Thr|Asp|Glu)(?!\\()/g, '$1(H-2O-1)0-1');\n  mf = mf.replace(/(Arg|Lys|Asn|Gln)(?!\\()/g, '$1(N-1H-3)0-1');\n\n  return mf;\n}\n","import { aminoAcids } from './aminoAcids';\n\nexport function getAA(code) {\n  if (code.length === 1) {\n    for (let i = 0; i < aminoAcids.length; i++) {\n      if (aminoAcids[i].aa1 === code) {\n        return aminoAcids[i];\n      }\n    }\n  }\n  if (code.length === 3) {\n    for (let i = 0; i < aminoAcids.length; i++) {\n      if (aminoAcids[i].aa3 === code) {\n        return aminoAcids[i];\n      }\n    }\n  }\n}\n","import { getAA } from './getAA';\n\n// SOURCE: https://en.wikipedia.org/wiki/Amino_acid\n\nexport function chargePeptide(mf, options = {}) {\n  if (options.pH === undefined) options.pH = 0;\n  if (Array.isArray(mf)) {\n    for (let i = 0; i < mf.length; i++) {\n      mf[i] = chargeOnePeptide(mf[i], options);\n    }\n    return mf;\n  } else {\n    return chargeOnePeptide(mf, options);\n  }\n}\n\nfunction chargeOnePeptide(mf, options) {\n  let pH = options.pH;\n  // we will allow to charge the peptide at a specific pH\n\n  // first amino acids (N-terminal)\n  if (mf.match(/^H[A-Z][a-z]{2}/)) {\n    let firstAA = mf.replace(/^H([A-Z][a-z]{2}).*/, '$1');\n    if (getAA(firstAA) && pH < getAA(firstAA).pKaN) {\n      mf = mf.replace(/^H([^+])/, 'H+H$1');\n    }\n  }\n\n  // last amino acids (C-terminal)\n  if (mf.match(/[A-Z][a-z]{2}OH$/)) {\n    let lastAA = mf.replace(/.*([A-Z][a-z]{2})OH$/, '$1');\n    if (getAA(lastAA) && pH > getAA(lastAA).pKaC) {\n      mf = mf.replace(/OH$/, 'O-');\n    }\n  }\n\n  // basic AA\n  if (pH < getAA('Arg').sc.pKa) mf = mf.replace(/(Arg)(?!\\()/g, '$1(H+)');\n  if (pH < getAA('His').sc.pKa) mf = mf.replace(/(His)(?!\\()/g, '$1(H+)');\n  if (pH < getAA('Lys').sc.pKa) mf = mf.replace(/(Lys)(?!\\()/g, '$1(H+)');\n\n  // acid AA\n  if (pH > getAA('Asp').sc.pKa) mf = mf.replace(/(Asp)(?!\\()/g, '$1(H-1-)');\n  if (pH > getAA('Glu').sc.pKa) mf = mf.replace(/(Glu)(?!\\()/g, '$1(H-1-)');\n\n  if (pH > getAA('Cys').sc.pKa) mf = mf.replace(/(Cys)(?!\\()/g, '$1(H-1-)');\n\n  return mf;\n}\n","import { aminoAcids } from './aminoAcids';\n\nexport function sequenceToMF(mf) {\n  // this function will check if it is a sequence of aa in 1 letter or 3 letters and convert them if it is the case\n  // it could be a multiline mf !\n  // if it is a multiline we could make some \"tricks\" ...\n\n  let newMF = mf;\n  // SEQRES   1 B  256  MET PRO VAL GLU ILE THR VAL LYS GLU LEU LEU GLU ALA\n  // SEQRES   2 B  256  GLY VAL HIS PHE GLY HIS GLU ARG LYS ARG TRP ASN PRO\n  // or\n  // MET PRO VAL GLU ILE THR VAL LYS GLU LEU LEU GLU ALA\n  // GLY VAL HIS PHE GLY HIS GLU ARG LYS ARG TRP ASN PRO\n  if (mf.search(/[A-Z]{3} [A-Z]{3} [A-Z]{3}/) > -1) {\n    // this is a PDB !\n    let tmpmf = mf.replace(/[\\r\\n]+/g, ' ');\n    tmpmf = tmpmf.replace(/(SEQRES|[0-9]+| [A-Z] | [0-9A-Z]{4-50})/g, '');\n    // we need to correct the uppercase / lowercase\n    let parts = tmpmf.split(' ');\n    newMF = 'H';\n    for (let i = 0; i < parts.length; i++) {\n      newMF += parts[i].substr(0, 1) + parts[i].substr(1).toLowerCase();\n    }\n    newMF += 'OH';\n  } else if (mf.includes('(') && isOneLetterCode(mf)) {\n    // we expect one letter code with modification\n    newMF = '';\n    let nTerminal = 'H';\n    let cTerminal = 'OH';\n    let parenthesisCounter = 0;\n    for (let i = 0; i < mf.length; i++) {\n      let currentSymbol = mf[i];\n      if (\n        currentSymbol === '(' ||\n        currentSymbol === ')' ||\n        parenthesisCounter > 0\n      ) {\n        if (currentSymbol === '(') {\n          parenthesisCounter++;\n          if (i === 0) nTerminal = '';\n        }\n        if (currentSymbol === ')') {\n          parenthesisCounter--;\n          if (i === mf.length - 1) cTerminal = '';\n        }\n        newMF += currentSymbol;\n        continue;\n      }\n      newMF += convertAA1To3(currentSymbol);\n    }\n    newMF = nTerminal + newMF + cTerminal;\n  } else if (\n    mf.search(/[A-Z]{3}/) > -1 &&\n    mf.search(/[a-zA-Z][a-z0-9]/) === -1\n  ) {\n    // UNIPROT\n    //   370        380        390        400        410        420\n    //GFKPNLRKTF VSGLFRESCG AHFYRGVDVK PFYIKKPVDN LFALMLILNR LRGWGVVGGM\n    //\n    //    430        440        450        460        470        480\n    //SDPRLYKVWV RLSSQVPSMF FGGTDLAADY YVVSPPTAVS VYTKTPYGRL LADTRTSGFR\n    // We remove all the number, all the spaces, etc\n    newMF = `H${convertAA1To3(newMF.replace(/[^A-Z]/g, ''))}OH`;\n  }\n\n  return newMF;\n}\n\nfunction convertAA1To3(mf) {\n  let newmf = '';\n  for (let i = 0; i < mf.length; i++) {\n    newmf += aa1To3(mf.charAt(i));\n  }\n  return newmf;\n}\n\nfunction aa1To3(code) {\n  for (let i = 0; i < aminoAcids.length; i++) {\n    if (aminoAcids[i].aa1 === code) {\n      return aminoAcids[i].aa3;\n    }\n  }\n  throw new Error(`Invalid 1 letter code: ${code}`);\n}\n\n// mf can contain as well parenthesis. We need to check if it is not yet a correct molecular formula\nfunction isOneLetterCode(mf) {\n  let parenthesisLevel = 0;\n  for (let char of mf) {\n    if (parenthesisLevel === 0 && char.match(/[a-z]/)) return false;\n    if (char === '(') parenthesisLevel++;\n    if (char === ')') parenthesisLevel--;\n  }\n  return true;\n}\n","export function generatePeptideFragments(mf, options) {\n  if (options === undefined) {\n    options = {\n      a: false,\n      b: true,\n      c: false,\n      x: false,\n      y: true,\n      z: false,\n      i: false,\n      ya: false,\n      yb: false,\n      yc: false,\n      zc: false,\n    };\n  }\n  options.maxInternal = options.maxInternal || Number.MAX_VALUE;\n  options.minInternal = options.minInternal || 0;\n\n  let mfs = [];\n  // need to allow 0-9 to deal with neutral loss\n  let mfparts = mf\n    .replace(/([a-z)0-9])([A-Z][a-z](?=[a-z]))/g, '$1 $2')\n    .split(/ /);\n\n  let nTerm = '';\n  let cTerm = '';\n\n  if (mfparts[0].startsWith('(')) {\n    nTerm += mfparts[0];\n    mfparts = mfparts.splice(1);\n  }\n\n  if (mfparts[mfparts.length - 1].includes('(')) {\n    cTerm += mfparts[mfparts.length - 1].replace(/^[^()]*/, '');\n    mfparts[mfparts.length - 1] = mfparts[mfparts.length - 1].replace(\n      /\\(.*/,\n      '',\n    );\n  }\n\n  for (let i = 1; i < mfparts.length; i++) {\n    nTerm += mfparts[i - 1];\n    cTerm = mfparts[mfparts.length - i] + cTerm;\n    addNTerm(mfs, nTerm, i, options);\n    addCTerm(mfs, cTerm, i, options);\n    if (options.i) mfs.push(`${mfparts[i]}HC-1O-1(+1)$i:${mfparts[i]}`);\n\n    if (options.ya || options.yb || options.yc || options.zc) {\n      // we have double fragmentations\n      for (\n        let j = i + 1;\n        j < Math.min(mfparts.length, options.maxInternal + i + 1);\n        j++\n      ) {\n        let iTerm = '';\n        if (j - i >= options.minInternal) {\n          for (let k = i; k < j; k++) {\n            iTerm += mfparts[k];\n          }\n          addITerm(mfs, iTerm, mfparts.length - i, j, options);\n        }\n      }\n    }\n  }\n\n  // todo does this make sense ??? I think we should remote those 3 lines\n  if (mfs.length === 0) {\n    mfs = mfs.concat([mf]);\n  }\n\n  return mfs;\n}\n\nfunction addNTerm(mfs, nTerm, i, options) {\n  if (options.a) mfs.push(`${nTerm}C-1O-1(+1)$a${i}`);\n  if (options.b) mfs.push(`${nTerm}(+1)$b${i}`);\n  if (options.c) mfs.push(`${nTerm}NH3(+1)$c${i}`);\n}\n\nfunction addITerm(mfs, iTerm, i, j, options) {\n  if (options.ya) mfs.push(`H${iTerm}C-1O-1(+1)$a${j}y${i}`);\n  if (options.yb) mfs.push(`H${iTerm}(+1)$b${j}y${i}`);\n  if (options.yc) mfs.push(`H${iTerm}NH3(+1)$c${j}y${i}`);\n  if (options.zc) mfs.push(`N-1${iTerm}NH3(+1)$c${j}z${i}`);\n}\n\nfunction addCTerm(mfs, cTerm, i, options) {\n  if (options.x) mfs.push(`CO(+1)${cTerm}$x${i}`);\n  if (options.y) mfs.push(`H2(+1)${cTerm}$y${i}`);\n  if (options.z) mfs.push(`N-1H-1(+1)${cTerm}$z${i}`);\n}\n","/*\nIotuibs:\n* minMissed (default: 0)\n* maxMissed (default: 0)\n* minResidue: 0;\n* maxResidue: infinity\n* enzyme: chymotrypsin, trypsin, glucph4, glucph8, thermolysin, cyanogenbromide : Mandatory, no default value !\n */\n\nexport function digestPeptide(sequence, options = {}) {\n  sequence = sequence.replace(/^H([^a-z])/, '$1').replace(/OH$/, '');\n\n  options.enzyme = options.enzyme || 'trypsin';\n  if (options.minMissed === undefined) options.minMissed = 0;\n  if (options.maxMissed === undefined) options.maxMissed = 0;\n  if (options.minResidue === undefined) options.minResidue = 0;\n  if (options.maxResidue === undefined) options.maxResidue = Number.MAX_VALUE;\n  let regexp = getRegexp(options.enzyme);\n  let fragments = sequence\n    .replace(regexp, '$1 ')\n    .split(/ /)\n    .filter((entry) => entry);\n\n  {\n    let from = 0;\n    for (let i = 0; i < fragments.length; i++) {\n      let nbResidue = fragments[i]\n        .replace(/([A-Z][a-z][a-z])/g, ' $1')\n        .split(/ /)\n        .filter((entry) => entry).length;\n      fragments[i] = {\n        sequence: fragments[i],\n        nbResidue,\n        from,\n        to: from + nbResidue - 1,\n      };\n      from += nbResidue;\n    }\n  }\n\n  let results = [];\n\n  for (let i = 0; i < fragments.length - options.minMissed; i++) {\n    for (\n      let j = options.minMissed;\n      j <= Math.min(options.maxMissed, fragments.length - i - 1);\n      j++\n    ) {\n      let fragment = '';\n      let nbResidue = 0;\n      for (let k = i; k <= i + j; k++) {\n        fragment += fragments[k].sequence;\n        nbResidue += fragments[k].nbResidue;\n      }\n      let from = fragments[i].from + 1;\n      let to = fragments[i + j].to + 1;\n      if (\n        fragment &&\n        nbResidue >= options.minResidue &&\n        nbResidue <= options.maxResidue\n      ) {\n        results.push(`H${fragment}OH$D${from}>${to}`);\n      }\n    }\n  }\n\n  return results;\n}\n\nfunction getRegexp(enzyme) {\n  switch (enzyme.toLowerCase().replace(/[^a-z0-9]/g, '')) {\n    case 'chymotrypsin':\n      return /(Phe|Tyr|Trp)(?!Pro)/g;\n    case 'trypsin':\n      return /(Lys|Arg)(?!Pro)/g;\n    case 'lysc':\n      return /(Lys)(?!Pro)/g;\n    case 'glucph4':\n      return /(Glu)(?!Pro|Glu)/g;\n    case 'glucph8':\n      return /(Asp|Glu)(?!Pro|Glu)/g;\n    case 'thermolysin': // N-term of  Leu, Phe, Val, Ile, Ala, Met\n      return /()(?=Ile|Leu|Val|Ala|Met|Phe)/g;\n    case 'cyanogenbromide':\n      return /(Met)/g;\n    case 'any':\n      return /()(?=[A-Z][a-z][a-z])/g;\n    default:\n      throw new Error(`Digestion enzyme: ${enzyme} is unknown`);\n  }\n}\n","import { aminoAcids } from './aminoAcids';\nimport * as IEP from './isoElectricPoint';\nimport { splitPeptide } from './splitPeptide.js';\n\nexport * from './allowNeutralLoss';\nexport * from './chargePeptide.js';\nexport * from './sequenceToMF.js';\nexport * from './generatePeptideFragments.js';\nexport * from './digestPeptide.js';\nexport * from './splitPeptide.js';\n\nexport function getInfo() {\n  return aminoAcids;\n}\n\n// sequence should be in the \"right\" format like HAlaGlyProOH\n\nexport function calculateIEP(sequence) {\n  let aas = splitPeptide(sequence);\n  let result = IEP.calculateIEP(aas);\n  return result;\n}\n\nexport function calculateIEPChart(sequence) {\n  let aas = splitPeptide(sequence);\n  let result = IEP.calculateChart(aas);\n  return result;\n}\n\nexport function getColorForIEP(iep) {\n  return IEP.getColor(iep);\n}\n\nexport function calculateCharge(sequence, ph) {\n  let aas = splitPeptide(sequence);\n  return IEP.calculateCharge(aas, ph);\n}\n","import {\n  digestPeptide,\n  chargePeptide,\n  allowNeutralLoss,\n  sequenceToMF,\n  generatePeptideFragments,\n} from 'peptide';\n\nexport function fragmentPeptide(sequence, options = {}) {\n  const { digestion = {}, protonation, fragmentation, protonationPH } = options;\n  sequence = sequenceToMF(sequence);\n\n  let fragmentsArray = [sequence];\n  // do we also have some digest fragments ?\n  if (digestion.enzyme) {\n    let digests = digestPeptide(sequence, digestion);\n    if (options.protonation) {\n      digests = chargePeptide(digests, {\n        pH: options.protonationPH,\n      });\n    }\n    fragmentsArray = fragmentsArray.concat(digests);\n  }\n\n  // allow neutral loss\n  if (options.allowNeutralLoss) {\n    sequence = allowNeutralLoss(sequence);\n  }\n\n  // apply protonation\n  if (protonation) {\n    sequence = chargePeptide(sequence, { pH: protonationPH });\n  }\n\n  // calculate fragmentation\n  let fragments = generatePeptideFragments(sequence, fragmentation);\n\n  fragmentsArray = fragmentsArray.concat(fragments);\n  return fragmentsArray;\n}\n","import { groupsToSequence } from 'chemical-groups';\nimport { generateMFs } from 'mf-generator';\n\nimport { fragmentPeptide } from './util/fragmentPeptide';\n\n/**\n * Add a database starting from a peptidic sequence\n *\n * @param {string}         [sequences] Sequence as a string of 1 letter or 3 letters code. Could also be a correct molecular formula respecting uppercase, lowercase. It can be comma separated if you have many peptide sequences\n * @param {object}         [options={}]\n * @param {boolean}        [options.estimate=false] - estimate the number of MF without filters\n * @param {string}         [options.ionizations='']\n * @param {function}       [options.onStep] - Callback to do after each step\n * @param {array}          [options.mfsArray=[]]\n * @param {boolean}        [options.protonation=false]\n * @param {number}         [options.protonationPH=7]\n * @param {boolean}        [options.allowNeutralLoss=false]\n * @param {number}         [options.limit=100000]\n *\n * @param {object}         [options.digestion={}] Object defining options for digestion\n * @param {number}         [options.digestion.minMissed=0] Minimal number of allowed missed cleavage\n * @param {number}         [options.digestion.maxMissed=0] Maximal number of allowed missed cleavage\n * @param {number}         [options.digestion.minResidue=0] Minimal number of residues\n * @param {number}         [options.digestion.maxResidue=+Infinity] Maximal number of residues\n * @param {string}         [options.digestion.enzyme] Mandatory field containing the name of the enzyme among: chymotrypsin, trypsin, glucph4, glucph8, thermolysin, cyanogenbromide\n *\n * @param {object}         [options.fragmentation={}] Object defining options for fragmentation\n * @param {boolean}        [options.fragmentation.a=false] If true allow fragments of type 'a'\n * @param {boolean}        [options.fragmentation.b=false] If true allow fragments of type 'b'\n * @param {boolean}        [options.fragmentation.c=false] If true allow fragments of type 'c'\n * @param {boolean}        [options.fragmentation.x=false] If true allow fragments of type 'x'\n * @param {boolean}        [options.fragmentation.y=false] If true allow fragments of type 'y'\n * @param {boolean}        [options.fragmentation.z=false] If true allow fragments of type 'z'\n * @param {boolean}        [options.fragmentation.ya=false] If true allow fragments of type 'ya'\n * @param {boolean}        [options.fragmentation.yb=false] If true allow fragments of type 'yb'\n * @param {boolean}        [options.fragmentation.yc=false] If true allow fragments of type 'yc'\n * @param {boolean}        [options.fragmentation.zc=false] If true allow fragments of type 'zc'\n * @param {number}         [options.fragmentation.minInternal=0] Minimal internal fragment length\n * @param {number}         [options.fragmentation.maxInternal=+Infinity] Maximal internal fragment length\n *\n * @param {object}         [options.filter={}] Object defining options for molecular formula filter\n * @param {number}         [options.filter.precision=1000] - The precision on the experimental mass\n * @param {number}         [options.filter.targetMass] - Target mass, allows to calculate error and filter results\n * @param {number[]}       [options.filter.targetMasses] - Target masses: SORTED array of numbers\n * @param {number[]}       [options.filter.targetIntensities] - Target intensities: SORTED array of numbers\n * @param {number}         [options.filter.minEM=0] - Minimal neutral monoisotopic mass\n * @param {number}         [options.filter.maxEM=+Infinity] - Maximal neutral monoisotopic mass\n * @param {number}         [options.filter.minMSEM=0] - Minimal observed monoisotopic mass\n * @param {number}         [options.filter.maxMSEM=+Infinity] - Maximal observed monoisotopic mass\n * @param {number}         [options.filter.minCharge=-Infinity] - Minimal charge\n * @param {number}         [options.filter.maxCharge=+Infinity] - Maximal charge\n * @param {object}         [options.filter.unsaturation={}]\n * @param {number}         [options.filter.unsaturation.min=-Infinity] - Minimal unsaturation\n * @param {number}         [options.filter.unsaturation.max=+Infinity] - Maximal unsaturation\n * @param {boolean}         [options.filter.unsaturation.onlyInteger=false] - Integer unsaturation\n * @param {boolean}         [options.filter.unsaturation.onlyNonInteger=false] - Non integer unsaturation\n * @param {function}       [options.filter.callback] - a function to filter the MF\n * @returns {Promise}\n */\n\nexport async function fromPeptidicSequence(sequences, options = {}) {\n  const {\n    digestion = {},\n    mfsArray: originalMFsArray = [],\n    allowNeutralLoss = false,\n    protonation = false,\n    protonationPH = 7,\n    fragmentation = {},\n    filter = {},\n    ionizations = '',\n    limit = 100000,\n    estimate = false,\n    onStep,\n    links = {},\n  } = options;\n\n  const hasLinked = sequences.includes('#');\n  const mfsArrayLinked = JSON.parse(JSON.stringify(originalMFsArray));\n  const mfsArrayUnlinked = JSON.parse(JSON.stringify(originalMFsArray));\n  const unlinked = [];\n  mfsArrayUnlinked.push(unlinked);\n\n  for (const sequence of sequences.split(/[,:]/)) {\n    let fragmentsArray = fragmentPeptide(sequence, {\n      digestion,\n      protonation,\n      fragmentation,\n      protonationPH,\n      allowNeutralLoss,\n    });\n    mfsArrayLinked.push(\n      fragmentsArray.filter((fragment) => fragment.includes('#')),\n    );\n    unlinked.push(\n      ...fragmentsArray.filter((fragment) => !fragment.includes('#')),\n    );\n  }\n\n  let combined = await generateMFs(mfsArrayUnlinked, {\n    ionizations,\n    filter,\n    estimate,\n    limit,\n    onStep,\n    links,\n  });\n\n  if (hasLinked) {\n    combined.push(\n      ...(await generateMFs(mfsArrayLinked, {\n        ionizations,\n        filter,\n        estimate,\n        limit,\n        onStep,\n        links,\n      })),\n    );\n  }\n\n  if (!estimate) {\n    combined.forEach((result) => {\n      result.sequence = groupsToSequence(\n        result.parts.filter((part) => part).join(' '),\n      );\n    });\n  }\n\n  return combined;\n}\n","import { generateMFs } from 'mf-generator';\n\n/**\n * Generates a database 'generated' from an array of molecular formula\n * @param {string} rangesString - a string representing the range to search\n * @param {object} [options={}]\n * @param {boolean} [options.estimate=false] - estimate the number of MF without filters\n * @param {function} [options.onStep] - Callback to do after each step\n * @param {string} [options.databaseName='generated']\n * @param {number} [options.limit=100000] - Maximum number of results\n * @param {boolean} [options.canonizeMF=true] - Canonize molecular formula\n * @param {boolean} [options.uniqueMFs=true] - Force canonization and make MF unique\n * @param {string} [options.ionizations=''] - Comma separated list of ionizations (to charge the molecule)\n * @param {object} [options.filter={}]\n * @param {number} [options.filter.minMass=0] - Minimal monoisotopic mass\n * @param {number} [options.filter.maxMass=+Infinity] - Maximal monoisotopic mass\n * @param {number} [options.filter.minEM=0] - Minimal neutral monoisotopic mass\n * @param {number} [options.filter.maxEM=+Infinity] - Maximal neutral monoisotopic mass\n * @param {number} [options.filter.minMSEM=0] - Minimal observed monoisotopic mass\n * @param {number} [options.filter.maxMSEM=+Infinity] - Maximal observed monoisotopic mass\n * @param {number} [options.filter.minCharge=-Infinity] - Minimal charge\n * @param {number} [options.filter.maxCharge=+Infinity] - Maximal charge\n *\n * @param {object} [options.filter.unsaturation={}]\n * @param {number} [options.filter.unsaturation.min=-Infinity] - Minimal unsaturation\n * @param {number} [options.filter.unsaturation.max=+Infinity] - Maximal unsaturation\n * @param {boolean} [options.filter.unsaturation.onlyInteger=false] - Integer unsaturation\n * @param {boolean} [options.filter.unsaturation.onlyNonInteger=false] - Non integer unsaturation\n * @param {function} [options.filter.callback] - a function to filter the MF\n * @param {object} [options.filter.atoms] - object of atom:{min, max}\n *\n * @returns {Promise} - list of possible molecular formula\n *\n * @example\n * const {EMDB} = require('emdb');\n * let emdb = new EMDB();\n * // semi-columns separated for combination, comma for 'or'\n * emdb.fromRange('C1-10, H1-10; Cl0-1 Br0-1'); // create a database 'generated' combining all possibilies\n * console.log(emdb.get('generated').length); // 80\n */\n\nexport async function fromRange(rangesString, options = {}) {\n  let ranges = rangesString.split(/ *[;\\r\\n] */);\n  for (let i = 0; i < ranges.length; i++) {\n    let range = ranges[i];\n    if (range.includes(',')) {\n      ranges[i] = range.split(/ *, */);\n    }\n  }\n\n  return generateMFs(ranges, options);\n}\n","/*!\n\nJSZip v3.10.1 - A JavaScript class for generating and reading zip files\n<http://stuartk.com/jszip>\n\n(c) 2009-2016 Stuart Knightley <stuart [at] stuartk.com>\nDual licenced under the MIT license or GPLv3. See https://raw.github.com/Stuk/jszip/main/LICENSE.markdown.\n\nJSZip uses the library pako released under the MIT license :\nhttps://github.com/nodeca/pako/blob/main/LICENSE\n*/\n\n!function(e){if(\"object\"==typeof exports&&\"undefined\"!=typeof module)module.exports=e();else if(\"function\"==typeof define&&define.amd)define([],e);else{(\"undefined\"!=typeof window?window:\"undefined\"!=typeof global?global:\"undefined\"!=typeof self?self:this).JSZip=e()}}(function(){return function s(a,o,h){function u(r,e){if(!o[r]){if(!a[r]){var t=\"function\"==typeof require&&require;if(!e&&t)return t(r,!0);if(l)return l(r,!0);var n=new Error(\"Cannot find module '\"+r+\"'\");throw n.code=\"MODULE_NOT_FOUND\",n}var i=o[r]={exports:{}};a[r][0].call(i.exports,function(e){var t=a[r][1][e];return u(t||e)},i,i.exports,s,a,o,h)}return o[r].exports}for(var l=\"function\"==typeof require&&require,e=0;e<h.length;e++)u(h[e]);return u}({1:[function(e,t,r){\"use strict\";var d=e(\"./utils\"),c=e(\"./support\"),p=\"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=\";r.encode=function(e){for(var t,r,n,i,s,a,o,h=[],u=0,l=e.length,f=l,c=\"string\"!==d.getTypeOf(e);u<e.length;)f=l-u,n=c?(t=e[u++],r=u<l?e[u++]:0,u<l?e[u++]:0):(t=e.charCodeAt(u++),r=u<l?e.charCodeAt(u++):0,u<l?e.charCodeAt(u++):0),i=t>>2,s=(3&t)<<4|r>>4,a=1<f?(15&r)<<2|n>>6:64,o=2<f?63&n:64,h.push(p.charAt(i)+p.charAt(s)+p.charAt(a)+p.charAt(o));return h.join(\"\")},r.decode=function(e){var t,r,n,i,s,a,o=0,h=0,u=\"data:\";if(e.substr(0,u.length)===u)throw new Error(\"Invalid base64 input, it looks like a data url.\");var l,f=3*(e=e.replace(/[^A-Za-z0-9+/=]/g,\"\")).length/4;if(e.charAt(e.length-1)===p.charAt(64)&&f--,e.charAt(e.length-2)===p.charAt(64)&&f--,f%1!=0)throw new Error(\"Invalid base64 input, bad content length.\");for(l=c.uint8array?new Uint8Array(0|f):new Array(0|f);o<e.length;)t=p.indexOf(e.charAt(o++))<<2|(i=p.indexOf(e.charAt(o++)))>>4,r=(15&i)<<4|(s=p.indexOf(e.charAt(o++)))>>2,n=(3&s)<<6|(a=p.indexOf(e.charAt(o++))),l[h++]=t,64!==s&&(l[h++]=r),64!==a&&(l[h++]=n);return l}},{\"./support\":30,\"./utils\":32}],2:[function(e,t,r){\"use strict\";var n=e(\"./external\"),i=e(\"./stream/DataWorker\"),s=e(\"./stream/Crc32Probe\"),a=e(\"./stream/DataLengthProbe\");function o(e,t,r,n,i){this.compressedSize=e,this.uncompressedSize=t,this.crc32=r,this.compression=n,this.compressedContent=i}o.prototype={getContentWorker:function(){var e=new i(n.Promise.resolve(this.compressedContent)).pipe(this.compression.uncompressWorker()).pipe(new a(\"data_length\")),t=this;return e.on(\"end\",function(){if(this.streamInfo.data_length!==t.uncompressedSize)throw new Error(\"Bug : uncompressed data size mismatch\")}),e},getCompressedWorker:function(){return new i(n.Promise.resolve(this.compressedContent)).withStreamInfo(\"compressedSize\",this.compressedSize).withStreamInfo(\"uncompressedSize\",this.uncompressedSize).withStreamInfo(\"crc32\",this.crc32).withStreamInfo(\"compression\",this.compression)}},o.createWorkerFrom=function(e,t,r){return e.pipe(new s).pipe(new a(\"uncompressedSize\")).pipe(t.compressWorker(r)).pipe(new a(\"compressedSize\")).withStreamInfo(\"compression\",t)},t.exports=o},{\"./external\":6,\"./stream/Crc32Probe\":25,\"./stream/DataLengthProbe\":26,\"./stream/DataWorker\":27}],3:[function(e,t,r){\"use strict\";var n=e(\"./stream/GenericWorker\");r.STORE={magic:\"\\0\\0\",compressWorker:function(){return new n(\"STORE compression\")},uncompressWorker:function(){return new n(\"STORE decompression\")}},r.DEFLATE=e(\"./flate\")},{\"./flate\":7,\"./stream/GenericWorker\":28}],4:[function(e,t,r){\"use strict\";var n=e(\"./utils\");var o=function(){for(var e,t=[],r=0;r<256;r++){e=r;for(var n=0;n<8;n++)e=1&e?3988292384^e>>>1:e>>>1;t[r]=e}return t}();t.exports=function(e,t){return void 0!==e&&e.length?\"string\"!==n.getTypeOf(e)?function(e,t,r,n){var i=o,s=n+r;e^=-1;for(var a=n;a<s;a++)e=e>>>8^i[255&(e^t[a])];return-1^e}(0|t,e,e.length,0):function(e,t,r,n){var i=o,s=n+r;e^=-1;for(var a=n;a<s;a++)e=e>>>8^i[255&(e^t.charCodeAt(a))];return-1^e}(0|t,e,e.length,0):0}},{\"./utils\":32}],5:[function(e,t,r){\"use strict\";r.base64=!1,r.binary=!1,r.dir=!1,r.createFolders=!0,r.date=null,r.compression=null,r.compressionOptions=null,r.comment=null,r.unixPermissions=null,r.dosPermissions=null},{}],6:[function(e,t,r){\"use strict\";var n=null;n=\"undefined\"!=typeof Promise?Promise:e(\"lie\"),t.exports={Promise:n}},{lie:37}],7:[function(e,t,r){\"use strict\";var n=\"undefined\"!=typeof Uint8Array&&\"undefined\"!=typeof Uint16Array&&\"undefined\"!=typeof Uint32Array,i=e(\"pako\"),s=e(\"./utils\"),a=e(\"./stream/GenericWorker\"),o=n?\"uint8array\":\"array\";function h(e,t){a.call(this,\"FlateWorker/\"+e),this._pako=null,this._pakoAction=e,this._pakoOptions=t,this.meta={}}r.magic=\"\\b\\0\",s.inherits(h,a),h.prototype.processChunk=function(e){this.meta=e.meta,null===this._pako&&this._createPako(),this._pako.push(s.transformTo(o,e.data),!1)},h.prototype.flush=function(){a.prototype.flush.call(this),null===this._pako&&this._createPako(),this._pako.push([],!0)},h.prototype.cleanUp=function(){a.prototype.cleanUp.call(this),this._pako=null},h.prototype._createPako=function(){this._pako=new i[this._pakoAction]({raw:!0,level:this._pakoOptions.level||-1});var t=this;this._pako.onData=function(e){t.push({data:e,meta:t.meta})}},r.compressWorker=function(e){return new h(\"Deflate\",e)},r.uncompressWorker=function(){return new h(\"Inflate\",{})}},{\"./stream/GenericWorker\":28,\"./utils\":32,pako:38}],8:[function(e,t,r){\"use strict\";function A(e,t){var r,n=\"\";for(r=0;r<t;r++)n+=String.fromCharCode(255&e),e>>>=8;return n}function n(e,t,r,n,i,s){var a,o,h=e.file,u=e.compression,l=s!==O.utf8encode,f=I.transformTo(\"string\",s(h.name)),c=I.transformTo(\"string\",O.utf8encode(h.name)),d=h.comment,p=I.transformTo(\"string\",s(d)),m=I.transformTo(\"string\",O.utf8encode(d)),_=c.length!==h.name.length,g=m.length!==d.length,b=\"\",v=\"\",y=\"\",w=h.dir,k=h.date,x={crc32:0,compressedSize:0,uncompressedSize:0};t&&!r||(x.crc32=e.crc32,x.compressedSize=e.compressedSize,x.uncompressedSize=e.uncompressedSize);var S=0;t&&(S|=8),l||!_&&!g||(S|=2048);var z=0,C=0;w&&(z|=16),\"UNIX\"===i?(C=798,z|=function(e,t){var r=e;return e||(r=t?16893:33204),(65535&r)<<16}(h.unixPermissions,w)):(C=20,z|=function(e){return 63&(e||0)}(h.dosPermissions)),a=k.getUTCHours(),a<<=6,a|=k.getUTCMinutes(),a<<=5,a|=k.getUTCSeconds()/2,o=k.getUTCFullYear()-1980,o<<=4,o|=k.getUTCMonth()+1,o<<=5,o|=k.getUTCDate(),_&&(v=A(1,1)+A(B(f),4)+c,b+=\"up\"+A(v.length,2)+v),g&&(y=A(1,1)+A(B(p),4)+m,b+=\"uc\"+A(y.length,2)+y);var E=\"\";return E+=\"\\n\\0\",E+=A(S,2),E+=u.magic,E+=A(a,2),E+=A(o,2),E+=A(x.crc32,4),E+=A(x.compressedSize,4),E+=A(x.uncompressedSize,4),E+=A(f.length,2),E+=A(b.length,2),{fileRecord:R.LOCAL_FILE_HEADER+E+f+b,dirRecord:R.CENTRAL_FILE_HEADER+A(C,2)+E+A(p.length,2)+\"\\0\\0\\0\\0\"+A(z,4)+A(n,4)+f+b+p}}var I=e(\"../utils\"),i=e(\"../stream/GenericWorker\"),O=e(\"../utf8\"),B=e(\"../crc32\"),R=e(\"../signature\");function s(e,t,r,n){i.call(this,\"ZipFileWorker\"),this.bytesWritten=0,this.zipComment=t,this.zipPlatform=r,this.encodeFileName=n,this.streamFiles=e,this.accumulate=!1,this.contentBuffer=[],this.dirRecords=[],this.currentSourceOffset=0,this.entriesCount=0,this.currentFile=null,this._sources=[]}I.inherits(s,i),s.prototype.push=function(e){var t=e.meta.percent||0,r=this.entriesCount,n=this._sources.length;this.accumulate?this.contentBuffer.push(e):(this.bytesWritten+=e.data.length,i.prototype.push.call(this,{data:e.data,meta:{currentFile:this.currentFile,percent:r?(t+100*(r-n-1))/r:100}}))},s.prototype.openedSource=function(e){this.currentSourceOffset=this.bytesWritten,this.currentFile=e.file.name;var t=this.streamFiles&&!e.file.dir;if(t){var r=n(e,t,!1,this.currentSourceOffset,this.zipPlatform,this.encodeFileName);this.push({data:r.fileRecord,meta:{percent:0}})}else this.accumulate=!0},s.prototype.closedSource=function(e){this.accumulate=!1;var t=this.streamFiles&&!e.file.dir,r=n(e,t,!0,this.currentSourceOffset,this.zipPlatform,this.encodeFileName);if(this.dirRecords.push(r.dirRecord),t)this.push({data:function(e){return R.DATA_DESCRIPTOR+A(e.crc32,4)+A(e.compressedSize,4)+A(e.uncompressedSize,4)}(e),meta:{percent:100}});else for(this.push({data:r.fileRecord,meta:{percent:0}});this.contentBuffer.length;)this.push(this.contentBuffer.shift());this.currentFile=null},s.prototype.flush=function(){for(var e=this.bytesWritten,t=0;t<this.dirRecords.length;t++)this.push({data:this.dirRecords[t],meta:{percent:100}});var r=this.bytesWritten-e,n=function(e,t,r,n,i){var s=I.transformTo(\"string\",i(n));return R.CENTRAL_DIRECTORY_END+\"\\0\\0\\0\\0\"+A(e,2)+A(e,2)+A(t,4)+A(r,4)+A(s.length,2)+s}(this.dirRecords.length,r,e,this.zipComment,this.encodeFileName);this.push({data:n,meta:{percent:100}})},s.prototype.prepareNextSource=function(){this.previous=this._sources.shift(),this.openedSource(this.previous.streamInfo),this.isPaused?this.previous.pause():this.previous.resume()},s.prototype.registerPrevious=function(e){this._sources.push(e);var t=this;return e.on(\"data\",function(e){t.processChunk(e)}),e.on(\"end\",function(){t.closedSource(t.previous.streamInfo),t._sources.length?t.prepareNextSource():t.end()}),e.on(\"error\",function(e){t.error(e)}),this},s.prototype.resume=function(){return!!i.prototype.resume.call(this)&&(!this.previous&&this._sources.length?(this.prepareNextSource(),!0):this.previous||this._sources.length||this.generatedError?void 0:(this.end(),!0))},s.prototype.error=function(e){var t=this._sources;if(!i.prototype.error.call(this,e))return!1;for(var r=0;r<t.length;r++)try{t[r].error(e)}catch(e){}return!0},s.prototype.lock=function(){i.prototype.lock.call(this);for(var e=this._sources,t=0;t<e.length;t++)e[t].lock()},t.exports=s},{\"../crc32\":4,\"../signature\":23,\"../stream/GenericWorker\":28,\"../utf8\":31,\"../utils\":32}],9:[function(e,t,r){\"use strict\";var u=e(\"../compressions\"),n=e(\"./ZipFileWorker\");r.generateWorker=function(e,a,t){var o=new n(a.streamFiles,t,a.platform,a.encodeFileName),h=0;try{e.forEach(function(e,t){h++;var r=function(e,t){var r=e||t,n=u[r];if(!n)throw new Error(r+\" is not a valid compression method !\");return n}(t.options.compression,a.compression),n=t.options.compressionOptions||a.compressionOptions||{},i=t.dir,s=t.date;t._compressWorker(r,n).withStreamInfo(\"file\",{name:e,dir:i,date:s,comment:t.comment||\"\",unixPermissions:t.unixPermissions,dosPermissions:t.dosPermissions}).pipe(o)}),o.entriesCount=h}catch(e){o.error(e)}return o}},{\"../compressions\":3,\"./ZipFileWorker\":8}],10:[function(e,t,r){\"use strict\";function n(){if(!(this instanceof n))return new n;if(arguments.length)throw new Error(\"The constructor with parameters has been removed in JSZip 3.0, please check the upgrade guide.\");this.files=Object.create(null),this.comment=null,this.root=\"\",this.clone=function(){var e=new n;for(var t in this)\"function\"!=typeof this[t]&&(e[t]=this[t]);return e}}(n.prototype=e(\"./object\")).loadAsync=e(\"./load\"),n.support=e(\"./support\"),n.defaults=e(\"./defaults\"),n.version=\"3.10.1\",n.loadAsync=function(e,t){return(new n).loadAsync(e,t)},n.external=e(\"./external\"),t.exports=n},{\"./defaults\":5,\"./external\":6,\"./load\":11,\"./object\":15,\"./support\":30}],11:[function(e,t,r){\"use strict\";var u=e(\"./utils\"),i=e(\"./external\"),n=e(\"./utf8\"),s=e(\"./zipEntries\"),a=e(\"./stream/Crc32Probe\"),l=e(\"./nodejsUtils\");function f(n){return new i.Promise(function(e,t){var r=n.decompressed.getContentWorker().pipe(new a);r.on(\"error\",function(e){t(e)}).on(\"end\",function(){r.streamInfo.crc32!==n.decompressed.crc32?t(new Error(\"Corrupted zip : CRC32 mismatch\")):e()}).resume()})}t.exports=function(e,o){var h=this;return o=u.extend(o||{},{base64:!1,checkCRC32:!1,optimizedBinaryString:!1,createFolders:!1,decodeFileName:n.utf8decode}),l.isNode&&l.isStream(e)?i.Promise.reject(new Error(\"JSZip can't accept a stream when loading a zip file.\")):u.prepareContent(\"the loaded zip file\",e,!0,o.optimizedBinaryString,o.base64).then(function(e){var t=new s(o);return t.load(e),t}).then(function(e){var t=[i.Promise.resolve(e)],r=e.files;if(o.checkCRC32)for(var n=0;n<r.length;n++)t.push(f(r[n]));return i.Promise.all(t)}).then(function(e){for(var t=e.shift(),r=t.files,n=0;n<r.length;n++){var i=r[n],s=i.fileNameStr,a=u.resolve(i.fileNameStr);h.file(a,i.decompressed,{binary:!0,optimizedBinaryString:!0,date:i.date,dir:i.dir,comment:i.fileCommentStr.length?i.fileCommentStr:null,unixPermissions:i.unixPermissions,dosPermissions:i.dosPermissions,createFolders:o.createFolders}),i.dir||(h.file(a).unsafeOriginalName=s)}return t.zipComment.length&&(h.comment=t.zipComment),h})}},{\"./external\":6,\"./nodejsUtils\":14,\"./stream/Crc32Probe\":25,\"./utf8\":31,\"./utils\":32,\"./zipEntries\":33}],12:[function(e,t,r){\"use strict\";var n=e(\"../utils\"),i=e(\"../stream/GenericWorker\");function s(e,t){i.call(this,\"Nodejs stream input adapter for \"+e),this._upstreamEnded=!1,this._bindStream(t)}n.inherits(s,i),s.prototype._bindStream=function(e){var t=this;(this._stream=e).pause(),e.on(\"data\",function(e){t.push({data:e,meta:{percent:0}})}).on(\"error\",function(e){t.isPaused?this.generatedError=e:t.error(e)}).on(\"end\",function(){t.isPaused?t._upstreamEnded=!0:t.end()})},s.prototype.pause=function(){return!!i.prototype.pause.call(this)&&(this._stream.pause(),!0)},s.prototype.resume=function(){return!!i.prototype.resume.call(this)&&(this._upstreamEnded?this.end():this._stream.resume(),!0)},t.exports=s},{\"../stream/GenericWorker\":28,\"../utils\":32}],13:[function(e,t,r){\"use strict\";var i=e(\"readable-stream\").Readable;function n(e,t,r){i.call(this,t),this._helper=e;var n=this;e.on(\"data\",function(e,t){n.push(e)||n._helper.pause(),r&&r(t)}).on(\"error\",function(e){n.emit(\"error\",e)}).on(\"end\",function(){n.push(null)})}e(\"../utils\").inherits(n,i),n.prototype._read=function(){this._helper.resume()},t.exports=n},{\"../utils\":32,\"readable-stream\":16}],14:[function(e,t,r){\"use strict\";t.exports={isNode:\"undefined\"!=typeof Buffer,newBufferFrom:function(e,t){if(Buffer.from&&Buffer.from!==Uint8Array.from)return Buffer.from(e,t);if(\"number\"==typeof e)throw new Error('The \"data\" argument must not be a number');return new Buffer(e,t)},allocBuffer:function(e){if(Buffer.alloc)return Buffer.alloc(e);var t=new Buffer(e);return t.fill(0),t},isBuffer:function(e){return Buffer.isBuffer(e)},isStream:function(e){return e&&\"function\"==typeof e.on&&\"function\"==typeof e.pause&&\"function\"==typeof e.resume}}},{}],15:[function(e,t,r){\"use strict\";function s(e,t,r){var n,i=u.getTypeOf(t),s=u.extend(r||{},f);s.date=s.date||new Date,null!==s.compression&&(s.compression=s.compression.toUpperCase()),\"string\"==typeof s.unixPermissions&&(s.unixPermissions=parseInt(s.unixPermissions,8)),s.unixPermissions&&16384&s.unixPermissions&&(s.dir=!0),s.dosPermissions&&16&s.dosPermissions&&(s.dir=!0),s.dir&&(e=g(e)),s.createFolders&&(n=_(e))&&b.call(this,n,!0);var a=\"string\"===i&&!1===s.binary&&!1===s.base64;r&&void 0!==r.binary||(s.binary=!a),(t instanceof c&&0===t.uncompressedSize||s.dir||!t||0===t.length)&&(s.base64=!1,s.binary=!0,t=\"\",s.compression=\"STORE\",i=\"string\");var o=null;o=t instanceof c||t instanceof l?t:p.isNode&&p.isStream(t)?new m(e,t):u.prepareContent(e,t,s.binary,s.optimizedBinaryString,s.base64);var h=new d(e,o,s);this.files[e]=h}var i=e(\"./utf8\"),u=e(\"./utils\"),l=e(\"./stream/GenericWorker\"),a=e(\"./stream/StreamHelper\"),f=e(\"./defaults\"),c=e(\"./compressedObject\"),d=e(\"./zipObject\"),o=e(\"./generate\"),p=e(\"./nodejsUtils\"),m=e(\"./nodejs/NodejsStreamInputAdapter\"),_=function(e){\"/\"===e.slice(-1)&&(e=e.substring(0,e.length-1));var t=e.lastIndexOf(\"/\");return 0<t?e.substring(0,t):\"\"},g=function(e){return\"/\"!==e.slice(-1)&&(e+=\"/\"),e},b=function(e,t){return t=void 0!==t?t:f.createFolders,e=g(e),this.files[e]||s.call(this,e,null,{dir:!0,createFolders:t}),this.files[e]};function h(e){return\"[object RegExp]\"===Object.prototype.toString.call(e)}var n={load:function(){throw new Error(\"This method has been removed in JSZip 3.0, please check the upgrade guide.\")},forEach:function(e){var t,r,n;for(t in this.files)n=this.files[t],(r=t.slice(this.root.length,t.length))&&t.slice(0,this.root.length)===this.root&&e(r,n)},filter:function(r){var n=[];return this.forEach(function(e,t){r(e,t)&&n.push(t)}),n},file:function(e,t,r){if(1!==arguments.length)return e=this.root+e,s.call(this,e,t,r),this;if(h(e)){var n=e;return this.filter(function(e,t){return!t.dir&&n.test(e)})}var i=this.files[this.root+e];return i&&!i.dir?i:null},folder:function(r){if(!r)return this;if(h(r))return this.filter(function(e,t){return t.dir&&r.test(e)});var e=this.root+r,t=b.call(this,e),n=this.clone();return n.root=t.name,n},remove:function(r){r=this.root+r;var e=this.files[r];if(e||(\"/\"!==r.slice(-1)&&(r+=\"/\"),e=this.files[r]),e&&!e.dir)delete this.files[r];else for(var t=this.filter(function(e,t){return t.name.slice(0,r.length)===r}),n=0;n<t.length;n++)delete this.files[t[n].name];return this},generate:function(){throw new Error(\"This method has been removed in JSZip 3.0, please check the upgrade guide.\")},generateInternalStream:function(e){var t,r={};try{if((r=u.extend(e||{},{streamFiles:!1,compression:\"STORE\",compressionOptions:null,type:\"\",platform:\"DOS\",comment:null,mimeType:\"application/zip\",encodeFileName:i.utf8encode})).type=r.type.toLowerCase(),r.compression=r.compression.toUpperCase(),\"binarystring\"===r.type&&(r.type=\"string\"),!r.type)throw new Error(\"No output type specified.\");u.checkSupport(r.type),\"darwin\"!==r.platform&&\"freebsd\"!==r.platform&&\"linux\"!==r.platform&&\"sunos\"!==r.platform||(r.platform=\"UNIX\"),\"win32\"===r.platform&&(r.platform=\"DOS\");var n=r.comment||this.comment||\"\";t=o.generateWorker(this,r,n)}catch(e){(t=new l(\"error\")).error(e)}return new a(t,r.type||\"string\",r.mimeType)},generateAsync:function(e,t){return this.generateInternalStream(e).accumulate(t)},generateNodeStream:function(e,t){return(e=e||{}).type||(e.type=\"nodebuffer\"),this.generateInternalStream(e).toNodejsStream(t)}};t.exports=n},{\"./compressedObject\":2,\"./defaults\":5,\"./generate\":9,\"./nodejs/NodejsStreamInputAdapter\":12,\"./nodejsUtils\":14,\"./stream/GenericWorker\":28,\"./stream/StreamHelper\":29,\"./utf8\":31,\"./utils\":32,\"./zipObject\":35}],16:[function(e,t,r){\"use strict\";t.exports=e(\"stream\")},{stream:void 0}],17:[function(e,t,r){\"use strict\";var n=e(\"./DataReader\");function i(e){n.call(this,e);for(var t=0;t<this.data.length;t++)e[t]=255&e[t]}e(\"../utils\").inherits(i,n),i.prototype.byteAt=function(e){return this.data[this.zero+e]},i.prototype.lastIndexOfSignature=function(e){for(var t=e.charCodeAt(0),r=e.charCodeAt(1),n=e.charCodeAt(2),i=e.charCodeAt(3),s=this.length-4;0<=s;--s)if(this.data[s]===t&&this.data[s+1]===r&&this.data[s+2]===n&&this.data[s+3]===i)return s-this.zero;return-1},i.prototype.readAndCheckSignature=function(e){var t=e.charCodeAt(0),r=e.charCodeAt(1),n=e.charCodeAt(2),i=e.charCodeAt(3),s=this.readData(4);return t===s[0]&&r===s[1]&&n===s[2]&&i===s[3]},i.prototype.readData=function(e){if(this.checkOffset(e),0===e)return[];var t=this.data.slice(this.zero+this.index,this.zero+this.index+e);return this.index+=e,t},t.exports=i},{\"../utils\":32,\"./DataReader\":18}],18:[function(e,t,r){\"use strict\";var n=e(\"../utils\");function i(e){this.data=e,this.length=e.length,this.index=0,this.zero=0}i.prototype={checkOffset:function(e){this.checkIndex(this.index+e)},checkIndex:function(e){if(this.length<this.zero+e||e<0)throw new Error(\"End of data reached (data length = \"+this.length+\", asked index = \"+e+\"). Corrupted zip ?\")},setIndex:function(e){this.checkIndex(e),this.index=e},skip:function(e){this.setIndex(this.index+e)},byteAt:function(){},readInt:function(e){var t,r=0;for(this.checkOffset(e),t=this.index+e-1;t>=this.index;t--)r=(r<<8)+this.byteAt(t);return this.index+=e,r},readString:function(e){return n.transformTo(\"string\",this.readData(e))},readData:function(){},lastIndexOfSignature:function(){},readAndCheckSignature:function(){},readDate:function(){var e=this.readInt(4);return new Date(Date.UTC(1980+(e>>25&127),(e>>21&15)-1,e>>16&31,e>>11&31,e>>5&63,(31&e)<<1))}},t.exports=i},{\"../utils\":32}],19:[function(e,t,r){\"use strict\";var n=e(\"./Uint8ArrayReader\");function i(e){n.call(this,e)}e(\"../utils\").inherits(i,n),i.prototype.readData=function(e){this.checkOffset(e);var t=this.data.slice(this.zero+this.index,this.zero+this.index+e);return this.index+=e,t},t.exports=i},{\"../utils\":32,\"./Uint8ArrayReader\":21}],20:[function(e,t,r){\"use strict\";var n=e(\"./DataReader\");function i(e){n.call(this,e)}e(\"../utils\").inherits(i,n),i.prototype.byteAt=function(e){return this.data.charCodeAt(this.zero+e)},i.prototype.lastIndexOfSignature=function(e){return this.data.lastIndexOf(e)-this.zero},i.prototype.readAndCheckSignature=function(e){return e===this.readData(4)},i.prototype.readData=function(e){this.checkOffset(e);var t=this.data.slice(this.zero+this.index,this.zero+this.index+e);return this.index+=e,t},t.exports=i},{\"../utils\":32,\"./DataReader\":18}],21:[function(e,t,r){\"use strict\";var n=e(\"./ArrayReader\");function i(e){n.call(this,e)}e(\"../utils\").inherits(i,n),i.prototype.readData=function(e){if(this.checkOffset(e),0===e)return new Uint8Array(0);var t=this.data.subarray(this.zero+this.index,this.zero+this.index+e);return this.index+=e,t},t.exports=i},{\"../utils\":32,\"./ArrayReader\":17}],22:[function(e,t,r){\"use strict\";var n=e(\"../utils\"),i=e(\"../support\"),s=e(\"./ArrayReader\"),a=e(\"./StringReader\"),o=e(\"./NodeBufferReader\"),h=e(\"./Uint8ArrayReader\");t.exports=function(e){var t=n.getTypeOf(e);return n.checkSupport(t),\"string\"!==t||i.uint8array?\"nodebuffer\"===t?new o(e):i.uint8array?new h(n.transformTo(\"uint8array\",e)):new s(n.transformTo(\"array\",e)):new a(e)}},{\"../support\":30,\"../utils\":32,\"./ArrayReader\":17,\"./NodeBufferReader\":19,\"./StringReader\":20,\"./Uint8ArrayReader\":21}],23:[function(e,t,r){\"use strict\";r.LOCAL_FILE_HEADER=\"PK\u0003\u0004\",r.CENTRAL_FILE_HEADER=\"PK\u0001\u0002\",r.CENTRAL_DIRECTORY_END=\"PK\u0005\u0006\",r.ZIP64_CENTRAL_DIRECTORY_LOCATOR=\"PK\u0006\u0007\",r.ZIP64_CENTRAL_DIRECTORY_END=\"PK\u0006\u0006\",r.DATA_DESCRIPTOR=\"PK\u0007\\b\"},{}],24:[function(e,t,r){\"use strict\";var n=e(\"./GenericWorker\"),i=e(\"../utils\");function s(e){n.call(this,\"ConvertWorker to \"+e),this.destType=e}i.inherits(s,n),s.prototype.processChunk=function(e){this.push({data:i.transformTo(this.destType,e.data),meta:e.meta})},t.exports=s},{\"../utils\":32,\"./GenericWorker\":28}],25:[function(e,t,r){\"use strict\";var n=e(\"./GenericWorker\"),i=e(\"../crc32\");function s(){n.call(this,\"Crc32Probe\"),this.withStreamInfo(\"crc32\",0)}e(\"../utils\").inherits(s,n),s.prototype.processChunk=function(e){this.streamInfo.crc32=i(e.data,this.streamInfo.crc32||0),this.push(e)},t.exports=s},{\"../crc32\":4,\"../utils\":32,\"./GenericWorker\":28}],26:[function(e,t,r){\"use strict\";var n=e(\"../utils\"),i=e(\"./GenericWorker\");function s(e){i.call(this,\"DataLengthProbe for \"+e),this.propName=e,this.withStreamInfo(e,0)}n.inherits(s,i),s.prototype.processChunk=function(e){if(e){var t=this.streamInfo[this.propName]||0;this.streamInfo[this.propName]=t+e.data.length}i.prototype.processChunk.call(this,e)},t.exports=s},{\"../utils\":32,\"./GenericWorker\":28}],27:[function(e,t,r){\"use strict\";var n=e(\"../utils\"),i=e(\"./GenericWorker\");function s(e){i.call(this,\"DataWorker\");var t=this;this.dataIsReady=!1,this.index=0,this.max=0,this.data=null,this.type=\"\",this._tickScheduled=!1,e.then(function(e){t.dataIsReady=!0,t.data=e,t.max=e&&e.length||0,t.type=n.getTypeOf(e),t.isPaused||t._tickAndRepeat()},function(e){t.error(e)})}n.inherits(s,i),s.prototype.cleanUp=function(){i.prototype.cleanUp.call(this),this.data=null},s.prototype.resume=function(){return!!i.prototype.resume.call(this)&&(!this._tickScheduled&&this.dataIsReady&&(this._tickScheduled=!0,n.delay(this._tickAndRepeat,[],this)),!0)},s.prototype._tickAndRepeat=function(){this._tickScheduled=!1,this.isPaused||this.isFinished||(this._tick(),this.isFinished||(n.delay(this._tickAndRepeat,[],this),this._tickScheduled=!0))},s.prototype._tick=function(){if(this.isPaused||this.isFinished)return!1;var e=null,t=Math.min(this.max,this.index+16384);if(this.index>=this.max)return this.end();switch(this.type){case\"string\":e=this.data.substring(this.index,t);break;case\"uint8array\":e=this.data.subarray(this.index,t);break;case\"array\":case\"nodebuffer\":e=this.data.slice(this.index,t)}return this.index=t,this.push({data:e,meta:{percent:this.max?this.index/this.max*100:0}})},t.exports=s},{\"../utils\":32,\"./GenericWorker\":28}],28:[function(e,t,r){\"use strict\";function n(e){this.name=e||\"default\",this.streamInfo={},this.generatedError=null,this.extraStreamInfo={},this.isPaused=!0,this.isFinished=!1,this.isLocked=!1,this._listeners={data:[],end:[],error:[]},this.previous=null}n.prototype={push:function(e){this.emit(\"data\",e)},end:function(){if(this.isFinished)return!1;this.flush();try{this.emit(\"end\"),this.cleanUp(),this.isFinished=!0}catch(e){this.emit(\"error\",e)}return!0},error:function(e){return!this.isFinished&&(this.isPaused?this.generatedError=e:(this.isFinished=!0,this.emit(\"error\",e),this.previous&&this.previous.error(e),this.cleanUp()),!0)},on:function(e,t){return this._listeners[e].push(t),this},cleanUp:function(){this.streamInfo=this.generatedError=this.extraStreamInfo=null,this._listeners=[]},emit:function(e,t){if(this._listeners[e])for(var r=0;r<this._listeners[e].length;r++)this._listeners[e][r].call(this,t)},pipe:function(e){return e.registerPrevious(this)},registerPrevious:function(e){if(this.isLocked)throw new Error(\"The stream '\"+this+\"' has already been used.\");this.streamInfo=e.streamInfo,this.mergeStreamInfo(),this.previous=e;var t=this;return e.on(\"data\",function(e){t.processChunk(e)}),e.on(\"end\",function(){t.end()}),e.on(\"error\",function(e){t.error(e)}),this},pause:function(){return!this.isPaused&&!this.isFinished&&(this.isPaused=!0,this.previous&&this.previous.pause(),!0)},resume:function(){if(!this.isPaused||this.isFinished)return!1;var e=this.isPaused=!1;return this.generatedError&&(this.error(this.generatedError),e=!0),this.previous&&this.previous.resume(),!e},flush:function(){},processChunk:function(e){this.push(e)},withStreamInfo:function(e,t){return this.extraStreamInfo[e]=t,this.mergeStreamInfo(),this},mergeStreamInfo:function(){for(var e in this.extraStreamInfo)Object.prototype.hasOwnProperty.call(this.extraStreamInfo,e)&&(this.streamInfo[e]=this.extraStreamInfo[e])},lock:function(){if(this.isLocked)throw new Error(\"The stream '\"+this+\"' has already been used.\");this.isLocked=!0,this.previous&&this.previous.lock()},toString:function(){var e=\"Worker \"+this.name;return this.previous?this.previous+\" -> \"+e:e}},t.exports=n},{}],29:[function(e,t,r){\"use strict\";var h=e(\"../utils\"),i=e(\"./ConvertWorker\"),s=e(\"./GenericWorker\"),u=e(\"../base64\"),n=e(\"../support\"),a=e(\"../external\"),o=null;if(n.nodestream)try{o=e(\"../nodejs/NodejsStreamOutputAdapter\")}catch(e){}function l(e,o){return new a.Promise(function(t,r){var n=[],i=e._internalType,s=e._outputType,a=e._mimeType;e.on(\"data\",function(e,t){n.push(e),o&&o(t)}).on(\"error\",function(e){n=[],r(e)}).on(\"end\",function(){try{var e=function(e,t,r){switch(e){case\"blob\":return h.newBlob(h.transformTo(\"arraybuffer\",t),r);case\"base64\":return u.encode(t);default:return h.transformTo(e,t)}}(s,function(e,t){var r,n=0,i=null,s=0;for(r=0;r<t.length;r++)s+=t[r].length;switch(e){case\"string\":return t.join(\"\");case\"array\":return Array.prototype.concat.apply([],t);case\"uint8array\":for(i=new Uint8Array(s),r=0;r<t.length;r++)i.set(t[r],n),n+=t[r].length;return i;case\"nodebuffer\":return Buffer.concat(t);default:throw new Error(\"concat : unsupported type '\"+e+\"'\")}}(i,n),a);t(e)}catch(e){r(e)}n=[]}).resume()})}function f(e,t,r){var n=t;switch(t){case\"blob\":case\"arraybuffer\":n=\"uint8array\";break;case\"base64\":n=\"string\"}try{this._internalType=n,this._outputType=t,this._mimeType=r,h.checkSupport(n),this._worker=e.pipe(new i(n)),e.lock()}catch(e){this._worker=new s(\"error\"),this._worker.error(e)}}f.prototype={accumulate:function(e){return l(this,e)},on:function(e,t){var r=this;return\"data\"===e?this._worker.on(e,function(e){t.call(r,e.data,e.meta)}):this._worker.on(e,function(){h.delay(t,arguments,r)}),this},resume:function(){return h.delay(this._worker.resume,[],this._worker),this},pause:function(){return this._worker.pause(),this},toNodejsStream:function(e){if(h.checkSupport(\"nodestream\"),\"nodebuffer\"!==this._outputType)throw new Error(this._outputType+\" is not supported by this method\");return new o(this,{objectMode:\"nodebuffer\"!==this._outputType},e)}},t.exports=f},{\"../base64\":1,\"../external\":6,\"../nodejs/NodejsStreamOutputAdapter\":13,\"../support\":30,\"../utils\":32,\"./ConvertWorker\":24,\"./GenericWorker\":28}],30:[function(e,t,r){\"use strict\";if(r.base64=!0,r.array=!0,r.string=!0,r.arraybuffer=\"undefined\"!=typeof ArrayBuffer&&\"undefined\"!=typeof Uint8Array,r.nodebuffer=\"undefined\"!=typeof Buffer,r.uint8array=\"undefined\"!=typeof Uint8Array,\"undefined\"==typeof ArrayBuffer)r.blob=!1;else{var n=new ArrayBuffer(0);try{r.blob=0===new Blob([n],{type:\"application/zip\"}).size}catch(e){try{var i=new(self.BlobBuilder||self.WebKitBlobBuilder||self.MozBlobBuilder||self.MSBlobBuilder);i.append(n),r.blob=0===i.getBlob(\"application/zip\").size}catch(e){r.blob=!1}}}try{r.nodestream=!!e(\"readable-stream\").Readable}catch(e){r.nodestream=!1}},{\"readable-stream\":16}],31:[function(e,t,s){\"use strict\";for(var o=e(\"./utils\"),h=e(\"./support\"),r=e(\"./nodejsUtils\"),n=e(\"./stream/GenericWorker\"),u=new Array(256),i=0;i<256;i++)u[i]=252<=i?6:248<=i?5:240<=i?4:224<=i?3:192<=i?2:1;u[254]=u[254]=1;function a(){n.call(this,\"utf-8 decode\"),this.leftOver=null}function l(){n.call(this,\"utf-8 encode\")}s.utf8encode=function(e){return h.nodebuffer?r.newBufferFrom(e,\"utf-8\"):function(e){var t,r,n,i,s,a=e.length,o=0;for(i=0;i<a;i++)55296==(64512&(r=e.charCodeAt(i)))&&i+1<a&&56320==(64512&(n=e.charCodeAt(i+1)))&&(r=65536+(r-55296<<10)+(n-56320),i++),o+=r<128?1:r<2048?2:r<65536?3:4;for(t=h.uint8array?new Uint8Array(o):new Array(o),i=s=0;s<o;i++)55296==(64512&(r=e.charCodeAt(i)))&&i+1<a&&56320==(64512&(n=e.charCodeAt(i+1)))&&(r=65536+(r-55296<<10)+(n-56320),i++),r<128?t[s++]=r:(r<2048?t[s++]=192|r>>>6:(r<65536?t[s++]=224|r>>>12:(t[s++]=240|r>>>18,t[s++]=128|r>>>12&63),t[s++]=128|r>>>6&63),t[s++]=128|63&r);return t}(e)},s.utf8decode=function(e){return h.nodebuffer?o.transformTo(\"nodebuffer\",e).toString(\"utf-8\"):function(e){var t,r,n,i,s=e.length,a=new Array(2*s);for(t=r=0;t<s;)if((n=e[t++])<128)a[r++]=n;else if(4<(i=u[n]))a[r++]=65533,t+=i-1;else{for(n&=2===i?31:3===i?15:7;1<i&&t<s;)n=n<<6|63&e[t++],i--;1<i?a[r++]=65533:n<65536?a[r++]=n:(n-=65536,a[r++]=55296|n>>10&1023,a[r++]=56320|1023&n)}return a.length!==r&&(a.subarray?a=a.subarray(0,r):a.length=r),o.applyFromCharCode(a)}(e=o.transformTo(h.uint8array?\"uint8array\":\"array\",e))},o.inherits(a,n),a.prototype.processChunk=function(e){var t=o.transformTo(h.uint8array?\"uint8array\":\"array\",e.data);if(this.leftOver&&this.leftOver.length){if(h.uint8array){var r=t;(t=new Uint8Array(r.length+this.leftOver.length)).set(this.leftOver,0),t.set(r,this.leftOver.length)}else t=this.leftOver.concat(t);this.leftOver=null}var n=function(e,t){var r;for((t=t||e.length)>e.length&&(t=e.length),r=t-1;0<=r&&128==(192&e[r]);)r--;return r<0?t:0===r?t:r+u[e[r]]>t?r:t}(t),i=t;n!==t.length&&(h.uint8array?(i=t.subarray(0,n),this.leftOver=t.subarray(n,t.length)):(i=t.slice(0,n),this.leftOver=t.slice(n,t.length))),this.push({data:s.utf8decode(i),meta:e.meta})},a.prototype.flush=function(){this.leftOver&&this.leftOver.length&&(this.push({data:s.utf8decode(this.leftOver),meta:{}}),this.leftOver=null)},s.Utf8DecodeWorker=a,o.inherits(l,n),l.prototype.processChunk=function(e){this.push({data:s.utf8encode(e.data),meta:e.meta})},s.Utf8EncodeWorker=l},{\"./nodejsUtils\":14,\"./stream/GenericWorker\":28,\"./support\":30,\"./utils\":32}],32:[function(e,t,a){\"use strict\";var o=e(\"./support\"),h=e(\"./base64\"),r=e(\"./nodejsUtils\"),u=e(\"./external\");function n(e){return e}function l(e,t){for(var r=0;r<e.length;++r)t[r]=255&e.charCodeAt(r);return t}e(\"setimmediate\"),a.newBlob=function(t,r){a.checkSupport(\"blob\");try{return new Blob([t],{type:r})}catch(e){try{var n=new(self.BlobBuilder||self.WebKitBlobBuilder||self.MozBlobBuilder||self.MSBlobBuilder);return n.append(t),n.getBlob(r)}catch(e){throw new Error(\"Bug : can't construct the Blob.\")}}};var i={stringifyByChunk:function(e,t,r){var n=[],i=0,s=e.length;if(s<=r)return String.fromCharCode.apply(null,e);for(;i<s;)\"array\"===t||\"nodebuffer\"===t?n.push(String.fromCharCode.apply(null,e.slice(i,Math.min(i+r,s)))):n.push(String.fromCharCode.apply(null,e.subarray(i,Math.min(i+r,s)))),i+=r;return n.join(\"\")},stringifyByChar:function(e){for(var t=\"\",r=0;r<e.length;r++)t+=String.fromCharCode(e[r]);return t},applyCanBeUsed:{uint8array:function(){try{return o.uint8array&&1===String.fromCharCode.apply(null,new Uint8Array(1)).length}catch(e){return!1}}(),nodebuffer:function(){try{return o.nodebuffer&&1===String.fromCharCode.apply(null,r.allocBuffer(1)).length}catch(e){return!1}}()}};function s(e){var t=65536,r=a.getTypeOf(e),n=!0;if(\"uint8array\"===r?n=i.applyCanBeUsed.uint8array:\"nodebuffer\"===r&&(n=i.applyCanBeUsed.nodebuffer),n)for(;1<t;)try{return i.stringifyByChunk(e,r,t)}catch(e){t=Math.floor(t/2)}return i.stringifyByChar(e)}function f(e,t){for(var r=0;r<e.length;r++)t[r]=e[r];return t}a.applyFromCharCode=s;var c={};c.string={string:n,array:function(e){return l(e,new Array(e.length))},arraybuffer:function(e){return c.string.uint8array(e).buffer},uint8array:function(e){return l(e,new Uint8Array(e.length))},nodebuffer:function(e){return l(e,r.allocBuffer(e.length))}},c.array={string:s,array:n,arraybuffer:function(e){return new Uint8Array(e).buffer},uint8array:function(e){return new Uint8Array(e)},nodebuffer:function(e){return r.newBufferFrom(e)}},c.arraybuffer={string:function(e){return s(new Uint8Array(e))},array:function(e){return f(new Uint8Array(e),new Array(e.byteLength))},arraybuffer:n,uint8array:function(e){return new Uint8Array(e)},nodebuffer:function(e){return r.newBufferFrom(new Uint8Array(e))}},c.uint8array={string:s,array:function(e){return f(e,new Array(e.length))},arraybuffer:function(e){return e.buffer},uint8array:n,nodebuffer:function(e){return r.newBufferFrom(e)}},c.nodebuffer={string:s,array:function(e){return f(e,new Array(e.length))},arraybuffer:function(e){return c.nodebuffer.uint8array(e).buffer},uint8array:function(e){return f(e,new Uint8Array(e.length))},nodebuffer:n},a.transformTo=function(e,t){if(t=t||\"\",!e)return t;a.checkSupport(e);var r=a.getTypeOf(t);return c[r][e](t)},a.resolve=function(e){for(var t=e.split(\"/\"),r=[],n=0;n<t.length;n++){var i=t[n];\".\"===i||\"\"===i&&0!==n&&n!==t.length-1||(\"..\"===i?r.pop():r.push(i))}return r.join(\"/\")},a.getTypeOf=function(e){return\"string\"==typeof e?\"string\":\"[object Array]\"===Object.prototype.toString.call(e)?\"array\":o.nodebuffer&&r.isBuffer(e)?\"nodebuffer\":o.uint8array&&e instanceof Uint8Array?\"uint8array\":o.arraybuffer&&e instanceof ArrayBuffer?\"arraybuffer\":void 0},a.checkSupport=function(e){if(!o[e.toLowerCase()])throw new Error(e+\" is not supported by this platform\")},a.MAX_VALUE_16BITS=65535,a.MAX_VALUE_32BITS=-1,a.pretty=function(e){var t,r,n=\"\";for(r=0;r<(e||\"\").length;r++)n+=\"\\\\x\"+((t=e.charCodeAt(r))<16?\"0\":\"\")+t.toString(16).toUpperCase();return n},a.delay=function(e,t,r){setImmediate(function(){e.apply(r||null,t||[])})},a.inherits=function(e,t){function r(){}r.prototype=t.prototype,e.prototype=new r},a.extend=function(){var e,t,r={};for(e=0;e<arguments.length;e++)for(t in arguments[e])Object.prototype.hasOwnProperty.call(arguments[e],t)&&void 0===r[t]&&(r[t]=arguments[e][t]);return r},a.prepareContent=function(r,e,n,i,s){return u.Promise.resolve(e).then(function(n){return o.blob&&(n instanceof Blob||-1!==[\"[object File]\",\"[object Blob]\"].indexOf(Object.prototype.toString.call(n)))&&\"undefined\"!=typeof FileReader?new u.Promise(function(t,r){var e=new FileReader;e.onload=function(e){t(e.target.result)},e.onerror=function(e){r(e.target.error)},e.readAsArrayBuffer(n)}):n}).then(function(e){var t=a.getTypeOf(e);return t?(\"arraybuffer\"===t?e=a.transformTo(\"uint8array\",e):\"string\"===t&&(s?e=h.decode(e):n&&!0!==i&&(e=function(e){return l(e,o.uint8array?new Uint8Array(e.length):new Array(e.length))}(e))),e):u.Promise.reject(new Error(\"Can't read the data of '\"+r+\"'. Is it in a supported JavaScript type (String, Blob, ArrayBuffer, etc) ?\"))})}},{\"./base64\":1,\"./external\":6,\"./nodejsUtils\":14,\"./support\":30,setimmediate:54}],33:[function(e,t,r){\"use strict\";var n=e(\"./reader/readerFor\"),i=e(\"./utils\"),s=e(\"./signature\"),a=e(\"./zipEntry\"),o=e(\"./support\");function h(e){this.files=[],this.loadOptions=e}h.prototype={checkSignature:function(e){if(!this.reader.readAndCheckSignature(e)){this.reader.index-=4;var t=this.reader.readString(4);throw new Error(\"Corrupted zip or bug: unexpected signature (\"+i.pretty(t)+\", expected \"+i.pretty(e)+\")\")}},isSignature:function(e,t){var r=this.reader.index;this.reader.setIndex(e);var n=this.reader.readString(4)===t;return this.reader.setIndex(r),n},readBlockEndOfCentral:function(){this.diskNumber=this.reader.readInt(2),this.diskWithCentralDirStart=this.reader.readInt(2),this.centralDirRecordsOnThisDisk=this.reader.readInt(2),this.centralDirRecords=this.reader.readInt(2),this.centralDirSize=this.reader.readInt(4),this.centralDirOffset=this.reader.readInt(4),this.zipCommentLength=this.reader.readInt(2);var e=this.reader.readData(this.zipCommentLength),t=o.uint8array?\"uint8array\":\"array\",r=i.transformTo(t,e);this.zipComment=this.loadOptions.decodeFileName(r)},readBlockZip64EndOfCentral:function(){this.zip64EndOfCentralSize=this.reader.readInt(8),this.reader.skip(4),this.diskNumber=this.reader.readInt(4),this.diskWithCentralDirStart=this.reader.readInt(4),this.centralDirRecordsOnThisDisk=this.reader.readInt(8),this.centralDirRecords=this.reader.readInt(8),this.centralDirSize=this.reader.readInt(8),this.centralDirOffset=this.reader.readInt(8),this.zip64ExtensibleData={};for(var e,t,r,n=this.zip64EndOfCentralSize-44;0<n;)e=this.reader.readInt(2),t=this.reader.readInt(4),r=this.reader.readData(t),this.zip64ExtensibleData[e]={id:e,length:t,value:r}},readBlockZip64EndOfCentralLocator:function(){if(this.diskWithZip64CentralDirStart=this.reader.readInt(4),this.relativeOffsetEndOfZip64CentralDir=this.reader.readInt(8),this.disksCount=this.reader.readInt(4),1<this.disksCount)throw new Error(\"Multi-volumes zip are not supported\")},readLocalFiles:function(){var e,t;for(e=0;e<this.files.length;e++)t=this.files[e],this.reader.setIndex(t.localHeaderOffset),this.checkSignature(s.LOCAL_FILE_HEADER),t.readLocalPart(this.reader),t.handleUTF8(),t.processAttributes()},readCentralDir:function(){var e;for(this.reader.setIndex(this.centralDirOffset);this.reader.readAndCheckSignature(s.CENTRAL_FILE_HEADER);)(e=new a({zip64:this.zip64},this.loadOptions)).readCentralPart(this.reader),this.files.push(e);if(this.centralDirRecords!==this.files.length&&0!==this.centralDirRecords&&0===this.files.length)throw new Error(\"Corrupted zip or bug: expected \"+this.centralDirRecords+\" records in central dir, got \"+this.files.length)},readEndOfCentral:function(){var e=this.reader.lastIndexOfSignature(s.CENTRAL_DIRECTORY_END);if(e<0)throw!this.isSignature(0,s.LOCAL_FILE_HEADER)?new Error(\"Can't find end of central directory : is this a zip file ? If it is, see https://stuk.github.io/jszip/documentation/howto/read_zip.html\"):new Error(\"Corrupted zip: can't find end of central directory\");this.reader.setIndex(e);var t=e;if(this.checkSignature(s.CENTRAL_DIRECTORY_END),this.readBlockEndOfCentral(),this.diskNumber===i.MAX_VALUE_16BITS||this.diskWithCentralDirStart===i.MAX_VALUE_16BITS||this.centralDirRecordsOnThisDisk===i.MAX_VALUE_16BITS||this.centralDirRecords===i.MAX_VALUE_16BITS||this.centralDirSize===i.MAX_VALUE_32BITS||this.centralDirOffset===i.MAX_VALUE_32BITS){if(this.zip64=!0,(e=this.reader.lastIndexOfSignature(s.ZIP64_CENTRAL_DIRECTORY_LOCATOR))<0)throw new Error(\"Corrupted zip: can't find the ZIP64 end of central directory locator\");if(this.reader.setIndex(e),this.checkSignature(s.ZIP64_CENTRAL_DIRECTORY_LOCATOR),this.readBlockZip64EndOfCentralLocator(),!this.isSignature(this.relativeOffsetEndOfZip64CentralDir,s.ZIP64_CENTRAL_DIRECTORY_END)&&(this.relativeOffsetEndOfZip64CentralDir=this.reader.lastIndexOfSignature(s.ZIP64_CENTRAL_DIRECTORY_END),this.relativeOffsetEndOfZip64CentralDir<0))throw new Error(\"Corrupted zip: can't find the ZIP64 end of central directory\");this.reader.setIndex(this.relativeOffsetEndOfZip64CentralDir),this.checkSignature(s.ZIP64_CENTRAL_DIRECTORY_END),this.readBlockZip64EndOfCentral()}var r=this.centralDirOffset+this.centralDirSize;this.zip64&&(r+=20,r+=12+this.zip64EndOfCentralSize);var n=t-r;if(0<n)this.isSignature(t,s.CENTRAL_FILE_HEADER)||(this.reader.zero=n);else if(n<0)throw new Error(\"Corrupted zip: missing \"+Math.abs(n)+\" bytes.\")},prepareReader:function(e){this.reader=n(e)},load:function(e){this.prepareReader(e),this.readEndOfCentral(),this.readCentralDir(),this.readLocalFiles()}},t.exports=h},{\"./reader/readerFor\":22,\"./signature\":23,\"./support\":30,\"./utils\":32,\"./zipEntry\":34}],34:[function(e,t,r){\"use strict\";var n=e(\"./reader/readerFor\"),s=e(\"./utils\"),i=e(\"./compressedObject\"),a=e(\"./crc32\"),o=e(\"./utf8\"),h=e(\"./compressions\"),u=e(\"./support\");function l(e,t){this.options=e,this.loadOptions=t}l.prototype={isEncrypted:function(){return 1==(1&this.bitFlag)},useUTF8:function(){return 2048==(2048&this.bitFlag)},readLocalPart:function(e){var t,r;if(e.skip(22),this.fileNameLength=e.readInt(2),r=e.readInt(2),this.fileName=e.readData(this.fileNameLength),e.skip(r),-1===this.compressedSize||-1===this.uncompressedSize)throw new Error(\"Bug or corrupted zip : didn't get enough information from the central directory (compressedSize === -1 || uncompressedSize === -1)\");if(null===(t=function(e){for(var t in h)if(Object.prototype.hasOwnProperty.call(h,t)&&h[t].magic===e)return h[t];return null}(this.compressionMethod)))throw new Error(\"Corrupted zip : compression \"+s.pretty(this.compressionMethod)+\" unknown (inner file : \"+s.transformTo(\"string\",this.fileName)+\")\");this.decompressed=new i(this.compressedSize,this.uncompressedSize,this.crc32,t,e.readData(this.compressedSize))},readCentralPart:function(e){this.versionMadeBy=e.readInt(2),e.skip(2),this.bitFlag=e.readInt(2),this.compressionMethod=e.readString(2),this.date=e.readDate(),this.crc32=e.readInt(4),this.compressedSize=e.readInt(4),this.uncompressedSize=e.readInt(4);var t=e.readInt(2);if(this.extraFieldsLength=e.readInt(2),this.fileCommentLength=e.readInt(2),this.diskNumberStart=e.readInt(2),this.internalFileAttributes=e.readInt(2),this.externalFileAttributes=e.readInt(4),this.localHeaderOffset=e.readInt(4),this.isEncrypted())throw new Error(\"Encrypted zip are not supported\");e.skip(t),this.readExtraFields(e),this.parseZIP64ExtraField(e),this.fileComment=e.readData(this.fileCommentLength)},processAttributes:function(){this.unixPermissions=null,this.dosPermissions=null;var e=this.versionMadeBy>>8;this.dir=!!(16&this.externalFileAttributes),0==e&&(this.dosPermissions=63&this.externalFileAttributes),3==e&&(this.unixPermissions=this.externalFileAttributes>>16&65535),this.dir||\"/\"!==this.fileNameStr.slice(-1)||(this.dir=!0)},parseZIP64ExtraField:function(){if(this.extraFields[1]){var e=n(this.extraFields[1].value);this.uncompressedSize===s.MAX_VALUE_32BITS&&(this.uncompressedSize=e.readInt(8)),this.compressedSize===s.MAX_VALUE_32BITS&&(this.compressedSize=e.readInt(8)),this.localHeaderOffset===s.MAX_VALUE_32BITS&&(this.localHeaderOffset=e.readInt(8)),this.diskNumberStart===s.MAX_VALUE_32BITS&&(this.diskNumberStart=e.readInt(4))}},readExtraFields:function(e){var t,r,n,i=e.index+this.extraFieldsLength;for(this.extraFields||(this.extraFields={});e.index+4<i;)t=e.readInt(2),r=e.readInt(2),n=e.readData(r),this.extraFields[t]={id:t,length:r,value:n};e.setIndex(i)},handleUTF8:function(){var e=u.uint8array?\"uint8array\":\"array\";if(this.useUTF8())this.fileNameStr=o.utf8decode(this.fileName),this.fileCommentStr=o.utf8decode(this.fileComment);else{var t=this.findExtraFieldUnicodePath();if(null!==t)this.fileNameStr=t;else{var r=s.transformTo(e,this.fileName);this.fileNameStr=this.loadOptions.decodeFileName(r)}var n=this.findExtraFieldUnicodeComment();if(null!==n)this.fileCommentStr=n;else{var i=s.transformTo(e,this.fileComment);this.fileCommentStr=this.loadOptions.decodeFileName(i)}}},findExtraFieldUnicodePath:function(){var e=this.extraFields[28789];if(e){var t=n(e.value);return 1!==t.readInt(1)?null:a(this.fileName)!==t.readInt(4)?null:o.utf8decode(t.readData(e.length-5))}return null},findExtraFieldUnicodeComment:function(){var e=this.extraFields[25461];if(e){var t=n(e.value);return 1!==t.readInt(1)?null:a(this.fileComment)!==t.readInt(4)?null:o.utf8decode(t.readData(e.length-5))}return null}},t.exports=l},{\"./compressedObject\":2,\"./compressions\":3,\"./crc32\":4,\"./reader/readerFor\":22,\"./support\":30,\"./utf8\":31,\"./utils\":32}],35:[function(e,t,r){\"use strict\";function n(e,t,r){this.name=e,this.dir=r.dir,this.date=r.date,this.comment=r.comment,this.unixPermissions=r.unixPermissions,this.dosPermissions=r.dosPermissions,this._data=t,this._dataBinary=r.binary,this.options={compression:r.compression,compressionOptions:r.compressionOptions}}var s=e(\"./stream/StreamHelper\"),i=e(\"./stream/DataWorker\"),a=e(\"./utf8\"),o=e(\"./compressedObject\"),h=e(\"./stream/GenericWorker\");n.prototype={internalStream:function(e){var t=null,r=\"string\";try{if(!e)throw new Error(\"No output type specified.\");var n=\"string\"===(r=e.toLowerCase())||\"text\"===r;\"binarystring\"!==r&&\"text\"!==r||(r=\"string\"),t=this._decompressWorker();var i=!this._dataBinary;i&&!n&&(t=t.pipe(new a.Utf8EncodeWorker)),!i&&n&&(t=t.pipe(new a.Utf8DecodeWorker))}catch(e){(t=new h(\"error\")).error(e)}return new s(t,r,\"\")},async:function(e,t){return this.internalStream(e).accumulate(t)},nodeStream:function(e,t){return this.internalStream(e||\"nodebuffer\").toNodejsStream(t)},_compressWorker:function(e,t){if(this._data instanceof o&&this._data.compression.magic===e.magic)return this._data.getCompressedWorker();var r=this._decompressWorker();return this._dataBinary||(r=r.pipe(new a.Utf8EncodeWorker)),o.createWorkerFrom(r,e,t)},_decompressWorker:function(){return this._data instanceof o?this._data.getContentWorker():this._data instanceof h?this._data:new i(this._data)}};for(var u=[\"asText\",\"asBinary\",\"asNodeBuffer\",\"asUint8Array\",\"asArrayBuffer\"],l=function(){throw new Error(\"This method has been removed in JSZip 3.0, please check the upgrade guide.\")},f=0;f<u.length;f++)n.prototype[u[f]]=l;t.exports=n},{\"./compressedObject\":2,\"./stream/DataWorker\":27,\"./stream/GenericWorker\":28,\"./stream/StreamHelper\":29,\"./utf8\":31}],36:[function(e,l,t){(function(t){\"use strict\";var r,n,e=t.MutationObserver||t.WebKitMutationObserver;if(e){var i=0,s=new e(u),a=t.document.createTextNode(\"\");s.observe(a,{characterData:!0}),r=function(){a.data=i=++i%2}}else if(t.setImmediate||void 0===t.MessageChannel)r=\"document\"in t&&\"onreadystatechange\"in t.document.createElement(\"script\")?function(){var e=t.document.createElement(\"script\");e.onreadystatechange=function(){u(),e.onreadystatechange=null,e.parentNode.removeChild(e),e=null},t.document.documentElement.appendChild(e)}:function(){setTimeout(u,0)};else{var o=new t.MessageChannel;o.port1.onmessage=u,r=function(){o.port2.postMessage(0)}}var h=[];function u(){var e,t;n=!0;for(var r=h.length;r;){for(t=h,h=[],e=-1;++e<r;)t[e]();r=h.length}n=!1}l.exports=function(e){1!==h.push(e)||n||r()}}).call(this,\"undefined\"!=typeof global?global:\"undefined\"!=typeof self?self:\"undefined\"!=typeof window?window:{})},{}],37:[function(e,t,r){\"use strict\";var i=e(\"immediate\");function u(){}var l={},s=[\"REJECTED\"],a=[\"FULFILLED\"],n=[\"PENDING\"];function o(e){if(\"function\"!=typeof e)throw new TypeError(\"resolver must be a function\");this.state=n,this.queue=[],this.outcome=void 0,e!==u&&d(this,e)}function h(e,t,r){this.promise=e,\"function\"==typeof t&&(this.onFulfilled=t,this.callFulfilled=this.otherCallFulfilled),\"function\"==typeof r&&(this.onRejected=r,this.callRejected=this.otherCallRejected)}function f(t,r,n){i(function(){var e;try{e=r(n)}catch(e){return l.reject(t,e)}e===t?l.reject(t,new TypeError(\"Cannot resolve promise with itself\")):l.resolve(t,e)})}function c(e){var t=e&&e.then;if(e&&(\"object\"==typeof e||\"function\"==typeof e)&&\"function\"==typeof t)return function(){t.apply(e,arguments)}}function d(t,e){var r=!1;function n(e){r||(r=!0,l.reject(t,e))}function i(e){r||(r=!0,l.resolve(t,e))}var s=p(function(){e(i,n)});\"error\"===s.status&&n(s.value)}function p(e,t){var r={};try{r.value=e(t),r.status=\"success\"}catch(e){r.status=\"error\",r.value=e}return r}(t.exports=o).prototype.finally=function(t){if(\"function\"!=typeof t)return this;var r=this.constructor;return this.then(function(e){return r.resolve(t()).then(function(){return e})},function(e){return r.resolve(t()).then(function(){throw e})})},o.prototype.catch=function(e){return this.then(null,e)},o.prototype.then=function(e,t){if(\"function\"!=typeof e&&this.state===a||\"function\"!=typeof t&&this.state===s)return this;var r=new this.constructor(u);this.state!==n?f(r,this.state===a?e:t,this.outcome):this.queue.push(new h(r,e,t));return r},h.prototype.callFulfilled=function(e){l.resolve(this.promise,e)},h.prototype.otherCallFulfilled=function(e){f(this.promise,this.onFulfilled,e)},h.prototype.callRejected=function(e){l.reject(this.promise,e)},h.prototype.otherCallRejected=function(e){f(this.promise,this.onRejected,e)},l.resolve=function(e,t){var r=p(c,t);if(\"error\"===r.status)return l.reject(e,r.value);var n=r.value;if(n)d(e,n);else{e.state=a,e.outcome=t;for(var i=-1,s=e.queue.length;++i<s;)e.queue[i].callFulfilled(t)}return e},l.reject=function(e,t){e.state=s,e.outcome=t;for(var r=-1,n=e.queue.length;++r<n;)e.queue[r].callRejected(t);return e},o.resolve=function(e){if(e instanceof this)return e;return l.resolve(new this(u),e)},o.reject=function(e){var t=new this(u);return l.reject(t,e)},o.all=function(e){var r=this;if(\"[object Array]\"!==Object.prototype.toString.call(e))return this.reject(new TypeError(\"must be an array\"));var n=e.length,i=!1;if(!n)return this.resolve([]);var s=new Array(n),a=0,t=-1,o=new this(u);for(;++t<n;)h(e[t],t);return o;function h(e,t){r.resolve(e).then(function(e){s[t]=e,++a!==n||i||(i=!0,l.resolve(o,s))},function(e){i||(i=!0,l.reject(o,e))})}},o.race=function(e){var t=this;if(\"[object Array]\"!==Object.prototype.toString.call(e))return this.reject(new TypeError(\"must be an array\"));var r=e.length,n=!1;if(!r)return this.resolve([]);var i=-1,s=new this(u);for(;++i<r;)a=e[i],t.resolve(a).then(function(e){n||(n=!0,l.resolve(s,e))},function(e){n||(n=!0,l.reject(s,e))});var a;return s}},{immediate:36}],38:[function(e,t,r){\"use strict\";var n={};(0,e(\"./lib/utils/common\").assign)(n,e(\"./lib/deflate\"),e(\"./lib/inflate\"),e(\"./lib/zlib/constants\")),t.exports=n},{\"./lib/deflate\":39,\"./lib/inflate\":40,\"./lib/utils/common\":41,\"./lib/zlib/constants\":44}],39:[function(e,t,r){\"use strict\";var a=e(\"./zlib/deflate\"),o=e(\"./utils/common\"),h=e(\"./utils/strings\"),i=e(\"./zlib/messages\"),s=e(\"./zlib/zstream\"),u=Object.prototype.toString,l=0,f=-1,c=0,d=8;function p(e){if(!(this instanceof p))return new p(e);this.options=o.assign({level:f,method:d,chunkSize:16384,windowBits:15,memLevel:8,strategy:c,to:\"\"},e||{});var t=this.options;t.raw&&0<t.windowBits?t.windowBits=-t.windowBits:t.gzip&&0<t.windowBits&&t.windowBits<16&&(t.windowBits+=16),this.err=0,this.msg=\"\",this.ended=!1,this.chunks=[],this.strm=new s,this.strm.avail_out=0;var r=a.deflateInit2(this.strm,t.level,t.method,t.windowBits,t.memLevel,t.strategy);if(r!==l)throw new Error(i[r]);if(t.header&&a.deflateSetHeader(this.strm,t.header),t.dictionary){var n;if(n=\"string\"==typeof t.dictionary?h.string2buf(t.dictionary):\"[object ArrayBuffer]\"===u.call(t.dictionary)?new Uint8Array(t.dictionary):t.dictionary,(r=a.deflateSetDictionary(this.strm,n))!==l)throw new Error(i[r]);this._dict_set=!0}}function n(e,t){var r=new p(t);if(r.push(e,!0),r.err)throw r.msg||i[r.err];return r.result}p.prototype.push=function(e,t){var r,n,i=this.strm,s=this.options.chunkSize;if(this.ended)return!1;n=t===~~t?t:!0===t?4:0,\"string\"==typeof e?i.input=h.string2buf(e):\"[object ArrayBuffer]\"===u.call(e)?i.input=new Uint8Array(e):i.input=e,i.next_in=0,i.avail_in=i.input.length;do{if(0===i.avail_out&&(i.output=new o.Buf8(s),i.next_out=0,i.avail_out=s),1!==(r=a.deflate(i,n))&&r!==l)return this.onEnd(r),!(this.ended=!0);0!==i.avail_out&&(0!==i.avail_in||4!==n&&2!==n)||(\"string\"===this.options.to?this.onData(h.buf2binstring(o.shrinkBuf(i.output,i.next_out))):this.onData(o.shrinkBuf(i.output,i.next_out)))}while((0<i.avail_in||0===i.avail_out)&&1!==r);return 4===n?(r=a.deflateEnd(this.strm),this.onEnd(r),this.ended=!0,r===l):2!==n||(this.onEnd(l),!(i.avail_out=0))},p.prototype.onData=function(e){this.chunks.push(e)},p.prototype.onEnd=function(e){e===l&&(\"string\"===this.options.to?this.result=this.chunks.join(\"\"):this.result=o.flattenChunks(this.chunks)),this.chunks=[],this.err=e,this.msg=this.strm.msg},r.Deflate=p,r.deflate=n,r.deflateRaw=function(e,t){return(t=t||{}).raw=!0,n(e,t)},r.gzip=function(e,t){return(t=t||{}).gzip=!0,n(e,t)}},{\"./utils/common\":41,\"./utils/strings\":42,\"./zlib/deflate\":46,\"./zlib/messages\":51,\"./zlib/zstream\":53}],40:[function(e,t,r){\"use strict\";var c=e(\"./zlib/inflate\"),d=e(\"./utils/common\"),p=e(\"./utils/strings\"),m=e(\"./zlib/constants\"),n=e(\"./zlib/messages\"),i=e(\"./zlib/zstream\"),s=e(\"./zlib/gzheader\"),_=Object.prototype.toString;function a(e){if(!(this instanceof a))return new a(e);this.options=d.assign({chunkSize:16384,windowBits:0,to:\"\"},e||{});var t=this.options;t.raw&&0<=t.windowBits&&t.windowBits<16&&(t.windowBits=-t.windowBits,0===t.windowBits&&(t.windowBits=-15)),!(0<=t.windowBits&&t.windowBits<16)||e&&e.windowBits||(t.windowBits+=32),15<t.windowBits&&t.windowBits<48&&0==(15&t.windowBits)&&(t.windowBits|=15),this.err=0,this.msg=\"\",this.ended=!1,this.chunks=[],this.strm=new i,this.strm.avail_out=0;var r=c.inflateInit2(this.strm,t.windowBits);if(r!==m.Z_OK)throw new Error(n[r]);this.header=new s,c.inflateGetHeader(this.strm,this.header)}function o(e,t){var r=new a(t);if(r.push(e,!0),r.err)throw r.msg||n[r.err];return r.result}a.prototype.push=function(e,t){var r,n,i,s,a,o,h=this.strm,u=this.options.chunkSize,l=this.options.dictionary,f=!1;if(this.ended)return!1;n=t===~~t?t:!0===t?m.Z_FINISH:m.Z_NO_FLUSH,\"string\"==typeof e?h.input=p.binstring2buf(e):\"[object ArrayBuffer]\"===_.call(e)?h.input=new Uint8Array(e):h.input=e,h.next_in=0,h.avail_in=h.input.length;do{if(0===h.avail_out&&(h.output=new d.Buf8(u),h.next_out=0,h.avail_out=u),(r=c.inflate(h,m.Z_NO_FLUSH))===m.Z_NEED_DICT&&l&&(o=\"string\"==typeof l?p.string2buf(l):\"[object ArrayBuffer]\"===_.call(l)?new Uint8Array(l):l,r=c.inflateSetDictionary(this.strm,o)),r===m.Z_BUF_ERROR&&!0===f&&(r=m.Z_OK,f=!1),r!==m.Z_STREAM_END&&r!==m.Z_OK)return this.onEnd(r),!(this.ended=!0);h.next_out&&(0!==h.avail_out&&r!==m.Z_STREAM_END&&(0!==h.avail_in||n!==m.Z_FINISH&&n!==m.Z_SYNC_FLUSH)||(\"string\"===this.options.to?(i=p.utf8border(h.output,h.next_out),s=h.next_out-i,a=p.buf2string(h.output,i),h.next_out=s,h.avail_out=u-s,s&&d.arraySet(h.output,h.output,i,s,0),this.onData(a)):this.onData(d.shrinkBuf(h.output,h.next_out)))),0===h.avail_in&&0===h.avail_out&&(f=!0)}while((0<h.avail_in||0===h.avail_out)&&r!==m.Z_STREAM_END);return r===m.Z_STREAM_END&&(n=m.Z_FINISH),n===m.Z_FINISH?(r=c.inflateEnd(this.strm),this.onEnd(r),this.ended=!0,r===m.Z_OK):n!==m.Z_SYNC_FLUSH||(this.onEnd(m.Z_OK),!(h.avail_out=0))},a.prototype.onData=function(e){this.chunks.push(e)},a.prototype.onEnd=function(e){e===m.Z_OK&&(\"string\"===this.options.to?this.result=this.chunks.join(\"\"):this.result=d.flattenChunks(this.chunks)),this.chunks=[],this.err=e,this.msg=this.strm.msg},r.Inflate=a,r.inflate=o,r.inflateRaw=function(e,t){return(t=t||{}).raw=!0,o(e,t)},r.ungzip=o},{\"./utils/common\":41,\"./utils/strings\":42,\"./zlib/constants\":44,\"./zlib/gzheader\":47,\"./zlib/inflate\":49,\"./zlib/messages\":51,\"./zlib/zstream\":53}],41:[function(e,t,r){\"use strict\";var n=\"undefined\"!=typeof Uint8Array&&\"undefined\"!=typeof Uint16Array&&\"undefined\"!=typeof Int32Array;r.assign=function(e){for(var t=Array.prototype.slice.call(arguments,1);t.length;){var r=t.shift();if(r){if(\"object\"!=typeof r)throw new TypeError(r+\"must be non-object\");for(var n in r)r.hasOwnProperty(n)&&(e[n]=r[n])}}return e},r.shrinkBuf=function(e,t){return e.length===t?e:e.subarray?e.subarray(0,t):(e.length=t,e)};var i={arraySet:function(e,t,r,n,i){if(t.subarray&&e.subarray)e.set(t.subarray(r,r+n),i);else for(var s=0;s<n;s++)e[i+s]=t[r+s]},flattenChunks:function(e){var t,r,n,i,s,a;for(t=n=0,r=e.length;t<r;t++)n+=e[t].length;for(a=new Uint8Array(n),t=i=0,r=e.length;t<r;t++)s=e[t],a.set(s,i),i+=s.length;return a}},s={arraySet:function(e,t,r,n,i){for(var s=0;s<n;s++)e[i+s]=t[r+s]},flattenChunks:function(e){return[].concat.apply([],e)}};r.setTyped=function(e){e?(r.Buf8=Uint8Array,r.Buf16=Uint16Array,r.Buf32=Int32Array,r.assign(r,i)):(r.Buf8=Array,r.Buf16=Array,r.Buf32=Array,r.assign(r,s))},r.setTyped(n)},{}],42:[function(e,t,r){\"use strict\";var h=e(\"./common\"),i=!0,s=!0;try{String.fromCharCode.apply(null,[0])}catch(e){i=!1}try{String.fromCharCode.apply(null,new Uint8Array(1))}catch(e){s=!1}for(var u=new h.Buf8(256),n=0;n<256;n++)u[n]=252<=n?6:248<=n?5:240<=n?4:224<=n?3:192<=n?2:1;function l(e,t){if(t<65537&&(e.subarray&&s||!e.subarray&&i))return String.fromCharCode.apply(null,h.shrinkBuf(e,t));for(var r=\"\",n=0;n<t;n++)r+=String.fromCharCode(e[n]);return r}u[254]=u[254]=1,r.string2buf=function(e){var t,r,n,i,s,a=e.length,o=0;for(i=0;i<a;i++)55296==(64512&(r=e.charCodeAt(i)))&&i+1<a&&56320==(64512&(n=e.charCodeAt(i+1)))&&(r=65536+(r-55296<<10)+(n-56320),i++),o+=r<128?1:r<2048?2:r<65536?3:4;for(t=new h.Buf8(o),i=s=0;s<o;i++)55296==(64512&(r=e.charCodeAt(i)))&&i+1<a&&56320==(64512&(n=e.charCodeAt(i+1)))&&(r=65536+(r-55296<<10)+(n-56320),i++),r<128?t[s++]=r:(r<2048?t[s++]=192|r>>>6:(r<65536?t[s++]=224|r>>>12:(t[s++]=240|r>>>18,t[s++]=128|r>>>12&63),t[s++]=128|r>>>6&63),t[s++]=128|63&r);return t},r.buf2binstring=function(e){return l(e,e.length)},r.binstring2buf=function(e){for(var t=new h.Buf8(e.length),r=0,n=t.length;r<n;r++)t[r]=e.charCodeAt(r);return t},r.buf2string=function(e,t){var r,n,i,s,a=t||e.length,o=new Array(2*a);for(r=n=0;r<a;)if((i=e[r++])<128)o[n++]=i;else if(4<(s=u[i]))o[n++]=65533,r+=s-1;else{for(i&=2===s?31:3===s?15:7;1<s&&r<a;)i=i<<6|63&e[r++],s--;1<s?o[n++]=65533:i<65536?o[n++]=i:(i-=65536,o[n++]=55296|i>>10&1023,o[n++]=56320|1023&i)}return l(o,n)},r.utf8border=function(e,t){var r;for((t=t||e.length)>e.length&&(t=e.length),r=t-1;0<=r&&128==(192&e[r]);)r--;return r<0?t:0===r?t:r+u[e[r]]>t?r:t}},{\"./common\":41}],43:[function(e,t,r){\"use strict\";t.exports=function(e,t,r,n){for(var i=65535&e|0,s=e>>>16&65535|0,a=0;0!==r;){for(r-=a=2e3<r?2e3:r;s=s+(i=i+t[n++]|0)|0,--a;);i%=65521,s%=65521}return i|s<<16|0}},{}],44:[function(e,t,r){\"use strict\";t.exports={Z_NO_FLUSH:0,Z_PARTIAL_FLUSH:1,Z_SYNC_FLUSH:2,Z_FULL_FLUSH:3,Z_FINISH:4,Z_BLOCK:5,Z_TREES:6,Z_OK:0,Z_STREAM_END:1,Z_NEED_DICT:2,Z_ERRNO:-1,Z_STREAM_ERROR:-2,Z_DATA_ERROR:-3,Z_BUF_ERROR:-5,Z_NO_COMPRESSION:0,Z_BEST_SPEED:1,Z_BEST_COMPRESSION:9,Z_DEFAULT_COMPRESSION:-1,Z_FILTERED:1,Z_HUFFMAN_ONLY:2,Z_RLE:3,Z_FIXED:4,Z_DEFAULT_STRATEGY:0,Z_BINARY:0,Z_TEXT:1,Z_UNKNOWN:2,Z_DEFLATED:8}},{}],45:[function(e,t,r){\"use strict\";var o=function(){for(var e,t=[],r=0;r<256;r++){e=r;for(var n=0;n<8;n++)e=1&e?3988292384^e>>>1:e>>>1;t[r]=e}return t}();t.exports=function(e,t,r,n){var i=o,s=n+r;e^=-1;for(var a=n;a<s;a++)e=e>>>8^i[255&(e^t[a])];return-1^e}},{}],46:[function(e,t,r){\"use strict\";var h,c=e(\"../utils/common\"),u=e(\"./trees\"),d=e(\"./adler32\"),p=e(\"./crc32\"),n=e(\"./messages\"),l=0,f=4,m=0,_=-2,g=-1,b=4,i=2,v=8,y=9,s=286,a=30,o=19,w=2*s+1,k=15,x=3,S=258,z=S+x+1,C=42,E=113,A=1,I=2,O=3,B=4;function R(e,t){return e.msg=n[t],t}function T(e){return(e<<1)-(4<e?9:0)}function D(e){for(var t=e.length;0<=--t;)e[t]=0}function F(e){var t=e.state,r=t.pending;r>e.avail_out&&(r=e.avail_out),0!==r&&(c.arraySet(e.output,t.pending_buf,t.pending_out,r,e.next_out),e.next_out+=r,t.pending_out+=r,e.total_out+=r,e.avail_out-=r,t.pending-=r,0===t.pending&&(t.pending_out=0))}function N(e,t){u._tr_flush_block(e,0<=e.block_start?e.block_start:-1,e.strstart-e.block_start,t),e.block_start=e.strstart,F(e.strm)}function U(e,t){e.pending_buf[e.pending++]=t}function P(e,t){e.pending_buf[e.pending++]=t>>>8&255,e.pending_buf[e.pending++]=255&t}function L(e,t){var r,n,i=e.max_chain_length,s=e.strstart,a=e.prev_length,o=e.nice_match,h=e.strstart>e.w_size-z?e.strstart-(e.w_size-z):0,u=e.window,l=e.w_mask,f=e.prev,c=e.strstart+S,d=u[s+a-1],p=u[s+a];e.prev_length>=e.good_match&&(i>>=2),o>e.lookahead&&(o=e.lookahead);do{if(u[(r=t)+a]===p&&u[r+a-1]===d&&u[r]===u[s]&&u[++r]===u[s+1]){s+=2,r++;do{}while(u[++s]===u[++r]&&u[++s]===u[++r]&&u[++s]===u[++r]&&u[++s]===u[++r]&&u[++s]===u[++r]&&u[++s]===u[++r]&&u[++s]===u[++r]&&u[++s]===u[++r]&&s<c);if(n=S-(c-s),s=c-S,a<n){if(e.match_start=t,o<=(a=n))break;d=u[s+a-1],p=u[s+a]}}}while((t=f[t&l])>h&&0!=--i);return a<=e.lookahead?a:e.lookahead}function j(e){var t,r,n,i,s,a,o,h,u,l,f=e.w_size;do{if(i=e.window_size-e.lookahead-e.strstart,e.strstart>=f+(f-z)){for(c.arraySet(e.window,e.window,f,f,0),e.match_start-=f,e.strstart-=f,e.block_start-=f,t=r=e.hash_size;n=e.head[--t],e.head[t]=f<=n?n-f:0,--r;);for(t=r=f;n=e.prev[--t],e.prev[t]=f<=n?n-f:0,--r;);i+=f}if(0===e.strm.avail_in)break;if(a=e.strm,o=e.window,h=e.strstart+e.lookahead,u=i,l=void 0,l=a.avail_in,u<l&&(l=u),r=0===l?0:(a.avail_in-=l,c.arraySet(o,a.input,a.next_in,l,h),1===a.state.wrap?a.adler=d(a.adler,o,l,h):2===a.state.wrap&&(a.adler=p(a.adler,o,l,h)),a.next_in+=l,a.total_in+=l,l),e.lookahead+=r,e.lookahead+e.insert>=x)for(s=e.strstart-e.insert,e.ins_h=e.window[s],e.ins_h=(e.ins_h<<e.hash_shift^e.window[s+1])&e.hash_mask;e.insert&&(e.ins_h=(e.ins_h<<e.hash_shift^e.window[s+x-1])&e.hash_mask,e.prev[s&e.w_mask]=e.head[e.ins_h],e.head[e.ins_h]=s,s++,e.insert--,!(e.lookahead+e.insert<x)););}while(e.lookahead<z&&0!==e.strm.avail_in)}function Z(e,t){for(var r,n;;){if(e.lookahead<z){if(j(e),e.lookahead<z&&t===l)return A;if(0===e.lookahead)break}if(r=0,e.lookahead>=x&&(e.ins_h=(e.ins_h<<e.hash_shift^e.window[e.strstart+x-1])&e.hash_mask,r=e.prev[e.strstart&e.w_mask]=e.head[e.ins_h],e.head[e.ins_h]=e.strstart),0!==r&&e.strstart-r<=e.w_size-z&&(e.match_length=L(e,r)),e.match_length>=x)if(n=u._tr_tally(e,e.strstart-e.match_start,e.match_length-x),e.lookahead-=e.match_length,e.match_length<=e.max_lazy_match&&e.lookahead>=x){for(e.match_length--;e.strstart++,e.ins_h=(e.ins_h<<e.hash_shift^e.window[e.strstart+x-1])&e.hash_mask,r=e.prev[e.strstart&e.w_mask]=e.head[e.ins_h],e.head[e.ins_h]=e.strstart,0!=--e.match_length;);e.strstart++}else e.strstart+=e.match_length,e.match_length=0,e.ins_h=e.window[e.strstart],e.ins_h=(e.ins_h<<e.hash_shift^e.window[e.strstart+1])&e.hash_mask;else n=u._tr_tally(e,0,e.window[e.strstart]),e.lookahead--,e.strstart++;if(n&&(N(e,!1),0===e.strm.avail_out))return A}return e.insert=e.strstart<x-1?e.strstart:x-1,t===f?(N(e,!0),0===e.strm.avail_out?O:B):e.last_lit&&(N(e,!1),0===e.strm.avail_out)?A:I}function W(e,t){for(var r,n,i;;){if(e.lookahead<z){if(j(e),e.lookahead<z&&t===l)return A;if(0===e.lookahead)break}if(r=0,e.lookahead>=x&&(e.ins_h=(e.ins_h<<e.hash_shift^e.window[e.strstart+x-1])&e.hash_mask,r=e.prev[e.strstart&e.w_mask]=e.head[e.ins_h],e.head[e.ins_h]=e.strstart),e.prev_length=e.match_length,e.prev_match=e.match_start,e.match_length=x-1,0!==r&&e.prev_length<e.max_lazy_match&&e.strstart-r<=e.w_size-z&&(e.match_length=L(e,r),e.match_length<=5&&(1===e.strategy||e.match_length===x&&4096<e.strstart-e.match_start)&&(e.match_length=x-1)),e.prev_length>=x&&e.match_length<=e.prev_length){for(i=e.strstart+e.lookahead-x,n=u._tr_tally(e,e.strstart-1-e.prev_match,e.prev_length-x),e.lookahead-=e.prev_length-1,e.prev_length-=2;++e.strstart<=i&&(e.ins_h=(e.ins_h<<e.hash_shift^e.window[e.strstart+x-1])&e.hash_mask,r=e.prev[e.strstart&e.w_mask]=e.head[e.ins_h],e.head[e.ins_h]=e.strstart),0!=--e.prev_length;);if(e.match_available=0,e.match_length=x-1,e.strstart++,n&&(N(e,!1),0===e.strm.avail_out))return A}else if(e.match_available){if((n=u._tr_tally(e,0,e.window[e.strstart-1]))&&N(e,!1),e.strstart++,e.lookahead--,0===e.strm.avail_out)return A}else e.match_available=1,e.strstart++,e.lookahead--}return e.match_available&&(n=u._tr_tally(e,0,e.window[e.strstart-1]),e.match_available=0),e.insert=e.strstart<x-1?e.strstart:x-1,t===f?(N(e,!0),0===e.strm.avail_out?O:B):e.last_lit&&(N(e,!1),0===e.strm.avail_out)?A:I}function M(e,t,r,n,i){this.good_length=e,this.max_lazy=t,this.nice_length=r,this.max_chain=n,this.func=i}function H(){this.strm=null,this.status=0,this.pending_buf=null,this.pending_buf_size=0,this.pending_out=0,this.pending=0,this.wrap=0,this.gzhead=null,this.gzindex=0,this.method=v,this.last_flush=-1,this.w_size=0,this.w_bits=0,this.w_mask=0,this.window=null,this.window_size=0,this.prev=null,this.head=null,this.ins_h=0,this.hash_size=0,this.hash_bits=0,this.hash_mask=0,this.hash_shift=0,this.block_start=0,this.match_length=0,this.prev_match=0,this.match_available=0,this.strstart=0,this.match_start=0,this.lookahead=0,this.prev_length=0,this.max_chain_length=0,this.max_lazy_match=0,this.level=0,this.strategy=0,this.good_match=0,this.nice_match=0,this.dyn_ltree=new c.Buf16(2*w),this.dyn_dtree=new c.Buf16(2*(2*a+1)),this.bl_tree=new c.Buf16(2*(2*o+1)),D(this.dyn_ltree),D(this.dyn_dtree),D(this.bl_tree),this.l_desc=null,this.d_desc=null,this.bl_desc=null,this.bl_count=new c.Buf16(k+1),this.heap=new c.Buf16(2*s+1),D(this.heap),this.heap_len=0,this.heap_max=0,this.depth=new c.Buf16(2*s+1),D(this.depth),this.l_buf=0,this.lit_bufsize=0,this.last_lit=0,this.d_buf=0,this.opt_len=0,this.static_len=0,this.matches=0,this.insert=0,this.bi_buf=0,this.bi_valid=0}function G(e){var t;return e&&e.state?(e.total_in=e.total_out=0,e.data_type=i,(t=e.state).pending=0,t.pending_out=0,t.wrap<0&&(t.wrap=-t.wrap),t.status=t.wrap?C:E,e.adler=2===t.wrap?0:1,t.last_flush=l,u._tr_init(t),m):R(e,_)}function K(e){var t=G(e);return t===m&&function(e){e.window_size=2*e.w_size,D(e.head),e.max_lazy_match=h[e.level].max_lazy,e.good_match=h[e.level].good_length,e.nice_match=h[e.level].nice_length,e.max_chain_length=h[e.level].max_chain,e.strstart=0,e.block_start=0,e.lookahead=0,e.insert=0,e.match_length=e.prev_length=x-1,e.match_available=0,e.ins_h=0}(e.state),t}function Y(e,t,r,n,i,s){if(!e)return _;var a=1;if(t===g&&(t=6),n<0?(a=0,n=-n):15<n&&(a=2,n-=16),i<1||y<i||r!==v||n<8||15<n||t<0||9<t||s<0||b<s)return R(e,_);8===n&&(n=9);var o=new H;return(e.state=o).strm=e,o.wrap=a,o.gzhead=null,o.w_bits=n,o.w_size=1<<o.w_bits,o.w_mask=o.w_size-1,o.hash_bits=i+7,o.hash_size=1<<o.hash_bits,o.hash_mask=o.hash_size-1,o.hash_shift=~~((o.hash_bits+x-1)/x),o.window=new c.Buf8(2*o.w_size),o.head=new c.Buf16(o.hash_size),o.prev=new c.Buf16(o.w_size),o.lit_bufsize=1<<i+6,o.pending_buf_size=4*o.lit_bufsize,o.pending_buf=new c.Buf8(o.pending_buf_size),o.d_buf=1*o.lit_bufsize,o.l_buf=3*o.lit_bufsize,o.level=t,o.strategy=s,o.method=r,K(e)}h=[new M(0,0,0,0,function(e,t){var r=65535;for(r>e.pending_buf_size-5&&(r=e.pending_buf_size-5);;){if(e.lookahead<=1){if(j(e),0===e.lookahead&&t===l)return A;if(0===e.lookahead)break}e.strstart+=e.lookahead,e.lookahead=0;var n=e.block_start+r;if((0===e.strstart||e.strstart>=n)&&(e.lookahead=e.strstart-n,e.strstart=n,N(e,!1),0===e.strm.avail_out))return A;if(e.strstart-e.block_start>=e.w_size-z&&(N(e,!1),0===e.strm.avail_out))return A}return e.insert=0,t===f?(N(e,!0),0===e.strm.avail_out?O:B):(e.strstart>e.block_start&&(N(e,!1),e.strm.avail_out),A)}),new M(4,4,8,4,Z),new M(4,5,16,8,Z),new M(4,6,32,32,Z),new M(4,4,16,16,W),new M(8,16,32,32,W),new M(8,16,128,128,W),new M(8,32,128,256,W),new M(32,128,258,1024,W),new M(32,258,258,4096,W)],r.deflateInit=function(e,t){return Y(e,t,v,15,8,0)},r.deflateInit2=Y,r.deflateReset=K,r.deflateResetKeep=G,r.deflateSetHeader=function(e,t){return e&&e.state?2!==e.state.wrap?_:(e.state.gzhead=t,m):_},r.deflate=function(e,t){var r,n,i,s;if(!e||!e.state||5<t||t<0)return e?R(e,_):_;if(n=e.state,!e.output||!e.input&&0!==e.avail_in||666===n.status&&t!==f)return R(e,0===e.avail_out?-5:_);if(n.strm=e,r=n.last_flush,n.last_flush=t,n.status===C)if(2===n.wrap)e.adler=0,U(n,31),U(n,139),U(n,8),n.gzhead?(U(n,(n.gzhead.text?1:0)+(n.gzhead.hcrc?2:0)+(n.gzhead.extra?4:0)+(n.gzhead.name?8:0)+(n.gzhead.comment?16:0)),U(n,255&n.gzhead.time),U(n,n.gzhead.time>>8&255),U(n,n.gzhead.time>>16&255),U(n,n.gzhead.time>>24&255),U(n,9===n.level?2:2<=n.strategy||n.level<2?4:0),U(n,255&n.gzhead.os),n.gzhead.extra&&n.gzhead.extra.length&&(U(n,255&n.gzhead.extra.length),U(n,n.gzhead.extra.length>>8&255)),n.gzhead.hcrc&&(e.adler=p(e.adler,n.pending_buf,n.pending,0)),n.gzindex=0,n.status=69):(U(n,0),U(n,0),U(n,0),U(n,0),U(n,0),U(n,9===n.level?2:2<=n.strategy||n.level<2?4:0),U(n,3),n.status=E);else{var a=v+(n.w_bits-8<<4)<<8;a|=(2<=n.strategy||n.level<2?0:n.level<6?1:6===n.level?2:3)<<6,0!==n.strstart&&(a|=32),a+=31-a%31,n.status=E,P(n,a),0!==n.strstart&&(P(n,e.adler>>>16),P(n,65535&e.adler)),e.adler=1}if(69===n.status)if(n.gzhead.extra){for(i=n.pending;n.gzindex<(65535&n.gzhead.extra.length)&&(n.pending!==n.pending_buf_size||(n.gzhead.hcrc&&n.pending>i&&(e.adler=p(e.adler,n.pending_buf,n.pending-i,i)),F(e),i=n.pending,n.pending!==n.pending_buf_size));)U(n,255&n.gzhead.extra[n.gzindex]),n.gzindex++;n.gzhead.hcrc&&n.pending>i&&(e.adler=p(e.adler,n.pending_buf,n.pending-i,i)),n.gzindex===n.gzhead.extra.length&&(n.gzindex=0,n.status=73)}else n.status=73;if(73===n.status)if(n.gzhead.name){i=n.pending;do{if(n.pending===n.pending_buf_size&&(n.gzhead.hcrc&&n.pending>i&&(e.adler=p(e.adler,n.pending_buf,n.pending-i,i)),F(e),i=n.pending,n.pending===n.pending_buf_size)){s=1;break}s=n.gzindex<n.gzhead.name.length?255&n.gzhead.name.charCodeAt(n.gzindex++):0,U(n,s)}while(0!==s);n.gzhead.hcrc&&n.pending>i&&(e.adler=p(e.adler,n.pending_buf,n.pending-i,i)),0===s&&(n.gzindex=0,n.status=91)}else n.status=91;if(91===n.status)if(n.gzhead.comment){i=n.pending;do{if(n.pending===n.pending_buf_size&&(n.gzhead.hcrc&&n.pending>i&&(e.adler=p(e.adler,n.pending_buf,n.pending-i,i)),F(e),i=n.pending,n.pending===n.pending_buf_size)){s=1;break}s=n.gzindex<n.gzhead.comment.length?255&n.gzhead.comment.charCodeAt(n.gzindex++):0,U(n,s)}while(0!==s);n.gzhead.hcrc&&n.pending>i&&(e.adler=p(e.adler,n.pending_buf,n.pending-i,i)),0===s&&(n.status=103)}else n.status=103;if(103===n.status&&(n.gzhead.hcrc?(n.pending+2>n.pending_buf_size&&F(e),n.pending+2<=n.pending_buf_size&&(U(n,255&e.adler),U(n,e.adler>>8&255),e.adler=0,n.status=E)):n.status=E),0!==n.pending){if(F(e),0===e.avail_out)return n.last_flush=-1,m}else if(0===e.avail_in&&T(t)<=T(r)&&t!==f)return R(e,-5);if(666===n.status&&0!==e.avail_in)return R(e,-5);if(0!==e.avail_in||0!==n.lookahead||t!==l&&666!==n.status){var o=2===n.strategy?function(e,t){for(var r;;){if(0===e.lookahead&&(j(e),0===e.lookahead)){if(t===l)return A;break}if(e.match_length=0,r=u._tr_tally(e,0,e.window[e.strstart]),e.lookahead--,e.strstart++,r&&(N(e,!1),0===e.strm.avail_out))return A}return e.insert=0,t===f?(N(e,!0),0===e.strm.avail_out?O:B):e.last_lit&&(N(e,!1),0===e.strm.avail_out)?A:I}(n,t):3===n.strategy?function(e,t){for(var r,n,i,s,a=e.window;;){if(e.lookahead<=S){if(j(e),e.lookahead<=S&&t===l)return A;if(0===e.lookahead)break}if(e.match_length=0,e.lookahead>=x&&0<e.strstart&&(n=a[i=e.strstart-1])===a[++i]&&n===a[++i]&&n===a[++i]){s=e.strstart+S;do{}while(n===a[++i]&&n===a[++i]&&n===a[++i]&&n===a[++i]&&n===a[++i]&&n===a[++i]&&n===a[++i]&&n===a[++i]&&i<s);e.match_length=S-(s-i),e.match_length>e.lookahead&&(e.match_length=e.lookahead)}if(e.match_length>=x?(r=u._tr_tally(e,1,e.match_length-x),e.lookahead-=e.match_length,e.strstart+=e.match_length,e.match_length=0):(r=u._tr_tally(e,0,e.window[e.strstart]),e.lookahead--,e.strstart++),r&&(N(e,!1),0===e.strm.avail_out))return A}return e.insert=0,t===f?(N(e,!0),0===e.strm.avail_out?O:B):e.last_lit&&(N(e,!1),0===e.strm.avail_out)?A:I}(n,t):h[n.level].func(n,t);if(o!==O&&o!==B||(n.status=666),o===A||o===O)return 0===e.avail_out&&(n.last_flush=-1),m;if(o===I&&(1===t?u._tr_align(n):5!==t&&(u._tr_stored_block(n,0,0,!1),3===t&&(D(n.head),0===n.lookahead&&(n.strstart=0,n.block_start=0,n.insert=0))),F(e),0===e.avail_out))return n.last_flush=-1,m}return t!==f?m:n.wrap<=0?1:(2===n.wrap?(U(n,255&e.adler),U(n,e.adler>>8&255),U(n,e.adler>>16&255),U(n,e.adler>>24&255),U(n,255&e.total_in),U(n,e.total_in>>8&255),U(n,e.total_in>>16&255),U(n,e.total_in>>24&255)):(P(n,e.adler>>>16),P(n,65535&e.adler)),F(e),0<n.wrap&&(n.wrap=-n.wrap),0!==n.pending?m:1)},r.deflateEnd=function(e){var t;return e&&e.state?(t=e.state.status)!==C&&69!==t&&73!==t&&91!==t&&103!==t&&t!==E&&666!==t?R(e,_):(e.state=null,t===E?R(e,-3):m):_},r.deflateSetDictionary=function(e,t){var r,n,i,s,a,o,h,u,l=t.length;if(!e||!e.state)return _;if(2===(s=(r=e.state).wrap)||1===s&&r.status!==C||r.lookahead)return _;for(1===s&&(e.adler=d(e.adler,t,l,0)),r.wrap=0,l>=r.w_size&&(0===s&&(D(r.head),r.strstart=0,r.block_start=0,r.insert=0),u=new c.Buf8(r.w_size),c.arraySet(u,t,l-r.w_size,r.w_size,0),t=u,l=r.w_size),a=e.avail_in,o=e.next_in,h=e.input,e.avail_in=l,e.next_in=0,e.input=t,j(r);r.lookahead>=x;){for(n=r.strstart,i=r.lookahead-(x-1);r.ins_h=(r.ins_h<<r.hash_shift^r.window[n+x-1])&r.hash_mask,r.prev[n&r.w_mask]=r.head[r.ins_h],r.head[r.ins_h]=n,n++,--i;);r.strstart=n,r.lookahead=x-1,j(r)}return r.strstart+=r.lookahead,r.block_start=r.strstart,r.insert=r.lookahead,r.lookahead=0,r.match_length=r.prev_length=x-1,r.match_available=0,e.next_in=o,e.input=h,e.avail_in=a,r.wrap=s,m},r.deflateInfo=\"pako deflate (from Nodeca project)\"},{\"../utils/common\":41,\"./adler32\":43,\"./crc32\":45,\"./messages\":51,\"./trees\":52}],47:[function(e,t,r){\"use strict\";t.exports=function(){this.text=0,this.time=0,this.xflags=0,this.os=0,this.extra=null,this.extra_len=0,this.name=\"\",this.comment=\"\",this.hcrc=0,this.done=!1}},{}],48:[function(e,t,r){\"use strict\";t.exports=function(e,t){var r,n,i,s,a,o,h,u,l,f,c,d,p,m,_,g,b,v,y,w,k,x,S,z,C;r=e.state,n=e.next_in,z=e.input,i=n+(e.avail_in-5),s=e.next_out,C=e.output,a=s-(t-e.avail_out),o=s+(e.avail_out-257),h=r.dmax,u=r.wsize,l=r.whave,f=r.wnext,c=r.window,d=r.hold,p=r.bits,m=r.lencode,_=r.distcode,g=(1<<r.lenbits)-1,b=(1<<r.distbits)-1;e:do{p<15&&(d+=z[n++]<<p,p+=8,d+=z[n++]<<p,p+=8),v=m[d&g];t:for(;;){if(d>>>=y=v>>>24,p-=y,0===(y=v>>>16&255))C[s++]=65535&v;else{if(!(16&y)){if(0==(64&y)){v=m[(65535&v)+(d&(1<<y)-1)];continue t}if(32&y){r.mode=12;break e}e.msg=\"invalid literal/length code\",r.mode=30;break e}w=65535&v,(y&=15)&&(p<y&&(d+=z[n++]<<p,p+=8),w+=d&(1<<y)-1,d>>>=y,p-=y),p<15&&(d+=z[n++]<<p,p+=8,d+=z[n++]<<p,p+=8),v=_[d&b];r:for(;;){if(d>>>=y=v>>>24,p-=y,!(16&(y=v>>>16&255))){if(0==(64&y)){v=_[(65535&v)+(d&(1<<y)-1)];continue r}e.msg=\"invalid distance code\",r.mode=30;break e}if(k=65535&v,p<(y&=15)&&(d+=z[n++]<<p,(p+=8)<y&&(d+=z[n++]<<p,p+=8)),h<(k+=d&(1<<y)-1)){e.msg=\"invalid distance too far back\",r.mode=30;break e}if(d>>>=y,p-=y,(y=s-a)<k){if(l<(y=k-y)&&r.sane){e.msg=\"invalid distance too far back\",r.mode=30;break e}if(S=c,(x=0)===f){if(x+=u-y,y<w){for(w-=y;C[s++]=c[x++],--y;);x=s-k,S=C}}else if(f<y){if(x+=u+f-y,(y-=f)<w){for(w-=y;C[s++]=c[x++],--y;);if(x=0,f<w){for(w-=y=f;C[s++]=c[x++],--y;);x=s-k,S=C}}}else if(x+=f-y,y<w){for(w-=y;C[s++]=c[x++],--y;);x=s-k,S=C}for(;2<w;)C[s++]=S[x++],C[s++]=S[x++],C[s++]=S[x++],w-=3;w&&(C[s++]=S[x++],1<w&&(C[s++]=S[x++]))}else{for(x=s-k;C[s++]=C[x++],C[s++]=C[x++],C[s++]=C[x++],2<(w-=3););w&&(C[s++]=C[x++],1<w&&(C[s++]=C[x++]))}break}}break}}while(n<i&&s<o);n-=w=p>>3,d&=(1<<(p-=w<<3))-1,e.next_in=n,e.next_out=s,e.avail_in=n<i?i-n+5:5-(n-i),e.avail_out=s<o?o-s+257:257-(s-o),r.hold=d,r.bits=p}},{}],49:[function(e,t,r){\"use strict\";var I=e(\"../utils/common\"),O=e(\"./adler32\"),B=e(\"./crc32\"),R=e(\"./inffast\"),T=e(\"./inftrees\"),D=1,F=2,N=0,U=-2,P=1,n=852,i=592;function L(e){return(e>>>24&255)+(e>>>8&65280)+((65280&e)<<8)+((255&e)<<24)}function s(){this.mode=0,this.last=!1,this.wrap=0,this.havedict=!1,this.flags=0,this.dmax=0,this.check=0,this.total=0,this.head=null,this.wbits=0,this.wsize=0,this.whave=0,this.wnext=0,this.window=null,this.hold=0,this.bits=0,this.length=0,this.offset=0,this.extra=0,this.lencode=null,this.distcode=null,this.lenbits=0,this.distbits=0,this.ncode=0,this.nlen=0,this.ndist=0,this.have=0,this.next=null,this.lens=new I.Buf16(320),this.work=new I.Buf16(288),this.lendyn=null,this.distdyn=null,this.sane=0,this.back=0,this.was=0}function a(e){var t;return e&&e.state?(t=e.state,e.total_in=e.total_out=t.total=0,e.msg=\"\",t.wrap&&(e.adler=1&t.wrap),t.mode=P,t.last=0,t.havedict=0,t.dmax=32768,t.head=null,t.hold=0,t.bits=0,t.lencode=t.lendyn=new I.Buf32(n),t.distcode=t.distdyn=new I.Buf32(i),t.sane=1,t.back=-1,N):U}function o(e){var t;return e&&e.state?((t=e.state).wsize=0,t.whave=0,t.wnext=0,a(e)):U}function h(e,t){var r,n;return e&&e.state?(n=e.state,t<0?(r=0,t=-t):(r=1+(t>>4),t<48&&(t&=15)),t&&(t<8||15<t)?U:(null!==n.window&&n.wbits!==t&&(n.window=null),n.wrap=r,n.wbits=t,o(e))):U}function u(e,t){var r,n;return e?(n=new s,(e.state=n).window=null,(r=h(e,t))!==N&&(e.state=null),r):U}var l,f,c=!0;function j(e){if(c){var t;for(l=new I.Buf32(512),f=new I.Buf32(32),t=0;t<144;)e.lens[t++]=8;for(;t<256;)e.lens[t++]=9;for(;t<280;)e.lens[t++]=7;for(;t<288;)e.lens[t++]=8;for(T(D,e.lens,0,288,l,0,e.work,{bits:9}),t=0;t<32;)e.lens[t++]=5;T(F,e.lens,0,32,f,0,e.work,{bits:5}),c=!1}e.lencode=l,e.lenbits=9,e.distcode=f,e.distbits=5}function Z(e,t,r,n){var i,s=e.state;return null===s.window&&(s.wsize=1<<s.wbits,s.wnext=0,s.whave=0,s.window=new I.Buf8(s.wsize)),n>=s.wsize?(I.arraySet(s.window,t,r-s.wsize,s.wsize,0),s.wnext=0,s.whave=s.wsize):(n<(i=s.wsize-s.wnext)&&(i=n),I.arraySet(s.window,t,r-n,i,s.wnext),(n-=i)?(I.arraySet(s.window,t,r-n,n,0),s.wnext=n,s.whave=s.wsize):(s.wnext+=i,s.wnext===s.wsize&&(s.wnext=0),s.whave<s.wsize&&(s.whave+=i))),0}r.inflateReset=o,r.inflateReset2=h,r.inflateResetKeep=a,r.inflateInit=function(e){return u(e,15)},r.inflateInit2=u,r.inflate=function(e,t){var r,n,i,s,a,o,h,u,l,f,c,d,p,m,_,g,b,v,y,w,k,x,S,z,C=0,E=new I.Buf8(4),A=[16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15];if(!e||!e.state||!e.output||!e.input&&0!==e.avail_in)return U;12===(r=e.state).mode&&(r.mode=13),a=e.next_out,i=e.output,h=e.avail_out,s=e.next_in,n=e.input,o=e.avail_in,u=r.hold,l=r.bits,f=o,c=h,x=N;e:for(;;)switch(r.mode){case P:if(0===r.wrap){r.mode=13;break}for(;l<16;){if(0===o)break e;o--,u+=n[s++]<<l,l+=8}if(2&r.wrap&&35615===u){E[r.check=0]=255&u,E[1]=u>>>8&255,r.check=B(r.check,E,2,0),l=u=0,r.mode=2;break}if(r.flags=0,r.head&&(r.head.done=!1),!(1&r.wrap)||(((255&u)<<8)+(u>>8))%31){e.msg=\"incorrect header check\",r.mode=30;break}if(8!=(15&u)){e.msg=\"unknown compression method\",r.mode=30;break}if(l-=4,k=8+(15&(u>>>=4)),0===r.wbits)r.wbits=k;else if(k>r.wbits){e.msg=\"invalid window size\",r.mode=30;break}r.dmax=1<<k,e.adler=r.check=1,r.mode=512&u?10:12,l=u=0;break;case 2:for(;l<16;){if(0===o)break e;o--,u+=n[s++]<<l,l+=8}if(r.flags=u,8!=(255&r.flags)){e.msg=\"unknown compression method\",r.mode=30;break}if(57344&r.flags){e.msg=\"unknown header flags set\",r.mode=30;break}r.head&&(r.head.text=u>>8&1),512&r.flags&&(E[0]=255&u,E[1]=u>>>8&255,r.check=B(r.check,E,2,0)),l=u=0,r.mode=3;case 3:for(;l<32;){if(0===o)break e;o--,u+=n[s++]<<l,l+=8}r.head&&(r.head.time=u),512&r.flags&&(E[0]=255&u,E[1]=u>>>8&255,E[2]=u>>>16&255,E[3]=u>>>24&255,r.check=B(r.check,E,4,0)),l=u=0,r.mode=4;case 4:for(;l<16;){if(0===o)break e;o--,u+=n[s++]<<l,l+=8}r.head&&(r.head.xflags=255&u,r.head.os=u>>8),512&r.flags&&(E[0]=255&u,E[1]=u>>>8&255,r.check=B(r.check,E,2,0)),l=u=0,r.mode=5;case 5:if(1024&r.flags){for(;l<16;){if(0===o)break e;o--,u+=n[s++]<<l,l+=8}r.length=u,r.head&&(r.head.extra_len=u),512&r.flags&&(E[0]=255&u,E[1]=u>>>8&255,r.check=B(r.check,E,2,0)),l=u=0}else r.head&&(r.head.extra=null);r.mode=6;case 6:if(1024&r.flags&&(o<(d=r.length)&&(d=o),d&&(r.head&&(k=r.head.extra_len-r.length,r.head.extra||(r.head.extra=new Array(r.head.extra_len)),I.arraySet(r.head.extra,n,s,d,k)),512&r.flags&&(r.check=B(r.check,n,d,s)),o-=d,s+=d,r.length-=d),r.length))break e;r.length=0,r.mode=7;case 7:if(2048&r.flags){if(0===o)break e;for(d=0;k=n[s+d++],r.head&&k&&r.length<65536&&(r.head.name+=String.fromCharCode(k)),k&&d<o;);if(512&r.flags&&(r.check=B(r.check,n,d,s)),o-=d,s+=d,k)break e}else r.head&&(r.head.name=null);r.length=0,r.mode=8;case 8:if(4096&r.flags){if(0===o)break e;for(d=0;k=n[s+d++],r.head&&k&&r.length<65536&&(r.head.comment+=String.fromCharCode(k)),k&&d<o;);if(512&r.flags&&(r.check=B(r.check,n,d,s)),o-=d,s+=d,k)break e}else r.head&&(r.head.comment=null);r.mode=9;case 9:if(512&r.flags){for(;l<16;){if(0===o)break e;o--,u+=n[s++]<<l,l+=8}if(u!==(65535&r.check)){e.msg=\"header crc mismatch\",r.mode=30;break}l=u=0}r.head&&(r.head.hcrc=r.flags>>9&1,r.head.done=!0),e.adler=r.check=0,r.mode=12;break;case 10:for(;l<32;){if(0===o)break e;o--,u+=n[s++]<<l,l+=8}e.adler=r.check=L(u),l=u=0,r.mode=11;case 11:if(0===r.havedict)return e.next_out=a,e.avail_out=h,e.next_in=s,e.avail_in=o,r.hold=u,r.bits=l,2;e.adler=r.check=1,r.mode=12;case 12:if(5===t||6===t)break e;case 13:if(r.last){u>>>=7&l,l-=7&l,r.mode=27;break}for(;l<3;){if(0===o)break e;o--,u+=n[s++]<<l,l+=8}switch(r.last=1&u,l-=1,3&(u>>>=1)){case 0:r.mode=14;break;case 1:if(j(r),r.mode=20,6!==t)break;u>>>=2,l-=2;break e;case 2:r.mode=17;break;case 3:e.msg=\"invalid block type\",r.mode=30}u>>>=2,l-=2;break;case 14:for(u>>>=7&l,l-=7&l;l<32;){if(0===o)break e;o--,u+=n[s++]<<l,l+=8}if((65535&u)!=(u>>>16^65535)){e.msg=\"invalid stored block lengths\",r.mode=30;break}if(r.length=65535&u,l=u=0,r.mode=15,6===t)break e;case 15:r.mode=16;case 16:if(d=r.length){if(o<d&&(d=o),h<d&&(d=h),0===d)break e;I.arraySet(i,n,s,d,a),o-=d,s+=d,h-=d,a+=d,r.length-=d;break}r.mode=12;break;case 17:for(;l<14;){if(0===o)break e;o--,u+=n[s++]<<l,l+=8}if(r.nlen=257+(31&u),u>>>=5,l-=5,r.ndist=1+(31&u),u>>>=5,l-=5,r.ncode=4+(15&u),u>>>=4,l-=4,286<r.nlen||30<r.ndist){e.msg=\"too many length or distance symbols\",r.mode=30;break}r.have=0,r.mode=18;case 18:for(;r.have<r.ncode;){for(;l<3;){if(0===o)break e;o--,u+=n[s++]<<l,l+=8}r.lens[A[r.have++]]=7&u,u>>>=3,l-=3}for(;r.have<19;)r.lens[A[r.have++]]=0;if(r.lencode=r.lendyn,r.lenbits=7,S={bits:r.lenbits},x=T(0,r.lens,0,19,r.lencode,0,r.work,S),r.lenbits=S.bits,x){e.msg=\"invalid code lengths set\",r.mode=30;break}r.have=0,r.mode=19;case 19:for(;r.have<r.nlen+r.ndist;){for(;g=(C=r.lencode[u&(1<<r.lenbits)-1])>>>16&255,b=65535&C,!((_=C>>>24)<=l);){if(0===o)break e;o--,u+=n[s++]<<l,l+=8}if(b<16)u>>>=_,l-=_,r.lens[r.have++]=b;else{if(16===b){for(z=_+2;l<z;){if(0===o)break e;o--,u+=n[s++]<<l,l+=8}if(u>>>=_,l-=_,0===r.have){e.msg=\"invalid bit length repeat\",r.mode=30;break}k=r.lens[r.have-1],d=3+(3&u),u>>>=2,l-=2}else if(17===b){for(z=_+3;l<z;){if(0===o)break e;o--,u+=n[s++]<<l,l+=8}l-=_,k=0,d=3+(7&(u>>>=_)),u>>>=3,l-=3}else{for(z=_+7;l<z;){if(0===o)break e;o--,u+=n[s++]<<l,l+=8}l-=_,k=0,d=11+(127&(u>>>=_)),u>>>=7,l-=7}if(r.have+d>r.nlen+r.ndist){e.msg=\"invalid bit length repeat\",r.mode=30;break}for(;d--;)r.lens[r.have++]=k}}if(30===r.mode)break;if(0===r.lens[256]){e.msg=\"invalid code -- missing end-of-block\",r.mode=30;break}if(r.lenbits=9,S={bits:r.lenbits},x=T(D,r.lens,0,r.nlen,r.lencode,0,r.work,S),r.lenbits=S.bits,x){e.msg=\"invalid literal/lengths set\",r.mode=30;break}if(r.distbits=6,r.distcode=r.distdyn,S={bits:r.distbits},x=T(F,r.lens,r.nlen,r.ndist,r.distcode,0,r.work,S),r.distbits=S.bits,x){e.msg=\"invalid distances set\",r.mode=30;break}if(r.mode=20,6===t)break e;case 20:r.mode=21;case 21:if(6<=o&&258<=h){e.next_out=a,e.avail_out=h,e.next_in=s,e.avail_in=o,r.hold=u,r.bits=l,R(e,c),a=e.next_out,i=e.output,h=e.avail_out,s=e.next_in,n=e.input,o=e.avail_in,u=r.hold,l=r.bits,12===r.mode&&(r.back=-1);break}for(r.back=0;g=(C=r.lencode[u&(1<<r.lenbits)-1])>>>16&255,b=65535&C,!((_=C>>>24)<=l);){if(0===o)break e;o--,u+=n[s++]<<l,l+=8}if(g&&0==(240&g)){for(v=_,y=g,w=b;g=(C=r.lencode[w+((u&(1<<v+y)-1)>>v)])>>>16&255,b=65535&C,!(v+(_=C>>>24)<=l);){if(0===o)break e;o--,u+=n[s++]<<l,l+=8}u>>>=v,l-=v,r.back+=v}if(u>>>=_,l-=_,r.back+=_,r.length=b,0===g){r.mode=26;break}if(32&g){r.back=-1,r.mode=12;break}if(64&g){e.msg=\"invalid literal/length code\",r.mode=30;break}r.extra=15&g,r.mode=22;case 22:if(r.extra){for(z=r.extra;l<z;){if(0===o)break e;o--,u+=n[s++]<<l,l+=8}r.length+=u&(1<<r.extra)-1,u>>>=r.extra,l-=r.extra,r.back+=r.extra}r.was=r.length,r.mode=23;case 23:for(;g=(C=r.distcode[u&(1<<r.distbits)-1])>>>16&255,b=65535&C,!((_=C>>>24)<=l);){if(0===o)break e;o--,u+=n[s++]<<l,l+=8}if(0==(240&g)){for(v=_,y=g,w=b;g=(C=r.distcode[w+((u&(1<<v+y)-1)>>v)])>>>16&255,b=65535&C,!(v+(_=C>>>24)<=l);){if(0===o)break e;o--,u+=n[s++]<<l,l+=8}u>>>=v,l-=v,r.back+=v}if(u>>>=_,l-=_,r.back+=_,64&g){e.msg=\"invalid distance code\",r.mode=30;break}r.offset=b,r.extra=15&g,r.mode=24;case 24:if(r.extra){for(z=r.extra;l<z;){if(0===o)break e;o--,u+=n[s++]<<l,l+=8}r.offset+=u&(1<<r.extra)-1,u>>>=r.extra,l-=r.extra,r.back+=r.extra}if(r.offset>r.dmax){e.msg=\"invalid distance too far back\",r.mode=30;break}r.mode=25;case 25:if(0===h)break e;if(d=c-h,r.offset>d){if((d=r.offset-d)>r.whave&&r.sane){e.msg=\"invalid distance too far back\",r.mode=30;break}p=d>r.wnext?(d-=r.wnext,r.wsize-d):r.wnext-d,d>r.length&&(d=r.length),m=r.window}else m=i,p=a-r.offset,d=r.length;for(h<d&&(d=h),h-=d,r.length-=d;i[a++]=m[p++],--d;);0===r.length&&(r.mode=21);break;case 26:if(0===h)break e;i[a++]=r.length,h--,r.mode=21;break;case 27:if(r.wrap){for(;l<32;){if(0===o)break e;o--,u|=n[s++]<<l,l+=8}if(c-=h,e.total_out+=c,r.total+=c,c&&(e.adler=r.check=r.flags?B(r.check,i,c,a-c):O(r.check,i,c,a-c)),c=h,(r.flags?u:L(u))!==r.check){e.msg=\"incorrect data check\",r.mode=30;break}l=u=0}r.mode=28;case 28:if(r.wrap&&r.flags){for(;l<32;){if(0===o)break e;o--,u+=n[s++]<<l,l+=8}if(u!==(4294967295&r.total)){e.msg=\"incorrect length check\",r.mode=30;break}l=u=0}r.mode=29;case 29:x=1;break e;case 30:x=-3;break e;case 31:return-4;case 32:default:return U}return e.next_out=a,e.avail_out=h,e.next_in=s,e.avail_in=o,r.hold=u,r.bits=l,(r.wsize||c!==e.avail_out&&r.mode<30&&(r.mode<27||4!==t))&&Z(e,e.output,e.next_out,c-e.avail_out)?(r.mode=31,-4):(f-=e.avail_in,c-=e.avail_out,e.total_in+=f,e.total_out+=c,r.total+=c,r.wrap&&c&&(e.adler=r.check=r.flags?B(r.check,i,c,e.next_out-c):O(r.check,i,c,e.next_out-c)),e.data_type=r.bits+(r.last?64:0)+(12===r.mode?128:0)+(20===r.mode||15===r.mode?256:0),(0==f&&0===c||4===t)&&x===N&&(x=-5),x)},r.inflateEnd=function(e){if(!e||!e.state)return U;var t=e.state;return t.window&&(t.window=null),e.state=null,N},r.inflateGetHeader=function(e,t){var r;return e&&e.state?0==(2&(r=e.state).wrap)?U:((r.head=t).done=!1,N):U},r.inflateSetDictionary=function(e,t){var r,n=t.length;return e&&e.state?0!==(r=e.state).wrap&&11!==r.mode?U:11===r.mode&&O(1,t,n,0)!==r.check?-3:Z(e,t,n,n)?(r.mode=31,-4):(r.havedict=1,N):U},r.inflateInfo=\"pako inflate (from Nodeca project)\"},{\"../utils/common\":41,\"./adler32\":43,\"./crc32\":45,\"./inffast\":48,\"./inftrees\":50}],50:[function(e,t,r){\"use strict\";var D=e(\"../utils/common\"),F=[3,4,5,6,7,8,9,10,11,13,15,17,19,23,27,31,35,43,51,59,67,83,99,115,131,163,195,227,258,0,0],N=[16,16,16,16,16,16,16,16,17,17,17,17,18,18,18,18,19,19,19,19,20,20,20,20,21,21,21,21,16,72,78],U=[1,2,3,4,5,7,9,13,17,25,33,49,65,97,129,193,257,385,513,769,1025,1537,2049,3073,4097,6145,8193,12289,16385,24577,0,0],P=[16,16,16,16,17,17,18,18,19,19,20,20,21,21,22,22,23,23,24,24,25,25,26,26,27,27,28,28,29,29,64,64];t.exports=function(e,t,r,n,i,s,a,o){var h,u,l,f,c,d,p,m,_,g=o.bits,b=0,v=0,y=0,w=0,k=0,x=0,S=0,z=0,C=0,E=0,A=null,I=0,O=new D.Buf16(16),B=new D.Buf16(16),R=null,T=0;for(b=0;b<=15;b++)O[b]=0;for(v=0;v<n;v++)O[t[r+v]]++;for(k=g,w=15;1<=w&&0===O[w];w--);if(w<k&&(k=w),0===w)return i[s++]=20971520,i[s++]=20971520,o.bits=1,0;for(y=1;y<w&&0===O[y];y++);for(k<y&&(k=y),b=z=1;b<=15;b++)if(z<<=1,(z-=O[b])<0)return-1;if(0<z&&(0===e||1!==w))return-1;for(B[1]=0,b=1;b<15;b++)B[b+1]=B[b]+O[b];for(v=0;v<n;v++)0!==t[r+v]&&(a[B[t[r+v]]++]=v);if(d=0===e?(A=R=a,19):1===e?(A=F,I-=257,R=N,T-=257,256):(A=U,R=P,-1),b=y,c=s,S=v=E=0,l=-1,f=(C=1<<(x=k))-1,1===e&&852<C||2===e&&592<C)return 1;for(;;){for(p=b-S,_=a[v]<d?(m=0,a[v]):a[v]>d?(m=R[T+a[v]],A[I+a[v]]):(m=96,0),h=1<<b-S,y=u=1<<x;i[c+(E>>S)+(u-=h)]=p<<24|m<<16|_|0,0!==u;);for(h=1<<b-1;E&h;)h>>=1;if(0!==h?(E&=h-1,E+=h):E=0,v++,0==--O[b]){if(b===w)break;b=t[r+a[v]]}if(k<b&&(E&f)!==l){for(0===S&&(S=k),c+=y,z=1<<(x=b-S);x+S<w&&!((z-=O[x+S])<=0);)x++,z<<=1;if(C+=1<<x,1===e&&852<C||2===e&&592<C)return 1;i[l=E&f]=k<<24|x<<16|c-s|0}}return 0!==E&&(i[c+E]=b-S<<24|64<<16|0),o.bits=k,0}},{\"../utils/common\":41}],51:[function(e,t,r){\"use strict\";t.exports={2:\"need dictionary\",1:\"stream end\",0:\"\",\"-1\":\"file error\",\"-2\":\"stream error\",\"-3\":\"data error\",\"-4\":\"insufficient memory\",\"-5\":\"buffer error\",\"-6\":\"incompatible version\"}},{}],52:[function(e,t,r){\"use strict\";var i=e(\"../utils/common\"),o=0,h=1;function n(e){for(var t=e.length;0<=--t;)e[t]=0}var s=0,a=29,u=256,l=u+1+a,f=30,c=19,_=2*l+1,g=15,d=16,p=7,m=256,b=16,v=17,y=18,w=[0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0],k=[0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13],x=[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,3,7],S=[16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15],z=new Array(2*(l+2));n(z);var C=new Array(2*f);n(C);var E=new Array(512);n(E);var A=new Array(256);n(A);var I=new Array(a);n(I);var O,B,R,T=new Array(f);function D(e,t,r,n,i){this.static_tree=e,this.extra_bits=t,this.extra_base=r,this.elems=n,this.max_length=i,this.has_stree=e&&e.length}function F(e,t){this.dyn_tree=e,this.max_code=0,this.stat_desc=t}function N(e){return e<256?E[e]:E[256+(e>>>7)]}function U(e,t){e.pending_buf[e.pending++]=255&t,e.pending_buf[e.pending++]=t>>>8&255}function P(e,t,r){e.bi_valid>d-r?(e.bi_buf|=t<<e.bi_valid&65535,U(e,e.bi_buf),e.bi_buf=t>>d-e.bi_valid,e.bi_valid+=r-d):(e.bi_buf|=t<<e.bi_valid&65535,e.bi_valid+=r)}function L(e,t,r){P(e,r[2*t],r[2*t+1])}function j(e,t){for(var r=0;r|=1&e,e>>>=1,r<<=1,0<--t;);return r>>>1}function Z(e,t,r){var n,i,s=new Array(g+1),a=0;for(n=1;n<=g;n++)s[n]=a=a+r[n-1]<<1;for(i=0;i<=t;i++){var o=e[2*i+1];0!==o&&(e[2*i]=j(s[o]++,o))}}function W(e){var t;for(t=0;t<l;t++)e.dyn_ltree[2*t]=0;for(t=0;t<f;t++)e.dyn_dtree[2*t]=0;for(t=0;t<c;t++)e.bl_tree[2*t]=0;e.dyn_ltree[2*m]=1,e.opt_len=e.static_len=0,e.last_lit=e.matches=0}function M(e){8<e.bi_valid?U(e,e.bi_buf):0<e.bi_valid&&(e.pending_buf[e.pending++]=e.bi_buf),e.bi_buf=0,e.bi_valid=0}function H(e,t,r,n){var i=2*t,s=2*r;return e[i]<e[s]||e[i]===e[s]&&n[t]<=n[r]}function G(e,t,r){for(var n=e.heap[r],i=r<<1;i<=e.heap_len&&(i<e.heap_len&&H(t,e.heap[i+1],e.heap[i],e.depth)&&i++,!H(t,n,e.heap[i],e.depth));)e.heap[r]=e.heap[i],r=i,i<<=1;e.heap[r]=n}function K(e,t,r){var n,i,s,a,o=0;if(0!==e.last_lit)for(;n=e.pending_buf[e.d_buf+2*o]<<8|e.pending_buf[e.d_buf+2*o+1],i=e.pending_buf[e.l_buf+o],o++,0===n?L(e,i,t):(L(e,(s=A[i])+u+1,t),0!==(a=w[s])&&P(e,i-=I[s],a),L(e,s=N(--n),r),0!==(a=k[s])&&P(e,n-=T[s],a)),o<e.last_lit;);L(e,m,t)}function Y(e,t){var r,n,i,s=t.dyn_tree,a=t.stat_desc.static_tree,o=t.stat_desc.has_stree,h=t.stat_desc.elems,u=-1;for(e.heap_len=0,e.heap_max=_,r=0;r<h;r++)0!==s[2*r]?(e.heap[++e.heap_len]=u=r,e.depth[r]=0):s[2*r+1]=0;for(;e.heap_len<2;)s[2*(i=e.heap[++e.heap_len]=u<2?++u:0)]=1,e.depth[i]=0,e.opt_len--,o&&(e.static_len-=a[2*i+1]);for(t.max_code=u,r=e.heap_len>>1;1<=r;r--)G(e,s,r);for(i=h;r=e.heap[1],e.heap[1]=e.heap[e.heap_len--],G(e,s,1),n=e.heap[1],e.heap[--e.heap_max]=r,e.heap[--e.heap_max]=n,s[2*i]=s[2*r]+s[2*n],e.depth[i]=(e.depth[r]>=e.depth[n]?e.depth[r]:e.depth[n])+1,s[2*r+1]=s[2*n+1]=i,e.heap[1]=i++,G(e,s,1),2<=e.heap_len;);e.heap[--e.heap_max]=e.heap[1],function(e,t){var r,n,i,s,a,o,h=t.dyn_tree,u=t.max_code,l=t.stat_desc.static_tree,f=t.stat_desc.has_stree,c=t.stat_desc.extra_bits,d=t.stat_desc.extra_base,p=t.stat_desc.max_length,m=0;for(s=0;s<=g;s++)e.bl_count[s]=0;for(h[2*e.heap[e.heap_max]+1]=0,r=e.heap_max+1;r<_;r++)p<(s=h[2*h[2*(n=e.heap[r])+1]+1]+1)&&(s=p,m++),h[2*n+1]=s,u<n||(e.bl_count[s]++,a=0,d<=n&&(a=c[n-d]),o=h[2*n],e.opt_len+=o*(s+a),f&&(e.static_len+=o*(l[2*n+1]+a)));if(0!==m){do{for(s=p-1;0===e.bl_count[s];)s--;e.bl_count[s]--,e.bl_count[s+1]+=2,e.bl_count[p]--,m-=2}while(0<m);for(s=p;0!==s;s--)for(n=e.bl_count[s];0!==n;)u<(i=e.heap[--r])||(h[2*i+1]!==s&&(e.opt_len+=(s-h[2*i+1])*h[2*i],h[2*i+1]=s),n--)}}(e,t),Z(s,u,e.bl_count)}function X(e,t,r){var n,i,s=-1,a=t[1],o=0,h=7,u=4;for(0===a&&(h=138,u=3),t[2*(r+1)+1]=65535,n=0;n<=r;n++)i=a,a=t[2*(n+1)+1],++o<h&&i===a||(o<u?e.bl_tree[2*i]+=o:0!==i?(i!==s&&e.bl_tree[2*i]++,e.bl_tree[2*b]++):o<=10?e.bl_tree[2*v]++:e.bl_tree[2*y]++,s=i,u=(o=0)===a?(h=138,3):i===a?(h=6,3):(h=7,4))}function V(e,t,r){var n,i,s=-1,a=t[1],o=0,h=7,u=4;for(0===a&&(h=138,u=3),n=0;n<=r;n++)if(i=a,a=t[2*(n+1)+1],!(++o<h&&i===a)){if(o<u)for(;L(e,i,e.bl_tree),0!=--o;);else 0!==i?(i!==s&&(L(e,i,e.bl_tree),o--),L(e,b,e.bl_tree),P(e,o-3,2)):o<=10?(L(e,v,e.bl_tree),P(e,o-3,3)):(L(e,y,e.bl_tree),P(e,o-11,7));s=i,u=(o=0)===a?(h=138,3):i===a?(h=6,3):(h=7,4)}}n(T);var q=!1;function J(e,t,r,n){P(e,(s<<1)+(n?1:0),3),function(e,t,r,n){M(e),n&&(U(e,r),U(e,~r)),i.arraySet(e.pending_buf,e.window,t,r,e.pending),e.pending+=r}(e,t,r,!0)}r._tr_init=function(e){q||(function(){var e,t,r,n,i,s=new Array(g+1);for(n=r=0;n<a-1;n++)for(I[n]=r,e=0;e<1<<w[n];e++)A[r++]=n;for(A[r-1]=n,n=i=0;n<16;n++)for(T[n]=i,e=0;e<1<<k[n];e++)E[i++]=n;for(i>>=7;n<f;n++)for(T[n]=i<<7,e=0;e<1<<k[n]-7;e++)E[256+i++]=n;for(t=0;t<=g;t++)s[t]=0;for(e=0;e<=143;)z[2*e+1]=8,e++,s[8]++;for(;e<=255;)z[2*e+1]=9,e++,s[9]++;for(;e<=279;)z[2*e+1]=7,e++,s[7]++;for(;e<=287;)z[2*e+1]=8,e++,s[8]++;for(Z(z,l+1,s),e=0;e<f;e++)C[2*e+1]=5,C[2*e]=j(e,5);O=new D(z,w,u+1,l,g),B=new D(C,k,0,f,g),R=new D(new Array(0),x,0,c,p)}(),q=!0),e.l_desc=new F(e.dyn_ltree,O),e.d_desc=new F(e.dyn_dtree,B),e.bl_desc=new F(e.bl_tree,R),e.bi_buf=0,e.bi_valid=0,W(e)},r._tr_stored_block=J,r._tr_flush_block=function(e,t,r,n){var i,s,a=0;0<e.level?(2===e.strm.data_type&&(e.strm.data_type=function(e){var t,r=4093624447;for(t=0;t<=31;t++,r>>>=1)if(1&r&&0!==e.dyn_ltree[2*t])return o;if(0!==e.dyn_ltree[18]||0!==e.dyn_ltree[20]||0!==e.dyn_ltree[26])return h;for(t=32;t<u;t++)if(0!==e.dyn_ltree[2*t])return h;return o}(e)),Y(e,e.l_desc),Y(e,e.d_desc),a=function(e){var t;for(X(e,e.dyn_ltree,e.l_desc.max_code),X(e,e.dyn_dtree,e.d_desc.max_code),Y(e,e.bl_desc),t=c-1;3<=t&&0===e.bl_tree[2*S[t]+1];t--);return e.opt_len+=3*(t+1)+5+5+4,t}(e),i=e.opt_len+3+7>>>3,(s=e.static_len+3+7>>>3)<=i&&(i=s)):i=s=r+5,r+4<=i&&-1!==t?J(e,t,r,n):4===e.strategy||s===i?(P(e,2+(n?1:0),3),K(e,z,C)):(P(e,4+(n?1:0),3),function(e,t,r,n){var i;for(P(e,t-257,5),P(e,r-1,5),P(e,n-4,4),i=0;i<n;i++)P(e,e.bl_tree[2*S[i]+1],3);V(e,e.dyn_ltree,t-1),V(e,e.dyn_dtree,r-1)}(e,e.l_desc.max_code+1,e.d_desc.max_code+1,a+1),K(e,e.dyn_ltree,e.dyn_dtree)),W(e),n&&M(e)},r._tr_tally=function(e,t,r){return e.pending_buf[e.d_buf+2*e.last_lit]=t>>>8&255,e.pending_buf[e.d_buf+2*e.last_lit+1]=255&t,e.pending_buf[e.l_buf+e.last_lit]=255&r,e.last_lit++,0===t?e.dyn_ltree[2*r]++:(e.matches++,t--,e.dyn_ltree[2*(A[r]+u+1)]++,e.dyn_dtree[2*N(t)]++),e.last_lit===e.lit_bufsize-1},r._tr_align=function(e){P(e,2,3),L(e,m,z),function(e){16===e.bi_valid?(U(e,e.bi_buf),e.bi_buf=0,e.bi_valid=0):8<=e.bi_valid&&(e.pending_buf[e.pending++]=255&e.bi_buf,e.bi_buf>>=8,e.bi_valid-=8)}(e)}},{\"../utils/common\":41}],53:[function(e,t,r){\"use strict\";t.exports=function(){this.input=null,this.next_in=0,this.avail_in=0,this.total_in=0,this.output=null,this.next_out=0,this.avail_out=0,this.total_out=0,this.msg=\"\",this.state=null,this.data_type=2,this.adler=0}},{}],54:[function(e,t,r){(function(e){!function(r,n){\"use strict\";if(!r.setImmediate){var i,s,t,a,o=1,h={},u=!1,l=r.document,e=Object.getPrototypeOf&&Object.getPrototypeOf(r);e=e&&e.setTimeout?e:r,i=\"[object process]\"==={}.toString.call(r.process)?function(e){process.nextTick(function(){c(e)})}:function(){if(r.postMessage&&!r.importScripts){var e=!0,t=r.onmessage;return r.onmessage=function(){e=!1},r.postMessage(\"\",\"*\"),r.onmessage=t,e}}()?(a=\"setImmediate$\"+Math.random()+\"$\",r.addEventListener?r.addEventListener(\"message\",d,!1):r.attachEvent(\"onmessage\",d),function(e){r.postMessage(a+e,\"*\")}):r.MessageChannel?((t=new MessageChannel).port1.onmessage=function(e){c(e.data)},function(e){t.port2.postMessage(e)}):l&&\"onreadystatechange\"in l.createElement(\"script\")?(s=l.documentElement,function(e){var t=l.createElement(\"script\");t.onreadystatechange=function(){c(e),t.onreadystatechange=null,s.removeChild(t),t=null},s.appendChild(t)}):function(e){setTimeout(c,0,e)},e.setImmediate=function(e){\"function\"!=typeof e&&(e=new Function(\"\"+e));for(var t=new Array(arguments.length-1),r=0;r<t.length;r++)t[r]=arguments[r+1];var n={callback:e,args:t};return h[o]=n,i(o),o++},e.clearImmediate=f}function f(e){delete h[e]}function c(e){if(u)setTimeout(c,0,e);else{var t=h[e];if(t){u=!0;try{!function(e){var t=e.callback,r=e.args;switch(r.length){case 0:t();break;case 1:t(r[0]);break;case 2:t(r[0],r[1]);break;case 3:t(r[0],r[1],r[2]);break;default:t.apply(n,r)}}(t)}finally{f(e),u=!1}}}}function d(e){e.source===r&&\"string\"==typeof e.data&&0===e.data.indexOf(a)&&c(+e.data.slice(a.length))}}(\"undefined\"==typeof self?void 0===e?this:e:self)}).call(this,\"undefined\"!=typeof global?global:\"undefined\"!=typeof self?self:\"undefined\"!=typeof window?window:{})},{}]},{},[10])(10)});","var global = typeof self !== 'undefined' ? self : this;\nvar __self__ = (function () {\nfunction F() {\nthis.fetch = false;\nthis.DOMException = global.DOMException\n}\nF.prototype = global;\nreturn new F();\n})();\n(function(self) {\n\nvar irrelevant = (function (exports) {\n\n  var support = {\n    searchParams: 'URLSearchParams' in self,\n    iterable: 'Symbol' in self && 'iterator' in Symbol,\n    blob:\n      'FileReader' in self &&\n      'Blob' in self &&\n      (function() {\n        try {\n          new Blob();\n          return true\n        } catch (e) {\n          return false\n        }\n      })(),\n    formData: 'FormData' in self,\n    arrayBuffer: 'ArrayBuffer' in self\n  };\n\n  function isDataView(obj) {\n    return obj && DataView.prototype.isPrototypeOf(obj)\n  }\n\n  if (support.arrayBuffer) {\n    var viewClasses = [\n      '[object Int8Array]',\n      '[object Uint8Array]',\n      '[object Uint8ClampedArray]',\n      '[object Int16Array]',\n      '[object Uint16Array]',\n      '[object Int32Array]',\n      '[object Uint32Array]',\n      '[object Float32Array]',\n      '[object Float64Array]'\n    ];\n\n    var isArrayBufferView =\n      ArrayBuffer.isView ||\n      function(obj) {\n        return obj && viewClasses.indexOf(Object.prototype.toString.call(obj)) > -1\n      };\n  }\n\n  function normalizeName(name) {\n    if (typeof name !== 'string') {\n      name = String(name);\n    }\n    if (/[^a-z0-9\\-#$%&'*+.^_`|~]/i.test(name)) {\n      throw new TypeError('Invalid character in header field name')\n    }\n    return name.toLowerCase()\n  }\n\n  function normalizeValue(value) {\n    if (typeof value !== 'string') {\n      value = String(value);\n    }\n    return value\n  }\n\n  // Build a destructive iterator for the value list\n  function iteratorFor(items) {\n    var iterator = {\n      next: function() {\n        var value = items.shift();\n        return {done: value === undefined, value: value}\n      }\n    };\n\n    if (support.iterable) {\n      iterator[Symbol.iterator] = function() {\n        return iterator\n      };\n    }\n\n    return iterator\n  }\n\n  function Headers(headers) {\n    this.map = {};\n\n    if (headers instanceof Headers) {\n      headers.forEach(function(value, name) {\n        this.append(name, value);\n      }, this);\n    } else if (Array.isArray(headers)) {\n      headers.forEach(function(header) {\n        this.append(header[0], header[1]);\n      }, this);\n    } else if (headers) {\n      Object.getOwnPropertyNames(headers).forEach(function(name) {\n        this.append(name, headers[name]);\n      }, this);\n    }\n  }\n\n  Headers.prototype.append = function(name, value) {\n    name = normalizeName(name);\n    value = normalizeValue(value);\n    var oldValue = this.map[name];\n    this.map[name] = oldValue ? oldValue + ', ' + value : value;\n  };\n\n  Headers.prototype['delete'] = function(name) {\n    delete this.map[normalizeName(name)];\n  };\n\n  Headers.prototype.get = function(name) {\n    name = normalizeName(name);\n    return this.has(name) ? this.map[name] : null\n  };\n\n  Headers.prototype.has = function(name) {\n    return this.map.hasOwnProperty(normalizeName(name))\n  };\n\n  Headers.prototype.set = function(name, value) {\n    this.map[normalizeName(name)] = normalizeValue(value);\n  };\n\n  Headers.prototype.forEach = function(callback, thisArg) {\n    for (var name in this.map) {\n      if (this.map.hasOwnProperty(name)) {\n        callback.call(thisArg, this.map[name], name, this);\n      }\n    }\n  };\n\n  Headers.prototype.keys = function() {\n    var items = [];\n    this.forEach(function(value, name) {\n      items.push(name);\n    });\n    return iteratorFor(items)\n  };\n\n  Headers.prototype.values = function() {\n    var items = [];\n    this.forEach(function(value) {\n      items.push(value);\n    });\n    return iteratorFor(items)\n  };\n\n  Headers.prototype.entries = function() {\n    var items = [];\n    this.forEach(function(value, name) {\n      items.push([name, value]);\n    });\n    return iteratorFor(items)\n  };\n\n  if (support.iterable) {\n    Headers.prototype[Symbol.iterator] = Headers.prototype.entries;\n  }\n\n  function consumed(body) {\n    if (body.bodyUsed) {\n      return Promise.reject(new TypeError('Already read'))\n    }\n    body.bodyUsed = true;\n  }\n\n  function fileReaderReady(reader) {\n    return new Promise(function(resolve, reject) {\n      reader.onload = function() {\n        resolve(reader.result);\n      };\n      reader.onerror = function() {\n        reject(reader.error);\n      };\n    })\n  }\n\n  function readBlobAsArrayBuffer(blob) {\n    var reader = new FileReader();\n    var promise = fileReaderReady(reader);\n    reader.readAsArrayBuffer(blob);\n    return promise\n  }\n\n  function readBlobAsText(blob) {\n    var reader = new FileReader();\n    var promise = fileReaderReady(reader);\n    reader.readAsText(blob);\n    return promise\n  }\n\n  function readArrayBufferAsText(buf) {\n    var view = new Uint8Array(buf);\n    var chars = new Array(view.length);\n\n    for (var i = 0; i < view.length; i++) {\n      chars[i] = String.fromCharCode(view[i]);\n    }\n    return chars.join('')\n  }\n\n  function bufferClone(buf) {\n    if (buf.slice) {\n      return buf.slice(0)\n    } else {\n      var view = new Uint8Array(buf.byteLength);\n      view.set(new Uint8Array(buf));\n      return view.buffer\n    }\n  }\n\n  function Body() {\n    this.bodyUsed = false;\n\n    this._initBody = function(body) {\n      this._bodyInit = body;\n      if (!body) {\n        this._bodyText = '';\n      } else if (typeof body === 'string') {\n        this._bodyText = body;\n      } else if (support.blob && Blob.prototype.isPrototypeOf(body)) {\n        this._bodyBlob = body;\n      } else if (support.formData && FormData.prototype.isPrototypeOf(body)) {\n        this._bodyFormData = body;\n      } else if (support.searchParams && URLSearchParams.prototype.isPrototypeOf(body)) {\n        this._bodyText = body.toString();\n      } else if (support.arrayBuffer && support.blob && isDataView(body)) {\n        this._bodyArrayBuffer = bufferClone(body.buffer);\n        // IE 10-11 can't handle a DataView body.\n        this._bodyInit = new Blob([this._bodyArrayBuffer]);\n      } else if (support.arrayBuffer && (ArrayBuffer.prototype.isPrototypeOf(body) || isArrayBufferView(body))) {\n        this._bodyArrayBuffer = bufferClone(body);\n      } else {\n        this._bodyText = body = Object.prototype.toString.call(body);\n      }\n\n      if (!this.headers.get('content-type')) {\n        if (typeof body === 'string') {\n          this.headers.set('content-type', 'text/plain;charset=UTF-8');\n        } else if (this._bodyBlob && this._bodyBlob.type) {\n          this.headers.set('content-type', this._bodyBlob.type);\n        } else if (support.searchParams && URLSearchParams.prototype.isPrototypeOf(body)) {\n          this.headers.set('content-type', 'application/x-www-form-urlencoded;charset=UTF-8');\n        }\n      }\n    };\n\n    if (support.blob) {\n      this.blob = function() {\n        var rejected = consumed(this);\n        if (rejected) {\n          return rejected\n        }\n\n        if (this._bodyBlob) {\n          return Promise.resolve(this._bodyBlob)\n        } else if (this._bodyArrayBuffer) {\n          return Promise.resolve(new Blob([this._bodyArrayBuffer]))\n        } else if (this._bodyFormData) {\n          throw new Error('could not read FormData body as blob')\n        } else {\n          return Promise.resolve(new Blob([this._bodyText]))\n        }\n      };\n\n      this.arrayBuffer = function() {\n        if (this._bodyArrayBuffer) {\n          return consumed(this) || Promise.resolve(this._bodyArrayBuffer)\n        } else {\n          return this.blob().then(readBlobAsArrayBuffer)\n        }\n      };\n    }\n\n    this.text = function() {\n      var rejected = consumed(this);\n      if (rejected) {\n        return rejected\n      }\n\n      if (this._bodyBlob) {\n        return readBlobAsText(this._bodyBlob)\n      } else if (this._bodyArrayBuffer) {\n        return Promise.resolve(readArrayBufferAsText(this._bodyArrayBuffer))\n      } else if (this._bodyFormData) {\n        throw new Error('could not read FormData body as text')\n      } else {\n        return Promise.resolve(this._bodyText)\n      }\n    };\n\n    if (support.formData) {\n      this.formData = function() {\n        return this.text().then(decode)\n      };\n    }\n\n    this.json = function() {\n      return this.text().then(JSON.parse)\n    };\n\n    return this\n  }\n\n  // HTTP methods whose capitalization should be normalized\n  var methods = ['DELETE', 'GET', 'HEAD', 'OPTIONS', 'POST', 'PUT'];\n\n  function normalizeMethod(method) {\n    var upcased = method.toUpperCase();\n    return methods.indexOf(upcased) > -1 ? upcased : method\n  }\n\n  function Request(input, options) {\n    options = options || {};\n    var body = options.body;\n\n    if (input instanceof Request) {\n      if (input.bodyUsed) {\n        throw new TypeError('Already read')\n      }\n      this.url = input.url;\n      this.credentials = input.credentials;\n      if (!options.headers) {\n        this.headers = new Headers(input.headers);\n      }\n      this.method = input.method;\n      this.mode = input.mode;\n      this.signal = input.signal;\n      if (!body && input._bodyInit != null) {\n        body = input._bodyInit;\n        input.bodyUsed = true;\n      }\n    } else {\n      this.url = String(input);\n    }\n\n    this.credentials = options.credentials || this.credentials || 'same-origin';\n    if (options.headers || !this.headers) {\n      this.headers = new Headers(options.headers);\n    }\n    this.method = normalizeMethod(options.method || this.method || 'GET');\n    this.mode = options.mode || this.mode || null;\n    this.signal = options.signal || this.signal;\n    this.referrer = null;\n\n    if ((this.method === 'GET' || this.method === 'HEAD') && body) {\n      throw new TypeError('Body not allowed for GET or HEAD requests')\n    }\n    this._initBody(body);\n  }\n\n  Request.prototype.clone = function() {\n    return new Request(this, {body: this._bodyInit})\n  };\n\n  function decode(body) {\n    var form = new FormData();\n    body\n      .trim()\n      .split('&')\n      .forEach(function(bytes) {\n        if (bytes) {\n          var split = bytes.split('=');\n          var name = split.shift().replace(/\\+/g, ' ');\n          var value = split.join('=').replace(/\\+/g, ' ');\n          form.append(decodeURIComponent(name), decodeURIComponent(value));\n        }\n      });\n    return form\n  }\n\n  function parseHeaders(rawHeaders) {\n    var headers = new Headers();\n    // Replace instances of \\r\\n and \\n followed by at least one space or horizontal tab with a space\n    // https://tools.ietf.org/html/rfc7230#section-3.2\n    var preProcessedHeaders = rawHeaders.replace(/\\r?\\n[\\t ]+/g, ' ');\n    preProcessedHeaders.split(/\\r?\\n/).forEach(function(line) {\n      var parts = line.split(':');\n      var key = parts.shift().trim();\n      if (key) {\n        var value = parts.join(':').trim();\n        headers.append(key, value);\n      }\n    });\n    return headers\n  }\n\n  Body.call(Request.prototype);\n\n  function Response(bodyInit, options) {\n    if (!options) {\n      options = {};\n    }\n\n    this.type = 'default';\n    this.status = options.status === undefined ? 200 : options.status;\n    this.ok = this.status >= 200 && this.status < 300;\n    this.statusText = 'statusText' in options ? options.statusText : 'OK';\n    this.headers = new Headers(options.headers);\n    this.url = options.url || '';\n    this._initBody(bodyInit);\n  }\n\n  Body.call(Response.prototype);\n\n  Response.prototype.clone = function() {\n    return new Response(this._bodyInit, {\n      status: this.status,\n      statusText: this.statusText,\n      headers: new Headers(this.headers),\n      url: this.url\n    })\n  };\n\n  Response.error = function() {\n    var response = new Response(null, {status: 0, statusText: ''});\n    response.type = 'error';\n    return response\n  };\n\n  var redirectStatuses = [301, 302, 303, 307, 308];\n\n  Response.redirect = function(url, status) {\n    if (redirectStatuses.indexOf(status) === -1) {\n      throw new RangeError('Invalid status code')\n    }\n\n    return new Response(null, {status: status, headers: {location: url}})\n  };\n\n  exports.DOMException = self.DOMException;\n  try {\n    new exports.DOMException();\n  } catch (err) {\n    exports.DOMException = function(message, name) {\n      this.message = message;\n      this.name = name;\n      var error = Error(message);\n      this.stack = error.stack;\n    };\n    exports.DOMException.prototype = Object.create(Error.prototype);\n    exports.DOMException.prototype.constructor = exports.DOMException;\n  }\n\n  function fetch(input, init) {\n    return new Promise(function(resolve, reject) {\n      var request = new Request(input, init);\n\n      if (request.signal && request.signal.aborted) {\n        return reject(new exports.DOMException('Aborted', 'AbortError'))\n      }\n\n      var xhr = new XMLHttpRequest();\n\n      function abortXhr() {\n        xhr.abort();\n      }\n\n      xhr.onload = function() {\n        var options = {\n          status: xhr.status,\n          statusText: xhr.statusText,\n          headers: parseHeaders(xhr.getAllResponseHeaders() || '')\n        };\n        options.url = 'responseURL' in xhr ? xhr.responseURL : options.headers.get('X-Request-URL');\n        var body = 'response' in xhr ? xhr.response : xhr.responseText;\n        resolve(new Response(body, options));\n      };\n\n      xhr.onerror = function() {\n        reject(new TypeError('Network request failed'));\n      };\n\n      xhr.ontimeout = function() {\n        reject(new TypeError('Network request failed'));\n      };\n\n      xhr.onabort = function() {\n        reject(new exports.DOMException('Aborted', 'AbortError'));\n      };\n\n      xhr.open(request.method, request.url, true);\n\n      if (request.credentials === 'include') {\n        xhr.withCredentials = true;\n      } else if (request.credentials === 'omit') {\n        xhr.withCredentials = false;\n      }\n\n      if ('responseType' in xhr && support.blob) {\n        xhr.responseType = 'blob';\n      }\n\n      request.headers.forEach(function(value, name) {\n        xhr.setRequestHeader(name, value);\n      });\n\n      if (request.signal) {\n        request.signal.addEventListener('abort', abortXhr);\n\n        xhr.onreadystatechange = function() {\n          // DONE (success or failure)\n          if (xhr.readyState === 4) {\n            request.signal.removeEventListener('abort', abortXhr);\n          }\n        };\n      }\n\n      xhr.send(typeof request._bodyInit === 'undefined' ? null : request._bodyInit);\n    })\n  }\n\n  fetch.polyfill = true;\n\n  if (!self.fetch) {\n    self.fetch = fetch;\n    self.Headers = Headers;\n    self.Request = Request;\n    self.Response = Response;\n  }\n\n  exports.Headers = Headers;\n  exports.Request = Request;\n  exports.Response = Response;\n  exports.fetch = fetch;\n\n  Object.defineProperty(exports, '__esModule', { value: true });\n\n  return exports;\n\n})({});\n})(__self__);\n__self__.fetch.ponyfill = true;\n// Remove \"polyfill\" property added by whatwg-fetch\ndelete __self__.fetch.polyfill;\n// Choose between native implementation (global) or custom implementation (__self__)\n// var ctx = global.fetch ? global : __self__;\nvar ctx = __self__; // this line disable service worker support temporarily\nexports = ctx.fetch // To enable: import fetch from 'cross-fetch'\nexports.default = ctx.fetch // For TypeScript consumers without esModuleInterop.\nexports.fetch = ctx.fetch // To enable: import {fetch} from 'cross-fetch'\nexports.Headers = ctx.Headers\nexports.Request = ctx.Request\nexports.Response = ctx.Response\nmodule.exports = exports\n","import crossFetch from 'cross-fetch';\n\nexport async function fetchArrayBuffer(url) {\n  const result = await crossFetch(url);\n  return result.arrayBuffer();\n}\n","import JSZip from 'jszip';\n\nimport { fetchArrayBuffer } from './util/fetchArrayBuffer';\n\nconst loadingPromises = {};\n\nexport async function loadCommercials(options = {}) {\n  const {\n    url = 'https://couch.cheminfo.org/cheminfo-public/d2eb480198c80275a1d05dd3609414f9/upload/commercials.zip',\n  } = options;\n\n  if (!loadingPromises[url]) {\n    loadingPromises[url] = fetchArrayBuffer(url);\n  }\n  const buffer = await loadingPromises[url];\n  const jsZip = new JSZip();\n  let zip = await jsZip.loadAsync(buffer);\n  let fileData = await zip.files['commercials.json'].async('string');\n  let data = JSON.parse(fileData);\n\n  data.sort((a, b) => a.em - b.em);\n\n  return data;\n}\n","/* @license\nPapa Parse\nv5.3.2\nhttps://github.com/mholt/PapaParse\nLicense: MIT\n*/\n!function(e,t){\"function\"==typeof define&&define.amd?define([],t):\"object\"==typeof module&&\"undefined\"!=typeof exports?module.exports=t():e.Papa=t()}(this,function s(){\"use strict\";var f=\"undefined\"!=typeof self?self:\"undefined\"!=typeof window?window:void 0!==f?f:{};var n=!f.document&&!!f.postMessage,o=n&&/blob:/i.test((f.location||{}).protocol),a={},h=0,b={parse:function(e,t){var i=(t=t||{}).dynamicTyping||!1;M(i)&&(t.dynamicTypingFunction=i,i={});if(t.dynamicTyping=i,t.transform=!!M(t.transform)&&t.transform,t.worker&&b.WORKERS_SUPPORTED){var r=function(){if(!b.WORKERS_SUPPORTED)return!1;var e=(i=f.URL||f.webkitURL||null,r=s.toString(),b.BLOB_URL||(b.BLOB_URL=i.createObjectURL(new Blob([\"(\",r,\")();\"],{type:\"text/javascript\"})))),t=new f.Worker(e);var i,r;return t.onmessage=_,t.id=h++,a[t.id]=t}();return r.userStep=t.step,r.userChunk=t.chunk,r.userComplete=t.complete,r.userError=t.error,t.step=M(t.step),t.chunk=M(t.chunk),t.complete=M(t.complete),t.error=M(t.error),delete t.worker,void r.postMessage({input:e,config:t,workerId:r.id})}var n=null;b.NODE_STREAM_INPUT,\"string\"==typeof e?n=t.download?new l(t):new p(t):!0===e.readable&&M(e.read)&&M(e.on)?n=new g(t):(f.File&&e instanceof File||e instanceof Object)&&(n=new c(t));return n.stream(e)},unparse:function(e,t){var n=!1,_=!0,m=\",\",y=\"\\r\\n\",s='\"',a=s+s,i=!1,r=null,o=!1;!function(){if(\"object\"!=typeof t)return;\"string\"!=typeof t.delimiter||b.BAD_DELIMITERS.filter(function(e){return-1!==t.delimiter.indexOf(e)}).length||(m=t.delimiter);(\"boolean\"==typeof t.quotes||\"function\"==typeof t.quotes||Array.isArray(t.quotes))&&(n=t.quotes);\"boolean\"!=typeof t.skipEmptyLines&&\"string\"!=typeof t.skipEmptyLines||(i=t.skipEmptyLines);\"string\"==typeof t.newline&&(y=t.newline);\"string\"==typeof t.quoteChar&&(s=t.quoteChar);\"boolean\"==typeof t.header&&(_=t.header);if(Array.isArray(t.columns)){if(0===t.columns.length)throw new Error(\"Option columns is empty\");r=t.columns}void 0!==t.escapeChar&&(a=t.escapeChar+s);(\"boolean\"==typeof t.escapeFormulae||t.escapeFormulae instanceof RegExp)&&(o=t.escapeFormulae instanceof RegExp?t.escapeFormulae:/^[=+\\-@\\t\\r].*$/)}();var h=new RegExp(j(s),\"g\");\"string\"==typeof e&&(e=JSON.parse(e));if(Array.isArray(e)){if(!e.length||Array.isArray(e[0]))return u(null,e,i);if(\"object\"==typeof e[0])return u(r||Object.keys(e[0]),e,i)}else if(\"object\"==typeof e)return\"string\"==typeof e.data&&(e.data=JSON.parse(e.data)),Array.isArray(e.data)&&(e.fields||(e.fields=e.meta&&e.meta.fields||r),e.fields||(e.fields=Array.isArray(e.data[0])?e.fields:\"object\"==typeof e.data[0]?Object.keys(e.data[0]):[]),Array.isArray(e.data[0])||\"object\"==typeof e.data[0]||(e.data=[e.data])),u(e.fields||[],e.data||[],i);throw new Error(\"Unable to serialize unrecognized input\");function u(e,t,i){var r=\"\";\"string\"==typeof e&&(e=JSON.parse(e)),\"string\"==typeof t&&(t=JSON.parse(t));var n=Array.isArray(e)&&0<e.length,s=!Array.isArray(t[0]);if(n&&_){for(var a=0;a<e.length;a++)0<a&&(r+=m),r+=v(e[a],a);0<t.length&&(r+=y)}for(var o=0;o<t.length;o++){var h=n?e.length:t[o].length,u=!1,f=n?0===Object.keys(t[o]).length:0===t[o].length;if(i&&!n&&(u=\"greedy\"===i?\"\"===t[o].join(\"\").trim():1===t[o].length&&0===t[o][0].length),\"greedy\"===i&&n){for(var d=[],l=0;l<h;l++){var c=s?e[l]:l;d.push(t[o][c])}u=\"\"===d.join(\"\").trim()}if(!u){for(var p=0;p<h;p++){0<p&&!f&&(r+=m);var g=n&&s?e[p]:p;r+=v(t[o][g],p)}o<t.length-1&&(!i||0<h&&!f)&&(r+=y)}}return r}function v(e,t){if(null==e)return\"\";if(e.constructor===Date)return JSON.stringify(e).slice(1,25);var i=!1;o&&\"string\"==typeof e&&o.test(e)&&(e=\"'\"+e,i=!0);var r=e.toString().replace(h,a);return(i=i||!0===n||\"function\"==typeof n&&n(e,t)||Array.isArray(n)&&n[t]||function(e,t){for(var i=0;i<t.length;i++)if(-1<e.indexOf(t[i]))return!0;return!1}(r,b.BAD_DELIMITERS)||-1<r.indexOf(m)||\" \"===r.charAt(0)||\" \"===r.charAt(r.length-1))?s+r+s:r}}};if(b.RECORD_SEP=String.fromCharCode(30),b.UNIT_SEP=String.fromCharCode(31),b.BYTE_ORDER_MARK=\"\\ufeff\",b.BAD_DELIMITERS=[\"\\r\",\"\\n\",'\"',b.BYTE_ORDER_MARK],b.WORKERS_SUPPORTED=!n&&!!f.Worker,b.NODE_STREAM_INPUT=1,b.LocalChunkSize=10485760,b.RemoteChunkSize=5242880,b.DefaultDelimiter=\",\",b.Parser=E,b.ParserHandle=i,b.NetworkStreamer=l,b.FileStreamer=c,b.StringStreamer=p,b.ReadableStreamStreamer=g,f.jQuery){var d=f.jQuery;d.fn.parse=function(o){var i=o.config||{},h=[];return this.each(function(e){if(!(\"INPUT\"===d(this).prop(\"tagName\").toUpperCase()&&\"file\"===d(this).attr(\"type\").toLowerCase()&&f.FileReader)||!this.files||0===this.files.length)return!0;for(var t=0;t<this.files.length;t++)h.push({file:this.files[t],inputElem:this,instanceConfig:d.extend({},i)})}),e(),this;function e(){if(0!==h.length){var e,t,i,r,n=h[0];if(M(o.before)){var s=o.before(n.file,n.inputElem);if(\"object\"==typeof s){if(\"abort\"===s.action)return e=\"AbortError\",t=n.file,i=n.inputElem,r=s.reason,void(M(o.error)&&o.error({name:e},t,i,r));if(\"skip\"===s.action)return void u();\"object\"==typeof s.config&&(n.instanceConfig=d.extend(n.instanceConfig,s.config))}else if(\"skip\"===s)return void u()}var a=n.instanceConfig.complete;n.instanceConfig.complete=function(e){M(a)&&a(e,n.file,n.inputElem),u()},b.parse(n.file,n.instanceConfig)}else M(o.complete)&&o.complete()}function u(){h.splice(0,1),e()}}}function u(e){this._handle=null,this._finished=!1,this._completed=!1,this._halted=!1,this._input=null,this._baseIndex=0,this._partialLine=\"\",this._rowCount=0,this._start=0,this._nextChunk=null,this.isFirstChunk=!0,this._completeResults={data:[],errors:[],meta:{}},function(e){var t=w(e);t.chunkSize=parseInt(t.chunkSize),e.step||e.chunk||(t.chunkSize=null);this._handle=new i(t),(this._handle.streamer=this)._config=t}.call(this,e),this.parseChunk=function(e,t){if(this.isFirstChunk&&M(this._config.beforeFirstChunk)){var i=this._config.beforeFirstChunk(e);void 0!==i&&(e=i)}this.isFirstChunk=!1,this._halted=!1;var r=this._partialLine+e;this._partialLine=\"\";var n=this._handle.parse(r,this._baseIndex,!this._finished);if(!this._handle.paused()&&!this._handle.aborted()){var s=n.meta.cursor;this._finished||(this._partialLine=r.substring(s-this._baseIndex),this._baseIndex=s),n&&n.data&&(this._rowCount+=n.data.length);var a=this._finished||this._config.preview&&this._rowCount>=this._config.preview;if(o)f.postMessage({results:n,workerId:b.WORKER_ID,finished:a});else if(M(this._config.chunk)&&!t){if(this._config.chunk(n,this._handle),this._handle.paused()||this._handle.aborted())return void(this._halted=!0);n=void 0,this._completeResults=void 0}return this._config.step||this._config.chunk||(this._completeResults.data=this._completeResults.data.concat(n.data),this._completeResults.errors=this._completeResults.errors.concat(n.errors),this._completeResults.meta=n.meta),this._completed||!a||!M(this._config.complete)||n&&n.meta.aborted||(this._config.complete(this._completeResults,this._input),this._completed=!0),a||n&&n.meta.paused||this._nextChunk(),n}this._halted=!0},this._sendError=function(e){M(this._config.error)?this._config.error(e):o&&this._config.error&&f.postMessage({workerId:b.WORKER_ID,error:e,finished:!1})}}function l(e){var r;(e=e||{}).chunkSize||(e.chunkSize=b.RemoteChunkSize),u.call(this,e),this._nextChunk=n?function(){this._readChunk(),this._chunkLoaded()}:function(){this._readChunk()},this.stream=function(e){this._input=e,this._nextChunk()},this._readChunk=function(){if(this._finished)this._chunkLoaded();else{if(r=new XMLHttpRequest,this._config.withCredentials&&(r.withCredentials=this._config.withCredentials),n||(r.onload=v(this._chunkLoaded,this),r.onerror=v(this._chunkError,this)),r.open(this._config.downloadRequestBody?\"POST\":\"GET\",this._input,!n),this._config.downloadRequestHeaders){var e=this._config.downloadRequestHeaders;for(var t in e)r.setRequestHeader(t,e[t])}if(this._config.chunkSize){var i=this._start+this._config.chunkSize-1;r.setRequestHeader(\"Range\",\"bytes=\"+this._start+\"-\"+i)}try{r.send(this._config.downloadRequestBody)}catch(e){this._chunkError(e.message)}n&&0===r.status&&this._chunkError()}},this._chunkLoaded=function(){4===r.readyState&&(r.status<200||400<=r.status?this._chunkError():(this._start+=this._config.chunkSize?this._config.chunkSize:r.responseText.length,this._finished=!this._config.chunkSize||this._start>=function(e){var t=e.getResponseHeader(\"Content-Range\");if(null===t)return-1;return parseInt(t.substring(t.lastIndexOf(\"/\")+1))}(r),this.parseChunk(r.responseText)))},this._chunkError=function(e){var t=r.statusText||e;this._sendError(new Error(t))}}function c(e){var r,n;(e=e||{}).chunkSize||(e.chunkSize=b.LocalChunkSize),u.call(this,e);var s=\"undefined\"!=typeof FileReader;this.stream=function(e){this._input=e,n=e.slice||e.webkitSlice||e.mozSlice,s?((r=new FileReader).onload=v(this._chunkLoaded,this),r.onerror=v(this._chunkError,this)):r=new FileReaderSync,this._nextChunk()},this._nextChunk=function(){this._finished||this._config.preview&&!(this._rowCount<this._config.preview)||this._readChunk()},this._readChunk=function(){var e=this._input;if(this._config.chunkSize){var t=Math.min(this._start+this._config.chunkSize,this._input.size);e=n.call(e,this._start,t)}var i=r.readAsText(e,this._config.encoding);s||this._chunkLoaded({target:{result:i}})},this._chunkLoaded=function(e){this._start+=this._config.chunkSize,this._finished=!this._config.chunkSize||this._start>=this._input.size,this.parseChunk(e.target.result)},this._chunkError=function(){this._sendError(r.error)}}function p(e){var i;u.call(this,e=e||{}),this.stream=function(e){return i=e,this._nextChunk()},this._nextChunk=function(){if(!this._finished){var e,t=this._config.chunkSize;return t?(e=i.substring(0,t),i=i.substring(t)):(e=i,i=\"\"),this._finished=!i,this.parseChunk(e)}}}function g(e){u.call(this,e=e||{});var t=[],i=!0,r=!1;this.pause=function(){u.prototype.pause.apply(this,arguments),this._input.pause()},this.resume=function(){u.prototype.resume.apply(this,arguments),this._input.resume()},this.stream=function(e){this._input=e,this._input.on(\"data\",this._streamData),this._input.on(\"end\",this._streamEnd),this._input.on(\"error\",this._streamError)},this._checkIsFinished=function(){r&&1===t.length&&(this._finished=!0)},this._nextChunk=function(){this._checkIsFinished(),t.length?this.parseChunk(t.shift()):i=!0},this._streamData=v(function(e){try{t.push(\"string\"==typeof e?e:e.toString(this._config.encoding)),i&&(i=!1,this._checkIsFinished(),this.parseChunk(t.shift()))}catch(e){this._streamError(e)}},this),this._streamError=v(function(e){this._streamCleanUp(),this._sendError(e)},this),this._streamEnd=v(function(){this._streamCleanUp(),r=!0,this._streamData(\"\")},this),this._streamCleanUp=v(function(){this._input.removeListener(\"data\",this._streamData),this._input.removeListener(\"end\",this._streamEnd),this._input.removeListener(\"error\",this._streamError)},this)}function i(m){var a,o,h,r=Math.pow(2,53),n=-r,s=/^\\s*-?(\\d+\\.?|\\.\\d+|\\d+\\.\\d+)([eE][-+]?\\d+)?\\s*$/,u=/^(\\d{4}-[01]\\d-[0-3]\\dT[0-2]\\d:[0-5]\\d:[0-5]\\d\\.\\d+([+-][0-2]\\d:[0-5]\\d|Z))|(\\d{4}-[01]\\d-[0-3]\\dT[0-2]\\d:[0-5]\\d:[0-5]\\d([+-][0-2]\\d:[0-5]\\d|Z))|(\\d{4}-[01]\\d-[0-3]\\dT[0-2]\\d:[0-5]\\d([+-][0-2]\\d:[0-5]\\d|Z))$/,t=this,i=0,f=0,d=!1,e=!1,l=[],c={data:[],errors:[],meta:{}};if(M(m.step)){var p=m.step;m.step=function(e){if(c=e,_())g();else{if(g(),0===c.data.length)return;i+=e.data.length,m.preview&&i>m.preview?o.abort():(c.data=c.data[0],p(c,t))}}}function y(e){return\"greedy\"===m.skipEmptyLines?\"\"===e.join(\"\").trim():1===e.length&&0===e[0].length}function g(){return c&&h&&(k(\"Delimiter\",\"UndetectableDelimiter\",\"Unable to auto-detect delimiting character; defaulted to '\"+b.DefaultDelimiter+\"'\"),h=!1),m.skipEmptyLines&&(c.data=c.data.filter(function(e){return!y(e)})),_()&&function(){if(!c)return;function e(e,t){M(m.transformHeader)&&(e=m.transformHeader(e,t)),l.push(e)}if(Array.isArray(c.data[0])){for(var t=0;_()&&t<c.data.length;t++)c.data[t].forEach(e);c.data.splice(0,1)}else c.data.forEach(e)}(),function(){if(!c||!m.header&&!m.dynamicTyping&&!m.transform)return c;function e(e,t){var i,r=m.header?{}:[];for(i=0;i<e.length;i++){var n=i,s=e[i];m.header&&(n=i>=l.length?\"__parsed_extra\":l[i]),m.transform&&(s=m.transform(s,n)),s=v(n,s),\"__parsed_extra\"===n?(r[n]=r[n]||[],r[n].push(s)):r[n]=s}return m.header&&(i>l.length?k(\"FieldMismatch\",\"TooManyFields\",\"Too many fields: expected \"+l.length+\" fields but parsed \"+i,f+t):i<l.length&&k(\"FieldMismatch\",\"TooFewFields\",\"Too few fields: expected \"+l.length+\" fields but parsed \"+i,f+t)),r}var t=1;!c.data.length||Array.isArray(c.data[0])?(c.data=c.data.map(e),t=c.data.length):c.data=e(c.data,0);m.header&&c.meta&&(c.meta.fields=l);return f+=t,c}()}function _(){return m.header&&0===l.length}function v(e,t){return i=e,m.dynamicTypingFunction&&void 0===m.dynamicTyping[i]&&(m.dynamicTyping[i]=m.dynamicTypingFunction(i)),!0===(m.dynamicTyping[i]||m.dynamicTyping)?\"true\"===t||\"TRUE\"===t||\"false\"!==t&&\"FALSE\"!==t&&(function(e){if(s.test(e)){var t=parseFloat(e);if(n<t&&t<r)return!0}return!1}(t)?parseFloat(t):u.test(t)?new Date(t):\"\"===t?null:t):t;var i}function k(e,t,i,r){var n={type:e,code:t,message:i};void 0!==r&&(n.row=r),c.errors.push(n)}this.parse=function(e,t,i){var r=m.quoteChar||'\"';if(m.newline||(m.newline=function(e,t){e=e.substring(0,1048576);var i=new RegExp(j(t)+\"([^]*?)\"+j(t),\"gm\"),r=(e=e.replace(i,\"\")).split(\"\\r\"),n=e.split(\"\\n\"),s=1<n.length&&n[0].length<r[0].length;if(1===r.length||s)return\"\\n\";for(var a=0,o=0;o<r.length;o++)\"\\n\"===r[o][0]&&a++;return a>=r.length/2?\"\\r\\n\":\"\\r\"}(e,r)),h=!1,m.delimiter)M(m.delimiter)&&(m.delimiter=m.delimiter(e),c.meta.delimiter=m.delimiter);else{var n=function(e,t,i,r,n){var s,a,o,h;n=n||[\",\",\"\\t\",\"|\",\";\",b.RECORD_SEP,b.UNIT_SEP];for(var u=0;u<n.length;u++){var f=n[u],d=0,l=0,c=0;o=void 0;for(var p=new E({comments:r,delimiter:f,newline:t,preview:10}).parse(e),g=0;g<p.data.length;g++)if(i&&y(p.data[g]))c++;else{var _=p.data[g].length;l+=_,void 0!==o?0<_&&(d+=Math.abs(_-o),o=_):o=_}0<p.data.length&&(l/=p.data.length-c),(void 0===a||d<=a)&&(void 0===h||h<l)&&1.99<l&&(a=d,s=f,h=l)}return{successful:!!(m.delimiter=s),bestDelimiter:s}}(e,m.newline,m.skipEmptyLines,m.comments,m.delimitersToGuess);n.successful?m.delimiter=n.bestDelimiter:(h=!0,m.delimiter=b.DefaultDelimiter),c.meta.delimiter=m.delimiter}var s=w(m);return m.preview&&m.header&&s.preview++,a=e,o=new E(s),c=o.parse(a,t,i),g(),d?{meta:{paused:!0}}:c||{meta:{paused:!1}}},this.paused=function(){return d},this.pause=function(){d=!0,o.abort(),a=M(m.chunk)?\"\":a.substring(o.getCharIndex())},this.resume=function(){t.streamer._halted?(d=!1,t.streamer.parseChunk(a,!0)):setTimeout(t.resume,3)},this.aborted=function(){return e},this.abort=function(){e=!0,o.abort(),c.meta.aborted=!0,M(m.complete)&&m.complete(c),a=\"\"}}function j(e){return e.replace(/[.*+?^${}()|[\\]\\\\]/g,\"\\\\$&\")}function E(e){var S,O=(e=e||{}).delimiter,x=e.newline,I=e.comments,T=e.step,D=e.preview,A=e.fastMode,L=S=void 0===e.quoteChar||null===e.quoteChar?'\"':e.quoteChar;if(void 0!==e.escapeChar&&(L=e.escapeChar),(\"string\"!=typeof O||-1<b.BAD_DELIMITERS.indexOf(O))&&(O=\",\"),I===O)throw new Error(\"Comment character same as delimiter\");!0===I?I=\"#\":(\"string\"!=typeof I||-1<b.BAD_DELIMITERS.indexOf(I))&&(I=!1),\"\\n\"!==x&&\"\\r\"!==x&&\"\\r\\n\"!==x&&(x=\"\\n\");var F=0,z=!1;this.parse=function(r,t,i){if(\"string\"!=typeof r)throw new Error(\"Input must be a string\");var n=r.length,e=O.length,s=x.length,a=I.length,o=M(T),h=[],u=[],f=[],d=F=0;if(!r)return C();if(A||!1!==A&&-1===r.indexOf(S)){for(var l=r.split(x),c=0;c<l.length;c++){if(f=l[c],F+=f.length,c!==l.length-1)F+=x.length;else if(i)return C();if(!I||f.substring(0,a)!==I){if(o){if(h=[],k(f.split(O)),R(),z)return C()}else k(f.split(O));if(D&&D<=c)return h=h.slice(0,D),C(!0)}}return C()}for(var p=r.indexOf(O,F),g=r.indexOf(x,F),_=new RegExp(j(L)+j(S),\"g\"),m=r.indexOf(S,F);;)if(r[F]!==S)if(I&&0===f.length&&r.substring(F,F+a)===I){if(-1===g)return C();F=g+s,g=r.indexOf(x,F),p=r.indexOf(O,F)}else if(-1!==p&&(p<g||-1===g))f.push(r.substring(F,p)),F=p+e,p=r.indexOf(O,F);else{if(-1===g)break;if(f.push(r.substring(F,g)),w(g+s),o&&(R(),z))return C();if(D&&h.length>=D)return C(!0)}else for(m=F,F++;;){if(-1===(m=r.indexOf(S,m+1)))return i||u.push({type:\"Quotes\",code:\"MissingQuotes\",message:\"Quoted field unterminated\",row:h.length,index:F}),E();if(m===n-1)return E(r.substring(F,m).replace(_,S));if(S!==L||r[m+1]!==L){if(S===L||0===m||r[m-1]!==L){-1!==p&&p<m+1&&(p=r.indexOf(O,m+1)),-1!==g&&g<m+1&&(g=r.indexOf(x,m+1));var y=b(-1===g?p:Math.min(p,g));if(r.substr(m+1+y,e)===O){f.push(r.substring(F,m).replace(_,S)),r[F=m+1+y+e]!==S&&(m=r.indexOf(S,F)),p=r.indexOf(O,F),g=r.indexOf(x,F);break}var v=b(g);if(r.substring(m+1+v,m+1+v+s)===x){if(f.push(r.substring(F,m).replace(_,S)),w(m+1+v+s),p=r.indexOf(O,F),m=r.indexOf(S,F),o&&(R(),z))return C();if(D&&h.length>=D)return C(!0);break}u.push({type:\"Quotes\",code:\"InvalidQuotes\",message:\"Trailing quote on quoted field is malformed\",row:h.length,index:F}),m++}}else m++}return E();function k(e){h.push(e),d=F}function b(e){var t=0;if(-1!==e){var i=r.substring(m+1,e);i&&\"\"===i.trim()&&(t=i.length)}return t}function E(e){return i||(void 0===e&&(e=r.substring(F)),f.push(e),F=n,k(f),o&&R()),C()}function w(e){F=e,k(f),f=[],g=r.indexOf(x,F)}function C(e){return{data:h,errors:u,meta:{delimiter:O,linebreak:x,aborted:z,truncated:!!e,cursor:d+(t||0)}}}function R(){T(C()),h=[],u=[]}},this.abort=function(){z=!0},this.getCharIndex=function(){return F}}function _(e){var t=e.data,i=a[t.workerId],r=!1;if(t.error)i.userError(t.error,t.file);else if(t.results&&t.results.data){var n={abort:function(){r=!0,m(t.workerId,{data:[],errors:[],meta:{aborted:!0}})},pause:y,resume:y};if(M(i.userStep)){for(var s=0;s<t.results.data.length&&(i.userStep({data:t.results.data[s],errors:t.results.errors,meta:t.results.meta},n),!r);s++);delete t.results}else M(i.userChunk)&&(i.userChunk(t.results,n,t.file),delete t.results)}t.finished&&!r&&m(t.workerId,t.results)}function m(e,t){var i=a[e];M(i.userComplete)&&i.userComplete(t),i.terminate(),delete a[e]}function y(){throw new Error(\"Not implemented.\")}function w(e){if(\"object\"!=typeof e||null===e)return e;var t=Array.isArray(e)?[]:{};for(var i in e)t[i]=w(e[i]);return t}function v(e,t){return function(){e.apply(t,arguments)}}function M(e){return\"function\"==typeof e}return o&&(f.onmessage=function(e){var t=e.data;void 0===b.WORKER_ID&&t&&(b.WORKER_ID=t.workerId);if(\"string\"==typeof t.input)f.postMessage({workerId:b.WORKER_ID,results:b.parse(t.input,t.config),finished:!0});else if(f.File&&t.input instanceof File||t.input instanceof Object){var i=b.parse(t.input,t.config);i&&f.postMessage({workerId:b.WORKER_ID,results:i,finished:!0})}}),(l.prototype=Object.create(u.prototype)).constructor=l,(c.prototype=Object.create(u.prototype)).constructor=c,(p.prototype=Object.create(p.prototype)).constructor=p,(g.prototype=Object.create(u.prototype)).constructor=g,b});","var global = typeof self !== 'undefined' ? self : this;\nvar __self__ = (function () {\nfunction F() {\nthis.fetch = false;\nthis.DOMException = global.DOMException\n}\nF.prototype = global;\nreturn new F();\n})();\n(function(self) {\n\nvar irrelevant = (function (exports) {\n\n  var support = {\n    searchParams: 'URLSearchParams' in self,\n    iterable: 'Symbol' in self && 'iterator' in Symbol,\n    blob:\n      'FileReader' in self &&\n      'Blob' in self &&\n      (function() {\n        try {\n          new Blob();\n          return true\n        } catch (e) {\n          return false\n        }\n      })(),\n    formData: 'FormData' in self,\n    arrayBuffer: 'ArrayBuffer' in self\n  };\n\n  function isDataView(obj) {\n    return obj && DataView.prototype.isPrototypeOf(obj)\n  }\n\n  if (support.arrayBuffer) {\n    var viewClasses = [\n      '[object Int8Array]',\n      '[object Uint8Array]',\n      '[object Uint8ClampedArray]',\n      '[object Int16Array]',\n      '[object Uint16Array]',\n      '[object Int32Array]',\n      '[object Uint32Array]',\n      '[object Float32Array]',\n      '[object Float64Array]'\n    ];\n\n    var isArrayBufferView =\n      ArrayBuffer.isView ||\n      function(obj) {\n        return obj && viewClasses.indexOf(Object.prototype.toString.call(obj)) > -1\n      };\n  }\n\n  function normalizeName(name) {\n    if (typeof name !== 'string') {\n      name = String(name);\n    }\n    if (/[^a-z0-9\\-#$%&'*+.^_`|~]/i.test(name)) {\n      throw new TypeError('Invalid character in header field name')\n    }\n    return name.toLowerCase()\n  }\n\n  function normalizeValue(value) {\n    if (typeof value !== 'string') {\n      value = String(value);\n    }\n    return value\n  }\n\n  // Build a destructive iterator for the value list\n  function iteratorFor(items) {\n    var iterator = {\n      next: function() {\n        var value = items.shift();\n        return {done: value === undefined, value: value}\n      }\n    };\n\n    if (support.iterable) {\n      iterator[Symbol.iterator] = function() {\n        return iterator\n      };\n    }\n\n    return iterator\n  }\n\n  function Headers(headers) {\n    this.map = {};\n\n    if (headers instanceof Headers) {\n      headers.forEach(function(value, name) {\n        this.append(name, value);\n      }, this);\n    } else if (Array.isArray(headers)) {\n      headers.forEach(function(header) {\n        this.append(header[0], header[1]);\n      }, this);\n    } else if (headers) {\n      Object.getOwnPropertyNames(headers).forEach(function(name) {\n        this.append(name, headers[name]);\n      }, this);\n    }\n  }\n\n  Headers.prototype.append = function(name, value) {\n    name = normalizeName(name);\n    value = normalizeValue(value);\n    var oldValue = this.map[name];\n    this.map[name] = oldValue ? oldValue + ', ' + value : value;\n  };\n\n  Headers.prototype['delete'] = function(name) {\n    delete this.map[normalizeName(name)];\n  };\n\n  Headers.prototype.get = function(name) {\n    name = normalizeName(name);\n    return this.has(name) ? this.map[name] : null\n  };\n\n  Headers.prototype.has = function(name) {\n    return this.map.hasOwnProperty(normalizeName(name))\n  };\n\n  Headers.prototype.set = function(name, value) {\n    this.map[normalizeName(name)] = normalizeValue(value);\n  };\n\n  Headers.prototype.forEach = function(callback, thisArg) {\n    for (var name in this.map) {\n      if (this.map.hasOwnProperty(name)) {\n        callback.call(thisArg, this.map[name], name, this);\n      }\n    }\n  };\n\n  Headers.prototype.keys = function() {\n    var items = [];\n    this.forEach(function(value, name) {\n      items.push(name);\n    });\n    return iteratorFor(items)\n  };\n\n  Headers.prototype.values = function() {\n    var items = [];\n    this.forEach(function(value) {\n      items.push(value);\n    });\n    return iteratorFor(items)\n  };\n\n  Headers.prototype.entries = function() {\n    var items = [];\n    this.forEach(function(value, name) {\n      items.push([name, value]);\n    });\n    return iteratorFor(items)\n  };\n\n  if (support.iterable) {\n    Headers.prototype[Symbol.iterator] = Headers.prototype.entries;\n  }\n\n  function consumed(body) {\n    if (body.bodyUsed) {\n      return Promise.reject(new TypeError('Already read'))\n    }\n    body.bodyUsed = true;\n  }\n\n  function fileReaderReady(reader) {\n    return new Promise(function(resolve, reject) {\n      reader.onload = function() {\n        resolve(reader.result);\n      };\n      reader.onerror = function() {\n        reject(reader.error);\n      };\n    })\n  }\n\n  function readBlobAsArrayBuffer(blob) {\n    var reader = new FileReader();\n    var promise = fileReaderReady(reader);\n    reader.readAsArrayBuffer(blob);\n    return promise\n  }\n\n  function readBlobAsText(blob) {\n    var reader = new FileReader();\n    var promise = fileReaderReady(reader);\n    reader.readAsText(blob);\n    return promise\n  }\n\n  function readArrayBufferAsText(buf) {\n    var view = new Uint8Array(buf);\n    var chars = new Array(view.length);\n\n    for (var i = 0; i < view.length; i++) {\n      chars[i] = String.fromCharCode(view[i]);\n    }\n    return chars.join('')\n  }\n\n  function bufferClone(buf) {\n    if (buf.slice) {\n      return buf.slice(0)\n    } else {\n      var view = new Uint8Array(buf.byteLength);\n      view.set(new Uint8Array(buf));\n      return view.buffer\n    }\n  }\n\n  function Body() {\n    this.bodyUsed = false;\n\n    this._initBody = function(body) {\n      this._bodyInit = body;\n      if (!body) {\n        this._bodyText = '';\n      } else if (typeof body === 'string') {\n        this._bodyText = body;\n      } else if (support.blob && Blob.prototype.isPrototypeOf(body)) {\n        this._bodyBlob = body;\n      } else if (support.formData && FormData.prototype.isPrototypeOf(body)) {\n        this._bodyFormData = body;\n      } else if (support.searchParams && URLSearchParams.prototype.isPrototypeOf(body)) {\n        this._bodyText = body.toString();\n      } else if (support.arrayBuffer && support.blob && isDataView(body)) {\n        this._bodyArrayBuffer = bufferClone(body.buffer);\n        // IE 10-11 can't handle a DataView body.\n        this._bodyInit = new Blob([this._bodyArrayBuffer]);\n      } else if (support.arrayBuffer && (ArrayBuffer.prototype.isPrototypeOf(body) || isArrayBufferView(body))) {\n        this._bodyArrayBuffer = bufferClone(body);\n      } else {\n        this._bodyText = body = Object.prototype.toString.call(body);\n      }\n\n      if (!this.headers.get('content-type')) {\n        if (typeof body === 'string') {\n          this.headers.set('content-type', 'text/plain;charset=UTF-8');\n        } else if (this._bodyBlob && this._bodyBlob.type) {\n          this.headers.set('content-type', this._bodyBlob.type);\n        } else if (support.searchParams && URLSearchParams.prototype.isPrototypeOf(body)) {\n          this.headers.set('content-type', 'application/x-www-form-urlencoded;charset=UTF-8');\n        }\n      }\n    };\n\n    if (support.blob) {\n      this.blob = function() {\n        var rejected = consumed(this);\n        if (rejected) {\n          return rejected\n        }\n\n        if (this._bodyBlob) {\n          return Promise.resolve(this._bodyBlob)\n        } else if (this._bodyArrayBuffer) {\n          return Promise.resolve(new Blob([this._bodyArrayBuffer]))\n        } else if (this._bodyFormData) {\n          throw new Error('could not read FormData body as blob')\n        } else {\n          return Promise.resolve(new Blob([this._bodyText]))\n        }\n      };\n\n      this.arrayBuffer = function() {\n        if (this._bodyArrayBuffer) {\n          return consumed(this) || Promise.resolve(this._bodyArrayBuffer)\n        } else {\n          return this.blob().then(readBlobAsArrayBuffer)\n        }\n      };\n    }\n\n    this.text = function() {\n      var rejected = consumed(this);\n      if (rejected) {\n        return rejected\n      }\n\n      if (this._bodyBlob) {\n        return readBlobAsText(this._bodyBlob)\n      } else if (this._bodyArrayBuffer) {\n        return Promise.resolve(readArrayBufferAsText(this._bodyArrayBuffer))\n      } else if (this._bodyFormData) {\n        throw new Error('could not read FormData body as text')\n      } else {\n        return Promise.resolve(this._bodyText)\n      }\n    };\n\n    if (support.formData) {\n      this.formData = function() {\n        return this.text().then(decode)\n      };\n    }\n\n    this.json = function() {\n      return this.text().then(JSON.parse)\n    };\n\n    return this\n  }\n\n  // HTTP methods whose capitalization should be normalized\n  var methods = ['DELETE', 'GET', 'HEAD', 'OPTIONS', 'POST', 'PUT'];\n\n  function normalizeMethod(method) {\n    var upcased = method.toUpperCase();\n    return methods.indexOf(upcased) > -1 ? upcased : method\n  }\n\n  function Request(input, options) {\n    options = options || {};\n    var body = options.body;\n\n    if (input instanceof Request) {\n      if (input.bodyUsed) {\n        throw new TypeError('Already read')\n      }\n      this.url = input.url;\n      this.credentials = input.credentials;\n      if (!options.headers) {\n        this.headers = new Headers(input.headers);\n      }\n      this.method = input.method;\n      this.mode = input.mode;\n      this.signal = input.signal;\n      if (!body && input._bodyInit != null) {\n        body = input._bodyInit;\n        input.bodyUsed = true;\n      }\n    } else {\n      this.url = String(input);\n    }\n\n    this.credentials = options.credentials || this.credentials || 'same-origin';\n    if (options.headers || !this.headers) {\n      this.headers = new Headers(options.headers);\n    }\n    this.method = normalizeMethod(options.method || this.method || 'GET');\n    this.mode = options.mode || this.mode || null;\n    this.signal = options.signal || this.signal;\n    this.referrer = null;\n\n    if ((this.method === 'GET' || this.method === 'HEAD') && body) {\n      throw new TypeError('Body not allowed for GET or HEAD requests')\n    }\n    this._initBody(body);\n  }\n\n  Request.prototype.clone = function() {\n    return new Request(this, {body: this._bodyInit})\n  };\n\n  function decode(body) {\n    var form = new FormData();\n    body\n      .trim()\n      .split('&')\n      .forEach(function(bytes) {\n        if (bytes) {\n          var split = bytes.split('=');\n          var name = split.shift().replace(/\\+/g, ' ');\n          var value = split.join('=').replace(/\\+/g, ' ');\n          form.append(decodeURIComponent(name), decodeURIComponent(value));\n        }\n      });\n    return form\n  }\n\n  function parseHeaders(rawHeaders) {\n    var headers = new Headers();\n    // Replace instances of \\r\\n and \\n followed by at least one space or horizontal tab with a space\n    // https://tools.ietf.org/html/rfc7230#section-3.2\n    var preProcessedHeaders = rawHeaders.replace(/\\r?\\n[\\t ]+/g, ' ');\n    preProcessedHeaders.split(/\\r?\\n/).forEach(function(line) {\n      var parts = line.split(':');\n      var key = parts.shift().trim();\n      if (key) {\n        var value = parts.join(':').trim();\n        headers.append(key, value);\n      }\n    });\n    return headers\n  }\n\n  Body.call(Request.prototype);\n\n  function Response(bodyInit, options) {\n    if (!options) {\n      options = {};\n    }\n\n    this.type = 'default';\n    this.status = options.status === undefined ? 200 : options.status;\n    this.ok = this.status >= 200 && this.status < 300;\n    this.statusText = 'statusText' in options ? options.statusText : 'OK';\n    this.headers = new Headers(options.headers);\n    this.url = options.url || '';\n    this._initBody(bodyInit);\n  }\n\n  Body.call(Response.prototype);\n\n  Response.prototype.clone = function() {\n    return new Response(this._bodyInit, {\n      status: this.status,\n      statusText: this.statusText,\n      headers: new Headers(this.headers),\n      url: this.url\n    })\n  };\n\n  Response.error = function() {\n    var response = new Response(null, {status: 0, statusText: ''});\n    response.type = 'error';\n    return response\n  };\n\n  var redirectStatuses = [301, 302, 303, 307, 308];\n\n  Response.redirect = function(url, status) {\n    if (redirectStatuses.indexOf(status) === -1) {\n      throw new RangeError('Invalid status code')\n    }\n\n    return new Response(null, {status: status, headers: {location: url}})\n  };\n\n  exports.DOMException = self.DOMException;\n  try {\n    new exports.DOMException();\n  } catch (err) {\n    exports.DOMException = function(message, name) {\n      this.message = message;\n      this.name = name;\n      var error = Error(message);\n      this.stack = error.stack;\n    };\n    exports.DOMException.prototype = Object.create(Error.prototype);\n    exports.DOMException.prototype.constructor = exports.DOMException;\n  }\n\n  function fetch(input, init) {\n    return new Promise(function(resolve, reject) {\n      var request = new Request(input, init);\n\n      if (request.signal && request.signal.aborted) {\n        return reject(new exports.DOMException('Aborted', 'AbortError'))\n      }\n\n      var xhr = new XMLHttpRequest();\n\n      function abortXhr() {\n        xhr.abort();\n      }\n\n      xhr.onload = function() {\n        var options = {\n          status: xhr.status,\n          statusText: xhr.statusText,\n          headers: parseHeaders(xhr.getAllResponseHeaders() || '')\n        };\n        options.url = 'responseURL' in xhr ? xhr.responseURL : options.headers.get('X-Request-URL');\n        var body = 'response' in xhr ? xhr.response : xhr.responseText;\n        resolve(new Response(body, options));\n      };\n\n      xhr.onerror = function() {\n        reject(new TypeError('Network request failed'));\n      };\n\n      xhr.ontimeout = function() {\n        reject(new TypeError('Network request failed'));\n      };\n\n      xhr.onabort = function() {\n        reject(new exports.DOMException('Aborted', 'AbortError'));\n      };\n\n      xhr.open(request.method, request.url, true);\n\n      if (request.credentials === 'include') {\n        xhr.withCredentials = true;\n      } else if (request.credentials === 'omit') {\n        xhr.withCredentials = false;\n      }\n\n      if ('responseType' in xhr && support.blob) {\n        xhr.responseType = 'blob';\n      }\n\n      request.headers.forEach(function(value, name) {\n        xhr.setRequestHeader(name, value);\n      });\n\n      if (request.signal) {\n        request.signal.addEventListener('abort', abortXhr);\n\n        xhr.onreadystatechange = function() {\n          // DONE (success or failure)\n          if (xhr.readyState === 4) {\n            request.signal.removeEventListener('abort', abortXhr);\n          }\n        };\n      }\n\n      xhr.send(typeof request._bodyInit === 'undefined' ? null : request._bodyInit);\n    })\n  }\n\n  fetch.polyfill = true;\n\n  if (!self.fetch) {\n    self.fetch = fetch;\n    self.Headers = Headers;\n    self.Request = Request;\n    self.Response = Response;\n  }\n\n  exports.Headers = Headers;\n  exports.Request = Request;\n  exports.Response = Response;\n  exports.fetch = fetch;\n\n  Object.defineProperty(exports, '__esModule', { value: true });\n\n  return exports;\n\n})({});\n})(__self__);\n__self__.fetch.ponyfill = true;\n// Remove \"polyfill\" property added by whatwg-fetch\ndelete __self__.fetch.polyfill;\n// Choose between native implementation (global) or custom implementation (__self__)\n// var ctx = global.fetch ? global : __self__;\nvar ctx = __self__; // this line disable service worker support temporarily\nexports = ctx.fetch // To enable: import fetch from 'cross-fetch'\nexports.default = ctx.fetch // For TypeScript consumers without esModuleInterop.\nexports.fetch = ctx.fetch // To enable: import {fetch} from 'cross-fetch'\nexports.Headers = ctx.Headers\nexports.Request = ctx.Request\nexports.Response = ctx.Response\nmodule.exports = exports\n","// eslint-disable-next-line no-shadow\nimport fetch from 'cross-fetch';\n\nexport async function fetchText(url) {\n  const result = await fetch(url);\n  if (result.status !== 200) {\n    throw new Error(String(result.status));\n  }\n  return result.text();\n}\n","import { generateMFs } from 'mf-generator';\nimport { MF } from 'mf-parser';\nimport Papa from 'papaparse';\n\nimport { fetchText } from './util/fetchText.js';\n\nexport async function mfFromGoogleSheet(url, options = {}) {\n  let { urlReferences } = options;\n\n  if (urlReferences) {\n    let results = await Promise.all([fetchText(url), fetchText(urlReferences)]);\n    return parse(results[0], results[1]);\n  } else {\n    let result = await fetchText(url);\n    return parse(result);\n  }\n\n  async function parse(tsv, tsvReferences) {\n    let parsed = Papa.parse(tsv, {\n      delimiter: '\\t',\n      header: true,\n    });\n    let fields = parsed.meta.fields;\n    let infoFields = fields.filter(\n      (a) =>\n        !['mf', 'modif', 'ESI', 'MALDI', 'positive', 'negative'].includes(a),\n    );\n    let formulas = parsed.data;\n    let references = {};\n    if (tsvReferences) {\n      let referencesArray = Papa.parse(tsvReferences, {\n        delimiter: '\\t',\n        header: true,\n      }).data;\n\n      referencesArray.forEach((r) => {\n        references[r.label] = r;\n      });\n    }\n\n    let results = [];\n    for (let formula of formulas) {\n      if (tsvReferences) {\n        // we add references\n        let refs = formula.references.split(/[ ,]+/);\n        formula.references = [];\n        for (let ref of refs) {\n          formula.references.push(references[ref]);\n        }\n      }\n      // we need to calculate all the possibilities\n      try {\n        let mfs = await generateMFs([formula.mf], {\n          ionizations: formula.modif,\n        });\n        for (let mf of mfs) {\n          mf.info = {};\n          for (let infoField of infoFields) {\n            mf.info[infoField] = formula[infoField];\n          }\n          if (\n            !formula.ESI &&\n            !formula.MALDI &&\n            !formula.positive &&\n            !formula.negative\n          ) {\n            mf.filter = {\n              ESI: true,\n              MALDI: true,\n              positive: true,\n              negative: true,\n            };\n          } else {\n            mf.filter = {\n              ESI: formula.ESI === 'X',\n              MALDI: formula.MALDI === 'X',\n              positive: formula.positive === 'X',\n              negative: formula.negative === 'X',\n            };\n          }\n          mf.mf = new MF(mf.mf).toMF();\n          results.push(mf);\n        }\n      } catch (e) {\n        // eslint-disable-next-line no-console\n        console.warn(\n          'Non parsable molecular formula: ',\n          formula.mf,\n          formula.modif,\n          e.toString(),\n        );\n      }\n    }\n\n    results = results.filter((a) => {\n      return a.ms.em !== 0;\n    });\n\n    results.sort((a, b) => {\n      return a.ms.em - b.ms.em;\n    });\n\n    let uniqueResults = [results[0]];\n    for (let i = 1; i < results.length; i++) {\n      if (results[i - 1].ms.em !== results[i].ms.em) {\n        uniqueResults.push(results[i]);\n      }\n    }\n\n    return uniqueResults;\n  }\n}\n","import { mfFromGoogleSheet } from 'mf-from-google-sheet';\n\nexport async function loadGoogleSheet(options = {}) {\n  let {\n    refUUID = '1C_H9aiJyu9M9in7sHMOaz-d3Sv758rE72oLxEKH9ioA',\n    uuid = '1LrJCl9-xSZKhGA9Y8nKVkYwB-mEOHBkTXg5qYXeFpZY',\n  } = options;\n  if (options.uuid && !options.refUUID) refUUID = '';\n\n  let url = `https://googledocs.cheminfo.org/spreadsheets/d/${uuid}/export?format=tsv`;\n  let refURL = refUUID\n    ? `https://googledocs.cheminfo.org/spreadsheets/d/${refUUID}/export?format=tsv`\n    : '';\n  let data = await mfFromGoogleSheet(url, refURL);\n  data.sort((a, b) => a.em - b.em);\n  return data;\n}\n","import JSZip from 'jszip';\n\nimport { fetchArrayBuffer } from './util/fetchArrayBuffer';\n\nconst loadingPromises = {};\n\nexport async function loadKnapSack(options = {}) {\n  const {\n    url = 'https://couch.cheminfo.org/cheminfo-public/d2eb480198c80275a1d05dd3609414f9/upload/ms.zip',\n  } = options;\n\n  if (!loadingPromises[url]) {\n    loadingPromises[url] = fetchArrayBuffer(url);\n  }\n  const buffer = await loadingPromises[url];\n\n  const jsZip = new JSZip();\n  let zip = await jsZip.loadAsync(buffer);\n  let fileData = await zip.files['ms.json'].async('string');\n  let data = JSON.parse(fileData);\n\n  data.forEach((d) => {\n    d.url = `http://kanaya.naist.jp/knapsack_jsp/information.jsp?word=${d.id}`;\n  });\n\n  data.sort((a, b) => a.em - b.em);\n\n  return data;\n}\n","import { generalMatcher } from 'mf-matcher';\n\n/**\n    Searching by various criteria. This mass will not take into account the mass\n    of the mass of the electron\n* @param {object}   [filter={}]\n* @param {number}   [filter.minMW=0] - Minimal molecular weight\n* @param {number}   [filter.maxMW=+Infinity] - Maximal molecular weight\n* @param {number}   [filter.minEM=0] - Minimal monoisotopic mass\n* @param {number}   [filter.maxEM=+Infinity] - Maximal monoisotopic mass\n* @param {number}   [filter.minCharge=-Infinity] - Minimal charge\n* @param {number}   [filter.maxCharge=+Infinity] - Maximal charge\n* @param {object}   [filter.unsaturation={}]\n* @param {number}   [filter.unsaturation.min=-Infinity] - Minimal unsaturation\n* @param {number}   [filter.unsaturation.max=+Infinity] - Maximal unsaturation\n* @param {boolean}   [filter.unsaturation.onlyInteger=false] - Integer unsaturation\n* @param {boolean}   [filter.unsaturation.onlyNonInteger=false] - Non integer unsaturation\n* @param {object}   [filter.atoms] - object of atom:{min, max}\n\n* @param {object}   [options={}]\n* @param {array}    [options.databases] - an array containing the name of the databases so search, by default all\n* @param {boolean}  [options.flatten=false] - should we return the array as a flat result\n*/\n\nexport function search(emdb, filter, options = {}) {\n  let { databases = Object.keys(emdb.databases), flatten = false } = options;\n\n  let results = {};\n  for (let database of databases) {\n    results[database] = emdb.databases[database].filter((entry) =>\n      generalMatcher(entry, filter),\n    );\n  }\n\n  if (flatten) {\n    let flattenResults = [];\n    for (let database of databases) {\n      for (let entry of results[database]) {\n        entry.database = database;\n        flattenResults.push(entry);\n      }\n    }\n    return flattenResults;\n  } else {\n    return results;\n  }\n}\n","import { msemMatcher } from 'mf-matcher';\nimport { preprocessIonizations } from 'mf-utilities';\n\n/**\nSearch for an experimental monoisotopic mass\n* @param {number}   msem - The observed monoisotopic mass\n* @param {object}   [options={}]\n* @param {array}    [options.databases] - an array containing the name of the databases so search, by default all\n* @param {boolean}  [options.flatten=false] - should we return the array as a flat result\n* @param {string}   [options.ionizations] - list the allowed ionizations possibilities\n* @param {object}   [options.filter={}]\n* @param {number}   [options.filter.targetMass] - Target mass, allows to calculate error and filter results\n* @param {number[]} [options.filter.targetMasses] - Target masses: SORTED array of numbers\n* @param {number[]} [options.filter.targetIntensities] - Target intensities: SORTED array of numbers\n* @param {number}   [options.filter.minEM=0] - Minimal monoisotopic mass\n* @param {number}         [options.filter.maxEM=+Infinity] - Maximal monoisotopic mass\n* @param {number}         [options.filter.minMSEM=0] - Minimal monoisotopic mass observed by mass\n* @param {number}         [options.filter.maxMSEM=+Infinity] - Maximal monoisotopic mass observed by mass\n* @param {number}         [options.filter.minCharge=-Infinity] - Minimal charge\n* @param {number}         [options.filter.maxCharge=+Infinity] - Maximal charge\n* @param {object}         [options.filter.unsaturation={}]\n* @param {number}         [options.filter.unsaturation.min=-Infinity] - Minimal unsaturation\n* @param {number}         [options.filter.unsaturation.max=+Infinity] - Maximal unsaturation\n* @param {boolean}         [options.filter.unsaturation.onlyInteger=false] - Integer unsaturation\n* @param {boolean}         [options.filter.unsaturation.onlyNonInteger=false] - Non integer unsaturation\n* @param {object}         [options.filter.callback] - a function to filter the MF\n* @param {object}         [options.filter.atoms] - object of atom:{min, max}\n*/\n\nexport function searchMSEM(emdb, msem, options = {}) {\n  let filter = { ...(options.filter || {}), targetMass: msem };\n  let { databases = Object.keys(emdb.databases), flatten = false } = options;\n\n  let ionizations = preprocessIonizations(options.ionizations);\n  let results = {};\n  for (let database of databases) {\n    results[database] = [];\n  }\n  for (let ionization of ionizations) {\n    filter.ionization = ionization;\n    for (let database of databases) {\n      for (let entry of emdb.databases[database]) {\n        let match = msemMatcher(entry, filter);\n        if (match) {\n          results[database].push({\n            ...entry,\n            ms: match.ms,\n            ionization: match.ionization,\n          });\n        }\n      }\n    }\n  }\n  if (flatten) {\n    let flattenResults = [];\n    for (let database of databases) {\n      for (let entry of results[database]) {\n        entry.database = database;\n        flattenResults.push(entry);\n      }\n    }\n    flattenResults.sort((a, b) => Math.abs(a.ms.ppm) - Math.abs(b.ms.ppm));\n    return flattenResults;\n  } else {\n    Object.keys(results).forEach((k) =>\n      results[k].sort((a, b) => Math.abs(a.ms.ppm) - Math.abs(b.ms.ppm)),\n    );\n    return results;\n  }\n}\n","export const GAUSSIAN_EXP_FACTOR = -4 * Math.LN2;\nexport const ROOT_PI_OVER_LN2 = Math.sqrt(Math.PI / Math.LN2);\nexport const ROOT_THREE = Math.sqrt(3);\nexport const ROOT_2LN2 = Math.sqrt(2 * Math.LN2);\nexport const ROOT_2LN2_MINUS_ONE = Math.sqrt(2 * Math.LN2) - 1;\n//# sourceMappingURL=constants.js.map","// https://en.wikipedia.org/wiki/Error_function#Inverse_functions\n// This code yields to a good approximation\n// If needed a better implementation using polynomial can be found on https://en.wikipedia.org/wiki/Error_function#Inverse_functions\nexport default function erfinv(x) {\n    let a = 0.147;\n    if (x === 0)\n        return 0;\n    let ln1MinusXSqrd = Math.log(1 - x * x);\n    let lnEtcBy2Plus2 = ln1MinusXSqrd / 2 + 2 / (Math.PI * a);\n    let firstSqrt = Math.sqrt(lnEtcBy2Plus2 ** 2 - ln1MinusXSqrd / a);\n    let secondSqrt = Math.sqrt(firstSqrt - lnEtcBy2Plus2);\n    return secondSqrt * (x > 0 ? 1 : -1);\n}\n//# sourceMappingURL=erfinv.js.map","import { ROOT_2LN2, GAUSSIAN_EXP_FACTOR, ROOT_PI_OVER_LN2, } from '../../../util/constants';\nimport erfinv from '../../../util/erfinv';\nexport class Gaussian {\n    constructor(options = {}) {\n        const { fwhm = 500, sd } = options;\n        this.fwhm = sd ? gaussianWidthToFWHM(2 * sd) : fwhm;\n    }\n    fwhmToWidth(fwhm = this.fwhm) {\n        return gaussianFwhmToWidth(fwhm);\n    }\n    widthToFWHM(width) {\n        return gaussianWidthToFWHM(width);\n    }\n    fct(x) {\n        return gaussianFct(x, this.fwhm);\n    }\n    getArea(height = calculateGaussianHeight({ fwhm: this.fwhm })) {\n        return getGaussianArea({ fwhm: this.fwhm, height });\n    }\n    getFactor(area) {\n        return getGaussianFactor(area);\n    }\n    getData(options = {}) {\n        return getGaussianData(this, options);\n    }\n    calculateHeight(area = 1) {\n        return calculateGaussianHeight({ fwhm: this.fwhm, area });\n    }\n    getParameters() {\n        return ['fwhm'];\n    }\n}\nexport function calculateGaussianHeight(options) {\n    let { fwhm = 500, area = 1, sd } = options;\n    if (sd)\n        fwhm = gaussianWidthToFWHM(2 * sd);\n    return (2 * area) / ROOT_PI_OVER_LN2 / fwhm;\n}\n/**\n * Calculate the height of the gaussian function of a specific width (fwhm) at a speicifc\n * x position (the gaussian is centered on x=0)\n * @param x\n * @param fwhm\n * @returns y\n */\nexport function gaussianFct(x, fwhm) {\n    return Math.exp(GAUSSIAN_EXP_FACTOR * Math.pow(x / fwhm, 2));\n}\nexport function gaussianWidthToFWHM(width) {\n    return width * ROOT_2LN2;\n}\nexport function gaussianFwhmToWidth(fwhm) {\n    return fwhm / ROOT_2LN2;\n}\nexport function getGaussianArea(options) {\n    let { fwhm = 500, sd, height = 1 } = options;\n    if (sd)\n        fwhm = gaussianWidthToFWHM(2 * sd);\n    return (height * ROOT_PI_OVER_LN2 * fwhm) / 2;\n}\nexport function getGaussianFactor(area = 0.9999) {\n    return Math.sqrt(2) * erfinv(area);\n}\nexport function getGaussianData(shape = {}, options = {}) {\n    let { fwhm = 500, sd } = shape;\n    if (sd)\n        fwhm = gaussianWidthToFWHM(2 * sd);\n    let { length, factor = getGaussianFactor(), height = calculateGaussianHeight({ fwhm }), } = options;\n    if (!length) {\n        length = Math.min(Math.ceil(fwhm * factor), Math.pow(2, 25) - 1);\n        if (length % 2 === 0)\n            length++;\n    }\n    const center = (length - 1) / 2;\n    const data = new Float64Array(length);\n    for (let i = 0; i <= center; i++) {\n        data[i] = gaussianFct(i - center, fwhm) * height;\n        data[length - 1 - i] = data[i];\n    }\n    return data;\n}\n//# sourceMappingURL=Gaussian.js.map","import { ROOT_THREE } from '../../../util/constants';\nexport class Lorentzian {\n    constructor(options = {}) {\n        const { fwhm = 500 } = options;\n        this.fwhm = fwhm;\n    }\n    fwhmToWidth(fwhm = this.fwhm) {\n        return lorentzianFwhmToWidth(fwhm);\n    }\n    widthToFWHM(width) {\n        return lorentzianWidthToFWHM(width);\n    }\n    fct(x) {\n        return lorentzianFct(x, this.fwhm);\n    }\n    getArea(height = 1) {\n        return getLorentzianArea({ fwhm: this.fwhm, height });\n    }\n    getFactor(area) {\n        return getLorentzianFactor(area);\n    }\n    getData(options = {}) {\n        return getLorentzianData(this, options);\n    }\n    calculateHeight(area = 1) {\n        return calculateLorentzianHeight({ fwhm: this.fwhm, area });\n    }\n    getParameters() {\n        return ['fwhm'];\n    }\n}\nexport const calculateLorentzianHeight = ({ fwhm = 1, area = 1 }) => {\n    return (2 * area) / Math.PI / fwhm;\n};\nexport const getLorentzianArea = (options) => {\n    const { fwhm = 500, height = 1 } = options;\n    return (height * Math.PI * fwhm) / 2;\n};\nexport const lorentzianFct = (x, fwhm) => {\n    return fwhm ** 2 / (4 * x ** 2 + fwhm ** 2);\n};\nexport const lorentzianWidthToFWHM = (width) => {\n    return width * ROOT_THREE;\n};\nexport const lorentzianFwhmToWidth = (fwhm) => {\n    return fwhm / ROOT_THREE;\n};\nexport const getLorentzianFactor = (area = 0.9999) => {\n    if (area >= 1) {\n        throw new Error('area should be (0 - 1)');\n    }\n    const halfResidual = (1 - area) * 0.5;\n    const quantileFunction = (p) => Math.tan(Math.PI * (p - 0.5));\n    return ((quantileFunction(1 - halfResidual) - quantileFunction(halfResidual)) / 2);\n};\nexport const getLorentzianData = (shape = {}, options = {}) => {\n    let { fwhm = 500 } = shape;\n    let { length, factor = getLorentzianFactor(), height = calculateLorentzianHeight({ fwhm, area: 1 }), } = options;\n    if (!length) {\n        length = Math.min(Math.ceil(fwhm * factor), Math.pow(2, 25) - 1);\n        if (length % 2 === 0)\n            length++;\n    }\n    const center = (length - 1) / 2;\n    const data = new Float64Array(length);\n    for (let i = 0; i <= center; i++) {\n        data[i] = lorentzianFct(i - center, fwhm) * height;\n        data[length - 1 - i] = data[i];\n    }\n    return data;\n};\n//# sourceMappingURL=Lorentzian.js.map","import { GAUSSIAN_EXP_FACTOR, ROOT_2LN2_MINUS_ONE, ROOT_PI_OVER_LN2, } from '../../../util/constants';\nimport { gaussianFct, getGaussianFactor } from '../gaussian/Gaussian';\nimport { lorentzianFct, getLorentzianFactor } from '../lorentzian/Lorentzian';\nexport class PseudoVoigt {\n    constructor(options = {}) {\n        const { fwhm = 500, mu = 0.5 } = options;\n        this.mu = mu;\n        this.fwhm = fwhm;\n    }\n    fwhmToWidth(fwhm = this.fwhm, mu = this.mu) {\n        return pseudoVoigtFwhmToWidth(fwhm, mu);\n    }\n    widthToFWHM(width, mu = this.mu) {\n        return pseudoVoigtWidthToFWHM(width, mu);\n    }\n    fct(x) {\n        return pseudoVoigtFct(x, this.fwhm, this.mu);\n    }\n    getArea(height = 1) {\n        return getPseudoVoigtArea({ fwhm: this.fwhm, height, mu: this.mu });\n    }\n    getFactor(area) {\n        return getPseudoVoigtFactor(area);\n    }\n    getData(options = {}) {\n        const { length, factor, height = calculatePseudoVoigtHeight({\n            fwhm: this.fwhm,\n            mu: this.mu,\n            area: 1,\n        }), } = options;\n        return getPseudoVoigtData(this, { factor, length, height });\n    }\n    calculateHeight(area = 1) {\n        return calculatePseudoVoigtHeight({ fwhm: this.fwhm, mu: this.mu, area });\n    }\n    getParameters() {\n        return ['fwhm', 'mu'];\n    }\n}\nexport const calculatePseudoVoigtHeight = (options = {}) => {\n    let { fwhm = 1, mu = 0.5, area = 1 } = options;\n    return (2 * area) / (fwhm * (mu * ROOT_PI_OVER_LN2 + (1 - mu) * Math.PI));\n};\nexport const pseudoVoigtFct = (x, fwhm, mu) => {\n    return (1 - mu) * lorentzianFct(x, fwhm) + mu * gaussianFct(x, fwhm);\n};\nexport const pseudoVoigtWidthToFWHM = (width, mu = 0.5) => {\n    return width * (mu * ROOT_2LN2_MINUS_ONE + 1);\n};\nexport const pseudoVoigtFwhmToWidth = (fwhm, mu = 0.5) => {\n    return fwhm / (mu * ROOT_2LN2_MINUS_ONE + 1);\n};\nexport const getPseudoVoigtArea = (options) => {\n    const { fwhm = 500, height = 1, mu = 0.5 } = options;\n    return (fwhm * height * (mu * ROOT_PI_OVER_LN2 + (1 - mu) * Math.PI)) / 2;\n};\nexport const getPseudoVoigtFactor = (area = 0.9999, mu = 0.5) => {\n    return mu < 1 ? getLorentzianFactor(area) : getGaussianFactor(area);\n};\nexport const getPseudoVoigtData = (shape = {}, options = {}) => {\n    let { fwhm = 500, mu = 0.5 } = shape;\n    let { length, factor = getPseudoVoigtFactor(0.999, mu), height = calculatePseudoVoigtHeight({ fwhm, mu, area: 1 }), } = options;\n    if (!height) {\n        height =\n            1 /\n                ((mu / Math.sqrt(-GAUSSIAN_EXP_FACTOR / Math.PI)) * fwhm +\n                    ((1 - mu) * fwhm * Math.PI) / 2);\n    }\n    if (!length) {\n        length = Math.min(Math.ceil(fwhm * factor), Math.pow(2, 25) - 1);\n        if (length % 2 === 0)\n            length++;\n    }\n    const center = (length - 1) / 2;\n    const data = new Float64Array(length);\n    for (let i = 0; i <= center; i++) {\n        data[i] = pseudoVoigtFct(i - center, fwhm, mu) * height;\n        data[length - 1 - i] = data[i];\n    }\n    return data;\n};\n//# sourceMappingURL=PseudoVoigt.js.map","import { Gaussian } from './gaussian/Gaussian';\nimport { Lorentzian } from './lorentzian/Lorentzian';\nimport { PseudoVoigt } from './pseudoVoigt/PseudoVoigt';\n/**\n * Generate a instance of a specific kind of shape.\n */\nexport function getShape1D(shape) {\n    const { kind } = shape;\n    switch (kind) {\n        case 'gaussian':\n            return new Gaussian(shape);\n        case 'lorentzian':\n            return new Lorentzian(shape);\n        case 'pseudoVoigt':\n            return new PseudoVoigt(shape);\n        default: {\n            throw Error(`Unknown distribution ${kind}`);\n        }\n    }\n}\n//# sourceMappingURL=getShape1D.js.map","export default function addBaseline(data, baselineFct) {\n    if (!baselineFct)\n        return data;\n    let xs = data.x;\n    let ys = data.y;\n    for (let i = 0; i < xs.length; i++) {\n        ys[i] += baselineFct(xs[i]);\n    }\n    return data;\n}\n//# sourceMappingURL=addBaseline.js.map","const toString = Object.prototype.toString;\n/**\n * Checks if an object is an instance of an Array (array or typed array).\n *\n * @param {any} value - Object to check.\n * @returns {boolean} True if the object is an array.\n */\nexport function isAnyArray(value) {\n    return toString.call(value).endsWith('Array]');\n}\n//# sourceMappingURL=index.js.map","import { isAnyArray } from 'is-any-array';\n/**\n * This function xAdd the first array by the second array or a constant value to each element of the first array\n *\n * @param array1 - the first array\n * @param array2 - the second array or number\n */\nexport function xAdd(array1, array2) {\n    let isConstant = false;\n    let constant = 0;\n    if (isAnyArray(array2)) {\n        if (array1.length !== array2.length) {\n            throw new Error('xAdd: size of array1 and array2 must be identical');\n        }\n    }\n    else {\n        isConstant = true;\n        constant = array2;\n    }\n    let array3 = new Float64Array(array1.length);\n    if (isConstant) {\n        for (let i = 0; i < array1.length; i++) {\n            array3[i] = array1[i] + constant;\n        }\n    }\n    else {\n        for (let i = 0; i < array1.length; i++) {\n            array3[i] = array1[i] + array2[i];\n        }\n    }\n    return array3;\n}\n//# sourceMappingURL=xAdd.js.map","import { isAnyArray } from 'is-any-array';\n/**\n * Checks if input is of type array\n *\n * @param input - input\n */\nexport function xCheck(input) {\n    if (!isAnyArray(input)) {\n        throw new TypeError('input must be an array');\n    }\n    if (input.length === 0) {\n        throw new TypeError('input must not be empty');\n    }\n}\n//# sourceMappingURL=xCheck.js.map","/**\n * Returns the closest index of a `target`\n *\n * @param array - array of numbers\n * @param target - target\n * @returns - closest index\n */\nexport function xFindClosestIndex(array, target, options = {}) {\n    const { sorted = true } = options;\n    if (sorted) {\n        let low = 0;\n        let high = array.length - 1;\n        let middle = 0;\n        while (high - low > 1) {\n            middle = low + ((high - low) >> 1);\n            if (array[middle] < target) {\n                low = middle;\n            }\n            else if (array[middle] > target) {\n                high = middle;\n            }\n            else {\n                return middle;\n            }\n        }\n        if (low < array.length - 1) {\n            if (Math.abs(target - array[low]) < Math.abs(array[low + 1] - target)) {\n                return low;\n            }\n            else {\n                return low + 1;\n            }\n        }\n        else {\n            return low;\n        }\n    }\n    else {\n        let index = 0;\n        let diff = Number.POSITIVE_INFINITY;\n        for (let i = 0; i < array.length; i++) {\n            const currentDiff = Math.abs(array[i] - target);\n            if (currentDiff < diff) {\n                diff = currentDiff;\n                index = i;\n            }\n        }\n        return index;\n    }\n}\n//# sourceMappingURL=xFindClosestIndex.js.map","import { xFindClosestIndex } from './xFindClosestIndex';\n/**\n * Returns an object with {fromIndex, toIndex} for a specific from / to\n *\n * @param x - array of numbers\n * @param options - Options\n */\nexport function xGetFromToIndex(x, options = {}) {\n    let { fromIndex, toIndex, from, to } = options;\n    if (fromIndex === undefined) {\n        if (from !== undefined) {\n            fromIndex = xFindClosestIndex(x, from);\n        }\n        else {\n            fromIndex = 0;\n        }\n    }\n    if (toIndex === undefined) {\n        if (to !== undefined) {\n            toIndex = xFindClosestIndex(x, to);\n        }\n        else {\n            toIndex = x.length - 1;\n        }\n    }\n    if (fromIndex < 0)\n        fromIndex = 0;\n    if (toIndex < 0)\n        toIndex = 0;\n    if (fromIndex >= x.length)\n        fromIndex = x.length - 1;\n    if (toIndex >= x.length)\n        toIndex = x.length - 1;\n    if (fromIndex > toIndex)\n        [fromIndex, toIndex] = [toIndex, fromIndex];\n    return { fromIndex, toIndex };\n}\n//# sourceMappingURL=xGetFromToIndex.js.map","import { xCheck } from './xCheck';\nimport { xGetFromToIndex } from './xGetFromToIndex';\n/**\n * Computes the maximal value of an array of values\n *\n * @param array - array of numbers\n * @param options - options\n */\nexport function xMaxValue(array, options = {}) {\n    xCheck(array);\n    const { fromIndex, toIndex } = xGetFromToIndex(array, options);\n    let maxValue = array[fromIndex];\n    for (let i = fromIndex + 1; i <= toIndex; i++) {\n        if (array[i] > maxValue) {\n            maxValue = array[i];\n        }\n    }\n    return maxValue;\n}\n//# sourceMappingURL=xMaxValue.js.map","const LOOP = 8;\nconst FLOAT_MUL = 1 / 16777216;\nconst sh1 = 15;\nconst sh2 = 18;\nconst sh3 = 11;\nfunction multiply_uint32(n, m) {\n    n >>>= 0;\n    m >>>= 0;\n    const nlo = n & 0xffff;\n    const nhi = n - nlo;\n    return (((nhi * m) >>> 0) + nlo * m) >>> 0;\n}\nexport default class XSadd {\n    constructor(seed = Date.now()) {\n        this.state = new Uint32Array(4);\n        this.init(seed);\n        this.random = this.getFloat.bind(this);\n    }\n    /**\n     * Returns a 32-bit integer r (0 <= r < 2^32)\n     */\n    getUint32() {\n        this.nextState();\n        return (this.state[3] + this.state[2]) >>> 0;\n    }\n    /**\n     * Returns a floating point number r (0.0 <= r < 1.0)\n     */\n    getFloat() {\n        return (this.getUint32() >>> 8) * FLOAT_MUL;\n    }\n    init(seed) {\n        if (!Number.isInteger(seed)) {\n            throw new TypeError('seed must be an integer');\n        }\n        this.state[0] = seed;\n        this.state[1] = 0;\n        this.state[2] = 0;\n        this.state[3] = 0;\n        for (let i = 1; i < LOOP; i++) {\n            this.state[i & 3] ^=\n                (i +\n                    multiply_uint32(1812433253, this.state[(i - 1) & 3] ^ ((this.state[(i - 1) & 3] >>> 30) >>> 0))) >>>\n                    0;\n        }\n        this.periodCertification();\n        for (let i = 0; i < LOOP; i++) {\n            this.nextState();\n        }\n    }\n    periodCertification() {\n        if (this.state[0] === 0 &&\n            this.state[1] === 0 &&\n            this.state[2] === 0 &&\n            this.state[3] === 0) {\n            this.state[0] = 88; // X\n            this.state[1] = 83; // S\n            this.state[2] = 65; // A\n            this.state[3] = 68; // D\n        }\n    }\n    nextState() {\n        let t = this.state[0];\n        t ^= t << sh1;\n        t ^= t >>> sh2;\n        t ^= this.state[3] << sh3;\n        this.state[0] = this.state[1];\n        this.state[1] = this.state[2];\n        this.state[2] = this.state[3];\n        this.state[3] = t;\n    }\n}\n","import XSAdd from 'ml-xsadd';\n/**\n * Create a random array of numbers of a specific length\n *\n * @return - array of random floats normally distributed\n */\nlet spare;\nlet hasSpare = false;\nexport function createRandomArray(options = {}) {\n    const { mean = 0, standardDeviation = 1, length = 1000, range = 1, seed, distribution = 'normal', } = options;\n    const generator = new XSAdd(seed);\n    let returnArray = new Float64Array(length);\n    switch (distribution) {\n        case 'normal':\n            for (let i = 0; i < length; i++) {\n                returnArray[i] = generateGaussian(mean, standardDeviation, generator);\n            }\n            break;\n        case 'uniform':\n            for (let i = 0; i < length; i++) {\n                returnArray[i] = (generator.random() - 0.5) * range + mean;\n            }\n            break;\n        default:\n            // eslint-disable-next-line @typescript-eslint/restrict-template-expressions\n            throw new Error(`unknown distribution: ${distribution}`);\n    }\n    return returnArray;\n}\nfunction generateGaussian(mean, standardDeviation, generator) {\n    let val, u, v, s;\n    if (hasSpare) {\n        hasSpare = false;\n        val = spare * standardDeviation + mean;\n    }\n    else {\n        do {\n            u = generator.random() * 2 - 1;\n            v = generator.random() * 2 - 1;\n            s = u * u + v * v;\n        } while (s >= 1 || s === 0);\n        s = Math.sqrt((-2 * Math.log(s)) / s);\n        spare = v * s;\n        hasSpare = true;\n        val = mean + standardDeviation * u * s;\n    }\n    return val;\n}\n//# sourceMappingURL=createRandomArray.js.map","import { xMaxValue, xAdd, createRandomArray } from 'ml-spectra-processing';\nexport default function addNoise(data, options = {}) {\n    const { seed = 0, distribution = 'normal', percent = 1 } = options;\n    const range = (xMaxValue(data.y) * percent) / 100;\n    const noise = createRandomArray({\n        distribution,\n        seed,\n        mean: 0,\n        standardDeviation: range,\n        range,\n        length: data.x.length,\n    });\n    data.y = xAdd(data.y, noise);\n    return data;\n}\n//# sourceMappingURL=addNoise.js.map","import { getShape1D } from 'ml-peak-shape-generator';\nimport addBaseline from './util/addBaseline';\nimport addNoise from './util/addNoise';\nexport class SpectrumGenerator {\n    constructor(options = {}) {\n        const { from = 0, to = 1000, nbPoints = 10001, peakWidthFct, shape = { kind: 'gaussian', fwhm: 5 }, } = options;\n        this.from = from;\n        this.to = to;\n        this.nbPoints = nbPoints;\n        this.interval = (this.to - this.from) / (this.nbPoints - 1);\n        this.peakWidthFct = peakWidthFct;\n        this.maxPeakHeight = Number.MIN_SAFE_INTEGER;\n        this.data = {\n            x: new Float64Array(this.nbPoints),\n            y: new Float64Array(this.nbPoints),\n        };\n        let shapeGenerator = getShape1D(shape);\n        this.shape = shapeGenerator;\n        assertNumber(this.from, 'from');\n        assertNumber(this.to, 'to');\n        assertInteger(this.nbPoints, 'nbPoints');\n        if (this.to <= this.from) {\n            throw new RangeError('to option must be larger than from');\n        }\n        if (this.peakWidthFct && typeof this.peakWidthFct !== 'function') {\n            throw new TypeError('peakWidthFct option must be a function');\n        }\n        this.reset();\n    }\n    /**\n     * Add a series of peaks to the spectrum.\n     * @param peaks - Peaks to add.\n     */\n    addPeaks(peaks, options) {\n        if (!Array.isArray(peaks) &&\n            (typeof peaks !== 'object' ||\n                peaks.x === undefined ||\n                peaks.y === undefined ||\n                !Array.isArray(peaks.x) ||\n                !Array.isArray(peaks.y) ||\n                peaks.x.length !== peaks.y.length)) {\n            throw new TypeError('peaks must be an array or an object containing x[] and y[]');\n        }\n        if (Array.isArray(peaks)) {\n            for (const peak of peaks) {\n                this.addPeak(peak, options);\n            }\n        }\n        else {\n            for (let i = 0; i < peaks.x.length; i++) {\n                this.addPeak([peaks.x[i], peaks.y[i]], options);\n            }\n        }\n    }\n    /**\n     * Add a single peak to the spectrum.\n     * A peak may be either defined as [x,y,fwhm,...] or as {x, y, shape}\n     * @param peak\n     * @param options\n     */\n    addPeak(peak, options = {}) {\n        if (Array.isArray(peak) && peak.length < 2) {\n            throw new Error('peak must be an array with two (or three) values or an object with {x,y,width?}');\n        }\n        if (!Array.isArray(peak) &&\n            (peak.x === undefined || peak.y === undefined)) {\n            throw new Error('peak must be an array with two (or three) values or an object with {x,y,width?}');\n        }\n        let xPosition;\n        let intensity;\n        let peakFWHM;\n        let peakWidth;\n        let peakShapeOptions;\n        if (Array.isArray(peak)) {\n            [xPosition, intensity, peakFWHM, peakShapeOptions] = peak;\n        }\n        else {\n            xPosition = peak.x;\n            intensity = peak.y;\n            peakWidth = peak.width;\n            peakShapeOptions = peak.shape;\n        }\n        if (intensity > this.maxPeakHeight)\n            this.maxPeakHeight = intensity;\n        let { shape: shapeOptions } = options;\n        if (peakShapeOptions) {\n            shapeOptions = shapeOptions\n                ? { ...shapeOptions, ...peakShapeOptions }\n                : peakShapeOptions;\n        }\n        const shape = shapeOptions\n            ? getShape1D(shapeOptions)\n            : Object.assign(Object.create(Object.getPrototypeOf(this.shape)), this.shape);\n        let { width, widthLeft, widthRight } = options;\n        /*\n         if we don't force the fwhm we just take the one from the shape\n         however we have many way to force it:\n         - use [x,y,fwhm]\n         - define `width` that will be converted to fwhm\n         - define `widthLeft` and `widthRight` to define asymmetric peaks\n         - have a callback `peakWidthFct`\n         This should evolve in the future because we will not always have `fwhm`\n         */\n        const fwhm = peakFWHM !== undefined\n            ? peakFWHM\n            : peakWidth\n                ? shape.widthToFWHM(peakWidth)\n                : this.peakWidthFct\n                    ? this.peakWidthFct(xPosition)\n                    : width !== undefined\n                        ? width\n                        : shape.fwhm;\n        if (!widthLeft)\n            widthLeft = fwhm;\n        if (!widthRight)\n            widthRight = fwhm;\n        if (!widthLeft || !widthRight) {\n            throw new Error('Width left or right is undefined or zero');\n        }\n        let factor = options.factor === undefined ? shape.getFactor() : options.factor;\n        const firstValue = xPosition - (widthLeft / 2) * factor;\n        const lastValue = xPosition + (widthRight / 2) * factor;\n        const firstPoint = Math.max(0, Math.floor((firstValue - this.from) / this.interval));\n        const lastPoint = Math.min(this.nbPoints - 1, Math.ceil((lastValue - this.from) / this.interval));\n        const middlePoint = Math.round((xPosition - this.from) / this.interval);\n        // PEAK SHAPE MAY BE ASYMMETRC (widthLeft and widthRight) !\n        // we calculate the left part of the shape\n        shape.fwhm = widthLeft;\n        for (let index = firstPoint; index < Math.max(middlePoint, 0); index++) {\n            this.data.y[index] +=\n                intensity * shape.fct(this.data.x[index] - xPosition);\n        }\n        // we calculate the right part of the gaussian\n        shape.fwhm = widthRight;\n        for (let index = Math.min(middlePoint, lastPoint); index <= lastPoint; index++) {\n            this.data.y[index] +=\n                intensity * shape.fct(this.data.x[index] - xPosition);\n        }\n    }\n    /**\n     * Add a baseline to the spectrum.\n     * @param baselineFct - Mathematical function producing the baseline you want.\n     */\n    addBaseline(baselineFct) {\n        addBaseline(this.data, baselineFct);\n        return this;\n    }\n    /**\n     * Add noise to the spectrum.\n     *\n     * @param percent - Noise's amplitude in percents of the spectrum max value. Default: 1.\n     */\n    addNoise(options) {\n        addNoise(this.data, options);\n        return this;\n    }\n    /**\n     * Get the generated spectrum.\n     */\n    getSpectrum(options = {}) {\n        if (typeof options === 'boolean') {\n            options = { copy: options };\n        }\n        const { copy = true, threshold = 0 } = options;\n        if (threshold) {\n            let minPeakHeight = this.maxPeakHeight * threshold;\n            let x = [];\n            let y = [];\n            for (let i = 0; i < this.data.x.length; i++) {\n                if (this.data.y[i] >= minPeakHeight) {\n                    x.push(this.data.x[i]);\n                    y.push(this.data.y[i]);\n                }\n            }\n            return { x, y };\n        }\n        if (copy) {\n            return {\n                x: this.data.x.slice(),\n                y: this.data.y.slice(),\n            };\n        }\n        else {\n            return this.data;\n        }\n    }\n    /**\n     * Resets the generator with an empty spectrum.\n     */\n    reset() {\n        const spectrum = this.data;\n        for (let i = 0; i < this.nbPoints; i++) {\n            spectrum.x[i] = this.from + i * this.interval;\n        }\n        return this;\n    }\n}\nfunction assertInteger(value, name) {\n    if (!Number.isInteger(value)) {\n        throw new TypeError(`${name} option must be an integer`);\n    }\n}\nfunction assertNumber(value, name) {\n    if (!Number.isFinite(value)) {\n        throw new TypeError(`${name} option must be a number`);\n    }\n}\n/**\n * Generates a spectrum and returns it.\n * @param peaks - List of peaks to put in the spectrum.\n * @param options\n */\nexport function generateSpectrum(peaks, options = {}) {\n    const { generator: generatorOptions, noise, baseline, threshold, peakOptions, } = options;\n    const generator = new SpectrumGenerator(generatorOptions);\n    generator.addPeaks(peaks, peakOptions);\n    if (baseline)\n        generator.addBaseline(baseline);\n    if (noise) {\n        generator.addNoise(noise);\n    }\n    return generator.getSpectrum({\n        threshold,\n    });\n}\n//# sourceMappingURL=SpectrumGenerator.js.map","export function closestPointX(array, target) {\n  let low = 0;\n  let high = array.length - 1;\n  let middle = 0;\n  while (high - low > 1) {\n    middle = low + ((high - low) >> 1);\n    if (array[middle].x < target) {\n      low = middle;\n    } else if (array[middle].x > target) {\n      high = middle;\n    } else {\n      return array[middle];\n    }\n  }\n\n  if (low < array.length - 1) {\n    if (Math.abs(target - array[low].x) < Math.abs(array[low + 1].x - target)) {\n      return array[low];\n    } else {\n      return array[low + 1];\n    }\n  } else {\n    return array[low];\n  }\n}\n","/**\n * Join x values if there are similar\n */\n\nexport function joinX(self, threshold = Number.EPSILON) {\n  // when we join we will use the center of mass\n  if (self.array.length === 0) return [];\n  self.sortX();\n  let current = self.array[0];\n  let result = [current];\n  for (let i = 1; i < self.array.length; i++) {\n    const item = self.array[i];\n    if (item.x - current.x <= threshold) {\n      // weighted sum\n      current.x =\n        (item.y / (current.y + item.y)) * (item.x - current.x) + current.x;\n      current.y += item.y;\n    } else {\n      current = {\n        x: item.x,\n        y: item.y,\n      };\n      if (item.composition) current.composition = item.composition;\n      result.push(current);\n    }\n  }\n  self.array = result;\n  self.ySorted = false;\n  return self;\n}\n","export function multiply(a, b, options = {}) {\n  const { minY = 1e-8, maxLines = 5000, deltaX = 1e-2 } = options;\n  const result = new a.constructor();\n\n  a.sortY();\n  b.sortY();\n\n  for (let entryA of a.array) {\n    for (let entryB of b.array) {\n      let y = entryA.y * entryB.y;\n      if (y > minY) {\n        const composition = calculateComposition(entryA, entryB);\n        if (composition) {\n          result.push({ x: entryA.x + entryB.x, y, composition });\n        } else {\n          result.push({ x: entryA.x + entryB.x, y });\n        }\n      }\n      if (result.length > maxLines * 2) {\n        result.joinX(deltaX);\n        result.topY(maxLines);\n      }\n    }\n  }\n  result.joinX(deltaX);\n  result.topY(maxLines);\n  a.move(result);\n  return a;\n}\n\nfunction calculateComposition(entryA, entryB) {\n  if (!entryA.composition || !entryB.composition) return;\n  let toReturn = {};\n  const keys = [\n    ...new Set(\n      Object.keys(entryA.composition).concat(Object.keys(entryB.composition)),\n    ),\n  ];\n  for (let key of keys) {\n    toReturn[key] =\n      (entryA.composition[key] || 0) + (entryB.composition[key] || 0);\n  }\n  return toReturn;\n}\n","// https://en.wikipedia.org/wiki/Exponentiation_by_squaring\n\nexport function power(a, p, options = {}) {\n  if (p <= 0) throw new Error('power must be larger than 0');\n  if (p === 1) return a;\n  if (p === 2) {\n    return a.square();\n  }\n\n  p--;\n  let base = a.copy(); // linear time\n  while (p !== 0) {\n    if ((p & 1) !== 0) {\n      a.multiply(base, options); // executed <= log2(p) times\n    }\n    p >>= 1;\n    if (p !== 0) base.square(options); // executed <= log2(p) times\n  }\n\n  return a;\n}\n","import { closestPointX } from './utils/closestPointX.js';\nimport { joinX } from './utils/joinX.js';\nimport { multiply } from './utils/multiply.js';\nimport { power } from './utils/power.js';\n\n/**\n * Internal class to deal with isotopic distribution calculations\n */\nexport class Distribution {\n  constructor(array) {\n    if (Array.isArray(array)) {\n      this.array = array;\n      this.xSorted = false;\n      this.ySorted = false;\n    } else {\n      this.array = [];\n      this.xSorted = true;\n      this.ySorted = true;\n    }\n  }\n\n  get length() {\n    return this.array.length;\n  }\n\n  get xs() {\n    return this.array.map((p) => p.x);\n  }\n\n  get ys() {\n    return this.array.map((p) => p.y);\n  }\n\n  get minX() {\n    if (!this.xSorted) this.sortX();\n    return this.array[0].x;\n  }\n\n  get maxX() {\n    if (!this.xSorted) this.sortX();\n    return this.array[this.array.length - 1].x;\n  }\n\n  get minY() {\n    if (!this.ySorted) this.sortY();\n    return this.array[0].y;\n  }\n\n  get maxY() {\n    if (!this.ySorted) this.sortY();\n    return this.array[this.array.length - 1];\n  }\n\n  multiplyY(value) {\n    this.array.forEach((item) => (item.y *= value));\n  }\n\n  setArray(array) {\n    this.array = array;\n    this.xSorted = false;\n    this.ySorted = false;\n  }\n\n  move(other) {\n    this.xSorted = other.xSorted;\n    this.ySorted = other.ySorted;\n    this.array = other.array;\n  }\n\n  push(point) {\n    this.array.push(point);\n\n    this.xSorted = false;\n    this.ySorted = false;\n  }\n\n  sortX() {\n    this.ySorted = false;\n    if (this.xSorted) return this;\n    this.array.sort((a, b) => a.x - b.x);\n    this.xSorted = true;\n    return this;\n  }\n\n  sortY() {\n    this.xSorted = false;\n    if (this.ySorted) return this;\n    this.array.sort((a, b) => b.y - a.y);\n    this.ySorted = true;\n    return this;\n  }\n\n  normalize() {\n    let sum = 0;\n    for (let item of this.array) {\n      sum += item.y;\n    }\n    for (let item of this.array) {\n      item.y /= sum;\n    }\n    return this;\n  }\n\n  topY(limit) {\n    if (!limit) return this;\n    if (this.array.length <= limit) return this;\n    this.sortY();\n    this.array.splice(limit);\n    return this;\n  }\n\n  square(options = {}) {\n    return this.multiply(this, options);\n  }\n\n  multiply(b, options) {\n    return multiply(this, b, options);\n  }\n\n  power(p, options) {\n    return power(this, p, options);\n  }\n\n  copy() {\n    let distCopy = new Distribution();\n    distCopy.xSorted = this.xSorted;\n    distCopy.ySorted = this.ySorted;\n    distCopy.array = JSON.parse(JSON.stringify(this.array));\n    return distCopy;\n  }\n\n  maxToOne() {\n    if (this.array.length === 0) return this;\n    let currentMax = this.array[0].y;\n    for (let item of this.array) {\n      if (item.y > currentMax) currentMax = item.y;\n    }\n    for (let item of this.array) {\n      item.y /= currentMax;\n    }\n    return this;\n  }\n\n  joinX(threshold) {\n    return joinX(this, threshold);\n  }\n\n  append(distribution) {\n    for (let item of distribution.array) {\n      this.array.push(item);\n    }\n    this.xSorted = false;\n    this.ySorted = false;\n  }\n\n  closestPointX(target) {\n    this.sortX();\n    return closestPointX(this.array, target);\n  }\n}\n","import { stableIsotopesObject } from 'chemical-elements';\nimport { subscript, superscript } from 'mf-parser';\n\nexport function getDerivedCompositionInfo(composition) {\n  const shortComposition = {};\n  let label = '';\n  let shortLabel = '';\n  for (let key in composition) {\n    let isotopeLabel = '';\n    for (let i = 0; i < key.length; i++) {\n      if (superscript[key[i]]) {\n        isotopeLabel += superscript[key[i]];\n      } else {\n        isotopeLabel += key[i];\n      }\n    }\n    if (composition[key] > 1) {\n      const number = String(composition[key]);\n      for (let i = 0; i < number.length; i++) {\n        isotopeLabel += subscript[number[i]];\n      }\n    }\n    label += isotopeLabel;\n    if (stableIsotopesObject[key].mostAbundant) continue;\n    shortLabel += isotopeLabel;\n    shortComposition[key] = composition[key];\n  }\n\n  return { label, shortComposition, shortLabel };\n}\n","import { ELECTRON_MASS } from 'chemical-elements';\nimport { MF } from 'mf-parser';\nimport { preprocessIonizations, getMsInfo } from 'mf-utilities';\nimport { SpectrumGenerator } from 'spectrum-generator';\n\nimport { Distribution } from './Distribution';\nimport { getDerivedCompositionInfo } from './utils/getDerivedCompositionInfo';\n\nconst MINIMAL_FWHM = 1e-8;\n\n/**\n * An object containing two arrays\n * @typedef {object} XY\n * @property {number[]} x - The x array\n * @property {number[]} y - The y array\n */\n\n/**\n * A class that allows to manage isotopic distribution\n */\nexport class IsotopicDistribution {\n  /**\n   * Class that manage isotopic distribution\n   * @param {string|array} value - Molecular formula or an array of parts\n   * @param {object} [options={}]\n   * @param {string} [options.ionizations=''] - string containing a comma separated list of modifications\n   * @param {number} [options.fwhm=0.01] - Amount of Dalton under which 2 peaks are joined\n   * @param {number} [options.maxLines=5000] - Maximal number of lines during calculations\n   * @param {number} [options.minY=1e-8] - Minimal signal height during calculations\n   * @param {number} [options.ensureCase=false] - Ensure uppercase / lowercase\n   * @param {number} [options.allowNeutral=true] - Should we keep the distribution if the molecule has no charge\n   */\n\n  constructor(value, options = {}) {\n    if (Array.isArray(value)) {\n      this.parts = JSON.parse(JSON.stringify(value));\n      for (let part of this.parts) {\n        part.confidence = 0;\n        part.isotopesInfo = new MF(\n          `${part.mf}(${part.ionization.mf})`,\n        ).getIsotopesInfo();\n      }\n    } else {\n      let mf = new MF(value, { ensureCase: options.ensureCase });\n      let mfInfo = mf.getInfo();\n      let ionizations = preprocessIonizations(options.ionizations);\n      let parts = mfInfo.parts || [mfInfo];\n      this.parts = [];\n      for (let partOriginal of parts) {\n        // we calculate information for each part\n        for (const ionization of ionizations) {\n          let part = JSON.parse(JSON.stringify(partOriginal));\n          part.em = part.monoisotopicMass; // TODO: To remove !!! we change the name !?\n          part.isotopesInfo = new MF(\n            `${part.mf}(${ionization.mf})`,\n          ).getIsotopesInfo();\n          part.confidence = 0;\n          let msInfo = getMsInfo(part, {\n            ionization,\n          });\n          part.ionization = msInfo.ionization;\n          part.ms = msInfo.ms;\n          this.parts.push(part);\n        }\n      }\n    }\n\n    this.cachedDistribution = undefined;\n    this.fwhm = options.fwhm === undefined ? 0.01 : options.fwhm;\n    // if fwhm is under 1e-8 there are some artifacts in the spectra\n    if (this.fwhm < MINIMAL_FWHM) this.fwhm = MINIMAL_FWHM;\n    this.minY = options.minY === undefined ? MINIMAL_FWHM : options.minY;\n    this.maxLines = options.maxLines || 5000;\n    this.allowNeutral =\n      options.allowNeutral === undefined ? true : options.allowNeutral;\n  }\n\n  getParts() {\n    return this.parts;\n  }\n\n  /**\n   * @return {Distribution} returns the total distribution (for all parts)\n   */\n  getDistribution() {\n    if (this.cachedDistribution) return this.cachedDistribution;\n    let options = {\n      maxLines: this.maxLines,\n      minY: this.minY,\n      deltaX: this.fwhm,\n    };\n    let finalDistribution = new Distribution();\n    this.confidence = 0;\n    // TODO need to cache each part without ionization\n    // in case of many ionization we don't need to recalculate everything !\n    for (let part of this.parts) {\n      let totalDistribution = new Distribution([\n        {\n          x: 0,\n          y: 1,\n          composition: this.fwhm === MINIMAL_FWHM ? {} : undefined,\n        },\n      ]);\n      let charge = part.ms.charge;\n      let absoluteCharge = Math.abs(charge);\n      if (charge || this.allowNeutral) {\n        for (let isotope of part.isotopesInfo.isotopes) {\n          if (isotope.number < 0) return { array: [] };\n          if (isotope.number > 0) {\n            const newDistribution = JSON.parse(\n              JSON.stringify(isotope.distribution),\n            );\n            if (this.fwhm === MINIMAL_FWHM) {\n              // add composition\n              for (const entry of newDistribution) {\n                entry.composition = { [Math.round(entry.x) + isotope.atom]: 1 };\n              }\n            }\n            let distribution = new Distribution(newDistribution);\n            distribution.power(isotope.number, options);\n            totalDistribution.multiply(distribution, options);\n          }\n        }\n        this.confidence += totalDistribution.array.reduce(\n          (sum, value) => sum + value.y,\n          0,\n        );\n\n        // we finally deal with the charge\n\n        if (charge) {\n          totalDistribution.array.forEach((e) => {\n            e.x = (e.x - ELECTRON_MASS * charge) / absoluteCharge;\n          });\n        }\n\n        if (totalDistribution.array && totalDistribution.array.length > 0) {\n          totalDistribution.sortX();\n          part.fromX = totalDistribution.array[0].x;\n          part.toX =\n            totalDistribution.array[totalDistribution.array.length - 1].x;\n        }\n\n        if (\n          part.ms.target &&\n          part.ms.target.intensity &&\n          part.ms.target.intensity !== 1\n        ) {\n          // intensity is the value of the monoisotopic mass !\n          // need to find the intensity of the peak corresponding\n          // to the monoisotopic mass\n          if (part.ms.target.mass) {\n            let target = totalDistribution.closestPointX(part.ms.target.mass);\n            totalDistribution.multiplyY(part.ms.target.intensity / target.y);\n          } else {\n            totalDistribution.multiplyY(part.ms.target.intensity);\n          }\n        } else if (part.intensity && part.intensity !== 1) {\n          totalDistribution.multiplyY(part.intensity);\n        }\n\n        part.isotopicDistribution = totalDistribution.array;\n\n        if (finalDistribution.array.length === 0) {\n          finalDistribution = totalDistribution;\n        } else {\n          finalDistribution.append(totalDistribution);\n        }\n      }\n    }\n    if (finalDistribution) finalDistribution.joinX(this.fwhm);\n\n    for (let entry of finalDistribution.array) {\n      if (!entry.composition) continue;\n      Object.assign(entry, getDerivedCompositionInfo(entry.composition));\n    }\n\n    this.confidence /= this.parts.length;\n    this.cachedDistribution = finalDistribution;\n    return finalDistribution;\n  }\n\n  getCSV() {\n    return this.getText({ delimiter: ', ' });\n  }\n\n  getTSV() {\n    return this.getText({ delimiter: '\\t' });\n  }\n\n  getTable(options = {}) {\n    const { maxValue, xLabel = 'x', yLabel = 'y' } = options;\n    let points = this.getDistribution().array;\n    let factor = 1;\n    if (maxValue) {\n      let maxY = this.getMaxY(points);\n      factor = maxValue / maxY;\n    }\n    return points.map((point) => {\n      let newPoint = {};\n      newPoint[xLabel] = point.x;\n      newPoint[yLabel] = point.y * factor;\n      return newPoint;\n    });\n  }\n\n  getText(options = {}) {\n    const { delimiter = '\\t', numberDecimals = 3 } = options;\n    let points = this.getDistribution().array;\n    let csv = [];\n    for (let point of points) {\n      csv.push(\n        `${point.x.toFixed(5)}${delimiter}${(point.y * 100).toFixed(\n          numberDecimals,\n        )}`,\n      );\n    }\n    return csv.join('\\n');\n  }\n\n  getMaxY(points) {\n    let maxY = points[0].y;\n    for (let point of points) {\n      if (point.y > maxY) maxY = point.y;\n    }\n    return maxY;\n  }\n\n  getSumY(points) {\n    let sumY = 0;\n    for (let point of points) {\n      sumY += point.y;\n    }\n    return sumY;\n  }\n\n  /**\n   * Returns the isotopic distribution as an array of peaks\n   * @param {object} [options={}]\n   * @param {number} [options.maxValue=100]\n   * @param {number} [options.sumValue] // if sumValue is defined, maxValue is ignored\n   * @return {Array<any>} an object containing at least the 2 properties: x:[] and y:[]\n   */\n  getPeaks(options = {}) {\n    const { maxValue = 100, sumValue } = options;\n    let peaks = this.getDistribution().array;\n    if (peaks.length === 0) return [];\n    let factor = 1;\n    if (sumValue) {\n      let sumY = this.getSumY(peaks);\n      factor = sumY / sumValue;\n    } else if (maxValue) {\n      let maxY = this.getMaxY(peaks);\n      factor = maxY / maxValue;\n    }\n    if (factor !== 1) {\n      // we need to copy the array because we prefer no side effects\n      peaks = JSON.parse(JSON.stringify(peaks));\n      for (const peak of peaks) {\n        peak.y = peak.y / factor;\n      }\n    }\n    return peaks;\n  }\n\n  /**\n   * Returns the isotopic distirubtion\n   * @param {object} [options={}]\n   * @param {number} [options.maxValue=100]\n   * @param {number} [options.sumValue] // if sumValue is defined, maxValue is ignored\n   * @return {XY} an object containing at least the 2 properties: x:[] and y:[]\n   */\n  getXY(options = {}) {\n    let peaks = this.getPeaks(options);\n\n    if (peaks.length === 0) {\n      return { x: [], y: [] };\n    }\n\n    const result = {\n      x: peaks.map((a) => a.x),\n      y: peaks.map((a) => a.y),\n    };\n\n    for (let key of Object.keys(peaks[0]).filter(\n      (k) => k !== 'x' && k !== 'y',\n    )) {\n      result[key] = peaks.map((a) => a[key]);\n    }\n\n    return result;\n  }\n\n  /**\n   * Returns the isotopic distribution as the sum of gaussian\n   * @param {object} [options={}]\n   * @param {number} [options.gaussianWidth=10]\n   * @param {number} [options.threshold=0.00001] // minimal height to return point\n   * @param {number} [options.maxLength=1e6] // minimal height to return point\n   * @param {number} [options.maxValue] // rescale Y to reach maxValue\n   * @param {function} [options.peakWidthFct=(mz)=>(this.fwhm)]\n   * @return {XY} isotopic distribution as an object containing 2 properties: x:[] and y:[]\n   */\n\n  getGaussian(options = {}) {\n    const {\n      peakWidthFct = () => this.fwhm,\n      threshold = 0.00001,\n      gaussianWidth = 10,\n      maxValue,\n      maxLength = 1e6,\n    } = options;\n\n    let points = this.getTable({ maxValue });\n    if (points.length === 0) return { x: [], y: [] };\n    const from = Math.floor(options.from || points[0].x - 2);\n    const to = Math.ceil(options.to || points[points.length - 1].x + 2);\n    const nbPoints = Math.round(((to - from) * gaussianWidth) / this.fwhm + 1);\n    if (nbPoints > maxLength) {\n      throw Error(\n        `Number of points is over the maxLength: ${nbPoints}>${maxLength}`,\n      );\n    }\n    let gaussianOptions = {\n      from,\n      to,\n      nbPoints,\n      peakWidthFct,\n    };\n\n    let spectrumGenerator = new SpectrumGenerator(gaussianOptions);\n    for (let point of points) {\n      spectrumGenerator.addPeak([point.x, point.y]);\n    }\n    let spectrum = spectrumGenerator.getSpectrum({ threshold });\n    return spectrum;\n  }\n}\n","export function calculateOverlapFromDiff(diffs) {\n    if (diffs[1].length === 0)\n        return 0;\n    let sumPos = 0;\n    for (let i = 0; i < diffs[1].length; i++) {\n        sumPos += Math.abs(diffs[1][i]);\n    }\n    return 1 - sumPos;\n}\n//# sourceMappingURL=calculateOverlapFromDiff.js.map","/**\n * This code requires the use of an array like  [[x1,y1],[x2,y2], ...]\n * If it is not the right format, we will just convert it\n * Otherwise we return the correct format\n * @param {Peaks} peaks\n * @returns [number[], number[]]\n */\nexport function checkPeaks(peaks) {\n    // if it is already a 2D array of points, we just return them\n    if (Array.isArray(peaks) && Array.isArray(peaks[0]) && peaks.length === 2) {\n        return peaks;\n    }\n    if (Array.isArray(peaks.x) && Array.isArray(peaks.y)) {\n        return [peaks.x, peaks.y];\n    }\n    const x = new Array(peaks.length);\n    const y = new Array(peaks.length);\n    for (let i = 0; i < peaks.length; i++) {\n        x[i] = peaks[i][0];\n        y[i] = peaks[i][1];\n    }\n    return [x, y];\n}\n//# sourceMappingURL=checkPeaks.js.map","export function extract(array, from, to) {\n    const newArray = [[], []];\n    let j = 0;\n    const length = array[0] ? array[0].length : 0;\n    for (let i = 0; i < length; i++) {\n        if ((!from || array[0][i] >= from) && (!to || array[0][i] <= to)) {\n            newArray[0][j] = array[0][i];\n            newArray[1][j] = array[1][i];\n            j++;\n        }\n    }\n    return newArray;\n}\n//# sourceMappingURL=extract.js.map","// returns an new array based on array1 where there is a peak of array2 at a distance under width/2\nexport function getCommonArray(array1, array2, width) {\n    const newArray = [[], []];\n    let pos2 = 0;\n    width /= 2;\n    let j = 0;\n    const array1Length = array1[0] ? array1[0].length : 0;\n    const array2Length = array2[0] ? array2[0].length : 0;\n    for (let i = 0; i < array1Length; i++) {\n        while (pos2 < array2Length && array1[0][i] > array2[0][pos2] + width) {\n            pos2++;\n        }\n        if (pos2 < array2Length && array1[0][i] > array2[0][pos2] - width) {\n            newArray[0][j] = array1[0][i];\n            newArray[1][j] = array1[1][i];\n            j++;\n        }\n    }\n    return newArray;\n}\n//# sourceMappingURL=getCommonArray.js.map","'use strict';\n\nfunction compareNumbers(a, b) {\n    return a - b;\n}\n\n/**\n * Computes the sum of the given values\n * @param {Array} values\n * @returns {number}\n */\nexports.sum = function sum(values) {\n    var sum = 0;\n    for (var i = 0; i < values.length; i++) {\n        sum += values[i];\n    }\n    return sum;\n};\n\n/**\n * Computes the maximum of the given values\n * @param {Array} values\n * @returns {number}\n */\nexports.max = function max(values) {\n    var max = values[0];\n    var l = values.length;\n    for (var i = 1; i < l; i++) {\n        if (values[i] > max) max = values[i];\n    }\n    return max;\n};\n\n/**\n * Computes the minimum of the given values\n * @param {Array} values\n * @returns {number}\n */\nexports.min = function min(values) {\n    var min = values[0];\n    var l = values.length;\n    for (var i = 1; i < l; i++) {\n        if (values[i] < min) min = values[i];\n    }\n    return min;\n};\n\n/**\n * Computes the min and max of the given values\n * @param {Array} values\n * @returns {{min: number, max: number}}\n */\nexports.minMax = function minMax(values) {\n    var min = values[0];\n    var max = values[0];\n    var l = values.length;\n    for (var i = 1; i < l; i++) {\n        if (values[i] < min) min = values[i];\n        if (values[i] > max) max = values[i];\n    }\n    return {\n        min: min,\n        max: max\n    };\n};\n\n/**\n * Computes the arithmetic mean of the given values\n * @param {Array} values\n * @returns {number}\n */\nexports.arithmeticMean = function arithmeticMean(values) {\n    var sum = 0;\n    var l = values.length;\n    for (var i = 0; i < l; i++) {\n        sum += values[i];\n    }\n    return sum / l;\n};\n\n/**\n * {@link arithmeticMean}\n */\nexports.mean = exports.arithmeticMean;\n\n/**\n * Computes the geometric mean of the given values\n * @param {Array} values\n * @returns {number}\n */\nexports.geometricMean = function geometricMean(values) {\n    var mul = 1;\n    var l = values.length;\n    for (var i = 0; i < l; i++) {\n        mul *= values[i];\n    }\n    return Math.pow(mul, 1 / l);\n};\n\n/**\n * Computes the mean of the log of the given values\n * If the return value is exponentiated, it gives the same result as the\n * geometric mean.\n * @param {Array} values\n * @returns {number}\n */\nexports.logMean = function logMean(values) {\n    var lnsum = 0;\n    var l = values.length;\n    for (var i = 0; i < l; i++) {\n        lnsum += Math.log(values[i]);\n    }\n    return lnsum / l;\n};\n\n/**\n * Computes the weighted grand mean for a list of means and sample sizes\n * @param {Array} means - Mean values for each set of samples\n * @param {Array} samples - Number of original values for each set of samples\n * @returns {number}\n */\nexports.grandMean = function grandMean(means, samples) {\n    var sum = 0;\n    var n = 0;\n    var l = means.length;\n    for (var i = 0; i < l; i++) {\n        sum += samples[i] * means[i];\n        n += samples[i];\n    }\n    return sum / n;\n};\n\n/**\n * Computes the truncated mean of the given values using a given percentage\n * @param {Array} values\n * @param {number} percent - The percentage of values to keep (range: [0,1])\n * @param {boolean} [alreadySorted=false]\n * @returns {number}\n */\nexports.truncatedMean = function truncatedMean(values, percent, alreadySorted) {\n    if (alreadySorted === undefined) alreadySorted = false;\n    if (!alreadySorted) {\n        values = [].concat(values).sort(compareNumbers);\n    }\n    var l = values.length;\n    var k = Math.floor(l * percent);\n    var sum = 0;\n    for (var i = k; i < (l - k); i++) {\n        sum += values[i];\n    }\n    return sum / (l - 2 * k);\n};\n\n/**\n * Computes the harmonic mean of the given values\n * @param {Array} values\n * @returns {number}\n */\nexports.harmonicMean = function harmonicMean(values) {\n    var sum = 0;\n    var l = values.length;\n    for (var i = 0; i < l; i++) {\n        if (values[i] === 0) {\n            throw new RangeError('value at index ' + i + 'is zero');\n        }\n        sum += 1 / values[i];\n    }\n    return l / sum;\n};\n\n/**\n * Computes the contraharmonic mean of the given values\n * @param {Array} values\n * @returns {number}\n */\nexports.contraHarmonicMean = function contraHarmonicMean(values) {\n    var r1 = 0;\n    var r2 = 0;\n    var l = values.length;\n    for (var i = 0; i < l; i++) {\n        r1 += values[i] * values[i];\n        r2 += values[i];\n    }\n    if (r2 < 0) {\n        throw new RangeError('sum of values is negative');\n    }\n    return r1 / r2;\n};\n\n/**\n * Computes the median of the given values\n * @param {Array} values\n * @param {boolean} [alreadySorted=false]\n * @returns {number}\n */\nexports.median = function median(values, alreadySorted) {\n    if (alreadySorted === undefined) alreadySorted = false;\n    if (!alreadySorted) {\n        values = [].concat(values).sort(compareNumbers);\n    }\n    var l = values.length;\n    var half = Math.floor(l / 2);\n    if (l % 2 === 0) {\n        return (values[half - 1] + values[half]) * 0.5;\n    } else {\n        return values[half];\n    }\n};\n\n/**\n * Computes the variance of the given values\n * @param {Array} values\n * @param {boolean} [unbiased=true] - if true, divide by (n-1); if false, divide by n.\n * @returns {number}\n */\nexports.variance = function variance(values, unbiased) {\n    if (unbiased === undefined) unbiased = true;\n    var theMean = exports.mean(values);\n    var theVariance = 0;\n    var l = values.length;\n\n    for (var i = 0; i < l; i++) {\n        var x = values[i] - theMean;\n        theVariance += x * x;\n    }\n\n    if (unbiased) {\n        return theVariance / (l - 1);\n    } else {\n        return theVariance / l;\n    }\n};\n\n/**\n * Computes the standard deviation of the given values\n * @param {Array} values\n * @param {boolean} [unbiased=true] - if true, divide by (n-1); if false, divide by n.\n * @returns {number}\n */\nexports.standardDeviation = function standardDeviation(values, unbiased) {\n    return Math.sqrt(exports.variance(values, unbiased));\n};\n\nexports.standardError = function standardError(values) {\n    return exports.standardDeviation(values) / Math.sqrt(values.length);\n};\n\n/**\n * IEEE Transactions on biomedical engineering, vol. 52, no. 1, january 2005, p. 76-\n * Calculate the standard deviation via the Median of the absolute deviation\n *  The formula for the standard deviation only holds for Gaussian random variables.\n * @returns {{mean: number, stdev: number}}\n */\nexports.robustMeanAndStdev = function robustMeanAndStdev(y) {\n    var mean = 0, stdev = 0;\n    var length = y.length, i = 0;\n    for (i = 0; i < length; i++) {\n        mean += y[i];\n    }\n    mean /= length;\n    var averageDeviations = new Array(length);\n    for (i = 0; i < length; i++)\n        averageDeviations[i] = Math.abs(y[i] - mean);\n    averageDeviations.sort(compareNumbers);\n    if (length % 2 === 1) {\n        stdev = averageDeviations[(length - 1) / 2] / 0.6745;\n    } else {\n        stdev = 0.5 * (averageDeviations[length / 2] + averageDeviations[length / 2 - 1]) / 0.6745;\n    }\n\n    return {\n        mean: mean,\n        stdev: stdev\n    };\n};\n\nexports.quartiles = function quartiles(values, alreadySorted) {\n    if (typeof (alreadySorted) === 'undefined') alreadySorted = false;\n    if (!alreadySorted) {\n        values = [].concat(values).sort(compareNumbers);\n    }\n\n    var quart = values.length / 4;\n    var q1 = values[Math.ceil(quart) - 1];\n    var q2 = exports.median(values, true);\n    var q3 = values[Math.ceil(quart * 3) - 1];\n\n    return {q1: q1, q2: q2, q3: q3};\n};\n\nexports.pooledStandardDeviation = function pooledStandardDeviation(samples, unbiased) {\n    return Math.sqrt(exports.pooledVariance(samples, unbiased));\n};\n\nexports.pooledVariance = function pooledVariance(samples, unbiased) {\n    if (typeof (unbiased) === 'undefined') unbiased = true;\n    var sum = 0;\n    var length = 0, l = samples.length;\n    for (var i = 0; i < l; i++) {\n        var values = samples[i];\n        var vari = exports.variance(values);\n\n        sum += (values.length - 1) * vari;\n\n        if (unbiased)\n            length += values.length - 1;\n        else\n            length += values.length;\n    }\n    return sum / length;\n};\n\nexports.mode = function mode(values) {\n    var l = values.length,\n        itemCount = new Array(l),\n        i;\n    for (i = 0; i < l; i++) {\n        itemCount[i] = 0;\n    }\n    var itemArray = new Array(l);\n    var count = 0;\n\n    for (i = 0; i < l; i++) {\n        var index = itemArray.indexOf(values[i]);\n        if (index >= 0)\n            itemCount[index]++;\n        else {\n            itemArray[count] = values[i];\n            itemCount[count] = 1;\n            count++;\n        }\n    }\n\n    var maxValue = 0, maxIndex = 0;\n    for (i = 0; i < count; i++) {\n        if (itemCount[i] > maxValue) {\n            maxValue = itemCount[i];\n            maxIndex = i;\n        }\n    }\n\n    return itemArray[maxIndex];\n};\n\nexports.covariance = function covariance(vector1, vector2, unbiased) {\n    if (typeof (unbiased) === 'undefined') unbiased = true;\n    var mean1 = exports.mean(vector1);\n    var mean2 = exports.mean(vector2);\n\n    if (vector1.length !== vector2.length)\n        throw 'Vectors do not have the same dimensions';\n\n    var cov = 0, l = vector1.length;\n    for (var i = 0; i < l; i++) {\n        var x = vector1[i] - mean1;\n        var y = vector2[i] - mean2;\n        cov += x * y;\n    }\n\n    if (unbiased)\n        return cov / (l - 1);\n    else\n        return cov / l;\n};\n\nexports.skewness = function skewness(values, unbiased) {\n    if (typeof (unbiased) === 'undefined') unbiased = true;\n    var theMean = exports.mean(values);\n\n    var s2 = 0, s3 = 0, l = values.length;\n    for (var i = 0; i < l; i++) {\n        var dev = values[i] - theMean;\n        s2 += dev * dev;\n        s3 += dev * dev * dev;\n    }\n    var m2 = s2 / l;\n    var m3 = s3 / l;\n\n    var g = m3 / (Math.pow(m2, 3 / 2.0));\n    if (unbiased) {\n        var a = Math.sqrt(l * (l - 1));\n        var b = l - 2;\n        return (a / b) * g;\n    } else {\n        return g;\n    }\n};\n\nexports.kurtosis = function kurtosis(values, unbiased) {\n    if (typeof (unbiased) === 'undefined') unbiased = true;\n    var theMean = exports.mean(values);\n    var n = values.length, s2 = 0, s4 = 0;\n\n    for (var i = 0; i < n; i++) {\n        var dev = values[i] - theMean;\n        s2 += dev * dev;\n        s4 += dev * dev * dev * dev;\n    }\n    var m2 = s2 / n;\n    var m4 = s4 / n;\n\n    if (unbiased) {\n        var v = s2 / (n - 1);\n        var a = (n * (n + 1)) / ((n - 1) * (n - 2) * (n - 3));\n        var b = s4 / (v * v);\n        var c = ((n - 1) * (n - 1)) / ((n - 2) * (n - 3));\n\n        return a * b - 3 * c;\n    } else {\n        return m4 / (m2 * m2) - 3;\n    }\n};\n\nexports.entropy = function entropy(values, eps) {\n    if (typeof (eps) === 'undefined') eps = 0;\n    var sum = 0, l = values.length;\n    for (var i = 0; i < l; i++)\n        sum += values[i] * Math.log(values[i] + eps);\n    return -sum;\n};\n\nexports.weightedMean = function weightedMean(values, weights) {\n    var sum = 0, l = values.length;\n    for (var i = 0; i < l; i++)\n        sum += values[i] * weights[i];\n    return sum;\n};\n\nexports.weightedStandardDeviation = function weightedStandardDeviation(values, weights) {\n    return Math.sqrt(exports.weightedVariance(values, weights));\n};\n\nexports.weightedVariance = function weightedVariance(values, weights) {\n    var theMean = exports.weightedMean(values, weights);\n    var vari = 0, l = values.length;\n    var a = 0, b = 0;\n\n    for (var i = 0; i < l; i++) {\n        var z = values[i] - theMean;\n        var w = weights[i];\n\n        vari += w * (z * z);\n        b += w;\n        a += w * w;\n    }\n\n    return vari * (b / (b * b - a));\n};\n\nexports.center = function center(values, inPlace) {\n    if (typeof (inPlace) === 'undefined') inPlace = false;\n\n    var result = values;\n    if (!inPlace)\n        result = [].concat(values);\n\n    var theMean = exports.mean(result), l = result.length;\n    for (var i = 0; i < l; i++)\n        result[i] -= theMean;\n};\n\nexports.standardize = function standardize(values, standardDev, inPlace) {\n    if (typeof (standardDev) === 'undefined') standardDev = exports.standardDeviation(values);\n    if (typeof (inPlace) === 'undefined') inPlace = false;\n    var l = values.length;\n    var result = inPlace ? values : new Array(l);\n    for (var i = 0; i < l; i++)\n        result[i] = values[i] / standardDev;\n    return result;\n};\n\nexports.cumulativeSum = function cumulativeSum(array) {\n    var l = array.length;\n    var result = new Array(l);\n    result[0] = array[0];\n    for (var i = 1; i < l; i++)\n        result[i] = result[i - 1] + array[i];\n    return result;\n};\n","'use strict';\n\nvar arrayStat = require('./array');\n\nfunction compareNumbers(a, b) {\n    return a - b;\n}\n\nexports.max = function max(matrix) {\n    var max = -Infinity;\n    for (var i = 0; i < matrix.length; i++) {\n        for (var j = 0; j < matrix[i].length; j++) {\n            if (matrix[i][j] > max) max = matrix[i][j];\n        }\n    }\n    return max;\n};\n\nexports.min = function min(matrix) {\n    var min = Infinity;\n    for (var i = 0; i < matrix.length; i++) {\n        for (var j = 0; j < matrix[i].length; j++) {\n            if (matrix[i][j] < min) min = matrix[i][j];\n        }\n    }\n    return min;\n};\n\nexports.minMax = function minMax(matrix) {\n    var min = Infinity;\n    var max = -Infinity;\n    for (var i = 0; i < matrix.length; i++) {\n        for (var j = 0; j < matrix[i].length; j++) {\n            if (matrix[i][j] < min) min = matrix[i][j];\n            if (matrix[i][j] > max) max = matrix[i][j];\n        }\n    }\n    return {\n        min:min,\n        max:max\n    };\n};\n\nexports.entropy = function entropy(matrix, eps) {\n    if (typeof (eps) === 'undefined') {\n        eps = 0;\n    }\n    var sum = 0,\n        l1 = matrix.length,\n        l2 = matrix[0].length;\n    for (var i = 0; i < l1; i++) {\n        for (var j = 0; j < l2; j++) {\n            sum += matrix[i][j] * Math.log(matrix[i][j] + eps);\n        }\n    }\n    return -sum;\n};\n\nexports.mean = function mean(matrix, dimension) {\n    if (typeof (dimension) === 'undefined') {\n        dimension = 0;\n    }\n    var rows = matrix.length,\n        cols = matrix[0].length,\n        theMean, N, i, j;\n\n    if (dimension === -1) {\n        theMean = [0];\n        N = rows * cols;\n        for (i = 0; i < rows; i++) {\n            for (j = 0; j < cols; j++) {\n                theMean[0] += matrix[i][j];\n            }\n        }\n        theMean[0] /= N;\n    } else if (dimension === 0) {\n        theMean = new Array(cols);\n        N = rows;\n        for (j = 0; j < cols; j++) {\n            theMean[j] = 0;\n            for (i = 0; i < rows; i++) {\n                theMean[j] += matrix[i][j];\n            }\n            theMean[j] /= N;\n        }\n    } else if (dimension === 1) {\n        theMean = new Array(rows);\n        N = cols;\n        for (j = 0; j < rows; j++) {\n            theMean[j] = 0;\n            for (i = 0; i < cols; i++) {\n                theMean[j] += matrix[j][i];\n            }\n            theMean[j] /= N;\n        }\n    } else {\n        throw new Error('Invalid dimension');\n    }\n    return theMean;\n};\n\nexports.sum = function sum(matrix, dimension) {\n    if (typeof (dimension) === 'undefined') {\n        dimension = 0;\n    }\n    var rows = matrix.length,\n        cols = matrix[0].length,\n        theSum, i, j;\n\n    if (dimension === -1) {\n        theSum = [0];\n        for (i = 0; i < rows; i++) {\n            for (j = 0; j < cols; j++) {\n                theSum[0] += matrix[i][j];\n            }\n        }\n    } else if (dimension === 0) {\n        theSum = new Array(cols);\n        for (j = 0; j < cols; j++) {\n            theSum[j] = 0;\n            for (i = 0; i < rows; i++) {\n                theSum[j] += matrix[i][j];\n            }\n        }\n    } else if (dimension === 1) {\n        theSum = new Array(rows);\n        for (j = 0; j < rows; j++) {\n            theSum[j] = 0;\n            for (i = 0; i < cols; i++) {\n                theSum[j] += matrix[j][i];\n            }\n        }\n    } else {\n        throw new Error('Invalid dimension');\n    }\n    return theSum;\n};\n\nexports.product = function product(matrix, dimension) {\n    if (typeof (dimension) === 'undefined') {\n        dimension = 0;\n    }\n    var rows = matrix.length,\n        cols = matrix[0].length,\n        theProduct, i, j;\n\n    if (dimension === -1) {\n        theProduct = [1];\n        for (i = 0; i < rows; i++) {\n            for (j = 0; j < cols; j++) {\n                theProduct[0] *= matrix[i][j];\n            }\n        }\n    } else if (dimension === 0) {\n        theProduct = new Array(cols);\n        for (j = 0; j < cols; j++) {\n            theProduct[j] = 1;\n            for (i = 0; i < rows; i++) {\n                theProduct[j] *= matrix[i][j];\n            }\n        }\n    } else if (dimension === 1) {\n        theProduct = new Array(rows);\n        for (j = 0; j < rows; j++) {\n            theProduct[j] = 1;\n            for (i = 0; i < cols; i++) {\n                theProduct[j] *= matrix[j][i];\n            }\n        }\n    } else {\n        throw new Error('Invalid dimension');\n    }\n    return theProduct;\n};\n\nexports.standardDeviation = function standardDeviation(matrix, means, unbiased) {\n    var vari = exports.variance(matrix, means, unbiased), l = vari.length;\n    for (var i = 0; i < l; i++) {\n        vari[i] = Math.sqrt(vari[i]);\n    }\n    return vari;\n};\n\nexports.variance = function variance(matrix, means, unbiased) {\n    if (typeof (unbiased) === 'undefined') {\n        unbiased = true;\n    }\n    means = means || exports.mean(matrix);\n    var rows = matrix.length;\n    if (rows === 0) return [];\n    var cols = matrix[0].length;\n    var vari = new Array(cols);\n\n    for (var j = 0; j < cols; j++) {\n        var sum1 = 0, sum2 = 0, x = 0;\n        for (var i = 0; i < rows; i++) {\n            x = matrix[i][j] - means[j];\n            sum1 += x;\n            sum2 += x * x;\n        }\n        if (unbiased) {\n            vari[j] = (sum2 - ((sum1 * sum1) / rows)) / (rows - 1);\n        } else {\n            vari[j] = (sum2 - ((sum1 * sum1) / rows)) / rows;\n        }\n    }\n    return vari;\n};\n\nexports.median = function median(matrix) {\n    var rows = matrix.length, cols = matrix[0].length;\n    var medians = new Array(cols);\n\n    for (var i = 0; i < cols; i++) {\n        var data = new Array(rows);\n        for (var j = 0; j < rows; j++) {\n            data[j] = matrix[j][i];\n        }\n        data.sort(compareNumbers);\n        var N = data.length;\n        if (N % 2 === 0) {\n            medians[i] = (data[N / 2] + data[(N / 2) - 1]) * 0.5;\n        } else {\n            medians[i] = data[Math.floor(N / 2)];\n        }\n    }\n    return medians;\n};\n\nexports.mode = function mode(matrix) {\n    var rows = matrix.length,\n        cols = matrix[0].length,\n        modes = new Array(cols),\n        i, j;\n    for (i = 0; i < cols; i++) {\n        var itemCount = new Array(rows);\n        for (var k = 0; k < rows; k++) {\n            itemCount[k] = 0;\n        }\n        var itemArray = new Array(rows);\n        var count = 0;\n\n        for (j = 0; j < rows; j++) {\n            var index = itemArray.indexOf(matrix[j][i]);\n            if (index >= 0) {\n                itemCount[index]++;\n            } else {\n                itemArray[count] = matrix[j][i];\n                itemCount[count] = 1;\n                count++;\n            }\n        }\n\n        var maxValue = 0, maxIndex = 0;\n        for (j = 0; j < count; j++) {\n            if (itemCount[j] > maxValue) {\n                maxValue = itemCount[j];\n                maxIndex = j;\n            }\n        }\n\n        modes[i] = itemArray[maxIndex];\n    }\n    return modes;\n};\n\nexports.skewness = function skewness(matrix, unbiased) {\n    if (typeof (unbiased) === 'undefined') unbiased = true;\n    var means = exports.mean(matrix);\n    var n = matrix.length, l = means.length;\n    var skew = new Array(l);\n\n    for (var j = 0; j < l; j++) {\n        var s2 = 0, s3 = 0;\n        for (var i = 0; i < n; i++) {\n            var dev = matrix[i][j] - means[j];\n            s2 += dev * dev;\n            s3 += dev * dev * dev;\n        }\n\n        var m2 = s2 / n;\n        var m3 = s3 / n;\n        var g = m3 / Math.pow(m2, 3 / 2);\n\n        if (unbiased) {\n            var a = Math.sqrt(n * (n - 1));\n            var b = n - 2;\n            skew[j] = (a / b) * g;\n        } else {\n            skew[j] = g;\n        }\n    }\n    return skew;\n};\n\nexports.kurtosis = function kurtosis(matrix, unbiased) {\n    if (typeof (unbiased) === 'undefined') unbiased = true;\n    var means = exports.mean(matrix);\n    var n = matrix.length, m = matrix[0].length;\n    var kurt = new Array(m);\n\n    for (var j = 0; j < m; j++) {\n        var s2 = 0, s4 = 0;\n        for (var i = 0; i < n; i++) {\n            var dev = matrix[i][j] - means[j];\n            s2 += dev * dev;\n            s4 += dev * dev * dev * dev;\n        }\n        var m2 = s2 / n;\n        var m4 = s4 / n;\n\n        if (unbiased) {\n            var v = s2 / (n - 1);\n            var a = (n * (n + 1)) / ((n - 1) * (n - 2) * (n - 3));\n            var b = s4 / (v * v);\n            var c = ((n - 1) * (n - 1)) / ((n - 2) * (n - 3));\n            kurt[j] = a * b - 3 * c;\n        } else {\n            kurt[j] = m4 / (m2 * m2) - 3;\n        }\n    }\n    return kurt;\n};\n\nexports.standardError = function standardError(matrix) {\n    var samples = matrix.length;\n    var standardDeviations = exports.standardDeviation(matrix);\n    var l = standardDeviations.length;\n    var standardErrors = new Array(l);\n    var sqrtN = Math.sqrt(samples);\n\n    for (var i = 0; i < l; i++) {\n        standardErrors[i] = standardDeviations[i] / sqrtN;\n    }\n    return standardErrors;\n};\n\nexports.covariance = function covariance(matrix, dimension) {\n    return exports.scatter(matrix, undefined, dimension);\n};\n\nexports.scatter = function scatter(matrix, divisor, dimension) {\n    if (typeof (dimension) === 'undefined') {\n        dimension = 0;\n    }\n    if (typeof (divisor) === 'undefined') {\n        if (dimension === 0) {\n            divisor = matrix.length - 1;\n        } else if (dimension === 1) {\n            divisor = matrix[0].length - 1;\n        }\n    }\n    var means = exports.mean(matrix, dimension);\n    var rows = matrix.length;\n    if (rows === 0) {\n        return [[]];\n    }\n    var cols = matrix[0].length,\n        cov, i, j, s, k;\n\n    if (dimension === 0) {\n        cov = new Array(cols);\n        for (i = 0; i < cols; i++) {\n            cov[i] = new Array(cols);\n        }\n        for (i = 0; i < cols; i++) {\n            for (j = i; j < cols; j++) {\n                s = 0;\n                for (k = 0; k < rows; k++) {\n                    s += (matrix[k][j] - means[j]) * (matrix[k][i] - means[i]);\n                }\n                s /= divisor;\n                cov[i][j] = s;\n                cov[j][i] = s;\n            }\n        }\n    } else if (dimension === 1) {\n        cov = new Array(rows);\n        for (i = 0; i < rows; i++) {\n            cov[i] = new Array(rows);\n        }\n        for (i = 0; i < rows; i++) {\n            for (j = i; j < rows; j++) {\n                s = 0;\n                for (k = 0; k < cols; k++) {\n                    s += (matrix[j][k] - means[j]) * (matrix[i][k] - means[i]);\n                }\n                s /= divisor;\n                cov[i][j] = s;\n                cov[j][i] = s;\n            }\n        }\n    } else {\n        throw new Error('Invalid dimension');\n    }\n\n    return cov;\n};\n\nexports.correlation = function correlation(matrix) {\n    var means = exports.mean(matrix),\n        standardDeviations = exports.standardDeviation(matrix, true, means),\n        scores = exports.zScores(matrix, means, standardDeviations),\n        rows = matrix.length,\n        cols = matrix[0].length,\n        i, j;\n\n    var cor = new Array(cols);\n    for (i = 0; i < cols; i++) {\n        cor[i] = new Array(cols);\n    }\n    for (i = 0; i < cols; i++) {\n        for (j = i; j < cols; j++) {\n            var c = 0;\n            for (var k = 0, l = scores.length; k < l; k++) {\n                c += scores[k][j] * scores[k][i];\n            }\n            c /= rows - 1;\n            cor[i][j] = c;\n            cor[j][i] = c;\n        }\n    }\n    return cor;\n};\n\nexports.zScores = function zScores(matrix, means, standardDeviations) {\n    means = means || exports.mean(matrix);\n    if (typeof (standardDeviations) === 'undefined') standardDeviations = exports.standardDeviation(matrix, true, means);\n    return exports.standardize(exports.center(matrix, means, false), standardDeviations, true);\n};\n\nexports.center = function center(matrix, means, inPlace) {\n    means = means || exports.mean(matrix);\n    var result = matrix,\n        l = matrix.length,\n        i, j, jj;\n\n    if (!inPlace) {\n        result = new Array(l);\n        for (i = 0; i < l; i++) {\n            result[i] = new Array(matrix[i].length);\n        }\n    }\n\n    for (i = 0; i < l; i++) {\n        var row = result[i];\n        for (j = 0, jj = row.length; j < jj; j++) {\n            row[j] = matrix[i][j] - means[j];\n        }\n    }\n    return result;\n};\n\nexports.standardize = function standardize(matrix, standardDeviations, inPlace) {\n    if (typeof (standardDeviations) === 'undefined') standardDeviations = exports.standardDeviation(matrix);\n    var result = matrix,\n        l = matrix.length,\n        i, j, jj;\n\n    if (!inPlace) {\n        result = new Array(l);\n        for (i = 0; i < l; i++) {\n            result[i] = new Array(matrix[i].length);\n        }\n    }\n\n    for (i = 0; i < l; i++) {\n        var resultRow = result[i];\n        var sourceRow = matrix[i];\n        for (j = 0, jj = resultRow.length; j < jj; j++) {\n            if (standardDeviations[j] !== 0 && !isNaN(standardDeviations[j])) {\n                resultRow[j] = sourceRow[j] / standardDeviations[j];\n            }\n        }\n    }\n    return result;\n};\n\nexports.weightedVariance = function weightedVariance(matrix, weights) {\n    var means = exports.mean(matrix);\n    var rows = matrix.length;\n    if (rows === 0) return [];\n    var cols = matrix[0].length;\n    var vari = new Array(cols);\n\n    for (var j = 0; j < cols; j++) {\n        var sum = 0;\n        var a = 0, b = 0;\n\n        for (var i = 0; i < rows; i++) {\n            var z = matrix[i][j] - means[j];\n            var w = weights[i];\n\n            sum += w * (z * z);\n            b += w;\n            a += w * w;\n        }\n\n        vari[j] = sum * (b / (b * b - a));\n    }\n\n    return vari;\n};\n\nexports.weightedMean = function weightedMean(matrix, weights, dimension) {\n    if (typeof (dimension) === 'undefined') {\n        dimension = 0;\n    }\n    var rows = matrix.length;\n    if (rows === 0) return [];\n    var cols = matrix[0].length,\n        means, i, ii, j, w, row;\n\n    if (dimension === 0) {\n        means = new Array(cols);\n        for (i = 0; i < cols; i++) {\n            means[i] = 0;\n        }\n        for (i = 0; i < rows; i++) {\n            row = matrix[i];\n            w = weights[i];\n            for (j = 0; j < cols; j++) {\n                means[j] += row[j] * w;\n            }\n        }\n    } else if (dimension === 1) {\n        means = new Array(rows);\n        for (i = 0; i < rows; i++) {\n            means[i] = 0;\n        }\n        for (j = 0; j < rows; j++) {\n            row = matrix[j];\n            w = weights[j];\n            for (i = 0; i < cols; i++) {\n                means[j] += row[i] * w;\n            }\n        }\n    } else {\n        throw new Error('Invalid dimension');\n    }\n\n    var weightSum = arrayStat.sum(weights);\n    if (weightSum !== 0) {\n        for (i = 0, ii = means.length; i < ii; i++) {\n            means[i] /= weightSum;\n        }\n    }\n    return means;\n};\n\nexports.weightedCovariance = function weightedCovariance(matrix, weights, means, dimension) {\n    dimension = dimension || 0;\n    means = means || exports.weightedMean(matrix, weights, dimension);\n    var s1 = 0, s2 = 0;\n    for (var i = 0, ii = weights.length; i < ii; i++) {\n        s1 += weights[i];\n        s2 += weights[i] * weights[i];\n    }\n    var factor = s1 / (s1 * s1 - s2);\n    return exports.weightedScatter(matrix, weights, means, factor, dimension);\n};\n\nexports.weightedScatter = function weightedScatter(matrix, weights, means, factor, dimension) {\n    dimension = dimension || 0;\n    means = means || exports.weightedMean(matrix, weights, dimension);\n    if (typeof (factor) === 'undefined') {\n        factor = 1;\n    }\n    var rows = matrix.length;\n    if (rows === 0) {\n        return [[]];\n    }\n    var cols = matrix[0].length,\n        cov, i, j, k, s;\n\n    if (dimension === 0) {\n        cov = new Array(cols);\n        for (i = 0; i < cols; i++) {\n            cov[i] = new Array(cols);\n        }\n        for (i = 0; i < cols; i++) {\n            for (j = i; j < cols; j++) {\n                s = 0;\n                for (k = 0; k < rows; k++) {\n                    s += weights[k] * (matrix[k][j] - means[j]) * (matrix[k][i] - means[i]);\n                }\n                cov[i][j] = s * factor;\n                cov[j][i] = s * factor;\n            }\n        }\n    } else if (dimension === 1) {\n        cov = new Array(rows);\n        for (i = 0; i < rows; i++) {\n            cov[i] = new Array(rows);\n        }\n        for (i = 0; i < rows; i++) {\n            for (j = i; j < rows; j++) {\n                s = 0;\n                for (k = 0; k < cols; k++) {\n                    s += weights[k] * (matrix[j][k] - means[j]) * (matrix[i][k] - means[i]);\n                }\n                cov[i][j] = s * factor;\n                cov[j][i] = s * factor;\n            }\n        }\n    } else {\n        throw new Error('Invalid dimension');\n    }\n\n    return cov;\n};\n","'use strict';\n\nexports.array = require('./array');\nexports.matrix = require('./matrix');\n","import { array as StatArray } from 'ml-stat';\nexport function normalize(array) {\n    const min = StatArray.min(array[1]);\n    const max = StatArray.max(array[1]);\n    const sum = StatArray.sum(array[1]);\n    const length = array[1] ? array[1].length : 0;\n    if (sum !== 0) {\n        for (let i = 0; i < length; i++) {\n            array[1][i] /= sum;\n        }\n    }\n    return {\n        sum,\n        min,\n        max,\n    };\n}\n//# sourceMappingURL=normalize.js.map","import { extract } from './extract';\nimport { getCommonArray } from './getCommonArray';\nimport { normalize } from './normalize';\nimport { COMMON_SECOND, COMMON_FIRST } from './index';\n// this method will systematically take care of both array\nexport function commonExtractAndNormalize(array1, array2, width, from, to, common) {\n    if (!Array.isArray(array1) || !Array.isArray(array2)) {\n        return {\n            info: undefined,\n            data: undefined,\n        };\n    }\n    const extract1 = extract(array1, from, to);\n    const extract2 = extract(array2, from, to);\n    let common1, common2, info1, info2;\n    if (common & COMMON_SECOND) {\n        common1 = getCommonArray(extract1, extract2, width);\n        info1 = normalize(common1);\n    }\n    else {\n        common1 = extract1;\n        info1 = normalize(common1);\n    }\n    if (common & COMMON_FIRST) {\n        common2 = getCommonArray(extract2, extract1, width);\n        info2 = normalize(common2);\n    }\n    else {\n        common2 = extract2;\n        info2 = normalize(common2);\n    }\n    return {\n        info1,\n        info2,\n        data1: common1,\n        data2: common2,\n    };\n}\n//# sourceMappingURL=commonExtractAndNormalize.js.map","import { extract } from './extract';\nimport { normalize } from './normalize';\nexport function extractAndNormalize(array, from, to) {\n    if (!Array.isArray(array)) {\n        return {\n            info: undefined,\n            data: undefined,\n        };\n    }\n    const newArray = extract(array, from, to);\n    const info = normalize(newArray);\n    return {\n        info,\n        data: newArray,\n    };\n}\n//# sourceMappingURL=extractAndNormalize.js.map","// Adapted from: http://stackoverflow.com/questions/563198/how-do-you-detect-where-two-line-segments-intersect/1968345#1968345\nexport function getIntersection(segment1, segment2) {\n    const p0X = segment1[0][0];\n    const p0Y = segment1[0][1];\n    const p1X = segment1[1][0];\n    const p1Y = segment1[1][1];\n    const p2X = segment2[0][0];\n    const p2Y = segment2[0][1];\n    const p3X = segment2[1][0];\n    const p3Y = segment2[1][1];\n    const s1X = p1X - p0X;\n    const s1Y = p1Y - p0Y;\n    const s2X = p3X - p2X;\n    const s2Y = p3Y - p2Y;\n    const s = (-s1Y * (p0X - p2X) + s1X * (p0Y - p2Y)) / (-s2X * s1Y + s1X * s2Y);\n    const t = (s2X * (p0Y - p2Y) - s2Y * (p0X - p2X)) / (-s2X * s1Y + s1X * s2Y);\n    if (s >= 0 && s <= 1 && t >= 0 && t <= 1) {\n        return {\n            x: p0X + t * s1X,\n            y: p0Y + t * s1Y,\n        };\n    }\n    return null; // No collision\n}\n//# sourceMappingURL=getIntersection.js.map","import { calculateOverlapFromDiff } from './calculateOverlapFromDiff';\nimport { checkPeaks } from './checkPeaks';\nimport { commonExtractAndNormalize } from './commonExtractAndNormalize';\nimport { extract } from './extract';\nimport { extractAndNormalize } from './extractAndNormalize';\nimport { getCommonArray } from './getCommonArray.js';\nimport { getIntersection } from './getIntersection';\nimport { normalize } from './normalize';\nexport const COMMON_NO = 0;\nexport const COMMON_FIRST = 1;\nexport const COMMON_SECOND = 2;\nexport const COMMON_BOTH = 3;\n/**\n * A number, or a string containing a number.\n * @typedef {([number[],number[]]|[number,number][]|{x:number[],y:number[]})} Peaks\n */\n/**\n * Create a comparator class\n * {object} [options={}]\n * {string} [options.common=''] should we take only common peaks 'first', 'second', 'both', ''\n * {number} [options.widthBottom=2] bottom trapezoid width for similarity evaluation\n * {number} [options.widthTop=1] top trapezoid width for similarity evaluation\n * {number} [options.from] from region used for similarity calculation\n * {number} [options.to] to region used for similarity calculation\n */\nexport class Comparator {\n    constructor(options = {}) {\n        this.array1 = [];\n        this.array2 = [];\n        this.setOptions(options);\n    }\n    /*\n       2 formats are allowed:\n       [[x1,x2,...],[y1,y2,...]] or [[x1,y1],[x2,y2], ...]\n      */\n    setOptions(options = {}) {\n        if (typeof options.common === 'string') {\n            if (options.common.toLowerCase() === 'first') {\n                this.common = COMMON_FIRST;\n            }\n            else if (options.common.toLowerCase() === 'second') {\n                this.common = COMMON_SECOND;\n            }\n            else if (options.common.toLowerCase() === 'both') {\n                this.common = COMMON_BOTH;\n            }\n            else {\n                this.common = COMMON_NO;\n            }\n        }\n        else if (options.common === true) {\n            this.common = COMMON_BOTH;\n        }\n        else {\n            this.common = COMMON_NO;\n        }\n        this.trapezoid = options.trapezoid;\n        this.commonFactor = options.commonFactor || this.commonFactor || 4;\n        const { widthBottom = this.widthBottom || 2, widthTop = this.widthTop || 1, from = this.from, to = this.to, } = options;\n        this.setTrapezoid(widthBottom, widthTop);\n        this.setFromTo(from, to);\n    }\n    /**\n     *\n     * @param {Peaks} peaks\n     */\n    setPeaks1(peaks) {\n        this.array1 = checkPeaks(peaks);\n        if (this.common) {\n            const extracts = commonExtractAndNormalize(this.array1, this.array2, this.widthBottom, this.from, this.to, this.common);\n            this.array1Extract = extracts.data1;\n            this.array1ExtractInfo = extracts.info1;\n            this.array2Extract = extracts.data2;\n            this.array2ExtractInfo = extracts.info2;\n        }\n        else {\n            const extract = extractAndNormalize(this.array1, this.from, this.to);\n            this.array1Extract = extract.data;\n            this.array1ExtractInfo = extract.info;\n        }\n    }\n    /**\n     *\n     * @param {Peaks} peaks\n     */\n    setPeaks2(peaks) {\n        this.array2 = checkPeaks(peaks);\n        if (this.common) {\n            const extracts = commonExtractAndNormalize(this.array1, this.array2, this.widthBottom, this.from, this.to, this.common);\n            this.array1Extract = extracts.data1;\n            this.array1ExtractInfo = extracts.info1;\n            this.array2Extract = extracts.data2;\n            this.array2ExtractInfo = extracts.info2;\n        }\n        else {\n            const extract = extractAndNormalize(this.array2, this.from, this.to);\n            this.array2Extract = extract.data;\n            this.array2ExtractInfo = extract.info;\n        }\n    }\n    getExtract1() {\n        return this.array1Extract;\n    }\n    getExtract2() {\n        return this.array2Extract;\n    }\n    getExtractInfo1() {\n        return this.array1ExtractInfo;\n    }\n    getExtractInfo2() {\n        return this.array2ExtractInfo;\n    }\n    /**\n     * Set the new bottom and top width of the trapezoid\n     * @param {number} newWidthBottom\n     * @param {number} newWidthTop\n     */\n    setTrapezoid(newWidthBottom, newWidthTop) {\n        this.widthTop = newWidthTop;\n        this.widthBottom = newWidthBottom;\n        this.widthSlope = (this.widthBottom - this.widthTop) / 2;\n        if (this.widthBottom < this.widthTop) {\n            throw new Error('widthBottom has to be larger than widthTop');\n        }\n    }\n    /**\n     * Set the from / to for comparison\n     * @param {number} newFrom - set the new from value\n     * @param {number} newTo - set the new to value\n     * @returns\n     */\n    setFromTo(newFrom, newTo) {\n        if (newFrom === this.from && newTo === this.to)\n            return;\n        this.from = newFrom;\n        this.to = newTo;\n        if (this.common) {\n            const extracts = commonExtractAndNormalize(this.array1, this.array2, this.widthBottom, this.from, this.to, this.common, this.commonFactor);\n            this.array1Extract = extracts.data1;\n            this.array1ExtractInfo = extracts.info1;\n            this.array2Extract = extracts.data2;\n            this.array2ExtractInfo = extracts.info2;\n        }\n        else {\n            let extract1 = extractAndNormalize(this.array1, this.from, this.to);\n            this.array1Extract = extract1.data;\n            this.array1ExtractInfo = extract1.info;\n            let extract2 = extractAndNormalize(this.array2, this.from, this.to);\n            this.array2Extract = extract2.data;\n            this.array2ExtractInfo = extract2.info;\n        }\n    }\n    /**\n     *\n     * @param {number} x1\n     * @param {number} y1\n     * @param {number} x2\n     * @param {number} y2\n     * @returns\n     */\n    getOverlap(x1, y1, x2, y2) {\n        if (y1 === 0 || y2 === 0)\n            return 0;\n        // TAKE CARE !!! We multiply the diff by 2 !!!\n        const diff = Math.abs(x1 - x2) * 2;\n        if (diff > this.widthBottom)\n            return 0;\n        if (diff <= this.widthTop) {\n            return Math.min(y1, y2);\n        }\n        const maxValue = (Math.max(y1, y2) * (this.widthBottom - diff)) /\n            (this.widthBottom - this.widthTop);\n        return Math.min(y1, y2, maxValue);\n    }\n    /**\n     * This is the old trapezoid similarity\n     * @param {number} x1\n     * @param {number} y1\n     * @param {number} x2\n     * @param {number} y2\n     * @param {number} widthTop\n     * @param {number} widthBottom\n     * @returns\n     */\n    getOverlapTrapezoid(x1, y1, x2, y2, widthTop, widthBottom) {\n        // eslint-disable-next-line no-console\n        console.error('getOverlapTrapezoid should not be used anymore');\n        const factor = 2 / (widthTop + widthBottom); // correction for surface=1\n        if (y1 === 0 || y2 === 0)\n            return 0;\n        if (x1 === x2) {\n            // they have the same position\n            return Math.min(y1, y2);\n        }\n        const diff = Math.abs(x1 - x2);\n        if (diff >= widthBottom)\n            return 0;\n        if (y1 === y2) {\n            // do they have the same height ???\n            // we need to find the common length\n            if (diff <= widthTop) {\n                return ((widthTop + widthBottom) / 2 - diff) * y1 * factor;\n            }\n            else if (diff <= widthBottom) {\n                return ((((((widthBottom - diff) * y1) / 2) * (diff - widthTop)) /\n                    (widthBottom - widthTop)) *\n                    factor);\n            }\n            return 0;\n        }\n        else {\n            // the height are different and not the same position ...\n            // we need to consider only one segment to find its intersection\n            const small = Math.min(y1, y2);\n            const big = Math.max(y1, y2);\n            const targets = [\n                [\n                    [0, 0],\n                    [this.widthSlope, small],\n                ],\n                [\n                    [this.widthSlope, small],\n                    [this.widthSlope + widthTop, small],\n                ],\n                [\n                    [widthTop + this.widthSlope, small],\n                    [widthBottom, 0],\n                ],\n            ];\n            let segment;\n            if ((x1 > x2 && y1 > y2) || (x1 < x2 && y1 < y2)) {\n                segment = [\n                    [diff, 0],\n                    [diff + this.widthSlope, big],\n                ];\n            }\n            else {\n                segment = [\n                    [diff + this.widthSlope, big],\n                    [diff, 0],\n                ];\n            }\n            for (let i = 0; i < 3; i++) {\n                const intersection = getIntersection(targets[i], segment);\n                if (intersection) {\n                    switch (i) {\n                        case 0:\n                            return small - ((diff * intersection.y) / 2) * factor;\n                        case 1: // to simplify ...\n                            //     console.log(\"           \",widthSlope,small,big,intersection.x)\n                            return ((((this.widthSlope * small) / (2 * big)) * small +\n                                (widthTop + this.widthSlope - intersection.x) * small +\n                                (this.widthSlope * small) / 2) *\n                                factor);\n                        case 2:\n                            return (((widthBottom - diff) * intersection.y) / 2) * factor;\n                        default:\n                            throw new Error(`unexpected intersection value: ${i}`);\n                    }\n                }\n            }\n        }\n        return NaN;\n    }\n    /**\n     * This method calculates the total diff. The sum of positive value will yield to overlap\n     * @returns\n     */\n    calculateDiff() {\n        // we need to take 2 pointers\n        // and travel progressively between them ...\n        const newFirst = [\n            this.array1Extract[0].slice(),\n            this.array1Extract[1].slice(),\n        ];\n        const newSecond = [\n            this.array2Extract[0].slice(),\n            this.array2Extract[1].slice(),\n        ];\n        const array1Length = this.array1Extract[0]\n            ? this.array1Extract[0].length\n            : 0;\n        const array2Length = this.array2Extract[0]\n            ? this.array2Extract[0].length\n            : 0;\n        let pos1 = 0;\n        let pos2 = 0;\n        let previous2 = 0;\n        while (pos1 < array1Length) {\n            const diff = newFirst[0][pos1] - this.array2Extract[0][pos2];\n            if (Math.abs(diff) < this.widthBottom) {\n                // there is some overlap\n                let overlap;\n                if (this.trapezoid) {\n                    // old trapezoid overlap similarity\n                    overlap = this.getOverlapTrapezoid(newFirst[0][pos1], newFirst[1][pos1], newSecond[0][pos2], newSecond[1][pos2], this.widthTop, this.widthBottom);\n                }\n                else {\n                    overlap = this.getOverlap(newFirst[0][pos1], newFirst[1][pos1], newSecond[0][pos2], newSecond[1][pos2], this.widthTop, this.widthBottom);\n                }\n                newFirst[1][pos1] -= overlap;\n                newSecond[1][pos2] -= overlap;\n                if (pos2 < array2Length - 1) {\n                    pos2++;\n                }\n                else {\n                    pos1++;\n                    pos2 = previous2;\n                }\n            }\n            else if (diff > 0 && pos2 < array2Length - 1) {\n                pos2++;\n                previous2 = pos2;\n            }\n            else {\n                pos1++;\n                pos2 = previous2;\n            }\n        }\n        return newSecond;\n    }\n    /**\n     * Set the new peaks and return info\n     * @param {Peaks} newPeaks1\n     * @param {Peaks} newPeaks2\n     * @returns\n     */\n    getSimilarity(newPeaks1, newPeaks2) {\n        if (newPeaks1)\n            this.setPeaks1(newPeaks1);\n        if (newPeaks2)\n            this.setPeaks2(newPeaks2);\n        const diff = this.calculateDiff();\n        return {\n            diff,\n            extract1: this.getExtract1(),\n            extract2: this.getExtract2(),\n            extractInfo1: this.getExtractInfo1(),\n            extractInfo2: this.getExtractInfo2(),\n            similarity: calculateOverlapFromDiff(diff),\n            widthBottom: this.widthBottom,\n            widthTop: this.widthTop,\n        };\n    }\n    /**\n     * This works mainly when you have a array1 that is fixed\n     * newPeaks2 have to be normalized ! (sum to 1)\n     * @param {Peaks} newPeaks2\n     * @param {number} from\n     * @param {number} to\n     * @returns\n     */\n    fastSimilarity(newPeaks2, from, to) {\n        this.array1Extract = extract(this.array1, from, to);\n        this.array2Extract = newPeaks2;\n        if (this.common & COMMON_SECOND) {\n            this.array1Extract = getCommonArray(this.array1Extract, this.array2Extract, this.widthBottom);\n        }\n        normalize(this.array1Extract);\n        const diff = this.calculateDiff();\n        return calculateOverlapFromDiff(diff);\n    }\n}\n//# sourceMappingURL=index.js.map","import { IsotopicDistribution } from 'isotopic-distribution';\nimport { Comparator } from 'peaks-similarity';\n/**\nSearch for an experimental monoisotopic mass and calculate the similarity\n* @param {object}   [options={}]\n* @param {array}    [options.databases] - an array containing the name of the databases so search, by default all\n* @param {boolean}  [options.flatten] - should we return the array as a flat result\n* @param {function} [options.onStep] - Callback to do after each step\n* @param {string}   [options.ionizations=''] - Comma separated list of ionizations (to charge the molecule)\n* @param {object}   [options.minSimilarity=0.5] - min similarity value\n\n* @param {object}   [options.filter={}]\n* @param {boolean}  [options.filter.forceIonization=false] - If true ignore existing ionizations\n* @param {number}   [options.filter.msem] - Observed monoisotopic mass in mass spectrometer\n* @param {number}   [options.filter.precision=1000] - The precision on the experimental mass\n* @param {number}   [options.filter.minCharge=-Infinity] - Minimal charge\n* @param {number}   [options.filter.maxCharge=+Infinity] - Maximal charge\n* @param {object}   [options.filter.unsaturation={}]\n* @param {number}   [options.filter.unsaturation.min=-Infinity] - Minimal unsaturation\n* @param {number}   [options.filter.unsaturation.max=+Infinity] - Maximal unsaturation\n* @param {boolean}   [options.filter.unsaturation.onlyInteger=false] - Integer unsaturation\n* @param {boolean}   [options.filter.unsaturation.onlyNonInteger=false] - Non integer unsaturation\n* @param {object}   [options.filter.atoms] - object of atom:{min, max}\n* @param {object}   [options.filter.callback] - a function to filter the MF\n* @param {object}   [options.similarity={}]\n* @param {object}   [options.similarity.widthBottom]\n* @param {object}   [options.similarity.widthTop]\n* @param {object}   [options.similarity.widthFunction] - function called with mass that should return an object width containing top and bottom\n* @param {object}   [options.similarity.zone={}]\n* @param {object}   [options.similarity.zone.low=-0.5] - window shift based on observed monoisotopic mass\n* @param {object}   [options.similarity.zone.high=2.5] - to value for the comparison window\n* @param {object}   [options.similarity.common]\n* @returns {Promise}\n*/\n\nexport async function searchSimilarity(emdb, options = {}) {\n  const { similarity = {}, minSimilarity = 0.5, filter = {}, onStep } = options;\n\n  let width = {\n    bottom: similarity.widthBottom,\n    top: similarity.widthTop,\n  };\n\n  if (\n    !emdb.experimentalSpectrum ||\n    !emdb.experimentalSpectrum.data.x.length > 0\n  ) {\n    throw Error(\n      'You need to add an experimental spectrum first using setMassSpectrum',\n    );\n  }\n\n  let experimentalData = emdb.experimentalSpectrum.data;\n  let sumY = emdb.experimentalSpectrum.sumY();\n\n  // the result of emdb query will be stored in a property 'ms'\n\n  let results = emdb.searchMSEM(filter.msem, options);\n  let flatEntries = [];\n  if (!options.flatten) {\n    for (let database of Object.keys(results)) {\n      for (let entry of results[database]) {\n        flatEntries.push(entry);\n      }\n    }\n  } else {\n    flatEntries = results;\n  }\n\n  let { widthFunction, zone = {} } = similarity;\n\n  if (widthFunction && typeof widthFunction === 'string') {\n    // eslint-disable-next-line no-new-func\n    widthFunction = new Function('mass', widthFunction);\n    let checkTopBottom = widthFunction(123);\n    if (!checkTopBottom.bottom || !checkTopBottom.top) {\n      throw Error(\n        'widthFunction should return an object with bottom and top properties',\n      );\n    }\n  }\n  const { low = -0.5, high = 2.5 } = zone;\n\n  // we need to calculate the similarity of the isotopic distribution\n  let similarityProcessor = new Comparator(similarity);\n  similarityProcessor.setPeaks1([experimentalData.x, experimentalData.y]);\n\n  for (let i = 0; i < flatEntries.length; i++) {\n    const entry = flatEntries[i];\n    if (onStep) await onStep(i);\n    let isotopicDistribution = new IsotopicDistribution(entry.mf, {\n      allowNeutral: false,\n      ionizations: [entry.ionization],\n    });\n\n    let distribution = isotopicDistribution.getDistribution();\n    // we need to define the comparison zone that depends of the charge\n    let from = entry.ms.em + low / Math.abs(entry.ms.charge);\n    let to = entry.ms.em + high / Math.abs(entry.ms.charge);\n    similarityProcessor.setFromTo(from, to);\n\n    if (widthFunction) {\n      width = widthFunction(entry.ms.em);\n      similarityProcessor.setTrapezoid(width.bottom, width.top);\n    }\n    similarityProcessor.setPeaks2([distribution.xs, distribution.ys]);\n    let result = similarityProcessor.getSimilarity();\n\n    result.extractInfo1.from = from;\n    result.extractInfo1.to = to;\n\n    if (result.similarity > minSimilarity) {\n      entry.ms.similarity = {\n        value: result.similarity,\n        experimental: result.extract1,\n        theoretical: result.extract2,\n        difference: result.diff,\n        experimentalInfo: result.extractInfo1,\n        thereoticalInfo: result.extractInfo2,\n        quantity: result.extractInfo1.sum / sumY,\n        width,\n      };\n    }\n  }\n\n  if (!options.flatten) {\n    for (let database of Object.keys(results)) {\n      results[database] = results[database]\n        .filter((entry) => entry.ms.similarity)\n        .sort((a, b) => b.ms.similarity.value - a.ms.similarity.value);\n      for (let entry of results[database]) {\n        flatEntries.push(entry);\n      }\n    }\n  } else {\n    results = results\n      .filter((entry) => entry.ms.similarity)\n      .sort((a, b) => b.ms.similarity.value - a.ms.similarity.value);\n  }\n\n  return results;\n}\n","import crossFetch from 'cross-fetch';\n\nexport async function fetchJSON(url) {\n  const result = await crossFetch(url);\n  return result.json();\n}\n","import { ELECTRON_MASS } from 'chemical-elements';\nimport { findMFs } from 'mf-finder';\nimport { MF } from 'mf-parser';\nimport { getMsInfo, preprocessIonizations } from 'mf-utilities';\n\nimport { fetchJSON } from './util/fetchJSON.js';\n\n/**\n * Generates a database 'pubchem' based on all molecular formula available\n * in the database and a monoisotopic mass.\n * @param {number|string|number[]} masses - Observed monoisotopic mass\n * @param {object} [options={}]\n * @param {string} [options.databaseName='pubchem']\n * @param {string} [options.ionizations=''] - string containing a comma separated list of modifications\n * @param {number} [options.precision=1000] - Precision of the monoisotopic mass in ppm\n * @param {string} [options.ranges=''] -\n * @param {number} [options.limit=1000] - Maximal number of entries to return\n * @param {number} [options.minPubchemEntries=5] - Minimal number of molecules having a specific MF\n * @param {string} [options.url='https://pubchem.cheminfo.org/mfs/v1/fromEM'] - URL of the webservice\n */\n\nexport async function searchPubchem(masses, options = {}) {\n  const {\n    url = 'https://pubchem.cheminfo.org/mfs/v1/fromEM',\n    precision = 1000,\n    limit = 1000,\n    ranges = '',\n    minPubchemEntries = 5,\n  } = options;\n\n  if (typeof masses === 'number') {\n    masses = [masses];\n  }\n  if (typeof masses === 'string') {\n    masses = masses.split(/[\\r\\n\\t,; ]+/).map(Number);\n  }\n\n  let allowedEMs;\n  if (ranges) {\n    const allowedEMsArray = [];\n    for (let mass of masses) {\n      (\n        await findMFs(mass, {\n          ionizations: options.ionizations,\n          precision,\n          ranges,\n          limit: 100000,\n        })\n      ).mfs.forEach((mf) => allowedEMsArray.push(mf.em));\n    }\n    allowedEMs = Float64Array.from(allowedEMsArray).sort();\n  }\n  let promises = [];\n  let ionizations = preprocessIonizations(options.ionizations);\n  for (let mass of masses) {\n    for (let ionization of ionizations) {\n      let realMass =\n        mass * Math.abs(ionization.charge || 1) -\n        ionization.em +\n        ELECTRON_MASS * ionization.charge;\n      const pubchemURL = `${url}?em=${realMass}&precision=${precision}&limit=${limit}&minPubchemEntries=${minPubchemEntries}`;\n      promises.push(fetchJSON(pubchemURL));\n    }\n  }\n\n  let results = await Promise.all(promises);\n\n  let mfs = [];\n  for (let i = 0; i < results.length; i++) {\n    for (const entry of results[i].data) {\n      if (\n        allowedEMs &&\n        !allowedEMs.find((em) => Math.abs(em - entry.em) < 0.0000001)\n      ) {\n        continue;\n      }\n      try {\n        let mfInfo = new MF(entry._id).getInfo();\n        mfInfo.ionization = ionizations[i];\n        mfInfo.em = mfInfo.monoisotopicMass;\n        mfInfo.ms = getMsInfo(mfInfo, {\n          targetMass: masses,\n        }).ms;\n        mfInfo.info = { nbPubchemEntries: entry.count };\n        mfs.push(mfInfo);\n      } catch (e) {\n        // eslint-disable-next-line no-console\n        console.warn(`${e}`);\n      }\n    }\n  }\n  // because we can combine many ionizations we should resort the data\n  mfs.sort((a, b) => Math.abs(a.ms.ppm) - Math.abs(b.ms.ppm));\n  return mfs;\n}\n","import { ELECTRON_MASS } from 'chemical-elements';\nimport { findMFs } from 'mf-finder';\nimport { MF } from 'mf-parser';\nimport { getMsInfo, preprocessIonizations } from 'mf-utilities';\n\nimport { fetchJSON } from './util/fetchJSON.js';\n\n/**\n * Generates a database 'pubchem' based on all molecular formula available\n * in the database and a monoisotopic mass.\n * @param {number|string|number[]} masses - Observed monoisotopic mass\n * @param {object} [options={}]\n * @param {string} [options.databaseName='pubchem']\n * @param {string} [options.ionizations=''] - string containing a comma separated list of modifications\n * @param {string} [options.ranges=''] -\n * @param {number} [options.precision=1000] - Precision of the monoisotopic mass in ppm\n * @param {number} [options.limit=1000] - Maximal number of entries to return\n * @param {string} [options.url='https://pubchem.cheminfo.org/activesOrNaturals/v1/fromEM'] - URL of the webservice\n */\n\nexport async function searchActivesOrNaturals(masses, options = {}) {\n  const {\n    url = 'https://pubchem.cheminfo.org/activesOrNaturals/v1/fromEM',\n    precision = 1000,\n    limit = 1000,\n    ranges = '',\n  } = options;\n\n  if (typeof masses === 'number') {\n    masses = [masses];\n  }\n  if (typeof masses === 'string') {\n    masses = masses.split(/[\\r\\n\\t,; ]+/).map(Number);\n  }\n  let promises = [];\n  let ionizations = preprocessIonizations(options.ionizations);\n\n  let allowedEMs; // we prefer to use the exact mass rather than MF\n  if (ranges) {\n    const allowedEMsArray = [];\n    for (let mass of masses) {\n      (\n        await findMFs(mass, {\n          ionizations: options.ionizations,\n          precision,\n          ranges,\n          limit: 100000,\n        })\n      ).mfs.forEach((mf) => allowedEMsArray.push(mf.em));\n    }\n    allowedEMs = Float64Array.from(allowedEMsArray).sort();\n  }\n\n  for (let mass of masses) {\n    for (let ionization of ionizations) {\n      let realMass =\n        mass * Math.abs(ionization.charge || 1) -\n        ionization.em +\n        ELECTRON_MASS * ionization.charge;\n\n      const pubchemURL = `${url}?em=${realMass}&precision=${precision}&limit=${limit}`;\n      promises.push(fetchJSON(pubchemURL));\n    }\n  }\n\n  let results = await Promise.all(promises);\n  let mfs = [];\n  for (let i = 0; i < results.length; i++) {\n    for (let mf of results[i].data) {\n      try {\n        // would it be more efficient to filter later ???\n        if (\n          allowedEMs &&\n          !allowedEMs.find((em) => Math.abs(em - mf.data.em) < 0.0000001)\n        ) {\n          continue;\n        }\n\n        let mfInfo = new MF(mf.data.mf).getInfo();\n        mfInfo.ionization = ionizations[i];\n        mfInfo.em = mfInfo.monoisotopicMass;\n        mfInfo.ms = getMsInfo(mfInfo, {\n          targetMass: masses,\n        }).ms;\n\n        const info = mf.data;\n        info.idCode = mf.id;\n\n        delete info.em;\n        delete info.charge;\n        delete info.unsaturation;\n        delete info.mf;\n\n        mfInfo.info = mf.data;\n        mfs.push(mfInfo);\n      } catch (e) {\n        // eslint-disable-next-line no-console\n        console.warn(`${e}`);\n      }\n    }\n  }\n\n  // we will now group the data per mf\n  const grouped = {};\n  for (const mf of mfs) {\n    if (!grouped[mf.mf]) {\n      grouped[mf.mf] = {\n        mf: mf.mf,\n        em: mf.em,\n        ms: mf.ms,\n        molecules: [],\n        nbNatural: 0,\n        nbBioactive: 0,\n        nbPubmed: 0,\n        nbWithMassSpectra: 0,\n      };\n    }\n    grouped[mf.mf].molecules.push(mf);\n\n    if (mf.info.naturalProduct) grouped[mf.mf].nbNatural++;\n    if (mf.info.bioActive) grouped[mf.mf].nbBioactive++;\n    if (mf.info.nbMassSpectra) grouped[mf.mf].nbWithMassSpectra++;\n    if (mf.info.pubmeds && mf.info.pubmeds.length > 0) {\n      grouped[mf.mf].nbPubmed++;\n    }\n  }\n\n  const groupedArray = Object.keys(grouped).map((key) => grouped[key]);\n  // because we can combine many ionizations we should resort the data\n  groupedArray.sort((a, b) => Math.abs(a.ms.ppm) - Math.abs(b.ms.ppm));\n\n  return groupedArray;\n}\n","const toString = Object.prototype.toString;\n/**\n * Checks if an object is an instance of an Array (array or typed array).\n *\n * @param {any} value - Object to check.\n * @returns {boolean} True if the object is an array.\n */\nexport function isAnyArray(value) {\n    return toString.call(value).endsWith('Array]');\n}\n//# sourceMappingURL=index.js.map","export default function maybeToPrecision(value, digits) {\n  if (value < 0) {\n    value = 0 - value;\n    if (typeof digits === 'number') {\n      return `- ${value.toPrecision(digits)}`;\n    } else {\n      return `- ${value.toString()}`;\n    }\n  } else {\n    if (typeof digits === 'number') {\n      return value.toPrecision(digits);\n    } else {\n      return value.toString();\n    }\n  }\n}\n","import { isAnyArray } from 'is-any-array';\n\nexport default function checkArraySize(x, y) {\n  if (!isAnyArray(x) || !isAnyArray(y)) {\n    throw new TypeError('x and y must be arrays');\n  }\n  if (x.length !== y.length) {\n    throw new RangeError('x and y arrays must have the same length');\n  }\n}\n","import { isAnyArray } from 'is-any-array';\n\nexport { default as maybeToPrecision } from './maybeToPrecision';\nexport { default as checkArrayLength } from './checkArrayLength';\n\nexport default class BaseRegression {\n  constructor() {\n    if (new.target === BaseRegression) {\n      throw new Error('BaseRegression must be subclassed');\n    }\n  }\n\n  predict(x) {\n    if (typeof x === 'number') {\n      return this._predict(x);\n    } else if (isAnyArray(x)) {\n      const y = [];\n      for (let i = 0; i < x.length; i++) {\n        y.push(this._predict(x[i]));\n      }\n      return y;\n    } else {\n      throw new TypeError('x must be a number or array');\n    }\n  }\n\n  _predict() {\n    throw new Error('_predict must be implemented');\n  }\n\n  train() {\n    // Do nothing for this package\n  }\n\n  toString() {\n    return '';\n  }\n\n  toLaTeX() {\n    return '';\n  }\n\n  /**\n   * Return the correlation coefficient of determination (r) and chi-square.\n   * @param {Array<number>} x\n   * @param {Array<number>} y\n   * @return {object}\n   */\n  score(x, y) {\n    if (!isAnyArray(x) || !isAnyArray(y) || x.length !== y.length) {\n      throw new Error('x and y must be arrays of the same length');\n    }\n\n    const n = x.length;\n    const y2 = new Array(n);\n    for (let i = 0; i < n; i++) {\n      y2[i] = this._predict(x[i]);\n    }\n\n    let xSum = 0;\n    let ySum = 0;\n    let chi2 = 0;\n    let rmsd = 0;\n    let xSquared = 0;\n    let ySquared = 0;\n    let xY = 0;\n    for (let i = 0; i < n; i++) {\n      xSum += y2[i];\n      ySum += y[i];\n      xSquared += y2[i] * y2[i];\n      ySquared += y[i] * y[i];\n      xY += y2[i] * y[i];\n      if (y[i] !== 0) {\n        chi2 += ((y[i] - y2[i]) * (y[i] - y2[i])) / y[i];\n      }\n      rmsd += (y[i] - y2[i]) * (y[i] - y2[i]);\n    }\n\n    const r =\n      (n * xY - xSum * ySum) /\n      Math.sqrt((n * xSquared - xSum * xSum) * (n * ySquared - ySum * ySum));\n\n    return {\n      r: r,\n      r2: r * r,\n      chi2: chi2,\n      rmsd: Math.sqrt(rmsd / n),\n    };\n  }\n}\n","(function(){function a(d){for(var e=0,f=d.length-1,g=void 0,h=void 0,i=void 0,j=c(e,f);!0;){if(f<=e)return d[j];if(f==e+1)return d[e]>d[f]&&b(d,e,f),d[j];for(g=c(e,f),d[g]>d[f]&&b(d,g,f),d[e]>d[f]&&b(d,e,f),d[g]>d[e]&&b(d,g,e),b(d,g,e+1),h=e+1,i=f;!0;){do h++;while(d[e]>d[h]);do i--;while(d[i]>d[e]);if(i<h)break;b(d,h,i)}b(d,e,i),i<=j&&(e=h),i>=j&&(f=i-1)}}var b=function b(d,e,f){var _ref;return _ref=[d[f],d[e]],d[e]=_ref[0],d[f]=_ref[1],_ref},c=function c(d,e){return~~((d+e)/2)};'undefined'!=typeof module&&module.exports?module.exports=a:window.median=a})();\n","import { isAnyArray } from 'is-any-array';\nimport quickSelectMedian from 'median-quickselect';\n\nfunction median(input) {\n  if (!isAnyArray(input)) {\n    throw new TypeError('input must be an array');\n  }\n\n  if (input.length === 0) {\n    throw new TypeError('input must not be empty');\n  }\n\n  return quickSelectMedian(input.slice());\n}\n\nexport { median as default };\n","import BaseRegression, {\n  checkArrayLength,\n  maybeToPrecision\n} from 'ml-regression-base';\nimport median from 'ml-array-median';\n\nexport default class TheilSenRegression extends BaseRegression {\n  /**\n   * Theil–Sen estimator\n   * https://en.wikipedia.org/wiki/Theil%E2%80%93Sen_estimator\n   * @param {Array<number>|boolean} x\n   * @param {Array<number>|object} y\n   * @constructor\n   */\n  constructor(x, y) {\n    super();\n    if (x === true) {\n      // loads the model\n      this.slope = y.slope;\n      this.intercept = y.intercept;\n      this.coefficients = y.coefficients;\n    } else {\n      // creates the model\n      checkArrayLength(x, y);\n      theilSen(this, x, y);\n    }\n  }\n\n  toJSON() {\n    return {\n      name: 'TheilSenRegression',\n      slope: this.slope,\n      intercept: this.intercept\n    };\n  }\n\n  _predict(input) {\n    return this.slope * input + this.intercept;\n  }\n\n  computeX(input) {\n    return (input - this.intercept) / this.slope;\n  }\n\n  toString(precision) {\n    var result = 'f(x) = ';\n    if (this.slope) {\n      var xFactor = maybeToPrecision(this.slope, precision);\n      result += `${Math.abs(xFactor - 1) < 1e-5 ? '' : `${xFactor} * `}x`;\n      if (this.intercept) {\n        var absIntercept = Math.abs(this.intercept);\n        var operator = absIntercept === this.intercept ? '+' : '-';\n        result +=\n          ` ${operator} ${maybeToPrecision(absIntercept, precision)}`;\n      }\n    } else {\n      result += maybeToPrecision(this.intercept, precision);\n    }\n    return result;\n  }\n\n  toLaTeX(precision) {\n    return this.toString(precision);\n  }\n\n  static load(json) {\n    if (json.name !== 'TheilSenRegression') {\n      throw new TypeError('not a Theil-Sen model');\n    }\n    return new TheilSenRegression(true, json);\n  }\n}\n\nfunction theilSen(regression, x, y) {\n  let len = x.length;\n  let slopes = new Array(len * len);\n  let count = 0;\n  for (let i = 0; i < len; ++i) {\n    for (let j = i + 1; j < len; ++j) {\n      if (x[i] !== x[j]) {\n        slopes[count++] = (y[j] - y[i]) / (x[j] - x[i]);\n      }\n    }\n  }\n  slopes.length = count;\n  let medianSlope = median(slopes);\n\n  let cuts = new Array(len);\n  for (let i = 0; i < len; ++i) {\n    cuts[i] = y[i] - medianSlope * x[i];\n  }\n\n  regression.slope = medianSlope;\n  regression.intercept = median(cuts);\n  regression.coefficients = [regression.intercept, regression.slope];\n}\n","import { isAnyArray } from 'is-any-array';\n/**\n * Checks if input is of type array\n *\n * @param input - input\n */\nexport function xCheck(input) {\n    if (!isAnyArray(input)) {\n        throw new TypeError('input must be an array');\n    }\n    if (input.length === 0) {\n        throw new TypeError('input must not be empty');\n    }\n}\n//# sourceMappingURL=xCheck.js.map","/**\n * Returns the closest index of a `target`\n *\n * @param array - array of numbers\n * @param target - target\n * @returns - closest index\n */\nexport function xFindClosestIndex(array, target, options = {}) {\n    const { sorted = true } = options;\n    if (sorted) {\n        let low = 0;\n        let high = array.length - 1;\n        let middle = 0;\n        while (high - low > 1) {\n            middle = low + ((high - low) >> 1);\n            if (array[middle] < target) {\n                low = middle;\n            }\n            else if (array[middle] > target) {\n                high = middle;\n            }\n            else {\n                return middle;\n            }\n        }\n        if (low < array.length - 1) {\n            if (Math.abs(target - array[low]) < Math.abs(array[low + 1] - target)) {\n                return low;\n            }\n            else {\n                return low + 1;\n            }\n        }\n        else {\n            return low;\n        }\n    }\n    else {\n        let index = 0;\n        let diff = Number.POSITIVE_INFINITY;\n        for (let i = 0; i < array.length; i++) {\n            const currentDiff = Math.abs(array[i] - target);\n            if (currentDiff < diff) {\n                diff = currentDiff;\n                index = i;\n            }\n        }\n        return index;\n    }\n}\n//# sourceMappingURL=xFindClosestIndex.js.map","import { xFindClosestIndex } from './xFindClosestIndex';\n/**\n * Returns an object with {fromIndex, toIndex} for a specific from / to\n *\n * @param x - array of numbers\n * @param options - Options\n */\nexport function xGetFromToIndex(x, options = {}) {\n    let { fromIndex, toIndex, from, to } = options;\n    if (fromIndex === undefined) {\n        if (from !== undefined) {\n            fromIndex = xFindClosestIndex(x, from);\n        }\n        else {\n            fromIndex = 0;\n        }\n    }\n    if (toIndex === undefined) {\n        if (to !== undefined) {\n            toIndex = xFindClosestIndex(x, to);\n        }\n        else {\n            toIndex = x.length - 1;\n        }\n    }\n    if (fromIndex < 0)\n        fromIndex = 0;\n    if (toIndex < 0)\n        toIndex = 0;\n    if (fromIndex >= x.length)\n        fromIndex = x.length - 1;\n    if (toIndex >= x.length)\n        toIndex = x.length - 1;\n    if (fromIndex > toIndex)\n        [fromIndex, toIndex] = [toIndex, fromIndex];\n    return { fromIndex, toIndex };\n}\n//# sourceMappingURL=xGetFromToIndex.js.map","import { xCheck } from './xCheck';\nimport { xGetFromToIndex } from './xGetFromToIndex';\n/**\n * Computes the maximal value of an array of values\n *\n * @param array - array of numbers\n * @param options - options\n */\nexport function xMaxValue(array, options = {}) {\n    xCheck(array);\n    const { fromIndex, toIndex } = xGetFromToIndex(array, options);\n    let maxValue = array[fromIndex];\n    for (let i = fromIndex + 1; i <= toIndex; i++) {\n        if (array[i] > maxValue) {\n            maxValue = array[i];\n        }\n    }\n    return maxValue;\n}\n//# sourceMappingURL=xMaxValue.js.map","import { xCheck } from './xCheck';\nimport { xGetFromToIndex } from './xGetFromToIndex';\n/**\n * Computes the minimal value of an array of values\n *\n * @param array - array of numbers\n * @param options - options\n */\nexport function xMinValue(array, options = {}) {\n    xCheck(array);\n    const { fromIndex, toIndex } = xGetFromToIndex(array, options);\n    let minValue = array[fromIndex];\n    for (let i = fromIndex + 1; i <= toIndex; i++) {\n        if (array[i] < minValue) {\n            minValue = array[i];\n        }\n    }\n    return minValue;\n}\n//# sourceMappingURL=xMinValue.js.map","import Regression from 'ml-regression-theil-sen';\nimport { xMinValue, xMaxValue } from 'ml-spectra-processing';\n\n/**\n * Calculates a function that allows post-calibration on mass spectra based on the error in assignment\n * @param {*} similarities\n * @param {object} [options={}]\n * @returns\n */\nexport function massShifts(similarities, options = {}) {\n  const { minSimilarity = 0.95, minLength = 10 } = options;\n\n  let results = [];\n  if (!Array.isArray(similarities)) {\n    for (let key of results) {\n      for (let entry of results[key]) {\n        results.push(entry);\n      }\n    }\n  } else {\n    results = similarities;\n  }\n\n  results = results.filter(\n    (result) =>\n      result.ms &&\n      result.ms.similarity &&\n      result.ms.similarity.value > minSimilarity,\n  );\n\n  if (results.length < minLength) {\n    throw new Error(\n      `X rescale can not be applied. We need at least ${minLength} peaks with over ${Math.round(\n        minSimilarity * 100,\n      )}% similarity`,\n    );\n  }\n\n  const data = results\n    .map((result) => {\n      return {\n        em: result.ms.em,\n        delta: result.ms.delta,\n      };\n    })\n    .sort((a, b) => a.em - b.em);\n\n  let shifts = { x: [], y: [] };\n  data.forEach((datum) => {\n    shifts.x.push(Number(datum.em));\n    shifts.y.push(Number(datum.delta));\n  });\n\n  const regression = new Regression(shifts.x, shifts.y);\n\n  let minX = xMinValue(shifts.x);\n  let maxX = xMaxValue(shifts.x);\n\n  let shiftsPPM = { x: shifts.x, y: [] };\n  data.forEach((datum) => {\n    shiftsPPM.y.push(Number((datum.delta / datum.em) * 1e6));\n  });\n\n  let regressionChart = { x: [], y: [] };\n\n  for (let i = minX; i < maxX; i += (maxX - minX) / 1000) {\n    regressionChart.x.push(i);\n    regressionChart.y.push(regression.predict(i));\n  }\n\n  return {\n    shifts,\n    shiftsPPM,\n    fit: regressionChart,\n    score: regression.score(shifts.x, shifts.y),\n\n    predictFct: regression.predict.bind(regression),\n    tex: regression.toLaTeX(3),\n    slope: regression.slope,\n    intercept: regression.intercept,\n    predictFctString: `${regression.slope} * mass + ${regression.intercept}`,\n  };\n}\n","import { Spectrum } from 'ms-spectrum';\n\nimport { appendFragmentsInfo } from './append/appendFragmentsInfo.js';\nimport { fromArray } from './from/fromArray.js';\nimport { fromMolecules } from './from/fromMolecules.js';\nimport { fromMonoisotopicMass } from './from/fromMonoisotopicMass.js';\nimport { fromNucleicSequence } from './from/fromNucleicSequence.js';\nimport { fromPeptidicSequence } from './from/fromPeptidicSequence.js';\nimport { fromRange } from './from/fromRange.js';\nimport { loadCommercials } from './loadCommercials.js';\nimport { loadGoogleSheet } from './loadGoogleSheet.js';\nimport { loadKnapSack } from './loadKnapSack.js';\nimport { search } from './search.js';\nimport { searchMSEM } from './searchMSEM.js';\nimport { searchSimilarity } from './searchSimilarity.js';\n\nexport * from './searchPubchem.js';\nexport * from './searchActivesOrNaturals.js';\nexport * from './massShifts.js';\n\n/**\n * A class that deals with database of monoisotopic mass and molecular formula\n */\nexport class EMDB {\n  constructor() {\n    this.databases = {};\n    this.experimentalSpectrum = undefined;\n  }\n\n  /**\n   *\n   * @param {*} data\n   * @param {object} [options={}]\n   * @param {number} [options.normed=true] Should we normed (sum Y to 1) the experimental spectrum ?\n   */\n  setExperimentalSpectrum(data, options = {}) {\n    const { normed = true } = options;\n    if (normed) {\n      this.experimentalSpectrum = new Spectrum(data).normedY();\n    } else {\n      this.experimentalSpectrum = new Spectrum(data);\n    }\n    return this.experimentalSpectrum;\n  }\n\n  /**\n   * Add a new database using the KnapSack content\n   * @param {*} options\n   */\n  async loadKnapSack(options = {}) {\n    const { databaseName = 'knapSack', forceReload = false } = options;\n    if (this.databases[databaseName] && !forceReload) return;\n    this.databases[databaseName] = await loadKnapSack();\n  }\n\n  /**\n   * Add a new database of 12000 commercial products\n   * @param {*} options\n   */\n  async loadCommercials(options = {}) {\n    const { databaseName = 'commercials', forceReload = false } = options;\n    if (this.databases[databaseName] && !forceReload) return;\n    this.databases[databaseName] = await loadCommercials();\n  }\n\n  get(databaseName) {\n    return this.databases[databaseName];\n  }\n\n  /**\n   * Load the contaminants database from a google sheet document\n   * @param {object} [options={}]\n   * @param {string} [options.databaseName='contaminants']\n   * @param {string} [options.forceReload=false]\n   */\n  async loadContaminants(options = {}) {\n    const { databaseName = 'contaminants', forceReload = false } = options;\n    if (this.databases[databaseName] && !forceReload) return;\n    this.databases[databaseName] = await loadGoogleSheet();\n  }\n\n  /**\n   * Load a google sheet containing MF information\n   * @param {object} [options={}]\n   * @param {string} [options.databaseName='sheet']\n   * @param {string} [options.forceReload=false]\n   */\n\n  async loadGoogleSheet(options = {}) {\n    const { databaseName = 'sheet', forceReload = false } = options;\n    if (this.databases[databaseName] && !forceReload) return;\n    this.databases[databaseName] = await loadGoogleSheet();\n  }\n\n  async loadTest() {\n    await this.fromArray(['C1-100'], {\n      databaseName: 'test',\n      ionizations: '+',\n    });\n  }\n\n  async loadNeutralTest(options = {}) {\n    const { maxC = 100 } = options;\n    await this.fromArray([`C1-${maxC}`], { databaseName: 'test' });\n  }\n\n  async fromMonoisotopicMass(mass, options = {}) {\n    const { databaseName = 'monoisotopic', append = false } = options;\n    let result = await fromMonoisotopicMass(mass, options);\n    replaceOrAppend(this, databaseName, result.mfs, append);\n    return result;\n  }\n\n  async fromArray(sequence, options = {}) {\n    const { databaseName = 'generated', append = false, estimate } = options;\n    const results = await fromArray(sequence, options);\n    if (estimate) return results;\n    replaceOrAppend(this, databaseName, results, append);\n  }\n\n  async fromMolecules(entries, ocl, options = {}) {\n    const { databaseName = 'molecules', append = false } = options;\n    const results = await fromMolecules(entries, ocl, options);\n    replaceOrAppend(this, databaseName, results, append);\n  }\n\n  async fromRange(sequence, options = {}) {\n    const { databaseName = 'generated', append = false, estimate } = options;\n    const results = await fromRange(sequence, options);\n    if (estimate) return results;\n    replaceOrAppend(this, databaseName, results, append);\n  }\n\n  async fromPeptidicSequence(sequence, options = {}) {\n    const { databaseName = 'peptidic', append = false, estimate } = options;\n    const results = await fromPeptidicSequence(sequence, options);\n    if (estimate) return results;\n    replaceOrAppend(this, databaseName, results, append);\n  }\n\n  /**\n   *\n   * @param {string} databaseName\n   * @param {object} [options={}]\n   * @param {number} [options.precision=100]\n   * @param {string} [options.ionizations='']\n   * @returns\n   */\n  async appendFragmentsInfo(databaseName, options = {}) {\n    const database = this.databases[databaseName];\n    await appendFragmentsInfo(this.experimentalSpectrum, database, options);\n    return database;\n  }\n\n  async fromNucleicSequence(sequence, options = {}) {\n    const { databaseName = 'nucleic', append = false, estimate } = options;\n    const results = await fromNucleicSequence(sequence, options);\n    if (estimate) return results;\n    replaceOrAppend(this, databaseName, results, append);\n  }\n\n  listDatabases() {\n    return Object.keys(this.databases).sort();\n  }\n\n  getInfo() {\n    return {\n      databases: Object.keys(this.databases)\n        .sort()\n        .map((key) => {\n          return { name: key, nbEntries: this.databases[key].length };\n        }),\n    };\n  }\n\n  search(filter, options = {}) {\n    return search(this, filter, options);\n  }\n\n  searchMSEM(filter, options = {}) {\n    return searchMSEM(this, filter, options);\n  }\n\n  searchSimilarity(options = {}) {\n    return searchSimilarity(this, options);\n  }\n}\n\nfunction replaceOrAppend(emdb, databaseName, results, append = false) {\n  if (!emdb.databases[databaseName] || !append) {\n    emdb.databases[databaseName] = results;\n    return;\n  }\n  emdb.databases[databaseName] = emdb.databases[databaseName].concat(results);\n}\n","export function appendInternals(data) {\n  // for each line (internal fragment) we calculate the vertical position\n  // where it should be drawn as well and the maximal number of lines\n  let maxNumberLines = 0;\n  for (let result of data.results) {\n    if (result.internal) {\n      result.slot = assignSlot(result.from, result.to, data.residues.residues);\n      if (result.slot > maxNumberLines) maxNumberLines = result.slot;\n    }\n  }\n  for (let row of data.rows) {\n    row.info.internals = maxNumberLines;\n  }\n}\n\n// we need to define the height of the line.\n// we need to find a height that is not yet used.\nfunction assignSlot(from, to, residues) {\n  let used = {};\n  if (from > 0) from--; // we ensure that we don't put on the same line to sequences that are consecutive\n  for (let i = from; i < to; i++) {\n    let residue = residues[i];\n    residue.paper.usedSlots.forEach((usedSlot, index) => {\n      used[index] = true;\n    });\n  }\n  let counter = 0;\n  while (true) {\n    if (!used[counter]) {\n      break;\n    }\n    counter++;\n  }\n  for (let i = from; i < to; i++) {\n    residues[i].paper.usedSlots[counter] = true;\n  }\n  return counter;\n}\n","import { groupsObject } from 'chemical-groups';\nimport * as Nucleotide from 'nucleotide';\nimport * as Peptide from 'peptide';\n\nconst ALTERNATIVES = ['', '¹', '²', '³', '⁴', '⁵', '⁶', '⁷', '⁸', '⁹'];\nconst SYMBOLS = ['Θ', 'Δ', 'Λ', 'Φ', 'Ω', 'Γ', 'Χ'];\n\nlet currentSymbol = 0;\n\n/**\n * Code that allows to split a sequence of amino acids or nucleotides natural or non natural\n * @param {string} [sequence]\n * @param {object} [options={}]\n * @param {string} [options.kind] - peptide, rna, ds-dna or dna. Default if contains U: rna, otherwise ds-dna\n * @param {string} [options.fivePrime=monophosphate] - alcohol, monophosphate, diphosphate, triphosphate\n * @param {string} [options.circular=false]\n */\n\nexport function appendResidues(data, sequence, options = {}) {\n  const { kind = 'peptide' } = options;\n\n  currentSymbol = 0;\n  // we normalize the sequence to 3 letter codes\n\n  if (kind === 'peptide') {\n    sequence = Peptide.sequenceToMF(sequence);\n  } else {\n    sequence = Nucleotide.sequenceToMF(sequence, options);\n  }\n\n  const result = {\n    begin: '',\n    end: '',\n    residues: [],\n  };\n\n  const STATE_BEGIN = 0;\n  const STATE_MIDDLE = 1;\n  const STATE_END = 2;\n\n  let parenthesisLevel = 0;\n  let state = STATE_BEGIN; // as long as we don't have an uppercase followed by 2 lowercases\n  for (let i = 0; i < sequence.length; i++) {\n    let currentChar = sequence.charAt(i);\n    let nextChar = i < sequence.length - 1 ? sequence.charAt(i + 1) : '';\n    let nextNextChar = i < sequence.length - 2 ? sequence.charAt(i + 2) : '';\n\n    if (\n      state === STATE_BEGIN &&\n      currentChar.match(/[A-Z]/) &&\n      nextChar.match(/[a-z]/) &&\n      nextNextChar.match(/[a-z]/) &&\n      parenthesisLevel === 0\n    ) {\n      state = STATE_MIDDLE;\n    }\n\n    if (\n      state === STATE_MIDDLE &&\n      !sequence.substring(i).match(/[A-Z][a-z][a-z]/) &&\n      !currentChar.match(/[a-z]/) &&\n      parenthesisLevel === 0\n    ) {\n      state = STATE_END;\n    } else if (\n      currentChar.match(/[A-Z]/) &&\n      nextChar.match(/[a-z]/) &&\n      nextNextChar.match(/[a-z]/) &&\n      parenthesisLevel === 0\n    ) {\n      result.residues.push('');\n    }\n\n    switch (state) {\n      case STATE_BEGIN:\n        result.begin = result.begin + currentChar;\n        break;\n      case STATE_MIDDLE:\n        result.residues[result.residues.length - 1] =\n          result.residues[result.residues.length - 1] + currentChar;\n        break;\n      case STATE_END:\n        result.end = result.end + currentChar;\n        break;\n      default:\n    }\n\n    if (currentChar === '(') {\n      parenthesisLevel++;\n    } else if (currentChar === ')') {\n      parenthesisLevel--;\n    }\n  }\n\n  // we process all the residues\n  let alternatives = {};\n  let replacements = {};\n  for (let i = 0; i < result.residues.length; i++) {\n    let label = result.residues[i];\n    let residue = {\n      value: label,\n      results: {\n        begin: [],\n        end: [],\n      },\n    };\n    residue.fromBegin = i + 1;\n    residue.fromEnd = result.residues.length - i;\n    residue.kind = 'residue';\n    if (label.includes('(')) {\n      getModifiedReplacement(label, residue, alternatives, replacements);\n    } else if (groupsObject[label] && groupsObject[label].oneLetter) {\n      residue.label = groupsObject[label].oneLetter;\n    } else {\n      getUnknownReplacement(label, residue, replacements);\n    }\n    result.residues[i] = residue;\n  }\n  result.begin = removeStartEndParenthesis(result.begin);\n  result.end = removeStartEndParenthesis(result.end);\n  if (result.begin.length > 2) {\n    let label = options.kind === 'peptide' ? 'Nter' : \"5'\";\n    replacements[result.begin] = {\n      label,\n    };\n    result.begin = label;\n  }\n  if (result.end.length > 2) {\n    let label = options.kind === 'peptide' ? 'Cter' : \"3'\";\n    replacements[result.end] = {\n      label,\n    };\n    result.end = label;\n  }\n\n  result.begin = { label: result.begin, kind: 'begin' };\n  result.end = { label: result.end, kind: 'end' };\n  result.alternatives = alternatives;\n  result.replacements = replacements;\n\n  result.all = [result.begin].concat(result.residues, [result.end]);\n\n  result.all.forEach((entry) => {\n    entry.info = {\n      nbOver: 0,\n      nbUnder: 0,\n    };\n  });\n\n  data.residues = result;\n}\n\nfunction getUnknownReplacement(unknownResidue, residue, replacements) {\n  if (!replacements[unknownResidue]) {\n    replacements[unknownResidue] = {\n      label: SYMBOLS[currentSymbol] || '?',\n      id: unknownResidue,\n    };\n  }\n  currentSymbol++;\n  residue.replaced = true;\n  residue.label = replacements[unknownResidue].label;\n}\n\nfunction getModifiedReplacement(\n  modifiedResidue,\n  residue,\n  alternatives,\n  replacements,\n) {\n  if (!replacements[modifiedResidue]) {\n    let position = modifiedResidue.indexOf('(');\n    let residueCode = modifiedResidue.substring(0, position);\n    let modification = removeStartEndParenthesis(\n      modifiedResidue.substring(position),\n    );\n\n    if (\n      groupsObject[residueCode] &&\n      groupsObject[residueCode].alternativeOneLetter\n    ) {\n      let alternativeOneLetter = groupsObject[residueCode].alternativeOneLetter;\n\n      if (!alternatives[alternativeOneLetter]) {\n        alternatives[alternativeOneLetter] = { count: 1 };\n      } else {\n        alternatives[alternativeOneLetter].count++;\n      }\n      replacements[modifiedResidue] = {\n        label:\n          ALTERNATIVES[alternatives[alternativeOneLetter].count - 1] +\n          alternativeOneLetter,\n        residue: residueCode,\n        modification,\n      };\n    } else {\n      getUnknownReplacement(modifiedResidue, residue, replacements);\n    }\n  }\n  residue.replaced = true;\n  residue.label = replacements[modifiedResidue].label;\n}\n\nfunction removeStartEndParenthesis(mf) {\n  if (mf[0] === '(' && mf[mf.length - 1] === ')') {\n    return mf.substring(1, mf.length - 1);\n  }\n  return mf;\n}\n","/*!\n* @svgdotjs/svg.js - A lightweight library for manipulating and animating SVG.\n* @version 3.1.1\n* https://svgjs.dev/\n*\n* @copyright Wout Fierens <wout@mick-wout.com>\n* @license MIT\n*\n* BUILT: Fri Jun 25 2021 15:37:54 GMT+0200 (Mitteleuropäische Sommerzeit)\n*/;\nconst methods$1 = {};\nconst names = [];\nfunction registerMethods(name, m) {\n  if (Array.isArray(name)) {\n    for (const _name of name) {\n      registerMethods(_name, m);\n    }\n\n    return;\n  }\n\n  if (typeof name === 'object') {\n    for (const _name in name) {\n      registerMethods(_name, name[_name]);\n    }\n\n    return;\n  }\n\n  addMethodNames(Object.getOwnPropertyNames(m));\n  methods$1[name] = Object.assign(methods$1[name] || {}, m);\n}\nfunction getMethodsFor(name) {\n  return methods$1[name] || {};\n}\nfunction getMethodNames() {\n  return [...new Set(names)];\n}\nfunction addMethodNames(_names) {\n  names.push(..._names);\n}\n\n// Map function\nfunction map(array, block) {\n  let i;\n  const il = array.length;\n  const result = [];\n\n  for (i = 0; i < il; i++) {\n    result.push(block(array[i]));\n  }\n\n  return result;\n} // Filter function\n\nfunction filter(array, block) {\n  let i;\n  const il = array.length;\n  const result = [];\n\n  for (i = 0; i < il; i++) {\n    if (block(array[i])) {\n      result.push(array[i]);\n    }\n  }\n\n  return result;\n} // Degrees to radians\n\nfunction radians(d) {\n  return d % 360 * Math.PI / 180;\n} // Radians to degrees\n\nfunction degrees(r) {\n  return r * 180 / Math.PI % 360;\n} // Convert dash-separated-string to camelCase\n\nfunction camelCase(s) {\n  return s.toLowerCase().replace(/-(.)/g, function (m, g) {\n    return g.toUpperCase();\n  });\n} // Convert camel cased string to dash separated\n\nfunction unCamelCase(s) {\n  return s.replace(/([A-Z])/g, function (m, g) {\n    return '-' + g.toLowerCase();\n  });\n} // Capitalize first letter of a string\n\nfunction capitalize(s) {\n  return s.charAt(0).toUpperCase() + s.slice(1);\n} // Calculate proportional width and height values when necessary\n\nfunction proportionalSize(element, width, height, box) {\n  if (width == null || height == null) {\n    box = box || element.bbox();\n\n    if (width == null) {\n      width = box.width / box.height * height;\n    } else if (height == null) {\n      height = box.height / box.width * width;\n    }\n  }\n\n  return {\n    width: width,\n    height: height\n  };\n}\n/**\r\n * This function adds support for string origins.\r\n * It searches for an origin in o.origin o.ox and o.originX.\r\n * This way, origin: {x: 'center', y: 50} can be passed as well as ox: 'center', oy: 50\r\n**/\n\nfunction getOrigin(o, element) {\n  const origin = o.origin; // First check if origin is in ox or originX\n\n  let ox = o.ox != null ? o.ox : o.originX != null ? o.originX : 'center';\n  let oy = o.oy != null ? o.oy : o.originY != null ? o.originY : 'center'; // Then check if origin was used and overwrite in that case\n\n  if (origin != null) {\n    [ox, oy] = Array.isArray(origin) ? origin : typeof origin === 'object' ? [origin.x, origin.y] : [origin, origin];\n  } // Make sure to only call bbox when actually needed\n\n\n  const condX = typeof ox === 'string';\n  const condY = typeof oy === 'string';\n\n  if (condX || condY) {\n    const {\n      height,\n      width,\n      x,\n      y\n    } = element.bbox(); // And only overwrite if string was passed for this specific axis\n\n    if (condX) {\n      ox = ox.includes('left') ? x : ox.includes('right') ? x + width : x + width / 2;\n    }\n\n    if (condY) {\n      oy = oy.includes('top') ? y : oy.includes('bottom') ? y + height : y + height / 2;\n    }\n  } // Return the origin as it is if it wasn't a string\n\n\n  return [ox, oy];\n}\n\nvar utils = {\n  __proto__: null,\n  map: map,\n  filter: filter,\n  radians: radians,\n  degrees: degrees,\n  camelCase: camelCase,\n  unCamelCase: unCamelCase,\n  capitalize: capitalize,\n  proportionalSize: proportionalSize,\n  getOrigin: getOrigin\n};\n\n// Default namespaces\nconst svg = 'http://www.w3.org/2000/svg';\nconst html = 'http://www.w3.org/1999/xhtml';\nconst xmlns = 'http://www.w3.org/2000/xmlns/';\nconst xlink = 'http://www.w3.org/1999/xlink';\nconst svgjs = 'http://svgjs.dev/svgjs';\n\nvar namespaces = {\n  __proto__: null,\n  svg: svg,\n  html: html,\n  xmlns: xmlns,\n  xlink: xlink,\n  svgjs: svgjs\n};\n\nconst globals = {\n  window: typeof window === 'undefined' ? null : window,\n  document: typeof document === 'undefined' ? null : document\n};\nfunction registerWindow(win = null, doc = null) {\n  globals.window = win;\n  globals.document = doc;\n}\nconst save = {};\nfunction saveWindow() {\n  save.window = globals.window;\n  save.document = globals.document;\n}\nfunction restoreWindow() {\n  globals.window = save.window;\n  globals.document = save.document;\n}\nfunction withWindow(win, fn) {\n  saveWindow();\n  registerWindow(win, win.document);\n  fn(win, win.document);\n  restoreWindow();\n}\nfunction getWindow() {\n  return globals.window;\n}\n\nclass Base {// constructor (node/*, {extensions = []} */) {\n  //   // this.tags = []\n  //   //\n  //   // for (let extension of extensions) {\n  //   //   extension.setup.call(this, node)\n  //   //   this.tags.push(extension.name)\n  //   // }\n  // }\n}\n\nconst elements = {};\nconst root = '___SYMBOL___ROOT___'; // Method for element creation\n\nfunction create(name, ns = svg) {\n  // create element\n  return globals.document.createElementNS(ns, name);\n}\nfunction makeInstance(element, isHTML = false) {\n  if (element instanceof Base) return element;\n\n  if (typeof element === 'object') {\n    return adopter(element);\n  }\n\n  if (element == null) {\n    return new elements[root]();\n  }\n\n  if (typeof element === 'string' && element.charAt(0) !== '<') {\n    return adopter(globals.document.querySelector(element));\n  } // Make sure, that HTML elements are created with the correct namespace\n\n\n  const wrapper = isHTML ? globals.document.createElement('div') : create('svg');\n  wrapper.innerHTML = element; // We can use firstChild here because we know,\n  // that the first char is < and thus an element\n\n  element = adopter(wrapper.firstChild); // make sure, that element doesnt have its wrapper attached\n\n  wrapper.removeChild(wrapper.firstChild);\n  return element;\n}\nfunction nodeOrNew(name, node) {\n  return node instanceof globals.window.Node ? node : create(name);\n} // Adopt existing svg elements\n\nfunction adopt(node) {\n  // check for presence of node\n  if (!node) return null; // make sure a node isn't already adopted\n\n  if (node.instance instanceof Base) return node.instance;\n\n  if (node.nodeName === '#document-fragment') {\n    return new elements.Fragment(node);\n  } // initialize variables\n\n\n  let className = capitalize(node.nodeName || 'Dom'); // Make sure that gradients are adopted correctly\n\n  if (className === 'LinearGradient' || className === 'RadialGradient') {\n    className = 'Gradient'; // Fallback to Dom if element is not known\n  } else if (!elements[className]) {\n    className = 'Dom';\n  }\n\n  return new elements[className](node);\n}\nlet adopter = adopt;\nfunction mockAdopt(mock = adopt) {\n  adopter = mock;\n}\nfunction register(element, name = element.name, asRoot = false) {\n  elements[name] = element;\n  if (asRoot) elements[root] = element;\n  addMethodNames(Object.getOwnPropertyNames(element.prototype));\n  return element;\n}\nfunction getClass(name) {\n  return elements[name];\n} // Element id sequence\n\nlet did = 1000; // Get next named element id\n\nfunction eid(name) {\n  return 'Svgjs' + capitalize(name) + did++;\n} // Deep new id assignment\n\nfunction assignNewId(node) {\n  // do the same for SVG child nodes as well\n  for (let i = node.children.length - 1; i >= 0; i--) {\n    assignNewId(node.children[i]);\n  }\n\n  if (node.id) {\n    node.id = eid(node.nodeName);\n    return node;\n  }\n\n  return node;\n} // Method for extending objects\n\nfunction extend(modules, methods) {\n  let key, i;\n  modules = Array.isArray(modules) ? modules : [modules];\n\n  for (i = modules.length - 1; i >= 0; i--) {\n    for (key in methods) {\n      modules[i].prototype[key] = methods[key];\n    }\n  }\n}\nfunction wrapWithAttrCheck(fn) {\n  return function (...args) {\n    const o = args[args.length - 1];\n\n    if (o && o.constructor === Object && !(o instanceof Array)) {\n      return fn.apply(this, args.slice(0, -1)).attr(o);\n    } else {\n      return fn.apply(this, args);\n    }\n  };\n}\n\nfunction siblings() {\n  return this.parent().children();\n} // Get the current position siblings\n\nfunction position() {\n  return this.parent().index(this);\n} // Get the next element (will return null if there is none)\n\nfunction next() {\n  return this.siblings()[this.position() + 1];\n} // Get the next element (will return null if there is none)\n\nfunction prev() {\n  return this.siblings()[this.position() - 1];\n} // Send given element one step forward\n\nfunction forward() {\n  const i = this.position();\n  const p = this.parent(); // move node one step forward\n\n  p.add(this.remove(), i + 1);\n  return this;\n} // Send given element one step backward\n\nfunction backward() {\n  const i = this.position();\n  const p = this.parent();\n  p.add(this.remove(), i ? i - 1 : 0);\n  return this;\n} // Send given element all the way to the front\n\nfunction front() {\n  const p = this.parent(); // Move node forward\n\n  p.add(this.remove());\n  return this;\n} // Send given element all the way to the back\n\nfunction back() {\n  const p = this.parent(); // Move node back\n\n  p.add(this.remove(), 0);\n  return this;\n} // Inserts a given element before the targeted element\n\nfunction before(element) {\n  element = makeInstance(element);\n  element.remove();\n  const i = this.position();\n  this.parent().add(element, i);\n  return this;\n} // Inserts a given element after the targeted element\n\nfunction after(element) {\n  element = makeInstance(element);\n  element.remove();\n  const i = this.position();\n  this.parent().add(element, i + 1);\n  return this;\n}\nfunction insertBefore(element) {\n  element = makeInstance(element);\n  element.before(this);\n  return this;\n}\nfunction insertAfter(element) {\n  element = makeInstance(element);\n  element.after(this);\n  return this;\n}\nregisterMethods('Dom', {\n  siblings,\n  position,\n  next,\n  prev,\n  forward,\n  backward,\n  front,\n  back,\n  before,\n  after,\n  insertBefore,\n  insertAfter\n});\n\n// Parse unit value\nconst numberAndUnit = /^([+-]?(\\d+(\\.\\d*)?|\\.\\d+)(e[+-]?\\d+)?)([a-z%]*)$/i; // Parse hex value\n\nconst hex = /^#?([a-f\\d]{2})([a-f\\d]{2})([a-f\\d]{2})$/i; // Parse rgb value\n\nconst rgb = /rgb\\((\\d+),(\\d+),(\\d+)\\)/; // Parse reference id\n\nconst reference = /(#[a-z_][a-z0-9\\-_]*)/i; // splits a transformation chain\n\nconst transforms = /\\)\\s*,?\\s*/; // Whitespace\n\nconst whitespace = /\\s/g; // Test hex value\n\nconst isHex = /^#[a-f0-9]{3}$|^#[a-f0-9]{6}$/i; // Test rgb value\n\nconst isRgb = /^rgb\\(/; // Test for blank string\n\nconst isBlank = /^(\\s+)?$/; // Test for numeric string\n\nconst isNumber = /^[+-]?(\\d+(\\.\\d*)?|\\.\\d+)(e[+-]?\\d+)?$/i; // Test for image url\n\nconst isImage = /\\.(jpg|jpeg|png|gif|svg)(\\?[^=]+.*)?/i; // split at whitespace and comma\n\nconst delimiter = /[\\s,]+/; // Test for path letter\n\nconst isPathLetter = /[MLHVCSQTAZ]/i;\n\nvar regex = {\n  __proto__: null,\n  numberAndUnit: numberAndUnit,\n  hex: hex,\n  rgb: rgb,\n  reference: reference,\n  transforms: transforms,\n  whitespace: whitespace,\n  isHex: isHex,\n  isRgb: isRgb,\n  isBlank: isBlank,\n  isNumber: isNumber,\n  isImage: isImage,\n  delimiter: delimiter,\n  isPathLetter: isPathLetter\n};\n\nfunction classes() {\n  const attr = this.attr('class');\n  return attr == null ? [] : attr.trim().split(delimiter);\n} // Return true if class exists on the node, false otherwise\n\nfunction hasClass(name) {\n  return this.classes().indexOf(name) !== -1;\n} // Add class to the node\n\nfunction addClass(name) {\n  if (!this.hasClass(name)) {\n    const array = this.classes();\n    array.push(name);\n    this.attr('class', array.join(' '));\n  }\n\n  return this;\n} // Remove class from the node\n\nfunction removeClass(name) {\n  if (this.hasClass(name)) {\n    this.attr('class', this.classes().filter(function (c) {\n      return c !== name;\n    }).join(' '));\n  }\n\n  return this;\n} // Toggle the presence of a class on the node\n\nfunction toggleClass(name) {\n  return this.hasClass(name) ? this.removeClass(name) : this.addClass(name);\n}\nregisterMethods('Dom', {\n  classes,\n  hasClass,\n  addClass,\n  removeClass,\n  toggleClass\n});\n\nfunction css(style, val) {\n  const ret = {};\n\n  if (arguments.length === 0) {\n    // get full style as object\n    this.node.style.cssText.split(/\\s*;\\s*/).filter(function (el) {\n      return !!el.length;\n    }).forEach(function (el) {\n      const t = el.split(/\\s*:\\s*/);\n      ret[t[0]] = t[1];\n    });\n    return ret;\n  }\n\n  if (arguments.length < 2) {\n    // get style properties as array\n    if (Array.isArray(style)) {\n      for (const name of style) {\n        const cased = camelCase(name);\n        ret[cased] = this.node.style[cased];\n      }\n\n      return ret;\n    } // get style for property\n\n\n    if (typeof style === 'string') {\n      return this.node.style[camelCase(style)];\n    } // set styles in object\n\n\n    if (typeof style === 'object') {\n      for (const name in style) {\n        // set empty string if null/undefined/'' was given\n        this.node.style[camelCase(name)] = style[name] == null || isBlank.test(style[name]) ? '' : style[name];\n      }\n    }\n  } // set style for property\n\n\n  if (arguments.length === 2) {\n    this.node.style[camelCase(style)] = val == null || isBlank.test(val) ? '' : val;\n  }\n\n  return this;\n} // Show element\n\nfunction show() {\n  return this.css('display', '');\n} // Hide element\n\nfunction hide() {\n  return this.css('display', 'none');\n} // Is element visible?\n\nfunction visible() {\n  return this.css('display') !== 'none';\n}\nregisterMethods('Dom', {\n  css,\n  show,\n  hide,\n  visible\n});\n\nfunction data(a, v, r) {\n  if (a == null) {\n    // get an object of attributes\n    return this.data(map(filter(this.node.attributes, el => el.nodeName.indexOf('data-') === 0), el => el.nodeName.slice(5)));\n  } else if (a instanceof Array) {\n    const data = {};\n\n    for (const key of a) {\n      data[key] = this.data(key);\n    }\n\n    return data;\n  } else if (typeof a === 'object') {\n    for (v in a) {\n      this.data(v, a[v]);\n    }\n  } else if (arguments.length < 2) {\n    try {\n      return JSON.parse(this.attr('data-' + a));\n    } catch (e) {\n      return this.attr('data-' + a);\n    }\n  } else {\n    this.attr('data-' + a, v === null ? null : r === true || typeof v === 'string' || typeof v === 'number' ? v : JSON.stringify(v));\n  }\n\n  return this;\n}\nregisterMethods('Dom', {\n  data\n});\n\nfunction remember(k, v) {\n  // remember every item in an object individually\n  if (typeof arguments[0] === 'object') {\n    for (const key in k) {\n      this.remember(key, k[key]);\n    }\n  } else if (arguments.length === 1) {\n    // retrieve memory\n    return this.memory()[k];\n  } else {\n    // store memory\n    this.memory()[k] = v;\n  }\n\n  return this;\n} // Erase a given memory\n\nfunction forget() {\n  if (arguments.length === 0) {\n    this._memory = {};\n  } else {\n    for (let i = arguments.length - 1; i >= 0; i--) {\n      delete this.memory()[arguments[i]];\n    }\n  }\n\n  return this;\n} // This triggers creation of a new hidden class which is not performant\n// However, this function is not rarely used so it will not happen frequently\n// Return local memory object\n\nfunction memory() {\n  return this._memory = this._memory || {};\n}\nregisterMethods('Dom', {\n  remember,\n  forget,\n  memory\n});\n\nfunction sixDigitHex(hex) {\n  return hex.length === 4 ? ['#', hex.substring(1, 2), hex.substring(1, 2), hex.substring(2, 3), hex.substring(2, 3), hex.substring(3, 4), hex.substring(3, 4)].join('') : hex;\n}\n\nfunction componentHex(component) {\n  const integer = Math.round(component);\n  const bounded = Math.max(0, Math.min(255, integer));\n  const hex = bounded.toString(16);\n  return hex.length === 1 ? '0' + hex : hex;\n}\n\nfunction is(object, space) {\n  for (let i = space.length; i--;) {\n    if (object[space[i]] == null) {\n      return false;\n    }\n  }\n\n  return true;\n}\n\nfunction getParameters(a, b) {\n  const params = is(a, 'rgb') ? {\n    _a: a.r,\n    _b: a.g,\n    _c: a.b,\n    _d: 0,\n    space: 'rgb'\n  } : is(a, 'xyz') ? {\n    _a: a.x,\n    _b: a.y,\n    _c: a.z,\n    _d: 0,\n    space: 'xyz'\n  } : is(a, 'hsl') ? {\n    _a: a.h,\n    _b: a.s,\n    _c: a.l,\n    _d: 0,\n    space: 'hsl'\n  } : is(a, 'lab') ? {\n    _a: a.l,\n    _b: a.a,\n    _c: a.b,\n    _d: 0,\n    space: 'lab'\n  } : is(a, 'lch') ? {\n    _a: a.l,\n    _b: a.c,\n    _c: a.h,\n    _d: 0,\n    space: 'lch'\n  } : is(a, 'cmyk') ? {\n    _a: a.c,\n    _b: a.m,\n    _c: a.y,\n    _d: a.k,\n    space: 'cmyk'\n  } : {\n    _a: 0,\n    _b: 0,\n    _c: 0,\n    space: 'rgb'\n  };\n  params.space = b || params.space;\n  return params;\n}\n\nfunction cieSpace(space) {\n  if (space === 'lab' || space === 'xyz' || space === 'lch') {\n    return true;\n  } else {\n    return false;\n  }\n}\n\nfunction hueToRgb(p, q, t) {\n  if (t < 0) t += 1;\n  if (t > 1) t -= 1;\n  if (t < 1 / 6) return p + (q - p) * 6 * t;\n  if (t < 1 / 2) return q;\n  if (t < 2 / 3) return p + (q - p) * (2 / 3 - t) * 6;\n  return p;\n}\n\nclass Color {\n  constructor(...inputs) {\n    this.init(...inputs);\n  } // Test if given value is a color\n\n\n  static isColor(color) {\n    return color && (color instanceof Color || this.isRgb(color) || this.test(color));\n  } // Test if given value is an rgb object\n\n\n  static isRgb(color) {\n    return color && typeof color.r === 'number' && typeof color.g === 'number' && typeof color.b === 'number';\n  }\n  /*\r\n  Generating random colors\r\n  */\n\n\n  static random(mode = 'vibrant', t, u) {\n    // Get the math modules\n    const {\n      random,\n      round,\n      sin,\n      PI: pi\n    } = Math; // Run the correct generator\n\n    if (mode === 'vibrant') {\n      const l = (81 - 57) * random() + 57;\n      const c = (83 - 45) * random() + 45;\n      const h = 360 * random();\n      const color = new Color(l, c, h, 'lch');\n      return color;\n    } else if (mode === 'sine') {\n      t = t == null ? random() : t;\n      const r = round(80 * sin(2 * pi * t / 0.5 + 0.01) + 150);\n      const g = round(50 * sin(2 * pi * t / 0.5 + 4.6) + 200);\n      const b = round(100 * sin(2 * pi * t / 0.5 + 2.3) + 150);\n      const color = new Color(r, g, b);\n      return color;\n    } else if (mode === 'pastel') {\n      const l = (94 - 86) * random() + 86;\n      const c = (26 - 9) * random() + 9;\n      const h = 360 * random();\n      const color = new Color(l, c, h, 'lch');\n      return color;\n    } else if (mode === 'dark') {\n      const l = 10 + 10 * random();\n      const c = (125 - 75) * random() + 86;\n      const h = 360 * random();\n      const color = new Color(l, c, h, 'lch');\n      return color;\n    } else if (mode === 'rgb') {\n      const r = 255 * random();\n      const g = 255 * random();\n      const b = 255 * random();\n      const color = new Color(r, g, b);\n      return color;\n    } else if (mode === 'lab') {\n      const l = 100 * random();\n      const a = 256 * random() - 128;\n      const b = 256 * random() - 128;\n      const color = new Color(l, a, b, 'lab');\n      return color;\n    } else if (mode === 'grey') {\n      const grey = 255 * random();\n      const color = new Color(grey, grey, grey);\n      return color;\n    } else {\n      throw new Error('Unsupported random color mode');\n    }\n  } // Test if given value is a color string\n\n\n  static test(color) {\n    return typeof color === 'string' && (isHex.test(color) || isRgb.test(color));\n  }\n\n  cmyk() {\n    // Get the rgb values for the current color\n    const {\n      _a,\n      _b,\n      _c\n    } = this.rgb();\n    const [r, g, b] = [_a, _b, _c].map(v => v / 255); // Get the cmyk values in an unbounded format\n\n    const k = Math.min(1 - r, 1 - g, 1 - b);\n\n    if (k === 1) {\n      // Catch the black case\n      return new Color(0, 0, 0, 1, 'cmyk');\n    }\n\n    const c = (1 - r - k) / (1 - k);\n    const m = (1 - g - k) / (1 - k);\n    const y = (1 - b - k) / (1 - k); // Construct the new color\n\n    const color = new Color(c, m, y, k, 'cmyk');\n    return color;\n  }\n\n  hsl() {\n    // Get the rgb values\n    const {\n      _a,\n      _b,\n      _c\n    } = this.rgb();\n    const [r, g, b] = [_a, _b, _c].map(v => v / 255); // Find the maximum and minimum values to get the lightness\n\n    const max = Math.max(r, g, b);\n    const min = Math.min(r, g, b);\n    const l = (max + min) / 2; // If the r, g, v values are identical then we are grey\n\n    const isGrey = max === min; // Calculate the hue and saturation\n\n    const delta = max - min;\n    const s = isGrey ? 0 : l > 0.5 ? delta / (2 - max - min) : delta / (max + min);\n    const h = isGrey ? 0 : max === r ? ((g - b) / delta + (g < b ? 6 : 0)) / 6 : max === g ? ((b - r) / delta + 2) / 6 : max === b ? ((r - g) / delta + 4) / 6 : 0; // Construct and return the new color\n\n    const color = new Color(360 * h, 100 * s, 100 * l, 'hsl');\n    return color;\n  }\n\n  init(a = 0, b = 0, c = 0, d = 0, space = 'rgb') {\n    // This catches the case when a falsy value is passed like ''\n    a = !a ? 0 : a; // Reset all values in case the init function is rerun with new color space\n\n    if (this.space) {\n      for (const component in this.space) {\n        delete this[this.space[component]];\n      }\n    }\n\n    if (typeof a === 'number') {\n      // Allow for the case that we don't need d...\n      space = typeof d === 'string' ? d : space;\n      d = typeof d === 'string' ? 0 : d; // Assign the values straight to the color\n\n      Object.assign(this, {\n        _a: a,\n        _b: b,\n        _c: c,\n        _d: d,\n        space\n      }); // If the user gave us an array, make the color from it\n    } else if (a instanceof Array) {\n      this.space = b || (typeof a[3] === 'string' ? a[3] : a[4]) || 'rgb';\n      Object.assign(this, {\n        _a: a[0],\n        _b: a[1],\n        _c: a[2],\n        _d: a[3] || 0\n      });\n    } else if (a instanceof Object) {\n      // Set the object up and assign its values directly\n      const values = getParameters(a, b);\n      Object.assign(this, values);\n    } else if (typeof a === 'string') {\n      if (isRgb.test(a)) {\n        const noWhitespace = a.replace(whitespace, '');\n        const [_a, _b, _c] = rgb.exec(noWhitespace).slice(1, 4).map(v => parseInt(v));\n        Object.assign(this, {\n          _a,\n          _b,\n          _c,\n          _d: 0,\n          space: 'rgb'\n        });\n      } else if (isHex.test(a)) {\n        const hexParse = v => parseInt(v, 16);\n\n        const [, _a, _b, _c] = hex.exec(sixDigitHex(a)).map(hexParse);\n        Object.assign(this, {\n          _a,\n          _b,\n          _c,\n          _d: 0,\n          space: 'rgb'\n        });\n      } else throw Error('Unsupported string format, can\\'t construct Color');\n    } // Now add the components as a convenience\n\n\n    const {\n      _a,\n      _b,\n      _c,\n      _d\n    } = this;\n    const components = this.space === 'rgb' ? {\n      r: _a,\n      g: _b,\n      b: _c\n    } : this.space === 'xyz' ? {\n      x: _a,\n      y: _b,\n      z: _c\n    } : this.space === 'hsl' ? {\n      h: _a,\n      s: _b,\n      l: _c\n    } : this.space === 'lab' ? {\n      l: _a,\n      a: _b,\n      b: _c\n    } : this.space === 'lch' ? {\n      l: _a,\n      c: _b,\n      h: _c\n    } : this.space === 'cmyk' ? {\n      c: _a,\n      m: _b,\n      y: _c,\n      k: _d\n    } : {};\n    Object.assign(this, components);\n  }\n\n  lab() {\n    // Get the xyz color\n    const {\n      x,\n      y,\n      z\n    } = this.xyz(); // Get the lab components\n\n    const l = 116 * y - 16;\n    const a = 500 * (x - y);\n    const b = 200 * (y - z); // Construct and return a new color\n\n    const color = new Color(l, a, b, 'lab');\n    return color;\n  }\n\n  lch() {\n    // Get the lab color directly\n    const {\n      l,\n      a,\n      b\n    } = this.lab(); // Get the chromaticity and the hue using polar coordinates\n\n    const c = Math.sqrt(a ** 2 + b ** 2);\n    let h = 180 * Math.atan2(b, a) / Math.PI;\n\n    if (h < 0) {\n      h *= -1;\n      h = 360 - h;\n    } // Make a new color and return it\n\n\n    const color = new Color(l, c, h, 'lch');\n    return color;\n  }\n  /*\r\n  Conversion Methods\r\n  */\n\n\n  rgb() {\n    if (this.space === 'rgb') {\n      return this;\n    } else if (cieSpace(this.space)) {\n      // Convert to the xyz color space\n      let {\n        x,\n        y,\n        z\n      } = this;\n\n      if (this.space === 'lab' || this.space === 'lch') {\n        // Get the values in the lab space\n        let {\n          l,\n          a,\n          b\n        } = this;\n\n        if (this.space === 'lch') {\n          const {\n            c,\n            h\n          } = this;\n          const dToR = Math.PI / 180;\n          a = c * Math.cos(dToR * h);\n          b = c * Math.sin(dToR * h);\n        } // Undo the nonlinear function\n\n\n        const yL = (l + 16) / 116;\n        const xL = a / 500 + yL;\n        const zL = yL - b / 200; // Get the xyz values\n\n        const ct = 16 / 116;\n        const mx = 0.008856;\n        const nm = 7.787;\n        x = 0.95047 * (xL ** 3 > mx ? xL ** 3 : (xL - ct) / nm);\n        y = 1.00000 * (yL ** 3 > mx ? yL ** 3 : (yL - ct) / nm);\n        z = 1.08883 * (zL ** 3 > mx ? zL ** 3 : (zL - ct) / nm);\n      } // Convert xyz to unbounded rgb values\n\n\n      const rU = x * 3.2406 + y * -1.5372 + z * -0.4986;\n      const gU = x * -0.9689 + y * 1.8758 + z * 0.0415;\n      const bU = x * 0.0557 + y * -0.2040 + z * 1.0570; // Convert the values to true rgb values\n\n      const pow = Math.pow;\n      const bd = 0.0031308;\n      const r = rU > bd ? 1.055 * pow(rU, 1 / 2.4) - 0.055 : 12.92 * rU;\n      const g = gU > bd ? 1.055 * pow(gU, 1 / 2.4) - 0.055 : 12.92 * gU;\n      const b = bU > bd ? 1.055 * pow(bU, 1 / 2.4) - 0.055 : 12.92 * bU; // Make and return the color\n\n      const color = new Color(255 * r, 255 * g, 255 * b);\n      return color;\n    } else if (this.space === 'hsl') {\n      // https://bgrins.github.io/TinyColor/docs/tinycolor.html\n      // Get the current hsl values\n      let {\n        h,\n        s,\n        l\n      } = this;\n      h /= 360;\n      s /= 100;\n      l /= 100; // If we are grey, then just make the color directly\n\n      if (s === 0) {\n        l *= 255;\n        const color = new Color(l, l, l);\n        return color;\n      } // TODO I have no idea what this does :D If you figure it out, tell me!\n\n\n      const q = l < 0.5 ? l * (1 + s) : l + s - l * s;\n      const p = 2 * l - q; // Get the rgb values\n\n      const r = 255 * hueToRgb(p, q, h + 1 / 3);\n      const g = 255 * hueToRgb(p, q, h);\n      const b = 255 * hueToRgb(p, q, h - 1 / 3); // Make a new color\n\n      const color = new Color(r, g, b);\n      return color;\n    } else if (this.space === 'cmyk') {\n      // https://gist.github.com/felipesabino/5066336\n      // Get the normalised cmyk values\n      const {\n        c,\n        m,\n        y,\n        k\n      } = this; // Get the rgb values\n\n      const r = 255 * (1 - Math.min(1, c * (1 - k) + k));\n      const g = 255 * (1 - Math.min(1, m * (1 - k) + k));\n      const b = 255 * (1 - Math.min(1, y * (1 - k) + k)); // Form the color and return it\n\n      const color = new Color(r, g, b);\n      return color;\n    } else {\n      return this;\n    }\n  }\n\n  toArray() {\n    const {\n      _a,\n      _b,\n      _c,\n      _d,\n      space\n    } = this;\n    return [_a, _b, _c, _d, space];\n  }\n\n  toHex() {\n    const [r, g, b] = this._clamped().map(componentHex);\n\n    return `#${r}${g}${b}`;\n  }\n\n  toRgb() {\n    const [rV, gV, bV] = this._clamped();\n\n    const string = `rgb(${rV},${gV},${bV})`;\n    return string;\n  }\n\n  toString() {\n    return this.toHex();\n  }\n\n  xyz() {\n    // Normalise the red, green and blue values\n    const {\n      _a: r255,\n      _b: g255,\n      _c: b255\n    } = this.rgb();\n    const [r, g, b] = [r255, g255, b255].map(v => v / 255); // Convert to the lab rgb space\n\n    const rL = r > 0.04045 ? Math.pow((r + 0.055) / 1.055, 2.4) : r / 12.92;\n    const gL = g > 0.04045 ? Math.pow((g + 0.055) / 1.055, 2.4) : g / 12.92;\n    const bL = b > 0.04045 ? Math.pow((b + 0.055) / 1.055, 2.4) : b / 12.92; // Convert to the xyz color space without bounding the values\n\n    const xU = (rL * 0.4124 + gL * 0.3576 + bL * 0.1805) / 0.95047;\n    const yU = (rL * 0.2126 + gL * 0.7152 + bL * 0.0722) / 1.00000;\n    const zU = (rL * 0.0193 + gL * 0.1192 + bL * 0.9505) / 1.08883; // Get the proper xyz values by applying the bounding\n\n    const x = xU > 0.008856 ? Math.pow(xU, 1 / 3) : 7.787 * xU + 16 / 116;\n    const y = yU > 0.008856 ? Math.pow(yU, 1 / 3) : 7.787 * yU + 16 / 116;\n    const z = zU > 0.008856 ? Math.pow(zU, 1 / 3) : 7.787 * zU + 16 / 116; // Make and return the color\n\n    const color = new Color(x, y, z, 'xyz');\n    return color;\n  }\n  /*\r\n  Input and Output methods\r\n  */\n\n\n  _clamped() {\n    const {\n      _a,\n      _b,\n      _c\n    } = this.rgb();\n    const {\n      max,\n      min,\n      round\n    } = Math;\n\n    const format = v => max(0, min(round(v), 255));\n\n    return [_a, _b, _c].map(format);\n  }\n  /*\r\n  Constructing colors\r\n  */\n\n\n}\n\nclass Point {\n  // Initialize\n  constructor(...args) {\n    this.init(...args);\n  } // Clone point\n\n\n  clone() {\n    return new Point(this);\n  }\n\n  init(x, y) {\n    const base = {\n      x: 0,\n      y: 0\n    }; // ensure source as object\n\n    const source = Array.isArray(x) ? {\n      x: x[0],\n      y: x[1]\n    } : typeof x === 'object' ? {\n      x: x.x,\n      y: x.y\n    } : {\n      x: x,\n      y: y\n    }; // merge source\n\n    this.x = source.x == null ? base.x : source.x;\n    this.y = source.y == null ? base.y : source.y;\n    return this;\n  }\n\n  toArray() {\n    return [this.x, this.y];\n  }\n\n  transform(m) {\n    return this.clone().transformO(m);\n  } // Transform point with matrix\n\n\n  transformO(m) {\n    if (!Matrix.isMatrixLike(m)) {\n      m = new Matrix(m);\n    }\n\n    const {\n      x,\n      y\n    } = this; // Perform the matrix multiplication\n\n    this.x = m.a * x + m.c * y + m.e;\n    this.y = m.b * x + m.d * y + m.f;\n    return this;\n  }\n\n}\nfunction point(x, y) {\n  return new Point(x, y).transform(this.screenCTM().inverse());\n}\n\nfunction closeEnough(a, b, threshold) {\n  return Math.abs(b - a) < (threshold || 1e-6);\n}\n\nclass Matrix {\n  constructor(...args) {\n    this.init(...args);\n  }\n\n  static formatTransforms(o) {\n    // Get all of the parameters required to form the matrix\n    const flipBoth = o.flip === 'both' || o.flip === true;\n    const flipX = o.flip && (flipBoth || o.flip === 'x') ? -1 : 1;\n    const flipY = o.flip && (flipBoth || o.flip === 'y') ? -1 : 1;\n    const skewX = o.skew && o.skew.length ? o.skew[0] : isFinite(o.skew) ? o.skew : isFinite(o.skewX) ? o.skewX : 0;\n    const skewY = o.skew && o.skew.length ? o.skew[1] : isFinite(o.skew) ? o.skew : isFinite(o.skewY) ? o.skewY : 0;\n    const scaleX = o.scale && o.scale.length ? o.scale[0] * flipX : isFinite(o.scale) ? o.scale * flipX : isFinite(o.scaleX) ? o.scaleX * flipX : flipX;\n    const scaleY = o.scale && o.scale.length ? o.scale[1] * flipY : isFinite(o.scale) ? o.scale * flipY : isFinite(o.scaleY) ? o.scaleY * flipY : flipY;\n    const shear = o.shear || 0;\n    const theta = o.rotate || o.theta || 0;\n    const origin = new Point(o.origin || o.around || o.ox || o.originX, o.oy || o.originY);\n    const ox = origin.x;\n    const oy = origin.y; // We need Point to be invalid if nothing was passed because we cannot default to 0 here. Thats why NaN\n\n    const position = new Point(o.position || o.px || o.positionX || NaN, o.py || o.positionY || NaN);\n    const px = position.x;\n    const py = position.y;\n    const translate = new Point(o.translate || o.tx || o.translateX, o.ty || o.translateY);\n    const tx = translate.x;\n    const ty = translate.y;\n    const relative = new Point(o.relative || o.rx || o.relativeX, o.ry || o.relativeY);\n    const rx = relative.x;\n    const ry = relative.y; // Populate all of the values\n\n    return {\n      scaleX,\n      scaleY,\n      skewX,\n      skewY,\n      shear,\n      theta,\n      rx,\n      ry,\n      tx,\n      ty,\n      ox,\n      oy,\n      px,\n      py\n    };\n  }\n\n  static fromArray(a) {\n    return {\n      a: a[0],\n      b: a[1],\n      c: a[2],\n      d: a[3],\n      e: a[4],\n      f: a[5]\n    };\n  }\n\n  static isMatrixLike(o) {\n    return o.a != null || o.b != null || o.c != null || o.d != null || o.e != null || o.f != null;\n  } // left matrix, right matrix, target matrix which is overwritten\n\n\n  static matrixMultiply(l, r, o) {\n    // Work out the product directly\n    const a = l.a * r.a + l.c * r.b;\n    const b = l.b * r.a + l.d * r.b;\n    const c = l.a * r.c + l.c * r.d;\n    const d = l.b * r.c + l.d * r.d;\n    const e = l.e + l.a * r.e + l.c * r.f;\n    const f = l.f + l.b * r.e + l.d * r.f; // make sure to use local variables because l/r and o could be the same\n\n    o.a = a;\n    o.b = b;\n    o.c = c;\n    o.d = d;\n    o.e = e;\n    o.f = f;\n    return o;\n  }\n\n  around(cx, cy, matrix) {\n    return this.clone().aroundO(cx, cy, matrix);\n  } // Transform around a center point\n\n\n  aroundO(cx, cy, matrix) {\n    const dx = cx || 0;\n    const dy = cy || 0;\n    return this.translateO(-dx, -dy).lmultiplyO(matrix).translateO(dx, dy);\n  } // Clones this matrix\n\n\n  clone() {\n    return new Matrix(this);\n  } // Decomposes this matrix into its affine parameters\n\n\n  decompose(cx = 0, cy = 0) {\n    // Get the parameters from the matrix\n    const a = this.a;\n    const b = this.b;\n    const c = this.c;\n    const d = this.d;\n    const e = this.e;\n    const f = this.f; // Figure out if the winding direction is clockwise or counterclockwise\n\n    const determinant = a * d - b * c;\n    const ccw = determinant > 0 ? 1 : -1; // Since we only shear in x, we can use the x basis to get the x scale\n    // and the rotation of the resulting matrix\n\n    const sx = ccw * Math.sqrt(a * a + b * b);\n    const thetaRad = Math.atan2(ccw * b, ccw * a);\n    const theta = 180 / Math.PI * thetaRad;\n    const ct = Math.cos(thetaRad);\n    const st = Math.sin(thetaRad); // We can then solve the y basis vector simultaneously to get the other\n    // two affine parameters directly from these parameters\n\n    const lam = (a * c + b * d) / determinant;\n    const sy = c * sx / (lam * a - b) || d * sx / (lam * b + a); // Use the translations\n\n    const tx = e - cx + cx * ct * sx + cy * (lam * ct * sx - st * sy);\n    const ty = f - cy + cx * st * sx + cy * (lam * st * sx + ct * sy); // Construct the decomposition and return it\n\n    return {\n      // Return the affine parameters\n      scaleX: sx,\n      scaleY: sy,\n      shear: lam,\n      rotate: theta,\n      translateX: tx,\n      translateY: ty,\n      originX: cx,\n      originY: cy,\n      // Return the matrix parameters\n      a: this.a,\n      b: this.b,\n      c: this.c,\n      d: this.d,\n      e: this.e,\n      f: this.f\n    };\n  } // Check if two matrices are equal\n\n\n  equals(other) {\n    if (other === this) return true;\n    const comp = new Matrix(other);\n    return closeEnough(this.a, comp.a) && closeEnough(this.b, comp.b) && closeEnough(this.c, comp.c) && closeEnough(this.d, comp.d) && closeEnough(this.e, comp.e) && closeEnough(this.f, comp.f);\n  } // Flip matrix on x or y, at a given offset\n\n\n  flip(axis, around) {\n    return this.clone().flipO(axis, around);\n  }\n\n  flipO(axis, around) {\n    return axis === 'x' ? this.scaleO(-1, 1, around, 0) : axis === 'y' ? this.scaleO(1, -1, 0, around) : this.scaleO(-1, -1, axis, around || axis); // Define an x, y flip point\n  } // Initialize\n\n\n  init(source) {\n    const base = Matrix.fromArray([1, 0, 0, 1, 0, 0]); // ensure source as object\n\n    source = source instanceof Element ? source.matrixify() : typeof source === 'string' ? Matrix.fromArray(source.split(delimiter).map(parseFloat)) : Array.isArray(source) ? Matrix.fromArray(source) : typeof source === 'object' && Matrix.isMatrixLike(source) ? source : typeof source === 'object' ? new Matrix().transform(source) : arguments.length === 6 ? Matrix.fromArray([].slice.call(arguments)) : base; // Merge the source matrix with the base matrix\n\n    this.a = source.a != null ? source.a : base.a;\n    this.b = source.b != null ? source.b : base.b;\n    this.c = source.c != null ? source.c : base.c;\n    this.d = source.d != null ? source.d : base.d;\n    this.e = source.e != null ? source.e : base.e;\n    this.f = source.f != null ? source.f : base.f;\n    return this;\n  }\n\n  inverse() {\n    return this.clone().inverseO();\n  } // Inverses matrix\n\n\n  inverseO() {\n    // Get the current parameters out of the matrix\n    const a = this.a;\n    const b = this.b;\n    const c = this.c;\n    const d = this.d;\n    const e = this.e;\n    const f = this.f; // Invert the 2x2 matrix in the top left\n\n    const det = a * d - b * c;\n    if (!det) throw new Error('Cannot invert ' + this); // Calculate the top 2x2 matrix\n\n    const na = d / det;\n    const nb = -b / det;\n    const nc = -c / det;\n    const nd = a / det; // Apply the inverted matrix to the top right\n\n    const ne = -(na * e + nc * f);\n    const nf = -(nb * e + nd * f); // Construct the inverted matrix\n\n    this.a = na;\n    this.b = nb;\n    this.c = nc;\n    this.d = nd;\n    this.e = ne;\n    this.f = nf;\n    return this;\n  }\n\n  lmultiply(matrix) {\n    return this.clone().lmultiplyO(matrix);\n  }\n\n  lmultiplyO(matrix) {\n    const r = this;\n    const l = matrix instanceof Matrix ? matrix : new Matrix(matrix);\n    return Matrix.matrixMultiply(l, r, this);\n  } // Left multiplies by the given matrix\n\n\n  multiply(matrix) {\n    return this.clone().multiplyO(matrix);\n  }\n\n  multiplyO(matrix) {\n    // Get the matrices\n    const l = this;\n    const r = matrix instanceof Matrix ? matrix : new Matrix(matrix);\n    return Matrix.matrixMultiply(l, r, this);\n  } // Rotate matrix\n\n\n  rotate(r, cx, cy) {\n    return this.clone().rotateO(r, cx, cy);\n  }\n\n  rotateO(r, cx = 0, cy = 0) {\n    // Convert degrees to radians\n    r = radians(r);\n    const cos = Math.cos(r);\n    const sin = Math.sin(r);\n    const {\n      a,\n      b,\n      c,\n      d,\n      e,\n      f\n    } = this;\n    this.a = a * cos - b * sin;\n    this.b = b * cos + a * sin;\n    this.c = c * cos - d * sin;\n    this.d = d * cos + c * sin;\n    this.e = e * cos - f * sin + cy * sin - cx * cos + cx;\n    this.f = f * cos + e * sin - cx * sin - cy * cos + cy;\n    return this;\n  } // Scale matrix\n\n\n  scale(x, y, cx, cy) {\n    return this.clone().scaleO(...arguments);\n  }\n\n  scaleO(x, y = x, cx = 0, cy = 0) {\n    // Support uniform scaling\n    if (arguments.length === 3) {\n      cy = cx;\n      cx = y;\n      y = x;\n    }\n\n    const {\n      a,\n      b,\n      c,\n      d,\n      e,\n      f\n    } = this;\n    this.a = a * x;\n    this.b = b * y;\n    this.c = c * x;\n    this.d = d * y;\n    this.e = e * x - cx * x + cx;\n    this.f = f * y - cy * y + cy;\n    return this;\n  } // Shear matrix\n\n\n  shear(a, cx, cy) {\n    return this.clone().shearO(a, cx, cy);\n  }\n\n  shearO(lx, cx = 0, cy = 0) {\n    const {\n      a,\n      b,\n      c,\n      d,\n      e,\n      f\n    } = this;\n    this.a = a + b * lx;\n    this.c = c + d * lx;\n    this.e = e + f * lx - cy * lx;\n    return this;\n  } // Skew Matrix\n\n\n  skew(x, y, cx, cy) {\n    return this.clone().skewO(...arguments);\n  }\n\n  skewO(x, y = x, cx = 0, cy = 0) {\n    // support uniformal skew\n    if (arguments.length === 3) {\n      cy = cx;\n      cx = y;\n      y = x;\n    } // Convert degrees to radians\n\n\n    x = radians(x);\n    y = radians(y);\n    const lx = Math.tan(x);\n    const ly = Math.tan(y);\n    const {\n      a,\n      b,\n      c,\n      d,\n      e,\n      f\n    } = this;\n    this.a = a + b * lx;\n    this.b = b + a * ly;\n    this.c = c + d * lx;\n    this.d = d + c * ly;\n    this.e = e + f * lx - cy * lx;\n    this.f = f + e * ly - cx * ly;\n    return this;\n  } // SkewX\n\n\n  skewX(x, cx, cy) {\n    return this.skew(x, 0, cx, cy);\n  } // SkewY\n\n\n  skewY(y, cx, cy) {\n    return this.skew(0, y, cx, cy);\n  }\n\n  toArray() {\n    return [this.a, this.b, this.c, this.d, this.e, this.f];\n  } // Convert matrix to string\n\n\n  toString() {\n    return 'matrix(' + this.a + ',' + this.b + ',' + this.c + ',' + this.d + ',' + this.e + ',' + this.f + ')';\n  } // Transform a matrix into another matrix by manipulating the space\n\n\n  transform(o) {\n    // Check if o is a matrix and then left multiply it directly\n    if (Matrix.isMatrixLike(o)) {\n      const matrix = new Matrix(o);\n      return matrix.multiplyO(this);\n    } // Get the proposed transformations and the current transformations\n\n\n    const t = Matrix.formatTransforms(o);\n    const current = this;\n    const {\n      x: ox,\n      y: oy\n    } = new Point(t.ox, t.oy).transform(current); // Construct the resulting matrix\n\n    const transformer = new Matrix().translateO(t.rx, t.ry).lmultiplyO(current).translateO(-ox, -oy).scaleO(t.scaleX, t.scaleY).skewO(t.skewX, t.skewY).shearO(t.shear).rotateO(t.theta).translateO(ox, oy); // If we want the origin at a particular place, we force it there\n\n    if (isFinite(t.px) || isFinite(t.py)) {\n      const origin = new Point(ox, oy).transform(transformer); // TODO: Replace t.px with isFinite(t.px)\n      // Doesnt work because t.px is also 0 if it wasnt passed\n\n      const dx = isFinite(t.px) ? t.px - origin.x : 0;\n      const dy = isFinite(t.py) ? t.py - origin.y : 0;\n      transformer.translateO(dx, dy);\n    } // Translate now after positioning\n\n\n    transformer.translateO(t.tx, t.ty);\n    return transformer;\n  } // Translate matrix\n\n\n  translate(x, y) {\n    return this.clone().translateO(x, y);\n  }\n\n  translateO(x, y) {\n    this.e += x || 0;\n    this.f += y || 0;\n    return this;\n  }\n\n  valueOf() {\n    return {\n      a: this.a,\n      b: this.b,\n      c: this.c,\n      d: this.d,\n      e: this.e,\n      f: this.f\n    };\n  }\n\n}\nfunction ctm() {\n  return new Matrix(this.node.getCTM());\n}\nfunction screenCTM() {\n  /* https://bugzilla.mozilla.org/show_bug.cgi?id=1344537\r\n     This is needed because FF does not return the transformation matrix\r\n     for the inner coordinate system when getScreenCTM() is called on nested svgs.\r\n     However all other Browsers do that */\n  if (typeof this.isRoot === 'function' && !this.isRoot()) {\n    const rect = this.rect(1, 1);\n    const m = rect.node.getScreenCTM();\n    rect.remove();\n    return new Matrix(m);\n  }\n\n  return new Matrix(this.node.getScreenCTM());\n}\nregister(Matrix, 'Matrix');\n\nfunction parser() {\n  // Reuse cached element if possible\n  if (!parser.nodes) {\n    const svg = makeInstance().size(2, 0);\n    svg.node.style.cssText = ['opacity: 0', 'position: absolute', 'left: -100%', 'top: -100%', 'overflow: hidden'].join(';');\n    svg.attr('focusable', 'false');\n    svg.attr('aria-hidden', 'true');\n    const path = svg.path().node;\n    parser.nodes = {\n      svg,\n      path\n    };\n  }\n\n  if (!parser.nodes.svg.node.parentNode) {\n    const b = globals.document.body || globals.document.documentElement;\n    parser.nodes.svg.addTo(b);\n  }\n\n  return parser.nodes;\n}\n\nfunction isNulledBox(box) {\n  return !box.width && !box.height && !box.x && !box.y;\n}\nfunction domContains(node) {\n  return node === globals.document || (globals.document.documentElement.contains || function (node) {\n    // This is IE - it does not support contains() for top-level SVGs\n    while (node.parentNode) {\n      node = node.parentNode;\n    }\n\n    return node === globals.document;\n  }).call(globals.document.documentElement, node);\n}\nclass Box {\n  constructor(...args) {\n    this.init(...args);\n  }\n\n  addOffset() {\n    // offset by window scroll position, because getBoundingClientRect changes when window is scrolled\n    this.x += globals.window.pageXOffset;\n    this.y += globals.window.pageYOffset;\n    return new Box(this);\n  }\n\n  init(source) {\n    const base = [0, 0, 0, 0];\n    source = typeof source === 'string' ? source.split(delimiter).map(parseFloat) : Array.isArray(source) ? source : typeof source === 'object' ? [source.left != null ? source.left : source.x, source.top != null ? source.top : source.y, source.width, source.height] : arguments.length === 4 ? [].slice.call(arguments) : base;\n    this.x = source[0] || 0;\n    this.y = source[1] || 0;\n    this.width = this.w = source[2] || 0;\n    this.height = this.h = source[3] || 0; // Add more bounding box properties\n\n    this.x2 = this.x + this.w;\n    this.y2 = this.y + this.h;\n    this.cx = this.x + this.w / 2;\n    this.cy = this.y + this.h / 2;\n    return this;\n  }\n\n  isNulled() {\n    return isNulledBox(this);\n  } // Merge rect box with another, return a new instance\n\n\n  merge(box) {\n    const x = Math.min(this.x, box.x);\n    const y = Math.min(this.y, box.y);\n    const width = Math.max(this.x + this.width, box.x + box.width) - x;\n    const height = Math.max(this.y + this.height, box.y + box.height) - y;\n    return new Box(x, y, width, height);\n  }\n\n  toArray() {\n    return [this.x, this.y, this.width, this.height];\n  }\n\n  toString() {\n    return this.x + ' ' + this.y + ' ' + this.width + ' ' + this.height;\n  }\n\n  transform(m) {\n    if (!(m instanceof Matrix)) {\n      m = new Matrix(m);\n    }\n\n    let xMin = Infinity;\n    let xMax = -Infinity;\n    let yMin = Infinity;\n    let yMax = -Infinity;\n    const pts = [new Point(this.x, this.y), new Point(this.x2, this.y), new Point(this.x, this.y2), new Point(this.x2, this.y2)];\n    pts.forEach(function (p) {\n      p = p.transform(m);\n      xMin = Math.min(xMin, p.x);\n      xMax = Math.max(xMax, p.x);\n      yMin = Math.min(yMin, p.y);\n      yMax = Math.max(yMax, p.y);\n    });\n    return new Box(xMin, yMin, xMax - xMin, yMax - yMin);\n  }\n\n}\n\nfunction getBox(el, getBBoxFn, retry) {\n  let box;\n\n  try {\n    // Try to get the box with the provided function\n    box = getBBoxFn(el.node); // If the box is worthless and not even in the dom, retry\n    // by throwing an error here...\n\n    if (isNulledBox(box) && !domContains(el.node)) {\n      throw new Error('Element not in the dom');\n    }\n  } catch (e) {\n    // ... and calling the retry handler here\n    box = retry(el);\n  }\n\n  return box;\n}\n\nfunction bbox() {\n  // Function to get bbox is getBBox()\n  const getBBox = node => node.getBBox(); // Take all measures so that a stupid browser renders the element\n  // so we can get the bbox from it when we try again\n\n\n  const retry = el => {\n    try {\n      const clone = el.clone().addTo(parser().svg).show();\n      const box = clone.node.getBBox();\n      clone.remove();\n      return box;\n    } catch (e) {\n      // We give up...\n      throw new Error(`Getting bbox of element \"${el.node.nodeName}\" is not possible: ${e.toString()}`);\n    }\n  };\n\n  const box = getBox(this, getBBox, retry);\n  const bbox = new Box(box);\n  return bbox;\n}\nfunction rbox(el) {\n  const getRBox = node => node.getBoundingClientRect();\n\n  const retry = el => {\n    // There is no point in trying tricks here because if we insert the element into the dom ourselves\n    // it obviously will be at the wrong position\n    throw new Error(`Getting rbox of element \"${el.node.nodeName}\" is not possible`);\n  };\n\n  const box = getBox(this, getRBox, retry);\n  const rbox = new Box(box); // If an element was passed, we want the bbox in the coordinate system of that element\n\n  if (el) {\n    return rbox.transform(el.screenCTM().inverseO());\n  } // Else we want it in absolute screen coordinates\n  // Therefore we need to add the scrollOffset\n\n\n  return rbox.addOffset();\n} // Checks whether the given point is inside the bounding box\n\nfunction inside(x, y) {\n  const box = this.bbox();\n  return x > box.x && y > box.y && x < box.x + box.width && y < box.y + box.height;\n}\nregisterMethods({\n  viewbox: {\n    viewbox(x, y, width, height) {\n      // act as getter\n      if (x == null) return new Box(this.attr('viewBox')); // act as setter\n\n      return this.attr('viewBox', new Box(x, y, width, height));\n    },\n\n    zoom(level, point) {\n      // Its best to rely on the attributes here and here is why:\n      // clientXYZ: Doesn't work on non-root svgs because they dont have a CSSBox (silly!)\n      // getBoundingClientRect: Doesn't work because Chrome just ignores width and height of nested svgs completely\n      //                        that means, their clientRect is always as big as the content.\n      //                        Furthermore this size is incorrect if the element is further transformed by its parents\n      // computedStyle: Only returns meaningful values if css was used with px. We dont go this route here!\n      // getBBox: returns the bounding box of its content - that doesnt help!\n      let {\n        width,\n        height\n      } = this.attr(['width', 'height']); // Width and height is a string when a number with a unit is present which we can't use\n      // So we try clientXYZ\n\n      if (!width && !height || typeof width === 'string' || typeof height === 'string') {\n        width = this.node.clientWidth;\n        height = this.node.clientHeight;\n      } // Giving up...\n\n\n      if (!width || !height) {\n        throw new Error('Impossible to get absolute width and height. Please provide an absolute width and height attribute on the zooming element');\n      }\n\n      const v = this.viewbox();\n      const zoomX = width / v.width;\n      const zoomY = height / v.height;\n      const zoom = Math.min(zoomX, zoomY);\n\n      if (level == null) {\n        return zoom;\n      }\n\n      let zoomAmount = zoom / level; // Set the zoomAmount to the highest value which is safe to process and recover from\n      // The * 100 is a bit of wiggle room for the matrix transformation\n\n      if (zoomAmount === Infinity) zoomAmount = Number.MAX_SAFE_INTEGER / 100;\n      point = point || new Point(width / 2 / zoomX + v.x, height / 2 / zoomY + v.y);\n      const box = new Box(v).transform(new Matrix({\n        scale: zoomAmount,\n        origin: point\n      }));\n      return this.viewbox(box);\n    }\n\n  }\n});\nregister(Box, 'Box');\n\nclass List extends Array {\n  constructor(arr = [], ...args) {\n    super(arr, ...args);\n    if (typeof arr === 'number') return this;\n    this.length = 0;\n    this.push(...arr);\n  }\n\n}\nextend([List], {\n  each(fnOrMethodName, ...args) {\n    if (typeof fnOrMethodName === 'function') {\n      return this.map((el, i, arr) => {\n        return fnOrMethodName.call(el, el, i, arr);\n      });\n    } else {\n      return this.map(el => {\n        return el[fnOrMethodName](...args);\n      });\n    }\n  },\n\n  toArray() {\n    return Array.prototype.concat.apply([], this);\n  }\n\n});\nconst reserved = ['toArray', 'constructor', 'each'];\n\nList.extend = function (methods) {\n  methods = methods.reduce((obj, name) => {\n    // Don't overwrite own methods\n    if (reserved.includes(name)) return obj; // Don't add private methods\n\n    if (name[0] === '_') return obj; // Relay every call to each()\n\n    obj[name] = function (...attrs) {\n      return this.each(name, ...attrs);\n    };\n\n    return obj;\n  }, {});\n  extend([List], methods);\n};\n\nfunction baseFind(query, parent) {\n  return new List(map((parent || globals.document).querySelectorAll(query), function (node) {\n    return adopt(node);\n  }));\n} // Scoped find method\n\nfunction find(query) {\n  return baseFind(query, this.node);\n}\nfunction findOne(query) {\n  return adopt(this.node.querySelector(query));\n}\n\nlet listenerId = 0;\nconst windowEvents = {};\nfunction getEvents(instance) {\n  let n = instance.getEventHolder(); // We dont want to save events in global space\n\n  if (n === globals.window) n = windowEvents;\n  if (!n.events) n.events = {};\n  return n.events;\n}\nfunction getEventTarget(instance) {\n  return instance.getEventTarget();\n}\nfunction clearEvents(instance) {\n  let n = instance.getEventHolder();\n  if (n === globals.window) n = windowEvents;\n  if (n.events) n.events = {};\n} // Add event binder in the SVG namespace\n\nfunction on(node, events, listener, binding, options) {\n  const l = listener.bind(binding || node);\n  const instance = makeInstance(node);\n  const bag = getEvents(instance);\n  const n = getEventTarget(instance); // events can be an array of events or a string of events\n\n  events = Array.isArray(events) ? events : events.split(delimiter); // add id to listener\n\n  if (!listener._svgjsListenerId) {\n    listener._svgjsListenerId = ++listenerId;\n  }\n\n  events.forEach(function (event) {\n    const ev = event.split('.')[0];\n    const ns = event.split('.')[1] || '*'; // ensure valid object\n\n    bag[ev] = bag[ev] || {};\n    bag[ev][ns] = bag[ev][ns] || {}; // reference listener\n\n    bag[ev][ns][listener._svgjsListenerId] = l; // add listener\n\n    n.addEventListener(ev, l, options || false);\n  });\n} // Add event unbinder in the SVG namespace\n\nfunction off(node, events, listener, options) {\n  const instance = makeInstance(node);\n  const bag = getEvents(instance);\n  const n = getEventTarget(instance); // listener can be a function or a number\n\n  if (typeof listener === 'function') {\n    listener = listener._svgjsListenerId;\n    if (!listener) return;\n  } // events can be an array of events or a string or undefined\n\n\n  events = Array.isArray(events) ? events : (events || '').split(delimiter);\n  events.forEach(function (event) {\n    const ev = event && event.split('.')[0];\n    const ns = event && event.split('.')[1];\n    let namespace, l;\n\n    if (listener) {\n      // remove listener reference\n      if (bag[ev] && bag[ev][ns || '*']) {\n        // removeListener\n        n.removeEventListener(ev, bag[ev][ns || '*'][listener], options || false);\n        delete bag[ev][ns || '*'][listener];\n      }\n    } else if (ev && ns) {\n      // remove all listeners for a namespaced event\n      if (bag[ev] && bag[ev][ns]) {\n        for (l in bag[ev][ns]) {\n          off(n, [ev, ns].join('.'), l);\n        }\n\n        delete bag[ev][ns];\n      }\n    } else if (ns) {\n      // remove all listeners for a specific namespace\n      for (event in bag) {\n        for (namespace in bag[event]) {\n          if (ns === namespace) {\n            off(n, [event, ns].join('.'));\n          }\n        }\n      }\n    } else if (ev) {\n      // remove all listeners for the event\n      if (bag[ev]) {\n        for (namespace in bag[ev]) {\n          off(n, [ev, namespace].join('.'));\n        }\n\n        delete bag[ev];\n      }\n    } else {\n      // remove all listeners on a given node\n      for (event in bag) {\n        off(n, event);\n      }\n\n      clearEvents(instance);\n    }\n  });\n}\nfunction dispatch(node, event, data, options) {\n  const n = getEventTarget(node); // Dispatch event\n\n  if (event instanceof globals.window.Event) {\n    n.dispatchEvent(event);\n  } else {\n    event = new globals.window.CustomEvent(event, {\n      detail: data,\n      cancelable: true,\n      ...options\n    });\n    n.dispatchEvent(event);\n  }\n\n  return event;\n}\n\nclass EventTarget extends Base {\n  addEventListener() {}\n\n  dispatch(event, data, options) {\n    return dispatch(this, event, data, options);\n  }\n\n  dispatchEvent(event) {\n    const bag = this.getEventHolder().events;\n    if (!bag) return true;\n    const events = bag[event.type];\n\n    for (const i in events) {\n      for (const j in events[i]) {\n        events[i][j](event);\n      }\n    }\n\n    return !event.defaultPrevented;\n  } // Fire given event\n\n\n  fire(event, data, options) {\n    this.dispatch(event, data, options);\n    return this;\n  }\n\n  getEventHolder() {\n    return this;\n  }\n\n  getEventTarget() {\n    return this;\n  } // Unbind event from listener\n\n\n  off(event, listener) {\n    off(this, event, listener);\n    return this;\n  } // Bind given event to listener\n\n\n  on(event, listener, binding, options) {\n    on(this, event, listener, binding, options);\n    return this;\n  }\n\n  removeEventListener() {}\n\n}\nregister(EventTarget, 'EventTarget');\n\nfunction noop() {} // Default animation values\n\nconst timeline = {\n  duration: 400,\n  ease: '>',\n  delay: 0\n}; // Default attribute values\n\nconst attrs = {\n  // fill and stroke\n  'fill-opacity': 1,\n  'stroke-opacity': 1,\n  'stroke-width': 0,\n  'stroke-linejoin': 'miter',\n  'stroke-linecap': 'butt',\n  fill: '#000000',\n  stroke: '#000000',\n  opacity: 1,\n  // position\n  x: 0,\n  y: 0,\n  cx: 0,\n  cy: 0,\n  // size\n  width: 0,\n  height: 0,\n  // radius\n  r: 0,\n  rx: 0,\n  ry: 0,\n  // gradient\n  offset: 0,\n  'stop-opacity': 1,\n  'stop-color': '#000000',\n  // text\n  'text-anchor': 'start'\n};\n\nvar defaults = {\n  __proto__: null,\n  noop: noop,\n  timeline: timeline,\n  attrs: attrs\n};\n\nclass SVGArray extends Array {\n  constructor(...args) {\n    super(...args);\n    this.init(...args);\n  }\n\n  clone() {\n    return new this.constructor(this);\n  }\n\n  init(arr) {\n    // This catches the case, that native map tries to create an array with new Array(1)\n    if (typeof arr === 'number') return this;\n    this.length = 0;\n    this.push(...this.parse(arr));\n    return this;\n  } // Parse whitespace separated string\n\n\n  parse(array = []) {\n    // If already is an array, no need to parse it\n    if (array instanceof Array) return array;\n    return array.trim().split(delimiter).map(parseFloat);\n  }\n\n  toArray() {\n    return Array.prototype.concat.apply([], this);\n  }\n\n  toSet() {\n    return new Set(this);\n  }\n\n  toString() {\n    return this.join(' ');\n  } // Flattens the array if needed\n\n\n  valueOf() {\n    const ret = [];\n    ret.push(...this);\n    return ret;\n  }\n\n}\n\nclass SVGNumber {\n  // Initialize\n  constructor(...args) {\n    this.init(...args);\n  }\n\n  convert(unit) {\n    return new SVGNumber(this.value, unit);\n  } // Divide number\n\n\n  divide(number) {\n    number = new SVGNumber(number);\n    return new SVGNumber(this / number, this.unit || number.unit);\n  }\n\n  init(value, unit) {\n    unit = Array.isArray(value) ? value[1] : unit;\n    value = Array.isArray(value) ? value[0] : value; // initialize defaults\n\n    this.value = 0;\n    this.unit = unit || ''; // parse value\n\n    if (typeof value === 'number') {\n      // ensure a valid numeric value\n      this.value = isNaN(value) ? 0 : !isFinite(value) ? value < 0 ? -3.4e+38 : +3.4e+38 : value;\n    } else if (typeof value === 'string') {\n      unit = value.match(numberAndUnit);\n\n      if (unit) {\n        // make value numeric\n        this.value = parseFloat(unit[1]); // normalize\n\n        if (unit[5] === '%') {\n          this.value /= 100;\n        } else if (unit[5] === 's') {\n          this.value *= 1000;\n        } // store unit\n\n\n        this.unit = unit[5];\n      }\n    } else {\n      if (value instanceof SVGNumber) {\n        this.value = value.valueOf();\n        this.unit = value.unit;\n      }\n    }\n\n    return this;\n  } // Subtract number\n\n\n  minus(number) {\n    number = new SVGNumber(number);\n    return new SVGNumber(this - number, this.unit || number.unit);\n  } // Add number\n\n\n  plus(number) {\n    number = new SVGNumber(number);\n    return new SVGNumber(this + number, this.unit || number.unit);\n  } // Multiply number\n\n\n  times(number) {\n    number = new SVGNumber(number);\n    return new SVGNumber(this * number, this.unit || number.unit);\n  }\n\n  toArray() {\n    return [this.value, this.unit];\n  }\n\n  toJSON() {\n    return this.toString();\n  }\n\n  toString() {\n    return (this.unit === '%' ? ~~(this.value * 1e8) / 1e6 : this.unit === 's' ? this.value / 1e3 : this.value) + this.unit;\n  }\n\n  valueOf() {\n    return this.value;\n  }\n\n}\n\nconst hooks = [];\nfunction registerAttrHook(fn) {\n  hooks.push(fn);\n} // Set svg element attribute\n\nfunction attr(attr, val, ns) {\n  // act as full getter\n  if (attr == null) {\n    // get an object of attributes\n    attr = {};\n    val = this.node.attributes;\n\n    for (const node of val) {\n      attr[node.nodeName] = isNumber.test(node.nodeValue) ? parseFloat(node.nodeValue) : node.nodeValue;\n    }\n\n    return attr;\n  } else if (attr instanceof Array) {\n    // loop through array and get all values\n    return attr.reduce((last, curr) => {\n      last[curr] = this.attr(curr);\n      return last;\n    }, {});\n  } else if (typeof attr === 'object' && attr.constructor === Object) {\n    // apply every attribute individually if an object is passed\n    for (val in attr) this.attr(val, attr[val]);\n  } else if (val === null) {\n    // remove value\n    this.node.removeAttribute(attr);\n  } else if (val == null) {\n    // act as a getter if the first and only argument is not an object\n    val = this.node.getAttribute(attr);\n    return val == null ? attrs[attr] : isNumber.test(val) ? parseFloat(val) : val;\n  } else {\n    // Loop through hooks and execute them to convert value\n    val = hooks.reduce((_val, hook) => {\n      return hook(attr, _val, this);\n    }, val); // ensure correct numeric values (also accepts NaN and Infinity)\n\n    if (typeof val === 'number') {\n      val = new SVGNumber(val);\n    } else if (Color.isColor(val)) {\n      // ensure full hex color\n      val = new Color(val);\n    } else if (val.constructor === Array) {\n      // Check for plain arrays and parse array values\n      val = new SVGArray(val);\n    } // if the passed attribute is leading...\n\n\n    if (attr === 'leading') {\n      // ... call the leading method instead\n      if (this.leading) {\n        this.leading(val);\n      }\n    } else {\n      // set given attribute on node\n      typeof ns === 'string' ? this.node.setAttributeNS(ns, attr, val.toString()) : this.node.setAttribute(attr, val.toString());\n    } // rebuild if required\n\n\n    if (this.rebuild && (attr === 'font-size' || attr === 'x')) {\n      this.rebuild();\n    }\n  }\n\n  return this;\n}\n\nclass Dom extends EventTarget {\n  constructor(node, attrs) {\n    super();\n    this.node = node;\n    this.type = node.nodeName;\n\n    if (attrs && node !== attrs) {\n      this.attr(attrs);\n    }\n  } // Add given element at a position\n\n\n  add(element, i) {\n    element = makeInstance(element); // If non-root svg nodes are added we have to remove their namespaces\n\n    if (element.removeNamespace && this.node instanceof globals.window.SVGElement) {\n      element.removeNamespace();\n    }\n\n    if (i == null) {\n      this.node.appendChild(element.node);\n    } else if (element.node !== this.node.childNodes[i]) {\n      this.node.insertBefore(element.node, this.node.childNodes[i]);\n    }\n\n    return this;\n  } // Add element to given container and return self\n\n\n  addTo(parent, i) {\n    return makeInstance(parent).put(this, i);\n  } // Returns all child elements\n\n\n  children() {\n    return new List(map(this.node.children, function (node) {\n      return adopt(node);\n    }));\n  } // Remove all elements in this container\n\n\n  clear() {\n    // remove children\n    while (this.node.hasChildNodes()) {\n      this.node.removeChild(this.node.lastChild);\n    }\n\n    return this;\n  } // Clone element\n\n\n  clone(deep = true) {\n    // write dom data to the dom so the clone can pickup the data\n    this.writeDataToDom(); // clone element and assign new id\n\n    return new this.constructor(assignNewId(this.node.cloneNode(deep)));\n  } // Iterates over all children and invokes a given block\n\n\n  each(block, deep) {\n    const children = this.children();\n    let i, il;\n\n    for (i = 0, il = children.length; i < il; i++) {\n      block.apply(children[i], [i, children]);\n\n      if (deep) {\n        children[i].each(block, deep);\n      }\n    }\n\n    return this;\n  }\n\n  element(nodeName, attrs) {\n    return this.put(new Dom(create(nodeName), attrs));\n  } // Get first child\n\n\n  first() {\n    return adopt(this.node.firstChild);\n  } // Get a element at the given index\n\n\n  get(i) {\n    return adopt(this.node.childNodes[i]);\n  }\n\n  getEventHolder() {\n    return this.node;\n  }\n\n  getEventTarget() {\n    return this.node;\n  } // Checks if the given element is a child\n\n\n  has(element) {\n    return this.index(element) >= 0;\n  }\n\n  html(htmlOrFn, outerHTML) {\n    return this.xml(htmlOrFn, outerHTML, html);\n  } // Get / set id\n\n\n  id(id) {\n    // generate new id if no id set\n    if (typeof id === 'undefined' && !this.node.id) {\n      this.node.id = eid(this.type);\n    } // don't set directly with this.node.id to make `null` work correctly\n\n\n    return this.attr('id', id);\n  } // Gets index of given element\n\n\n  index(element) {\n    return [].slice.call(this.node.childNodes).indexOf(element.node);\n  } // Get the last child\n\n\n  last() {\n    return adopt(this.node.lastChild);\n  } // matches the element vs a css selector\n\n\n  matches(selector) {\n    const el = this.node;\n    const matcher = el.matches || el.matchesSelector || el.msMatchesSelector || el.mozMatchesSelector || el.webkitMatchesSelector || el.oMatchesSelector || null;\n    return matcher && matcher.call(el, selector);\n  } // Returns the parent element instance\n\n\n  parent(type) {\n    let parent = this; // check for parent\n\n    if (!parent.node.parentNode) return null; // get parent element\n\n    parent = adopt(parent.node.parentNode);\n    if (!type) return parent; // loop trough ancestors if type is given\n\n    do {\n      if (typeof type === 'string' ? parent.matches(type) : parent instanceof type) return parent;\n    } while (parent = adopt(parent.node.parentNode));\n\n    return parent;\n  } // Basically does the same as `add()` but returns the added element instead\n\n\n  put(element, i) {\n    element = makeInstance(element);\n    this.add(element, i);\n    return element;\n  } // Add element to given container and return container\n\n\n  putIn(parent, i) {\n    return makeInstance(parent).add(this, i);\n  } // Remove element\n\n\n  remove() {\n    if (this.parent()) {\n      this.parent().removeElement(this);\n    }\n\n    return this;\n  } // Remove a given child\n\n\n  removeElement(element) {\n    this.node.removeChild(element.node);\n    return this;\n  } // Replace this with element\n\n\n  replace(element) {\n    element = makeInstance(element);\n\n    if (this.node.parentNode) {\n      this.node.parentNode.replaceChild(element.node, this.node);\n    }\n\n    return element;\n  }\n\n  round(precision = 2, map = null) {\n    const factor = 10 ** precision;\n    const attrs = this.attr(map);\n\n    for (const i in attrs) {\n      if (typeof attrs[i] === 'number') {\n        attrs[i] = Math.round(attrs[i] * factor) / factor;\n      }\n    }\n\n    this.attr(attrs);\n    return this;\n  } // Import / Export raw svg\n\n\n  svg(svgOrFn, outerSVG) {\n    return this.xml(svgOrFn, outerSVG, svg);\n  } // Return id on string conversion\n\n\n  toString() {\n    return this.id();\n  }\n\n  words(text) {\n    // This is faster than removing all children and adding a new one\n    this.node.textContent = text;\n    return this;\n  }\n\n  wrap(node) {\n    const parent = this.parent();\n\n    if (!parent) {\n      return this.addTo(node);\n    }\n\n    const position = parent.index(this);\n    return parent.put(node, position).put(this);\n  } // write svgjs data to the dom\n\n\n  writeDataToDom() {\n    // dump variables recursively\n    this.each(function () {\n      this.writeDataToDom();\n    });\n    return this;\n  } // Import / Export raw svg\n\n\n  xml(xmlOrFn, outerXML, ns) {\n    if (typeof xmlOrFn === 'boolean') {\n      ns = outerXML;\n      outerXML = xmlOrFn;\n      xmlOrFn = null;\n    } // act as getter if no svg string is given\n\n\n    if (xmlOrFn == null || typeof xmlOrFn === 'function') {\n      // The default for exports is, that the outerNode is included\n      outerXML = outerXML == null ? true : outerXML; // write svgjs data to the dom\n\n      this.writeDataToDom();\n      let current = this; // An export modifier was passed\n\n      if (xmlOrFn != null) {\n        current = adopt(current.node.cloneNode(true)); // If the user wants outerHTML we need to process this node, too\n\n        if (outerXML) {\n          const result = xmlOrFn(current);\n          current = result || current; // The user does not want this node? Well, then he gets nothing\n\n          if (result === false) return '';\n        } // Deep loop through all children and apply modifier\n\n\n        current.each(function () {\n          const result = xmlOrFn(this);\n\n          const _this = result || this; // If modifier returns false, discard node\n\n\n          if (result === false) {\n            this.remove(); // If modifier returns new node, use it\n          } else if (result && this !== _this) {\n            this.replace(_this);\n          }\n        }, true);\n      } // Return outer or inner content\n\n\n      return outerXML ? current.node.outerHTML : current.node.innerHTML;\n    } // Act as setter if we got a string\n    // The default for import is, that the current node is not replaced\n\n\n    outerXML = outerXML == null ? false : outerXML; // Create temporary holder\n\n    const well = create('wrapper', ns);\n    const fragment = globals.document.createDocumentFragment(); // Dump raw svg\n\n    well.innerHTML = xmlOrFn; // Transplant nodes into the fragment\n\n    for (let len = well.children.length; len--;) {\n      fragment.appendChild(well.firstElementChild);\n    }\n\n    const parent = this.parent(); // Add the whole fragment at once\n\n    return outerXML ? this.replace(fragment) && parent : this.add(fragment);\n  }\n\n}\nextend(Dom, {\n  attr,\n  find,\n  findOne\n});\nregister(Dom, 'Dom');\n\nclass Element extends Dom {\n  constructor(node, attrs) {\n    super(node, attrs); // initialize data object\n\n    this.dom = {}; // create circular reference\n\n    this.node.instance = this;\n\n    if (node.hasAttribute('svgjs:data')) {\n      // pull svgjs data from the dom (getAttributeNS doesn't work in html5)\n      this.setData(JSON.parse(node.getAttribute('svgjs:data')) || {});\n    }\n  } // Move element by its center\n\n\n  center(x, y) {\n    return this.cx(x).cy(y);\n  } // Move by center over x-axis\n\n\n  cx(x) {\n    return x == null ? this.x() + this.width() / 2 : this.x(x - this.width() / 2);\n  } // Move by center over y-axis\n\n\n  cy(y) {\n    return y == null ? this.y() + this.height() / 2 : this.y(y - this.height() / 2);\n  } // Get defs\n\n\n  defs() {\n    const root = this.root();\n    return root && root.defs();\n  } // Relative move over x and y axes\n\n\n  dmove(x, y) {\n    return this.dx(x).dy(y);\n  } // Relative move over x axis\n\n\n  dx(x = 0) {\n    return this.x(new SVGNumber(x).plus(this.x()));\n  } // Relative move over y axis\n\n\n  dy(y = 0) {\n    return this.y(new SVGNumber(y).plus(this.y()));\n  }\n\n  getEventHolder() {\n    return this;\n  } // Set height of element\n\n\n  height(height) {\n    return this.attr('height', height);\n  } // Move element to given x and y values\n\n\n  move(x, y) {\n    return this.x(x).y(y);\n  } // return array of all ancestors of given type up to the root svg\n\n\n  parents(until = this.root()) {\n    until = makeInstance(until);\n    const parents = new List();\n    let parent = this;\n\n    while ((parent = parent.parent()) && parent.node !== globals.document && parent.nodeName !== '#document-fragment') {\n      parents.push(parent);\n\n      if (parent.node === until.node) {\n        break;\n      }\n    }\n\n    return parents;\n  } // Get referenced element form attribute value\n\n\n  reference(attr) {\n    attr = this.attr(attr);\n    if (!attr) return null;\n    const m = (attr + '').match(reference);\n    return m ? makeInstance(m[1]) : null;\n  } // Get parent document\n\n\n  root() {\n    const p = this.parent(getClass(root));\n    return p && p.root();\n  } // set given data to the elements data property\n\n\n  setData(o) {\n    this.dom = o;\n    return this;\n  } // Set element size to given width and height\n\n\n  size(width, height) {\n    const p = proportionalSize(this, width, height);\n    return this.width(new SVGNumber(p.width)).height(new SVGNumber(p.height));\n  } // Set width of element\n\n\n  width(width) {\n    return this.attr('width', width);\n  } // write svgjs data to the dom\n\n\n  writeDataToDom() {\n    // remove previously set data\n    this.node.removeAttribute('svgjs:data');\n\n    if (Object.keys(this.dom).length) {\n      this.node.setAttribute('svgjs:data', JSON.stringify(this.dom)); // see #428\n    }\n\n    return super.writeDataToDom();\n  } // Move over x-axis\n\n\n  x(x) {\n    return this.attr('x', x);\n  } // Move over y-axis\n\n\n  y(y) {\n    return this.attr('y', y);\n  }\n\n}\nextend(Element, {\n  bbox,\n  rbox,\n  inside,\n  point,\n  ctm,\n  screenCTM\n});\nregister(Element, 'Element');\n\nconst sugar = {\n  stroke: ['color', 'width', 'opacity', 'linecap', 'linejoin', 'miterlimit', 'dasharray', 'dashoffset'],\n  fill: ['color', 'opacity', 'rule'],\n  prefix: function (t, a) {\n    return a === 'color' ? t : t + '-' + a;\n  }\n} // Add sugar for fill and stroke\n;\n['fill', 'stroke'].forEach(function (m) {\n  const extension = {};\n  let i;\n\n  extension[m] = function (o) {\n    if (typeof o === 'undefined') {\n      return this.attr(m);\n    }\n\n    if (typeof o === 'string' || o instanceof Color || Color.isRgb(o) || o instanceof Element) {\n      this.attr(m, o);\n    } else {\n      // set all attributes from sugar.fill and sugar.stroke list\n      for (i = sugar[m].length - 1; i >= 0; i--) {\n        if (o[sugar[m][i]] != null) {\n          this.attr(sugar.prefix(m, sugar[m][i]), o[sugar[m][i]]);\n        }\n      }\n    }\n\n    return this;\n  };\n\n  registerMethods(['Element', 'Runner'], extension);\n});\nregisterMethods(['Element', 'Runner'], {\n  // Let the user set the matrix directly\n  matrix: function (mat, b, c, d, e, f) {\n    // Act as a getter\n    if (mat == null) {\n      return new Matrix(this);\n    } // Act as a setter, the user can pass a matrix or a set of numbers\n\n\n    return this.attr('transform', new Matrix(mat, b, c, d, e, f));\n  },\n  // Map rotation to transform\n  rotate: function (angle, cx, cy) {\n    return this.transform({\n      rotate: angle,\n      ox: cx,\n      oy: cy\n    }, true);\n  },\n  // Map skew to transform\n  skew: function (x, y, cx, cy) {\n    return arguments.length === 1 || arguments.length === 3 ? this.transform({\n      skew: x,\n      ox: y,\n      oy: cx\n    }, true) : this.transform({\n      skew: [x, y],\n      ox: cx,\n      oy: cy\n    }, true);\n  },\n  shear: function (lam, cx, cy) {\n    return this.transform({\n      shear: lam,\n      ox: cx,\n      oy: cy\n    }, true);\n  },\n  // Map scale to transform\n  scale: function (x, y, cx, cy) {\n    return arguments.length === 1 || arguments.length === 3 ? this.transform({\n      scale: x,\n      ox: y,\n      oy: cx\n    }, true) : this.transform({\n      scale: [x, y],\n      ox: cx,\n      oy: cy\n    }, true);\n  },\n  // Map translate to transform\n  translate: function (x, y) {\n    return this.transform({\n      translate: [x, y]\n    }, true);\n  },\n  // Map relative translations to transform\n  relative: function (x, y) {\n    return this.transform({\n      relative: [x, y]\n    }, true);\n  },\n  // Map flip to transform\n  flip: function (direction = 'both', origin = 'center') {\n    if ('xybothtrue'.indexOf(direction) === -1) {\n      origin = direction;\n      direction = 'both';\n    }\n\n    return this.transform({\n      flip: direction,\n      origin: origin\n    }, true);\n  },\n  // Opacity\n  opacity: function (value) {\n    return this.attr('opacity', value);\n  }\n});\nregisterMethods('radius', {\n  // Add x and y radius\n  radius: function (x, y = x) {\n    const type = (this._element || this).type;\n    return type === 'radialGradient' ? this.attr('r', new SVGNumber(x)) : this.rx(x).ry(y);\n  }\n});\nregisterMethods('Path', {\n  // Get path length\n  length: function () {\n    return this.node.getTotalLength();\n  },\n  // Get point at length\n  pointAt: function (length) {\n    return new Point(this.node.getPointAtLength(length));\n  }\n});\nregisterMethods(['Element', 'Runner'], {\n  // Set font\n  font: function (a, v) {\n    if (typeof a === 'object') {\n      for (v in a) this.font(v, a[v]);\n\n      return this;\n    }\n\n    return a === 'leading' ? this.leading(v) : a === 'anchor' ? this.attr('text-anchor', v) : a === 'size' || a === 'family' || a === 'weight' || a === 'stretch' || a === 'variant' || a === 'style' ? this.attr('font-' + a, v) : this.attr(a, v);\n  }\n}); // Add events to elements\n\nconst methods = ['click', 'dblclick', 'mousedown', 'mouseup', 'mouseover', 'mouseout', 'mousemove', 'mouseenter', 'mouseleave', 'touchstart', 'touchmove', 'touchleave', 'touchend', 'touchcancel'].reduce(function (last, event) {\n  // add event to Element\n  const fn = function (f) {\n    if (f === null) {\n      this.off(event);\n    } else {\n      this.on(event, f);\n    }\n\n    return this;\n  };\n\n  last[event] = fn;\n  return last;\n}, {});\nregisterMethods('Element', methods);\n\nfunction untransform() {\n  return this.attr('transform', null);\n} // merge the whole transformation chain into one matrix and returns it\n\nfunction matrixify() {\n  const matrix = (this.attr('transform') || ''). // split transformations\n  split(transforms).slice(0, -1).map(function (str) {\n    // generate key => value pairs\n    const kv = str.trim().split('(');\n    return [kv[0], kv[1].split(delimiter).map(function (str) {\n      return parseFloat(str);\n    })];\n  }).reverse() // merge every transformation into one matrix\n  .reduce(function (matrix, transform) {\n    if (transform[0] === 'matrix') {\n      return matrix.lmultiply(Matrix.fromArray(transform[1]));\n    }\n\n    return matrix[transform[0]].apply(matrix, transform[1]);\n  }, new Matrix());\n  return matrix;\n} // add an element to another parent without changing the visual representation on the screen\n\nfunction toParent(parent, i) {\n  if (this === parent) return this;\n  const ctm = this.screenCTM();\n  const pCtm = parent.screenCTM().inverse();\n  this.addTo(parent, i).untransform().transform(pCtm.multiply(ctm));\n  return this;\n} // same as above with parent equals root-svg\n\nfunction toRoot(i) {\n  return this.toParent(this.root(), i);\n} // Add transformations\n\nfunction transform(o, relative) {\n  // Act as a getter if no object was passed\n  if (o == null || typeof o === 'string') {\n    const decomposed = new Matrix(this).decompose();\n    return o == null ? decomposed : decomposed[o];\n  }\n\n  if (!Matrix.isMatrixLike(o)) {\n    // Set the origin according to the defined transform\n    o = { ...o,\n      origin: getOrigin(o, this)\n    };\n  } // The user can pass a boolean, an Element or an Matrix or nothing\n\n\n  const cleanRelative = relative === true ? this : relative || false;\n  const result = new Matrix(cleanRelative).transform(o);\n  return this.attr('transform', result);\n}\nregisterMethods('Element', {\n  untransform,\n  matrixify,\n  toParent,\n  toRoot,\n  transform\n});\n\nclass Container extends Element {\n  flatten(parent = this, index) {\n    this.each(function () {\n      if (this instanceof Container) {\n        return this.flatten().ungroup();\n      }\n    });\n    return this;\n  }\n\n  ungroup(parent = this.parent(), index = parent.index(this)) {\n    // when parent != this, we want append all elements to the end\n    index = index === -1 ? parent.children().length : index;\n    this.each(function (i, children) {\n      // reverse each\n      return children[children.length - i - 1].toParent(parent, index);\n    });\n    return this.remove();\n  }\n\n}\nregister(Container, 'Container');\n\nclass Defs extends Container {\n  constructor(node, attrs = node) {\n    super(nodeOrNew('defs', node), attrs);\n  }\n\n  flatten() {\n    return this;\n  }\n\n  ungroup() {\n    return this;\n  }\n\n}\nregister(Defs, 'Defs');\n\nclass Shape extends Element {}\nregister(Shape, 'Shape');\n\nfunction rx(rx) {\n  return this.attr('rx', rx);\n} // Radius y value\n\nfunction ry(ry) {\n  return this.attr('ry', ry);\n} // Move over x-axis\n\nfunction x$3(x) {\n  return x == null ? this.cx() - this.rx() : this.cx(x + this.rx());\n} // Move over y-axis\n\nfunction y$3(y) {\n  return y == null ? this.cy() - this.ry() : this.cy(y + this.ry());\n} // Move by center over x-axis\n\nfunction cx$1(x) {\n  return this.attr('cx', x);\n} // Move by center over y-axis\n\nfunction cy$1(y) {\n  return this.attr('cy', y);\n} // Set width of element\n\nfunction width$2(width) {\n  return width == null ? this.rx() * 2 : this.rx(new SVGNumber(width).divide(2));\n} // Set height of element\n\nfunction height$2(height) {\n  return height == null ? this.ry() * 2 : this.ry(new SVGNumber(height).divide(2));\n}\n\nvar circled = {\n  __proto__: null,\n  rx: rx,\n  ry: ry,\n  x: x$3,\n  y: y$3,\n  cx: cx$1,\n  cy: cy$1,\n  width: width$2,\n  height: height$2\n};\n\nclass Ellipse extends Shape {\n  constructor(node, attrs = node) {\n    super(nodeOrNew('ellipse', node), attrs);\n  }\n\n  size(width, height) {\n    const p = proportionalSize(this, width, height);\n    return this.rx(new SVGNumber(p.width).divide(2)).ry(new SVGNumber(p.height).divide(2));\n  }\n\n}\nextend(Ellipse, circled);\nregisterMethods('Container', {\n  // Create an ellipse\n  ellipse: wrapWithAttrCheck(function (width = 0, height = width) {\n    return this.put(new Ellipse()).size(width, height).move(0, 0);\n  })\n});\nregister(Ellipse, 'Ellipse');\n\nclass Fragment extends Dom {\n  constructor(node = globals.document.createDocumentFragment()) {\n    super(node);\n  } // Import / Export raw xml\n\n\n  xml(xmlOrFn, outerXML, ns) {\n    if (typeof xmlOrFn === 'boolean') {\n      ns = outerXML;\n      outerXML = xmlOrFn;\n      xmlOrFn = null;\n    } // because this is a fragment we have to put all elements into a wrapper first\n    // before we can get the innerXML from it\n\n\n    if (xmlOrFn == null || typeof xmlOrFn === 'function') {\n      const wrapper = new Dom(create('wrapper', ns));\n      wrapper.add(this.node.cloneNode(true));\n      return wrapper.xml(false, ns);\n    } // Act as setter if we got a string\n\n\n    return super.xml(xmlOrFn, false, ns);\n  }\n\n}\n\nregister(Fragment, 'Fragment');\n\nfunction from(x, y) {\n  return (this._element || this).type === 'radialGradient' ? this.attr({\n    fx: new SVGNumber(x),\n    fy: new SVGNumber(y)\n  }) : this.attr({\n    x1: new SVGNumber(x),\n    y1: new SVGNumber(y)\n  });\n}\nfunction to(x, y) {\n  return (this._element || this).type === 'radialGradient' ? this.attr({\n    cx: new SVGNumber(x),\n    cy: new SVGNumber(y)\n  }) : this.attr({\n    x2: new SVGNumber(x),\n    y2: new SVGNumber(y)\n  });\n}\n\nvar gradiented = {\n  __proto__: null,\n  from: from,\n  to: to\n};\n\nclass Gradient extends Container {\n  constructor(type, attrs) {\n    super(nodeOrNew(type + 'Gradient', typeof type === 'string' ? null : type), attrs);\n  } // custom attr to handle transform\n\n\n  attr(a, b, c) {\n    if (a === 'transform') a = 'gradientTransform';\n    return super.attr(a, b, c);\n  }\n\n  bbox() {\n    return new Box();\n  }\n\n  targets() {\n    return baseFind('svg [fill*=\"' + this.id() + '\"]');\n  } // Alias string conversion to fill\n\n\n  toString() {\n    return this.url();\n  } // Update gradient\n\n\n  update(block) {\n    // remove all stops\n    this.clear(); // invoke passed block\n\n    if (typeof block === 'function') {\n      block.call(this, this);\n    }\n\n    return this;\n  } // Return the fill id\n\n\n  url() {\n    return 'url(\"#' + this.id() + '\")';\n  }\n\n}\nextend(Gradient, gradiented);\nregisterMethods({\n  Container: {\n    // Create gradient element in defs\n    gradient(...args) {\n      return this.defs().gradient(...args);\n    }\n\n  },\n  // define gradient\n  Defs: {\n    gradient: wrapWithAttrCheck(function (type, block) {\n      return this.put(new Gradient(type)).update(block);\n    })\n  }\n});\nregister(Gradient, 'Gradient');\n\nclass Pattern extends Container {\n  // Initialize node\n  constructor(node, attrs = node) {\n    super(nodeOrNew('pattern', node), attrs);\n  } // custom attr to handle transform\n\n\n  attr(a, b, c) {\n    if (a === 'transform') a = 'patternTransform';\n    return super.attr(a, b, c);\n  }\n\n  bbox() {\n    return new Box();\n  }\n\n  targets() {\n    return baseFind('svg [fill*=\"' + this.id() + '\"]');\n  } // Alias string conversion to fill\n\n\n  toString() {\n    return this.url();\n  } // Update pattern by rebuilding\n\n\n  update(block) {\n    // remove content\n    this.clear(); // invoke passed block\n\n    if (typeof block === 'function') {\n      block.call(this, this);\n    }\n\n    return this;\n  } // Return the fill id\n\n\n  url() {\n    return 'url(\"#' + this.id() + '\")';\n  }\n\n}\nregisterMethods({\n  Container: {\n    // Create pattern element in defs\n    pattern(...args) {\n      return this.defs().pattern(...args);\n    }\n\n  },\n  Defs: {\n    pattern: wrapWithAttrCheck(function (width, height, block) {\n      return this.put(new Pattern()).update(block).attr({\n        x: 0,\n        y: 0,\n        width: width,\n        height: height,\n        patternUnits: 'userSpaceOnUse'\n      });\n    })\n  }\n});\nregister(Pattern, 'Pattern');\n\nclass Image extends Shape {\n  constructor(node, attrs = node) {\n    super(nodeOrNew('image', node), attrs);\n  } // (re)load image\n\n\n  load(url, callback) {\n    if (!url) return this;\n    const img = new globals.window.Image();\n    on(img, 'load', function (e) {\n      const p = this.parent(Pattern); // ensure image size\n\n      if (this.width() === 0 && this.height() === 0) {\n        this.size(img.width, img.height);\n      }\n\n      if (p instanceof Pattern) {\n        // ensure pattern size if not set\n        if (p.width() === 0 && p.height() === 0) {\n          p.size(this.width(), this.height());\n        }\n      }\n\n      if (typeof callback === 'function') {\n        callback.call(this, e);\n      }\n    }, this);\n    on(img, 'load error', function () {\n      // dont forget to unbind memory leaking events\n      off(img);\n    });\n    return this.attr('href', img.src = url, xlink);\n  }\n\n}\nregisterAttrHook(function (attr, val, _this) {\n  // convert image fill and stroke to patterns\n  if (attr === 'fill' || attr === 'stroke') {\n    if (isImage.test(val)) {\n      val = _this.root().defs().image(val);\n    }\n  }\n\n  if (val instanceof Image) {\n    val = _this.root().defs().pattern(0, 0, pattern => {\n      pattern.add(val);\n    });\n  }\n\n  return val;\n});\nregisterMethods({\n  Container: {\n    // create image element, load image and set its size\n    image: wrapWithAttrCheck(function (source, callback) {\n      return this.put(new Image()).size(0, 0).load(source, callback);\n    })\n  }\n});\nregister(Image, 'Image');\n\nclass PointArray extends SVGArray {\n  // Get bounding box of points\n  bbox() {\n    let maxX = -Infinity;\n    let maxY = -Infinity;\n    let minX = Infinity;\n    let minY = Infinity;\n    this.forEach(function (el) {\n      maxX = Math.max(el[0], maxX);\n      maxY = Math.max(el[1], maxY);\n      minX = Math.min(el[0], minX);\n      minY = Math.min(el[1], minY);\n    });\n    return new Box(minX, minY, maxX - minX, maxY - minY);\n  } // Move point string\n\n\n  move(x, y) {\n    const box = this.bbox(); // get relative offset\n\n    x -= box.x;\n    y -= box.y; // move every point\n\n    if (!isNaN(x) && !isNaN(y)) {\n      for (let i = this.length - 1; i >= 0; i--) {\n        this[i] = [this[i][0] + x, this[i][1] + y];\n      }\n    }\n\n    return this;\n  } // Parse point string and flat array\n\n\n  parse(array = [0, 0]) {\n    const points = []; // if it is an array, we flatten it and therefore clone it to 1 depths\n\n    if (array instanceof Array) {\n      array = Array.prototype.concat.apply([], array);\n    } else {\n      // Else, it is considered as a string\n      // parse points\n      array = array.trim().split(delimiter).map(parseFloat);\n    } // validate points - https://svgwg.org/svg2-draft/shapes.html#DataTypePoints\n    // Odd number of coordinates is an error. In such cases, drop the last odd coordinate.\n\n\n    if (array.length % 2 !== 0) array.pop(); // wrap points in two-tuples\n\n    for (let i = 0, len = array.length; i < len; i = i + 2) {\n      points.push([array[i], array[i + 1]]);\n    }\n\n    return points;\n  } // Resize poly string\n\n\n  size(width, height) {\n    let i;\n    const box = this.bbox(); // recalculate position of all points according to new size\n\n    for (i = this.length - 1; i >= 0; i--) {\n      if (box.width) this[i][0] = (this[i][0] - box.x) * width / box.width + box.x;\n      if (box.height) this[i][1] = (this[i][1] - box.y) * height / box.height + box.y;\n    }\n\n    return this;\n  } // Convert array to line object\n\n\n  toLine() {\n    return {\n      x1: this[0][0],\n      y1: this[0][1],\n      x2: this[1][0],\n      y2: this[1][1]\n    };\n  } // Convert array to string\n\n\n  toString() {\n    const array = []; // convert to a poly point string\n\n    for (let i = 0, il = this.length; i < il; i++) {\n      array.push(this[i].join(','));\n    }\n\n    return array.join(' ');\n  }\n\n  transform(m) {\n    return this.clone().transformO(m);\n  } // transform points with matrix (similar to Point.transform)\n\n\n  transformO(m) {\n    if (!Matrix.isMatrixLike(m)) {\n      m = new Matrix(m);\n    }\n\n    for (let i = this.length; i--;) {\n      // Perform the matrix multiplication\n      const [x, y] = this[i];\n      this[i][0] = m.a * x + m.c * y + m.e;\n      this[i][1] = m.b * x + m.d * y + m.f;\n    }\n\n    return this;\n  }\n\n}\n\nconst MorphArray = PointArray; // Move by left top corner over x-axis\n\nfunction x$2(x) {\n  return x == null ? this.bbox().x : this.move(x, this.bbox().y);\n} // Move by left top corner over y-axis\n\nfunction y$2(y) {\n  return y == null ? this.bbox().y : this.move(this.bbox().x, y);\n} // Set width of element\n\nfunction width$1(width) {\n  const b = this.bbox();\n  return width == null ? b.width : this.size(width, b.height);\n} // Set height of element\n\nfunction height$1(height) {\n  const b = this.bbox();\n  return height == null ? b.height : this.size(b.width, height);\n}\n\nvar pointed = {\n  __proto__: null,\n  MorphArray: MorphArray,\n  x: x$2,\n  y: y$2,\n  width: width$1,\n  height: height$1\n};\n\nclass Line extends Shape {\n  // Initialize node\n  constructor(node, attrs = node) {\n    super(nodeOrNew('line', node), attrs);\n  } // Get array\n\n\n  array() {\n    return new PointArray([[this.attr('x1'), this.attr('y1')], [this.attr('x2'), this.attr('y2')]]);\n  } // Move by left top corner\n\n\n  move(x, y) {\n    return this.attr(this.array().move(x, y).toLine());\n  } // Overwrite native plot() method\n\n\n  plot(x1, y1, x2, y2) {\n    if (x1 == null) {\n      return this.array();\n    } else if (typeof y1 !== 'undefined') {\n      x1 = {\n        x1,\n        y1,\n        x2,\n        y2\n      };\n    } else {\n      x1 = new PointArray(x1).toLine();\n    }\n\n    return this.attr(x1);\n  } // Set element size to given width and height\n\n\n  size(width, height) {\n    const p = proportionalSize(this, width, height);\n    return this.attr(this.array().size(p.width, p.height).toLine());\n  }\n\n}\nextend(Line, pointed);\nregisterMethods({\n  Container: {\n    // Create a line element\n    line: wrapWithAttrCheck(function (...args) {\n      // make sure plot is called as a setter\n      // x1 is not necessarily a number, it can also be an array, a string and a PointArray\n      return Line.prototype.plot.apply(this.put(new Line()), args[0] != null ? args : [0, 0, 0, 0]);\n    })\n  }\n});\nregister(Line, 'Line');\n\nclass Marker extends Container {\n  // Initialize node\n  constructor(node, attrs = node) {\n    super(nodeOrNew('marker', node), attrs);\n  } // Set height of element\n\n\n  height(height) {\n    return this.attr('markerHeight', height);\n  }\n\n  orient(orient) {\n    return this.attr('orient', orient);\n  } // Set marker refX and refY\n\n\n  ref(x, y) {\n    return this.attr('refX', x).attr('refY', y);\n  } // Return the fill id\n\n\n  toString() {\n    return 'url(#' + this.id() + ')';\n  } // Update marker\n\n\n  update(block) {\n    // remove all content\n    this.clear(); // invoke passed block\n\n    if (typeof block === 'function') {\n      block.call(this, this);\n    }\n\n    return this;\n  } // Set width of element\n\n\n  width(width) {\n    return this.attr('markerWidth', width);\n  }\n\n}\nregisterMethods({\n  Container: {\n    marker(...args) {\n      // Create marker element in defs\n      return this.defs().marker(...args);\n    }\n\n  },\n  Defs: {\n    // Create marker\n    marker: wrapWithAttrCheck(function (width, height, block) {\n      // Set default viewbox to match the width and height, set ref to cx and cy and set orient to auto\n      return this.put(new Marker()).size(width, height).ref(width / 2, height / 2).viewbox(0, 0, width, height).attr('orient', 'auto').update(block);\n    })\n  },\n  marker: {\n    // Create and attach markers\n    marker(marker, width, height, block) {\n      let attr = ['marker']; // Build attribute name\n\n      if (marker !== 'all') attr.push(marker);\n      attr = attr.join('-'); // Set marker attribute\n\n      marker = arguments[1] instanceof Marker ? arguments[1] : this.defs().marker(width, height, block);\n      return this.attr(attr, marker);\n    }\n\n  }\n});\nregister(Marker, 'Marker');\n\n/***\r\nBase Class\r\n==========\r\nThe base stepper class that will be\r\n***/\n\nfunction makeSetterGetter(k, f) {\n  return function (v) {\n    if (v == null) return this[k];\n    this[k] = v;\n    if (f) f.call(this);\n    return this;\n  };\n}\n\nconst easing = {\n  '-': function (pos) {\n    return pos;\n  },\n  '<>': function (pos) {\n    return -Math.cos(pos * Math.PI) / 2 + 0.5;\n  },\n  '>': function (pos) {\n    return Math.sin(pos * Math.PI / 2);\n  },\n  '<': function (pos) {\n    return -Math.cos(pos * Math.PI / 2) + 1;\n  },\n  bezier: function (x1, y1, x2, y2) {\n    // see https://www.w3.org/TR/css-easing-1/#cubic-bezier-algo\n    return function (t) {\n      if (t < 0) {\n        if (x1 > 0) {\n          return y1 / x1 * t;\n        } else if (x2 > 0) {\n          return y2 / x2 * t;\n        } else {\n          return 0;\n        }\n      } else if (t > 1) {\n        if (x2 < 1) {\n          return (1 - y2) / (1 - x2) * t + (y2 - x2) / (1 - x2);\n        } else if (x1 < 1) {\n          return (1 - y1) / (1 - x1) * t + (y1 - x1) / (1 - x1);\n        } else {\n          return 1;\n        }\n      } else {\n        return 3 * t * (1 - t) ** 2 * y1 + 3 * t ** 2 * (1 - t) * y2 + t ** 3;\n      }\n    };\n  },\n  // see https://www.w3.org/TR/css-easing-1/#step-timing-function-algo\n  steps: function (steps, stepPosition = 'end') {\n    // deal with \"jump-\" prefix\n    stepPosition = stepPosition.split('-').reverse()[0];\n    let jumps = steps;\n\n    if (stepPosition === 'none') {\n      --jumps;\n    } else if (stepPosition === 'both') {\n      ++jumps;\n    } // The beforeFlag is essentially useless\n\n\n    return (t, beforeFlag = false) => {\n      // Step is called currentStep in referenced url\n      let step = Math.floor(t * steps);\n      const jumping = t * step % 1 === 0;\n\n      if (stepPosition === 'start' || stepPosition === 'both') {\n        ++step;\n      }\n\n      if (beforeFlag && jumping) {\n        --step;\n      }\n\n      if (t >= 0 && step < 0) {\n        step = 0;\n      }\n\n      if (t <= 1 && step > jumps) {\n        step = jumps;\n      }\n\n      return step / jumps;\n    };\n  }\n};\nclass Stepper {\n  done() {\n    return false;\n  }\n\n}\n/***\r\nEasing Functions\r\n================\r\n***/\n\nclass Ease extends Stepper {\n  constructor(fn = timeline.ease) {\n    super();\n    this.ease = easing[fn] || fn;\n  }\n\n  step(from, to, pos) {\n    if (typeof from !== 'number') {\n      return pos < 1 ? from : to;\n    }\n\n    return from + (to - from) * this.ease(pos);\n  }\n\n}\n/***\r\nController Types\r\n================\r\n***/\n\nclass Controller extends Stepper {\n  constructor(fn) {\n    super();\n    this.stepper = fn;\n  }\n\n  done(c) {\n    return c.done;\n  }\n\n  step(current, target, dt, c) {\n    return this.stepper(current, target, dt, c);\n  }\n\n}\n\nfunction recalculate() {\n  // Apply the default parameters\n  const duration = (this._duration || 500) / 1000;\n  const overshoot = this._overshoot || 0; // Calculate the PID natural response\n\n  const eps = 1e-10;\n  const pi = Math.PI;\n  const os = Math.log(overshoot / 100 + eps);\n  const zeta = -os / Math.sqrt(pi * pi + os * os);\n  const wn = 3.9 / (zeta * duration); // Calculate the Spring values\n\n  this.d = 2 * zeta * wn;\n  this.k = wn * wn;\n}\n\nclass Spring extends Controller {\n  constructor(duration = 500, overshoot = 0) {\n    super();\n    this.duration(duration).overshoot(overshoot);\n  }\n\n  step(current, target, dt, c) {\n    if (typeof current === 'string') return current;\n    c.done = dt === Infinity;\n    if (dt === Infinity) return target;\n    if (dt === 0) return current;\n    if (dt > 100) dt = 16;\n    dt /= 1000; // Get the previous velocity\n\n    const velocity = c.velocity || 0; // Apply the control to get the new position and store it\n\n    const acceleration = -this.d * velocity - this.k * (current - target);\n    const newPosition = current + velocity * dt + acceleration * dt * dt / 2; // Store the velocity\n\n    c.velocity = velocity + acceleration * dt; // Figure out if we have converged, and if so, pass the value\n\n    c.done = Math.abs(target - newPosition) + Math.abs(velocity) < 0.002;\n    return c.done ? target : newPosition;\n  }\n\n}\nextend(Spring, {\n  duration: makeSetterGetter('_duration', recalculate),\n  overshoot: makeSetterGetter('_overshoot', recalculate)\n});\nclass PID extends Controller {\n  constructor(p = 0.1, i = 0.01, d = 0, windup = 1000) {\n    super();\n    this.p(p).i(i).d(d).windup(windup);\n  }\n\n  step(current, target, dt, c) {\n    if (typeof current === 'string') return current;\n    c.done = dt === Infinity;\n    if (dt === Infinity) return target;\n    if (dt === 0) return current;\n    const p = target - current;\n    let i = (c.integral || 0) + p * dt;\n    const d = (p - (c.error || 0)) / dt;\n    const windup = this._windup; // antiwindup\n\n    if (windup !== false) {\n      i = Math.max(-windup, Math.min(i, windup));\n    }\n\n    c.error = p;\n    c.integral = i;\n    c.done = Math.abs(p) < 0.001;\n    return c.done ? target : current + (this.P * p + this.I * i + this.D * d);\n  }\n\n}\nextend(PID, {\n  windup: makeSetterGetter('_windup'),\n  p: makeSetterGetter('P'),\n  i: makeSetterGetter('I'),\n  d: makeSetterGetter('D')\n});\n\nconst segmentParameters = {\n  M: 2,\n  L: 2,\n  H: 1,\n  V: 1,\n  C: 6,\n  S: 4,\n  Q: 4,\n  T: 2,\n  A: 7,\n  Z: 0\n};\nconst pathHandlers = {\n  M: function (c, p, p0) {\n    p.x = p0.x = c[0];\n    p.y = p0.y = c[1];\n    return ['M', p.x, p.y];\n  },\n  L: function (c, p) {\n    p.x = c[0];\n    p.y = c[1];\n    return ['L', c[0], c[1]];\n  },\n  H: function (c, p) {\n    p.x = c[0];\n    return ['H', c[0]];\n  },\n  V: function (c, p) {\n    p.y = c[0];\n    return ['V', c[0]];\n  },\n  C: function (c, p) {\n    p.x = c[4];\n    p.y = c[5];\n    return ['C', c[0], c[1], c[2], c[3], c[4], c[5]];\n  },\n  S: function (c, p) {\n    p.x = c[2];\n    p.y = c[3];\n    return ['S', c[0], c[1], c[2], c[3]];\n  },\n  Q: function (c, p) {\n    p.x = c[2];\n    p.y = c[3];\n    return ['Q', c[0], c[1], c[2], c[3]];\n  },\n  T: function (c, p) {\n    p.x = c[0];\n    p.y = c[1];\n    return ['T', c[0], c[1]];\n  },\n  Z: function (c, p, p0) {\n    p.x = p0.x;\n    p.y = p0.y;\n    return ['Z'];\n  },\n  A: function (c, p) {\n    p.x = c[5];\n    p.y = c[6];\n    return ['A', c[0], c[1], c[2], c[3], c[4], c[5], c[6]];\n  }\n};\nconst mlhvqtcsaz = 'mlhvqtcsaz'.split('');\n\nfor (let i = 0, il = mlhvqtcsaz.length; i < il; ++i) {\n  pathHandlers[mlhvqtcsaz[i]] = function (i) {\n    return function (c, p, p0) {\n      if (i === 'H') c[0] = c[0] + p.x;else if (i === 'V') c[0] = c[0] + p.y;else if (i === 'A') {\n        c[5] = c[5] + p.x;\n        c[6] = c[6] + p.y;\n      } else {\n        for (let j = 0, jl = c.length; j < jl; ++j) {\n          c[j] = c[j] + (j % 2 ? p.y : p.x);\n        }\n      }\n      return pathHandlers[i](c, p, p0);\n    };\n  }(mlhvqtcsaz[i].toUpperCase());\n}\n\nfunction makeAbsolut(parser) {\n  const command = parser.segment[0];\n  return pathHandlers[command](parser.segment.slice(1), parser.p, parser.p0);\n}\n\nfunction segmentComplete(parser) {\n  return parser.segment.length && parser.segment.length - 1 === segmentParameters[parser.segment[0].toUpperCase()];\n}\n\nfunction startNewSegment(parser, token) {\n  parser.inNumber && finalizeNumber(parser, false);\n  const pathLetter = isPathLetter.test(token);\n\n  if (pathLetter) {\n    parser.segment = [token];\n  } else {\n    const lastCommand = parser.lastCommand;\n    const small = lastCommand.toLowerCase();\n    const isSmall = lastCommand === small;\n    parser.segment = [small === 'm' ? isSmall ? 'l' : 'L' : lastCommand];\n  }\n\n  parser.inSegment = true;\n  parser.lastCommand = parser.segment[0];\n  return pathLetter;\n}\n\nfunction finalizeNumber(parser, inNumber) {\n  if (!parser.inNumber) throw new Error('Parser Error');\n  parser.number && parser.segment.push(parseFloat(parser.number));\n  parser.inNumber = inNumber;\n  parser.number = '';\n  parser.pointSeen = false;\n  parser.hasExponent = false;\n\n  if (segmentComplete(parser)) {\n    finalizeSegment(parser);\n  }\n}\n\nfunction finalizeSegment(parser) {\n  parser.inSegment = false;\n\n  if (parser.absolute) {\n    parser.segment = makeAbsolut(parser);\n  }\n\n  parser.segments.push(parser.segment);\n}\n\nfunction isArcFlag(parser) {\n  if (!parser.segment.length) return false;\n  const isArc = parser.segment[0].toUpperCase() === 'A';\n  const length = parser.segment.length;\n  return isArc && (length === 4 || length === 5);\n}\n\nfunction isExponential(parser) {\n  return parser.lastToken.toUpperCase() === 'E';\n}\n\nfunction pathParser(d, toAbsolute = true) {\n  let index = 0;\n  let token = '';\n  const parser = {\n    segment: [],\n    inNumber: false,\n    number: '',\n    lastToken: '',\n    inSegment: false,\n    segments: [],\n    pointSeen: false,\n    hasExponent: false,\n    absolute: toAbsolute,\n    p0: new Point(),\n    p: new Point()\n  };\n\n  while (parser.lastToken = token, token = d.charAt(index++)) {\n    if (!parser.inSegment) {\n      if (startNewSegment(parser, token)) {\n        continue;\n      }\n    }\n\n    if (token === '.') {\n      if (parser.pointSeen || parser.hasExponent) {\n        finalizeNumber(parser, false);\n        --index;\n        continue;\n      }\n\n      parser.inNumber = true;\n      parser.pointSeen = true;\n      parser.number += token;\n      continue;\n    }\n\n    if (!isNaN(parseInt(token))) {\n      if (parser.number === '0' || isArcFlag(parser)) {\n        parser.inNumber = true;\n        parser.number = token;\n        finalizeNumber(parser, true);\n        continue;\n      }\n\n      parser.inNumber = true;\n      parser.number += token;\n      continue;\n    }\n\n    if (token === ' ' || token === ',') {\n      if (parser.inNumber) {\n        finalizeNumber(parser, false);\n      }\n\n      continue;\n    }\n\n    if (token === '-') {\n      if (parser.inNumber && !isExponential(parser)) {\n        finalizeNumber(parser, false);\n        --index;\n        continue;\n      }\n\n      parser.number += token;\n      parser.inNumber = true;\n      continue;\n    }\n\n    if (token.toUpperCase() === 'E') {\n      parser.number += token;\n      parser.hasExponent = true;\n      continue;\n    }\n\n    if (isPathLetter.test(token)) {\n      if (parser.inNumber) {\n        finalizeNumber(parser, false);\n      } else if (!segmentComplete(parser)) {\n        throw new Error('parser Error');\n      } else {\n        finalizeSegment(parser);\n      }\n\n      --index;\n    }\n  }\n\n  if (parser.inNumber) {\n    finalizeNumber(parser, false);\n  }\n\n  if (parser.inSegment && segmentComplete(parser)) {\n    finalizeSegment(parser);\n  }\n\n  return parser.segments;\n}\n\nfunction arrayToString(a) {\n  let s = '';\n\n  for (let i = 0, il = a.length; i < il; i++) {\n    s += a[i][0];\n\n    if (a[i][1] != null) {\n      s += a[i][1];\n\n      if (a[i][2] != null) {\n        s += ' ';\n        s += a[i][2];\n\n        if (a[i][3] != null) {\n          s += ' ';\n          s += a[i][3];\n          s += ' ';\n          s += a[i][4];\n\n          if (a[i][5] != null) {\n            s += ' ';\n            s += a[i][5];\n            s += ' ';\n            s += a[i][6];\n\n            if (a[i][7] != null) {\n              s += ' ';\n              s += a[i][7];\n            }\n          }\n        }\n      }\n    }\n  }\n\n  return s + ' ';\n}\n\nclass PathArray extends SVGArray {\n  // Get bounding box of path\n  bbox() {\n    parser().path.setAttribute('d', this.toString());\n    return new Box(parser.nodes.path.getBBox());\n  } // Move path string\n\n\n  move(x, y) {\n    // get bounding box of current situation\n    const box = this.bbox(); // get r