/**
 * mass-tools
 * @version v6.0.0
 * @link https://github.com/cheminfo/mass-tools#readme
 * @license MIT
 */
(function (global, factory) {
  typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
  typeof define === 'function' && define.amd ? define(['exports'], factory) :
  (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.MassTools = {}));
})(this, (function (exports) { 'use strict';

  function appendInternals(data) {
    // for each line (internal fragment) we calculate the vertical position
    // where it should be drawn as well and the maximal number of lines
    let maxNumberLines = 0;
    for (let result of data.results) {
      if (result.internal) {
        result.slot = assignSlot(result.from, result.to, data.residues.residues);
        if (result.slot > maxNumberLines) maxNumberLines = result.slot;
      }
    }
    for (let row of data.rows) {
      row.info.internals = maxNumberLines;
    }
  }

  // we need to define the height of the line.
  // we need to find a height that is not yet used.
  function assignSlot(from, to, residues) {
    let used = {};
    if (from > 0) from--; // we ensure that we don't put on the same line to sequences that are consecutive
    for (let i = from; i < to; i++) {
      let residue = residues[i];
      residue.paper.usedSlots.forEach((usedSlot, index) => {
        used[index] = true;
      });
    }
    let counter = 0;
    while (true) {
      if (!used[counter]) {
        break;
      }
      counter++;
    }
    for (let i = from; i < to; i++) {
      residues[i].paper.usedSlots[counter] = true;
    }
    return counter;
  }

  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]}bGvHHa}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@gWDEI[UYZfjji`T@",
      "coordinates": "!BbGvHGx@bGvH@ha}bOrH_Wxb@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_Wxb@JH_Wxb@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_Wxb@JH_Wxb@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`BbGvHGx@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_Wxb@JH_Xc|bGvHHa}_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}mC}_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|_gp_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|_gp_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@gxbGvH_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|_gp_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]}bGvHHa}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@gWDEI[UYZfjji`T@",
      "coordinates": "!BbGvHGx@bGvH@ha}bOrH_Wxb@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\\OtPThyEGl@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": "!BbGvHHa}_c~HM]}_`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@FL"
    },
    "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]}bGvHGx@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": "!BbGvHGx@bGvH@ha}_c~HM]}_`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]}bGvHGx@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": "!BbGvHGx@bGvH@ha}_c~HM]}_`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`BbGvHGx@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\\\\EdwFEI[UVVijjjfIP@",
      "coordinates": "!BbOrH_Wxb@JH_Xc|bGvHHa}_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}urHGxuwu~@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": "!BbGvHGx_`BH_Xa}uwvHHc|_c}~Oxa}uwvHGxbGwW_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|_gp_C}_gp_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|_gp_A}_gp_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]}bGvHHa}"
    },
    "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": "!BbKvHM^}_c}~@Hb}dXWHb}j|nHHc|AqOWoWxJV^Ho]\\BuwvHHb}"
    },
    "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": "!BbGtBbGwWbGvHGxbGu~@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}bOsWHc|_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@",
      "coordinates": ""
    },
    "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@",
      "coordinates": ""
    },
    "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]}bGvHHa}"
    },
    "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@",
      "coordinates": ""
    },
    "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]}bGvHHa}"
    },
    "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": "!BbGvHHa}b@JH_Wxb@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|OwpWy"
    },
    "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@gxbGvH_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@gxbGvH_WxbGt"
    },
    "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": "!BbGvHHa}_c~HM]}_`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@",
      "coordinates": ""
    },
    "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@",
      "coordinates": ""
    },
    "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@",
      "coordinates": ""
    },
    "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@",
      "coordinates": ""
    },
    "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@",
      "coordinates": ""
    },
    "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@",
      "coordinates": ""
    },
    "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@",
      "coordinates": ""
    },
    "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@",
      "coordinates": ""
    },
    "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@",
      "coordinates": ""
    },
    "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@",
      "coordinates": ""
    },
    "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\\]FBIuWxP^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~@GxbGt"
    },
    "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|GwpWy|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_WxbOsW_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@gxbGvH_WxbGt"
    },
    "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_`BHGx@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": "!BbGvHHa}_Xc|bGxb@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}{JxyO|XoSWC}W]poGQ\\Ou}]rmx\\Ou}]{qpza|qb}MJwlk^sFO|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}{JxyO|XoSWA}_W]poGQ\\GuMKuMh\\Gu}]{qpSF]tWQTvatP"
    },
    "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}{JxyO|XoSWA}W]poGQ\\Gu}]rmx\\Ou}]{qpza|qb}MJwlk^sFza|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}{JxyO|XoSWC}_W]poGQ\\GuMKuMh\\Gu}]{qpSF]tWQTvaSZGQ"
    },
    "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}{JxyO|XoSWA}_W]poGQ\\GuMKuMh\\Gu}]{qpSF]tWQTvatP"
    },
    "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@mQbGu}utnDM^HGwWzf~_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@mQbGuMcqLX@m^H@gwWKB__t]Q_`@SFGx@Owx@_mQ"
    },
    "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@mQbGu}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}_]^HMQbGuMcqLX@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@mQbGuMcqLX@m^H@gwWKB__t]Q_`@SFGx@Owx@_mQ"
    },
    "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@mQbGu}utnDM^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@mQbGuMcqLX@m^H@gwWKB__t]Q_`@SFOrHupH@_mQ_`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@mQbGu}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": "fgQZ`IQaPUg^BwhygnCqUxY|E~FwGHeEEdeEHhlTiDSISbRuUUUMLuMMMVDKBy@@",
      "coordinates": "!BIi[Rx{_grZOSXa}_]^H@mQbGuMcqLX@m^H@gwWKB__t]Q_`@SFALXHcW@`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@mQbGuMcqLX@m^H@gwWKB__t]Q_`@SFOrHupH@_mQ_`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\\KqpQARcg|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\\KqpQARcg|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": "!BNuSFPDlDTEHt_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": "!BNuSFPDlDTEHt_pHtP@H_TuPBOpcbpXBGtSItuPSU@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": "!BNuSFPDlDTEHt_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": "!BNuSFPDlDTEHt_pHtP@H_TuPBOpcbpXBGtSItuPSU@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": "!BNuSFPDlDTEHt_pHtP@H_TuPBOpcbpXBGtSItuPSU@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": "fkhh`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": "fgqZ`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": "fgPz`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_UARccFPEP{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_UARccFPEP{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_UARccFPEP{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_UARccFPEP{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_UARccFPEP{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": "!BNuSFPDlDTEHt_pHtP@H_TuOSU@HEF@`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": "!BNuSFPDlDTEHt_pHtP@H_TuOSU@HEF@`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": "!BNuSFPDlDTEHt_pHtP@H_TuOSU@HEF@`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": "!BNuSFPDlDTEHt_pHtP@HoTuOSU@HC~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": "!BNuSFPDlDTEHt_pHtP@H_TuOSU@HEF@`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": "!BNuSFPDlDTEHt_pHtP@H_TuOSU@HEF@`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": "!BNuSFPDlDTEHt_pHtP@H_TuOSU@HEF@`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`XKFjKB_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`XKFjKB_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": "!BNuSFPDlDTEHt_pHtP@H_TuOSU@HC~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": "!BNuSFPDlDTEHt_pHtP@H_TuOSU@HC~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": "!BNuSFPDlDTEHt_pHtP@H_TuOSU@HC~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": "!BNuSFPDlDTEHt_pHtP@H_TuOSU@HC~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": "!BNuSFPDlDTEHt_pHtP@H_TuOSU@HC~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\\YpMAMpBYMcvjbOplIwx@bGuMc}\\Bb@JH@dvOcuKPSXa}bGvHH`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\\YpMAMpBYMcvjb@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~HHa}"
    },
    "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": "!BNuSFPDlDTEHt_pHtP@H_TuOSU@HC~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": "fgpK@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": "!BNuSFPDlDTEHt_pHtP@H_TuOSU@HC~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": "!BNuSFPDlDTEHt_pHtP@H_TuOSU@HC~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": "!BNuSFPDlDTEHt_pHtP@H_TuOSU@HEF@`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": "!BNuSFPDlDTEHt_pHtP@H_TuOSU@HEF@`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": "!BNuSFPDlDTEHt_pHtP@H_TuPBOpcbpXBGtSItuPSU@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": "!BNuSFPDlDTEHt_pHtP@H_TuOSU@HEF@`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": "!BNuSFPDlDTEHt_pHtP@H_TuOSU@HC~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": "!BNuSFPDlDTEHt_pHtP@H_TuOSU@HEF@`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": "!BNuSFPDlDTEHt_pHtP@H_TuOSU@HC~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": "!BNuSFPDlDTEHt_pHtP@HoTuOSU@HEF@`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": "!BNuSFPDlDTEHt_pHtP@H_TuOSU@HC~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": "!BNuSFPDlDTEHt_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": "!BNuSFPDlDTEHt_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": "!BNuSFPDlDTEHt_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": "!BNuSFPDlDTEHt_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|gKMARH`Ygy|fpAfN`Hz@`H{PTb\\ltEIRtHBNHaTv|@YFYPTha}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": "!BNuSFPDlDTEHt_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": "fgpk@OAWBgENSgi{`|[^DWGHeEEMeDheIhjbihs`RuUUTsTuSUMQDSDHfP@",
      "coordinates": "!BTh|SI~ioOwy`iR\\SiV|SFGxw}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": "!BNuSFPDlDTEHt_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": "!BNuSFPDlDTEHt_pHtP@H_TuPBOpcbpXBGtSItuPSU@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": "!BNuSFPDlDTEHt_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": "!BNuSFPDlDTEHt_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": "!BNuSFPDlDTEHt_pHtP@H_TuPBOpcbpXBGtSItuPSU@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": "!BNuSFPDlDTEHt_pHtP@H_TuPBOpcbpXBGtSItuPSU@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@tURDM\\YpMAMpBYMcx`BKB]~@Ha}SXW@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": "fgxs@I^BuY{piqR\\`|[^DWGHeEEMeDeEHmUddhsgbuUUUSTuUUMIDSDHfP@",
      "coordinates": "!BDqc_tTnD_]\\fpH}MgrYRc}_|Dr_W_Wx@ThWM_|bOqRc}ARctu~@Gx@urH@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": "!BNuSFPDlDTEHt_pHtP@H_TuPBOpcbpXBGtSItuPSU@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}_cW_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[_}muRdIAMsuI~_{]|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]WHc|TmCQw}~N`ME~@Gx@upJH@h`B_`BH_X`BbGvHGxbGt"
    },
    "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_Wxb@JH_WxbOrHo]^}_`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@bGvHGx@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~HH`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|SFGxw}FH_]]}Dqa~Oxc|_c|SFA`lIqOW_Xa}_c~HHa}bOrH_WxbOq~@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": "!BNuSFPDlDTEHt_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": "!BNuSFPDlDTEHt_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": "!BNuSFPDlDTEHt_pHtP@H_TuPBOpcbpXBGtSItuPSU@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": "!BNuSFPDlDTEHt_pHtP@H_TuPBOpcbpXBGtSItuPSU@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@tURDM\\YpMAMpBYMcx`BKB]~@Ha}SXW@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{WHc|KB_W_Wx@_`@lIr\\SFBrH@h`B_`BH_WxbOrH_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": "!BNuSFPDlDTEHt_pHtP@H_TuPBOpcbpXBGtSItuPSU@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": "!BNuSFPDlDTEHt_pHtP@H_TuPBOpcbpXBGtSItuPSU@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": "fgpj`I^{BgEIrwY{`|[^C`bKblHrIQQSYQJIRYUYIRLxDmUUUTsMSUKTQDqBId@@",
      "coordinates": "!BKAb@tURD@m\\YpMAMpBYMcx`BKB]~@Ha}SXW@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": "!BNuSFPDlDTEHt_pHtP@H_TuPBOpcbpXBGtSItuPSU@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": "!BNuSFPDlDTEHt_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": "!BNuSFPDlDTEHt_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": "!BNuSFPDlDTEHt_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": "!BNuSFPDlDTEHt_pHtP@H_TuPBOpcbpXBGtSItuPSU@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": "!BNuSFPDlDTEHt_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": "!BNuSFPDlDTEHt_pHtP@H_TuPBOpcbpXBGtSItuPSU@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|SFGxw}FH_]]}DqbH@gx_c|SFA`lIqOW_Xa}_c~HHa}_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|SFGxw}FH_]]}DqbH@gx_c|SFA`lIqOW_Xa}_c~HHa}_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|SFGxw}FH_]]}DqbH@gx_c|SFA`lIqOW_Xa}_c~HHa}_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_WxbOq~@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|urH@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}_`CWHc|_`@"
    },
    "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_Wxb@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}sNaPThxSUCjhIP{PAeMTD}MTEI@IllDPB[|BIP{eCm@FUARAIPH_Pc|BGtHGzBGtw_Pa}_k|HGzBGt"
    },
    "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\\BHHa}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_`CWMF@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": "!BNuSFPDlDTEHt_pHtP@H_TuPBOpcbpXBGtSItuPSU@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": "!BNuSFPDlDTEHt_pHtP@H_TuPBOpcbpXBGtSItuPSU@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": "!BNuSFPDlDTEHt_pHtP@H_TuPBOpcbpXBGtSItuPSU@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": "!BNuSFPDlDTEHt_pHtP@H_TuPBOq_qopHBGtgD}D@RpPH_Wtw@aOTd}RStwQ@MD@RpQ_qcQSU@"
    },
    "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": "!BNuSFPDlDTEHt_pHtP@H_TuPBGpcbpXBGtSItuPSU@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}SXPTh{_w}GjXES[pAg_t]F@cm@Il@f@haTvuS[pAgPThQTbGvH@ha}_c~HGx@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": "!BNuSFPDlDTEHt_pHtP@H_TuPBOpcbpXBGtSItuPSU@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": "fcis@INBwlJ\\TgHp^MoBKcdRbbfrbTRdR\\RVYspZ}fnjjjjefifjiHbXaDr@@",
      "coordinates": "!BNuSFPDlDTEHt_pHtP@H_TuPBOq_qopHBGtgD}D@RpPH_Wtw@aOTd}RStwQ@MD@RpQ_qcQ"
    },
    "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": "!BNuSFPDlDTEHt_pHtP@H_TuPBOpcbpXBGtSItuPSU@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{WHc|KB_W_Wx@_`@lIr\\SFBrHHc|_`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@tURDM\\YpMAMpBYMcxc|KB]~@Ha}SXWHc|bOqMcx}RtDvH_Xa}bOrH@ha}_c~HHa}"
    },
    "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": "!BNuSFPDlDTEHt_pHtP@H_TuPBOpcbpXBGtSItuPSU@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": "!BNuSFPDlDTEHt_pHtP@H_TuPBOpcbpXBGtSItuPSU@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": "!BNuSFPDlDTEHt_pHtP@H_TuPBOpcbpXBGtSItuPSU@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": "!BNuSFPDlDTEHt_pHtP@H_TuPBOpcbpXBGtSItuPSU@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": "!BNuSFPDlDTEHt_pHtP@H_TuPBOpcbpXBGtSItuPSU@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@tURDM\\YpMAMpBYMcx`BKB]~@Ha}SXW@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": "!BNuSFPDlDTEHt_pHtP@H_TuPBOpcbpXBGtSItuPSU@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{WHc|KB_W_Wx@_`@lIr\\SFBrH@h`B_`BH_WxbOrH_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{WHc|KB_W_Wx@_`@lIr\\SFBrHHc|_`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": "!BNuSFPDlDTEHt_pHtP@H_TuPBOpcbpXBGtSItuPSU@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": "!BNuSFPDlDTEHt_pHtP@H_TuPBOpcbpXBGtSItuPSU@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": "!BNuSFPDlDTEHt_pHtP@H_TuPBOpcbpXBGtSItuPSU@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": "fgqh`I^{BgENSenswAxv|HnJpcHeEEMeDheIeUdeHs`RuUUUSLuMTmQDSDHfP@",
      "coordinates": "!BKAb@tURD@m\\YpMAMpBYMcx`BKB]~@Ha}SXW@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": "!BNuSFPDlDTEHt_pHtP@H_TuPBOpcbpXBGtSItuPSU@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": "!BNuSFPDlDTEHt_pHtP@H_TuPBOpcbpXBGtSItuPSU@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": "!BNuSFPDlDTEHt_pHtP@H_TuPBOpcbpXBGtSItuPSU@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_Wxb@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[~iRSpHUCneXDBYTEITAEPDiVA@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~HH`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": "!BNuSFPDlDTEHt_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@bOrHHa}_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@bOrHHa}_`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": "!BNuSFPDlDTEHt_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": "!BNuSFPDlDTEHt_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": "!BNuSFPDlDTEHt_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": "!BNuSFPDlDTEHt_pHtP@H_TuPBOpcbpXBGtSItuPSU@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": "!BNuSFPDlDTEHt_pHtP@H_TuPBOpcbpXBGtSItuPSU@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": "!BNuSFPDlDTEHt_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": "!BNuSFPDlDTEHt_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": "!BNuSFPDlDTEHt_pHtP@H_TuPBOpcbpXBGtSItuPSU@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": "!BNuSFPDlDTEHt_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": "!BNuSFPDlDTEHt_pHtP@H_TuPBOpcbpXBGtSItuPSU@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_gp_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|BGuPThxUCl{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_cQw}G_OwzH_[wW_c~H_Wx@G{|bM]}bGvHGxbGu~Oxa}bOq~Oxa}_c~H_WxuwvH_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[~kjXFjiV[Ry|fcm}MtGwWctvH_]Q_c}KaGwWbGvN`H}MgrX@_gx@h`gKB\\lbGvOSX}M@m^H@gwWbGvH@ha}_Xc|bGxb@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~@GxbGt"
    },
    "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": "fkhh`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
    }]
  }];

  const groupsObject = {};
  groups.forEach(e => {
    if (groupsObject[e.symbol]) {
      console.log('The symbol ' + e.symbol + ' is used more than once');
    }
    groupsObject[e.symbol] = e;
  });

  /**
   * Recreate a one letter sequence
   * @param {object} mf
   */

  function groupsToSequence(mf) {
    mf = mf.replace(/\([^(]*\)/g, '');
    let parts = mf.split(/(?=[A-Z ])/);
    let usefulParts = [];
    for (let part of parts) {
      if (part === ' ') {
        usefulParts.push(' ');
        continue;
      }
      if (!part.match(/^[A-Z][a-z]{2,6}/)) continue;
      if (groupsObject[part] && groupsObject[part].oneLetter) {
        usefulParts.push(groupsObject[part].oneLetter);
      } else {
        usefulParts.push('?');
      }
    }
    return usefulParts.join('').replace(/ +/g, ' ').trim();
  }

  /**
   * Ensure that the sequence is in uppercase taking into account possible modifications
   * @param {string} [options.circular=false]
   */

  function ensureUppercaseSequence(sequence) {
    let parenthesisCounter = 0;
    let parts = [];
    let part = '';
    for (let i = 0; i < sequence.length; i++) {
      let currentSymbol = sequence[i];
      if (currentSymbol === '(' && parenthesisCounter === 0 && part) {
        parts.push(part);
        part = currentSymbol;
      } else if (currentSymbol === ')' && parenthesisCounter === 0) {
        part += currentSymbol;
        parts.push(part);
        part = '';
      } else {
        part += currentSymbol;
      }
    }
    if (part) parts.push(part);
    for (let i = 0; i < parts.length; i++) {
      if (!parts[i].startsWith('(') && parts[i].match(/^[a-z]+$/)) {
        parts[i] = parts[i].toUpperCase();
      }
    }
    return parts.join('');
  }

  /**
   * Convert a nucleic sequence to a MF
   * @param {String} sequence
   * @param {object} [options={}]
   * @param {string} [options.kind] - rna, ds-dna or dna. Default if contains U: rna, otherwise ds-dna
   * @param {string} [options.fivePrime=monophosphate] - alcohol, monophosphate, diphosphate, triphosphate
   * @param {string} [options.circular=false]
   */

  function sequenceToMF$1(sequence) {
    let options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
    let fivePrimeTerminal = 'HO';
    let threePrimeTerminal = 'H';
    sequence = sequence.replace(/^HO/, '');
    sequence = sequence.replace(/H$/, '');
    sequence = sequence.trim();
    if (sequence === '') return '';
    sequence = ensureUppercaseSequence(sequence);

    // if the sequence is in lowercase but the parenthesis we should convert it to uppercase

    if (sequence.match(/^[a-z]+$/)) {
      sequence = sequence.toUpperCase();
    }
    let {
      kind,
      circular,
      fivePrime = 'monophosphate'
    } = options;
    fivePrime = fivePrime.replace(/[^a-zA-Z]/g, '').toLowerCase();
    if (!kind) {
      if (sequence.includes('U')) {
        kind = 'rna';
      } else {
        kind = 'ds-dna';
      }
    }
    kind = kind.replace(/[^A-Za-z]/g, '').toLowerCase();
    if (sequence.includes('(') && kind === 'dsdna') {
      throw new Error('Nucleotide sequenceToMF: modifications not allowed for ds-DNA');
    }
    let results = [[]];
    if (kind === 'dsdna') results.push([]);
    let parenthesisCounter = 0;
    for (let i = 0; i < sequence.length; i++) {
      let currentSymbol = sequence[i];
      while (sequence[i + 1] && sequence[i + 1].match(/[a-z]/)) {
        i++;
        currentSymbol += sequence[i];
      }
      if (currentSymbol.length > 1) {
        results[0].push(currentSymbol);
        continue;
      }
      if (currentSymbol === '(' || currentSymbol === ')' || parenthesisCounter > 0) {
        if (currentSymbol === '(') {
          parenthesisCounter++;
          if (i === 0) fivePrimeTerminal = '';
        }
        if (currentSymbol === ')') {
          parenthesisCounter--;
          if (i === sequence.length - 1) threePrimeTerminal = '';
        }
        switch (kind) {
          case 'dna':
          case 'rna':
            results[0].push(currentSymbol);
            break;
          default:
            // eslint-disable-next-line no-console
            console.warn(`Nucleotide sequenceToMF with modification: unknown kind: ${kind}`);
        }
        continue;
      }
      let nucleotideType = i === 0 ? fivePrime : 'monophosphate';
      currentSymbol = currentSymbol.replace(/[ \t\r\n]/, '');
      if (!currentSymbol) continue;
      switch (kind) {
        case 'dna':
          results[0].push(desoxyNucleotides[nucleotideType][currentSymbol]);
          break;
        case 'rna':
          results[0].push(oxyNucleotides[nucleotideType][currentSymbol]);
          break;
        case 'dsdna':
          results[0].push(desoxyNucleotides[nucleotideType][currentSymbol]);
          results[1].unshift(desoxyNucleotides[nucleotideType][complementary[currentSymbol]]);
          break;
        default:
          // eslint-disable-next-line no-console
          console.warn(`Nucleotide sequenceToMF: unknown kind: ${kind}`);
      }
    }
    if (!circular) {
      results.forEach(result => result.unshift(fivePrimeTerminal));
      results.forEach(result => result.push(threePrimeTerminal));
    }
    return results.map(result => result.join('')).join('.');
  }
  const complementary = {
    A: 'T',
    T: 'A',
    C: 'G',
    G: 'C'
  };
  const desoxyNucleotides = {
    alcohol: {},
    monophosphate: {},
    diphosphate: {},
    triphosphate: {}
  };
  groups.filter(group => group.kind === 'DNA').forEach(group => {
    if (group.oneLetter) {
      desoxyNucleotides.alcohol[group.oneLetter] = group.symbol;
    }
  });
  groups.filter(group => group.kind === 'DNAp').forEach(group => {
    if (group.oneLetter) {
      desoxyNucleotides.monophosphate[group.oneLetter] = group.symbol;
    }
  });
  groups.filter(group => group.kind === 'NucleotideP').forEach(group => {
    if (group.oneLetter) {
      desoxyNucleotides.monophosphate[group.oneLetter] = group.symbol;
    }
  });
  groups.filter(group => group.kind === 'DNApp').forEach(group => {
    if (group.oneLetter) {
      desoxyNucleotides.diphosphate[group.oneLetter] = group.symbol;
    }
  });
  groups.filter(group => group.kind === 'DNAppp').forEach(group => {
    if (group.oneLetter) {
      desoxyNucleotides.triphosphate[group.oneLetter] = group.symbol;
    }
  });
  const oxyNucleotides = {
    alcohol: {},
    monophosphate: {},
    diphosphate: {},
    triphosphate: {}
  };
  groups.filter(group => group.kind === 'RNA').forEach(group => {
    if (group.oneLetter) {
      oxyNucleotides.alcohol[group.oneLetter] = group.symbol;
    }
  });
  groups.filter(group => group.kind === 'RNAp').forEach(group => {
    if (group.oneLetter) {
      oxyNucleotides.monophosphate[group.oneLetter] = group.symbol;
    }
  });
  groups.filter(group => group.kind === 'NucleotideP').forEach(group => {
    if (group.oneLetter) {
      oxyNucleotides.monophosphate[group.oneLetter] = group.symbol;
    }
  });
  groups.filter(group => group.kind === 'RNApp').forEach(group => {
    if (group.oneLetter) {
      oxyNucleotides.diphosphate[group.oneLetter] = group.symbol;
    }
  });
  groups.filter(group => group.kind === 'RNAppp').forEach(group => {
    if (group.oneLetter) {
      oxyNucleotides.triphosphate[group.oneLetter] = group.symbol;
    }
  });

  function furanThreeTerm(nucleotide) {
    // last residue should become a furan
    let parts = nucleotide.replace(/ /g, '').replace(/([a-z)0-9])([A-Z][a-z](?=[a-z]))/g, '$1 $2').split(/ /);
    let last = parts.pop();
    if (!last.match(/D[atcg]mp(.*)$/)) {
      // eslint-disable-next-line no-console
      console.warn(`furanThreeTerm can not remove a non monophosphate nucleic acid: ${last}`);
      return parts.join('') + last;
    }
    return parts.join('') + last.replace(/D[atcg]mp(.*)$/, 'Furp');
  }

  function addFiveTerm(mfs, fiveTerm, i, options) {
    if (options.a) mfs.push(`${fiveTerm}O-1H-1$a${i}`); // neutral ok
    if (options.ab && i > 1) mfs.push(`${furanThreeTerm(fiveTerm)}$a${i}-B`); // A minus base
    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
    if (options.c) mfs.push(`${fiveTerm}PO2$c${i}`); // neutral ok
    if (options.d) mfs.push(`${fiveTerm}PO3H2$d${i}`);
    if (options.dh2o) mfs.push(`${fiveTerm}PO2$d${i}-H2O`);
  }

  /**
   * Define static variable corresponding to the various Kinds of a molecular formula part.
   */

  const Kind = {
    BEGIN: 'begin',
    ATOM: 'atom',
    MULTIPLIER_RANGE: 'multiplierRange',
    ISOTOPE: 'isotope',
    ISOTOPE_RATIO: 'isotopeRatio',
    CHARGE: 'charge',
    SALT: 'salt',
    OPENING_PARENTHESIS: 'openingParenthesis',
    CLOSING_PARENTHESIS: 'closingParenthesis',
    PRE_MULTIPLIER: 'preMultiplier',
    MULTIPLIER: 'multiplier',
    TEXT: 'text',
    ANCHOR: 'anchor',
    COMMENT: 'comment'
  };

  /**
   * Parse a string to extract the charge
   * The charge may be in the form --, +++, +3, -2, 4+, 2-
   * @param {*} charge
   */

  function parseCharge(charge) {
    charge = charge.replace(/[()]/g, '');
    let chargeNumber = 0;
    if (charge.match(/^[+-]+$/)) {
      for (let i = 0; i < charge.length; i++) {
        if (charge.charAt(i) === '+') chargeNumber++;else chargeNumber--;
      }
    } else if (charge.match(/^[0-9]+[+-]$/)) {
      chargeNumber = Number(charge.charAt(charge.length - 1) + charge.substring(0, charge.length - 1));
    } else {
      chargeNumber = Number(charge);
    }
    return chargeNumber;
  }

  /**
   * Parse a mf to an array of kind / value
   * @param {String} mf
   */

  function parse(mf) {
    return new MFParser().parse(mf);
  }
  class MFParser {
    parse() {
      let mf = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : '';
      this.mf = mf;
      this.i = 0;
      this.result = [];
      let lastKind = Kind.BEGIN;
      while (this.i < mf.length) {
        if (this.result.length > 0 && this.result[this.result.length - 1].kind !== Kind.TEXT) {
          lastKind = this.result[this.result.length - 1].kind;
        }
        let char = mf.charAt(this.i);
        let ascii = mf.charCodeAt(this.i);
        let nextAscii = 0;
        if (this.i + 1 < mf.length) nextAscii = mf.charCodeAt(this.i + 1);
        if (ascii > 47 && ascii < 58 || char === '-' && nextAscii > 47 && nextAscii < 58) {
          // a number
          let value = this.getNumber(ascii);
          if (lastKind === Kind.SALT || lastKind === Kind.BEGIN || lastKind === Kind.OPENING_PARENTHESIS) {
            if (value.to) {
              throw new MFError(this.mf, this.i, 'Premultiplier may not contain a -');
            }
            this.result.push({
              kind: Kind.PRE_MULTIPLIER,
              value: value.from
            });
          } else if (lastKind === Kind.ANCHOR) {
            if (value.to) {
              throw new MFError(this.mf, this.i, 'Anchor ID may not contain -');
            }
            this.result[this.result.length - 1].value = value.from;
          } else if (value.to) {
            this.result.push({
              kind: Kind.MULTIPLIER_RANGE,
              value: {
                from: Math.min(value.from, value.to),
                to: Math.max(value.from, value.to)
              }
            });
          } else {
            this.result.push({
              kind: Kind.MULTIPLIER,
              value: value.from
            });
          }
          continue;
        } else if (char === '.') {
          // a point
          this.result.push({
            kind: Kind.SALT,
            value: char
          });
          // it is not in a number otherwise it would have been taken before
          // it must be in a salt
        } else if (char === '#') {
          // an anchor
          this.result.push({
            kind: Kind.ANCHOR,
            value: 0
          });
          // it is not in a number otherwise it would have been taken before
          // it must be in a salt
        } else if (ascii > 64 && ascii < 91) {
          // an uppercase = new atom
          let value = this.getAtom(ascii);
          this.result.push({
            kind: Kind.ATOM,
            value
          });
          continue;
        } else if (ascii > 96 && ascii < 123) {
          // a lowercase
          throw new MFError(this.mf, this.i, 'found a lowercase not following an uppercase');
        } else if (char === '(') {
          let charge = this.getParenthesisCharge(ascii);
          if (charge) {
            this.result.push({
              kind: Kind.CHARGE,
              value: charge
            });
          } else {
            this.result.push({
              kind: Kind.OPENING_PARENTHESIS,
              value: '('
            });
          }
        } else if (char === ')') {
          this.result.push({
            kind: Kind.CLOSING_PARENTHESIS,
            value: ')'
          });
        } else if (char === '[') {
          // defines an isotope
          let isotope = this.getIsotope(ascii);
          this.result.push({
            kind: Kind.ISOTOPE,
            value: isotope
          });
        } else if (char === ']') {
          throw new MFError(this.mf, this.i, 'should never meet an closing bracket not in isotopes');
        } else if (char === '{') {
          // can define an exotic isotopic ratio or mixtures of groups
          let isotopeRatio = this.getCurlyBracketIsotopeRatio(ascii);
          if (lastKind === Kind.ATOM) {
            let lastResult = this.result[this.result.length - 1];
            lastResult.kind = Kind.ISOTOPE_RATIO;
            lastResult.value = {
              atom: lastResult.value,
              ratio: isotopeRatio
            };
          } else {
            throw new MFError(this.mf, this.i, 'isotopic composition has to follow an atom');
          }
        } else if (char === '}') {
          throw new MFError(this.mf, this.i, 'found a unexpected closing curly bracket');
        } else if (char === '+') {
          // charge not in parenthesis
          let charge = this.getNonParenthesisCharge(ascii);
          this.result.push({
            kind: Kind.CHARGE,
            value: charge
          });
        } else if (char === '-') {
          // charge not in parenthesis
          let charge = this.getNonParenthesisCharge(ascii);
          this.result.push({
            kind: Kind.CHARGE,
            value: charge
          });
        } else if (char === '$') {
          // it is a comment after
          this.result.push({
            kind: Kind.COMMENT,
            value: this.mf.substring(this.i + 1)
          });
          break;
        } else {
          this.result.push({
            kind: Kind.TEXT,
            value: char
          });
        }
        this.i++;
      }
      this.checkParenthesis();
      return this.result;
    }
    checkParenthesis() {
      let counter = 0;
      for (let line of this.result) {
        if (line.kind === Kind.OPENING_PARENTHESIS) counter++;
        if (line.kind === Kind.CLOSING_PARENTHESIS) counter--;
      }
      if (counter !== 0) {
        throw new MFError(this.mf, this.i, 'number of opening and closing parenthesis not equal');
      }
    }
    getNumber(ascii) {
      let number = '';
      let previous;
      do {
        previous = ascii;
        number += String.fromCharCode(ascii);
        this.i++;
        ascii = this.mf.charCodeAt(this.i);
      } while (ascii > 47 && ascii < 58 || ascii === 46 || ascii === 45 || ascii === 47); // number . - /
      // we need to deal with the case there is a from / to
      if (previous === 46) this.i--;
      let indexOfDash = number.indexOf('-', 1);
      if (indexOfDash > -1) {
        return {
          from: parseNumberWithDivision(number.substr(0, indexOfDash)),
          to: parseNumberWithDivision(number.substr(indexOfDash + 1))
        };
      }
      return {
        from: parseNumberWithDivision(number)
      };
    }
    getAtom(ascii) {
      let atom = '';
      do {
        atom += String.fromCharCode(ascii);
        this.i++;
        ascii = this.mf.charCodeAt(this.i);
      } while (ascii > 96 && ascii < 123);
      return atom;
    }
    getIsotope(ascii) {
      // [13C]
      let substring = '';
      do {
        substring += String.fromCharCode(ascii);
        this.i++;
        ascii = this.mf.charCodeAt(this.i);
      } while (ascii !== 93 && this.i <= this.mf.length);
      let atom = substring.replace(/[^a-zA-Z]/g, '');
      let isotope = Number(substring.replace(/[^0-9]/g, ''));
      return {
        atom,
        isotope
      };
    }
    getCurlyBracketIsotopeRatio(ascii) {
      let substring = '';
      let first = true;
      do {
        if (!first) {
          substring += String.fromCharCode(ascii);
        } else {
          first = false;
        }
        this.i++;
        ascii = this.mf.charCodeAt(this.i);
      } while (ascii !== 125 && this.i <= this.mf.length); // closing curly bracket
      if (substring.match(/^[0-9,]+$/)) {
        return substring.split(',').map(a => Number(a));
      }
      throw new MFError(this.mf, this.i, 'Curly brackets should contain only number and comma');
    }
    getParenthesisCharge(ascii) {
      let substring = '';
      let begin = this.i;
      do {
        substring += String.fromCharCode(ascii);
        this.i++;
        ascii = this.mf.charCodeAt(this.i);
      } while (ascii !== 41 && this.i <= this.mf.length); // closing parenthesis
      if (substring.match(/^\([0-9+-]+$/)) {
        return parseCharge(substring.substring(1));
      } else {
        this.i = begin;
        return undefined;
      }
    }
    getNonParenthesisCharge(ascii) {
      let substring = '';
      do {
        substring += String.fromCharCode(ascii);
        this.i++;
        ascii = this.mf.charCodeAt(this.i);
      } while (ascii === 43 || ascii === 45 || ascii > 47 && ascii < 58);
      this.i--;
      return parseCharge(substring);
    }
  }
  class MFError extends SyntaxError {
    constructor(mf, i, message) {
      let text = `${message}\n\n${mf}\n${' '.repeat(i)}^`;
      super(text);
    }
  }
  function parseNumberWithDivision(string) {
    if (string.includes('/')) {
      let parts = string.split('/');
      if (parts.length !== 2) {
        throw new TypeError('Can not parse MF with number like: ', string);
      }
      return Number(parts[0]) / Number(parts[1]);
    } else {
      return Number(string);
    }
  }

  const superscript = {
    0: '⁰',
    1: '¹',
    2: '²',
    3: '³',
    4: '⁴',
    5: '⁵',
    6: '⁶',
    7: '⁷',
    8: '⁸',
    9: '⁹',
    '+': '⁺',
    '-': '⁻',
    '(': '⁽',
    ')': '⁾',
    '{': '⁽',
    '}': '⁾',
    '.': '˙',
    ',': '˙'
  };
  const subscript = {
    0: '₀',
    1: '₁',
    2: '₂',
    3: '₃',
    4: '₄',
    5: '₅',
    6: '₆',
    7: '₇',
    8: '₈',
    9: '₉',
    '(': '₍',
    ')': '₎',
    '{': '₍',
    '}': '₎',
    '.': ' ',
    ',': ' '
  };

  /**
   * Defines static variables corresponding to the various formatting possibilities
   */

  const Format = {
    SUBSCRIPT: 'subscript',
    SUPERSCRIPT: 'superscript',
    SUPERIMPOSE: 'superimpose',
    TEXT: 'text'
  };

  function formatCharge(charge) {
    if (charge === 1) return '+';
    if (charge > 1) return `+${charge}`;
    if (charge < 0) return String(charge);
    return '';
  }

  /**
   * Converts an array of mf elements to an array of formatting information
   * @param {object[]} lines of the parse method
   */

  function toDisplay(lines) {
    let results = [];
    let result = {};
    for (let line of lines) {
      switch (line.kind) {
        case Kind.MULTIPLIER:
          if (line.value !== 1) {
            result = {
              kind: Format.SUBSCRIPT,
              value: String(line.value)
            };
            results.push(result);
          }
          break;
        case Kind.MULTIPLIER_RANGE:
          result = {
            kind: Format.SUBSCRIPT,
            value: `${String(line.value.from)}-${line.value.to}`
          };
          results.push(result);
          break;
        case Kind.CHARGE:
          if (result.kind === Format.SUBSCRIPT) {
            result.kind = Format.SUPERIMPOSE;
            result.over = formatCharge(line.value);
            result.under = result.value;
            result.value = undefined;
          } else {
            result = {
              kind: Format.SUPERSCRIPT,
              value: formatCharge(line.value)
            };
            results.push(result);
          }
          break;
        case Kind.ISOTOPE:
          result = {
            kind: Format.SUPERSCRIPT,
            value: line.value.isotope
          };
          results.push(result);
          result = {
            kind: Format.TEXT,
            value: line.value.atom
          };
          results.push(result);
          break;
        case Kind.ISOTOPE_RATIO:
          if (result.kind === Format.TEXT) {
            result.value += line.value.atom;
          } else {
            result = {
              kind: Format.TEXT,
              value: line.value.atom
            };
            results.push(result);
          }
          result = {
            kind: Format.SUPERSCRIPT,
            value: `{${line.value.ratio.join(',')}}`
          };
          results.push(result);
          break;
        case Kind.SALT:
          if (result.kind === Format.TEXT) {
            result.value += ' • ';
          } else {
            result = {
              kind: Format.TEXT,
              value: ' • '
            };
            results.push(result);
          }
          break;
        default:
          if (result.kind === Format.TEXT) {
            result.value += line.value;
          } else {
            result = {
              kind: Format.TEXT,
              value: line.value
            };
            results.push(result);
          }
      }
    }
    return results;
  }

  const Style$1 = {
    SUPERIMPOSE: 'flex-direction: column;display: inline-flex;justify-content: center;text-align: left;vertical-align: middle;',
    SUPERIMPOSE_SUP_SUB: 'line-height: 1; font-size: 70%'
  };

  function toHtml(lines) {
    let html = [];
    for (let line of lines) {
      switch (line.kind) {
        case Format.SUBSCRIPT:
          html.push(`<sub>${line.value}</sub>`);
          break;
        case Format.SUPERSCRIPT:
          html.push(`<sup>${line.value}</sup>`);
          break;
        case Format.SUPERIMPOSE:
          html.push(`<span style="${Style$1.SUPERIMPOSE}">`);
          html.push(`<sup style="${Style$1.SUPERIMPOSE_SUP_SUB}">${line.over}</sup>`);
          html.push(`<sub style="${Style$1.SUPERIMPOSE_SUP_SUB}">${line.under}</sub>`);
          html.push('</span>');
          break;
        default:
          html.push(line.value);
      }
    }
    return html.join('');
  }

  const ELECTRON_MASS = 5.4857990907e-4;

  const elementsAndIsotopes = [{
    number: 1,
    isotopes: [{
      nominal: 1,
      mass: 1.00782503223,
      abundance: 0.999885
    }, {
      nominal: 2,
      mass: 2.01410177812,
      abundance: 0.000115
    }, {
      nominal: 3,
      mass: 3.0160492779
    }, {
      nominal: 4,
      mass: 4.02643
    }, {
      nominal: 5,
      mass: 5.035311
    }, {
      nominal: 6,
      mass: 6.04496
    }, {
      nominal: 7,
      mass: 7.0527
    }],
    symbol: 'H',
    mass: 1.0079407540557772,
    name: 'Hydrogen',
    monoisotopicMass: 1.00782503223
  }, {
    number: 2,
    isotopes: [{
      nominal: 3,
      mass: 3.0160293201,
      abundance: 0.00000134
    }, {
      nominal: 4,
      mass: 4.00260325413,
      abundance: 0.99999866
    }, {
      nominal: 5,
      mass: 5.012057
    }, {
      nominal: 6,
      mass: 6.018885891
    }, {
      nominal: 7,
      mass: 7.0279907
    }, {
      nominal: 8,
      mass: 8.03393439
    }, {
      nominal: 9,
      mass: 9.043946
    }, {
      nominal: 10,
      mass: 10.05279
    }],
    symbol: 'He',
    mass: 4.002601932120929,
    name: 'Helium',
    monoisotopicMass: 4.00260325413
  }, {
    number: 3,
    isotopes: [{
      nominal: 3,
      mass: 3.0308
    }, {
      nominal: 4,
      mass: 4.02719
    }, {
      nominal: 5,
      mass: 5.012538
    }, {
      nominal: 6,
      mass: 6.0151228874,
      abundance: 0.0759
    }, {
      nominal: 7,
      mass: 7.0160034366,
      abundance: 0.9241
    }, {
      nominal: 8,
      mass: 8.022486246
    }, {
      nominal: 9,
      mass: 9.02679019
    }, {
      nominal: 10,
      mass: 10.035483
    }, {
      nominal: 11,
      mass: 11.04372358
    }, {
      nominal: 12,
      mass: 12.052517
    }, {
      nominal: 13,
      mass: 13.06263
    }],
    symbol: 'Li',
    mass: 6.94003660291572,
    name: 'Lithium',
    monoisotopicMass: 7.0160034366
  }, {
    number: 4,
    isotopes: [{
      nominal: 5,
      mass: 5.0399
    }, {
      nominal: 6,
      mass: 6.0197264
    }, {
      nominal: 7,
      mass: 7.016928717
    }, {
      nominal: 8,
      mass: 8.005305102
    }, {
      nominal: 9,
      mass: 9.012183065,
      abundance: 1
    }, {
      nominal: 10,
      mass: 10.013534695
    }, {
      nominal: 11,
      mass: 11.02166108
    }, {
      nominal: 12,
      mass: 12.0269221
    }, {
      nominal: 13,
      mass: 13.036135
    }, {
      nominal: 14,
      mass: 14.04289
    }, {
      nominal: 15,
      mass: 15.05342
    }, {
      nominal: 16,
      mass: 16.06167
    }],
    symbol: 'Be',
    mass: 9.012183065,
    name: 'Beryllium',
    monoisotopicMass: 9.012183065
  }, {
    number: 5,
    isotopes: [{
      nominal: 6,
      mass: 6.0508
    }, {
      nominal: 7,
      mass: 7.029712
    }, {
      nominal: 8,
      mass: 8.0246073
    }, {
      nominal: 9,
      mass: 9.01332965
    }, {
      nominal: 10,
      mass: 10.01293695,
      abundance: 0.199
    }, {
      nominal: 11,
      mass: 11.00930536,
      abundance: 0.801
    }, {
      nominal: 12,
      mass: 12.0143527
    }, {
      nominal: 13,
      mass: 13.0177802
    }, {
      nominal: 14,
      mass: 14.025404
    }, {
      nominal: 15,
      mass: 15.031088
    }, {
      nominal: 16,
      mass: 16.039842
    }, {
      nominal: 17,
      mass: 17.04699
    }, {
      nominal: 18,
      mass: 18.05566
    }, {
      nominal: 19,
      mass: 19.0631
    }, {
      nominal: 20,
      mass: 20.07207
    }, {
      nominal: 21,
      mass: 21.08129
    }],
    symbol: 'B',
    mass: 10.811028046410001,
    name: 'Boron',
    monoisotopicMass: 11.00930536
  }, {
    number: 6,
    isotopes: [{
      nominal: 8,
      mass: 8.037643
    }, {
      nominal: 9,
      mass: 9.0310372
    }, {
      nominal: 10,
      mass: 10.01685331
    }, {
      nominal: 11,
      mass: 11.0114336
    }, {
      nominal: 12,
      mass: 12,
      abundance: 0.9893
    }, {
      nominal: 13,
      mass: 13.00335483507,
      abundance: 0.0107
    }, {
      nominal: 14,
      mass: 14.0032419884
    }, {
      nominal: 15,
      mass: 15.01059926
    }, {
      nominal: 16,
      mass: 16.0147013
    }, {
      nominal: 17,
      mass: 17.022577
    }, {
      nominal: 18,
      mass: 18.026751
    }, {
      nominal: 19,
      mass: 19.0348
    }, {
      nominal: 20,
      mass: 20.04032
    }, {
      nominal: 21,
      mass: 21.049
    }, {
      nominal: 22,
      mass: 22.05753
    }, {
      nominal: 23,
      mass: 23.0689
    }],
    symbol: 'C',
    mass: 12.010735896735248,
    name: 'Carbon',
    monoisotopicMass: 12
  }, {
    number: 7,
    isotopes: [{
      nominal: 10,
      mass: 10.04165
    }, {
      nominal: 11,
      mass: 11.026091
    }, {
      nominal: 12,
      mass: 12.0186132
    }, {
      nominal: 13,
      mass: 13.00573861
    }, {
      nominal: 14,
      mass: 14.00307400443,
      abundance: 0.99636
    }, {
      nominal: 15,
      mass: 15.00010889888,
      abundance: 0.00364
    }, {
      nominal: 16,
      mass: 16.0061019
    }, {
      nominal: 17,
      mass: 17.008449
    }, {
      nominal: 18,
      mass: 18.014078
    }, {
      nominal: 19,
      mass: 19.017022
    }, {
      nominal: 20,
      mass: 20.023366
    }, {
      nominal: 21,
      mass: 21.02711
    }, {
      nominal: 22,
      mass: 22.03439
    }, {
      nominal: 23,
      mass: 23.04114
    }, {
      nominal: 24,
      mass: 24.05039
    }, {
      nominal: 25,
      mass: 25.0601
    }],
    symbol: 'N',
    mass: 14.006703211445798,
    name: 'Nitrogen',
    monoisotopicMass: 14.00307400443
  }, {
    number: 8,
    isotopes: [{
      nominal: 12,
      mass: 12.034262
    }, {
      nominal: 13,
      mass: 13.024815
    }, {
      nominal: 14,
      mass: 14.00859636
    }, {
      nominal: 15,
      mass: 15.00306562
    }, {
      nominal: 16,
      mass: 15.99491461957,
      abundance: 0.99757
    }, {
      nominal: 17,
      mass: 16.9991317565,
      abundance: 0.00038
    }, {
      nominal: 18,
      mass: 17.99915961286,
      abundance: 0.00205
    }, {
      nominal: 19,
      mass: 19.003578
    }, {
      nominal: 20,
      mass: 20.00407535
    }, {
      nominal: 21,
      mass: 21.008655
    }, {
      nominal: 22,
      mass: 22.009966
    }, {
      nominal: 23,
      mass: 23.015696
    }, {
      nominal: 24,
      mass: 24.01986
    }, {
      nominal: 25,
      mass: 25.02936
    }, {
      nominal: 26,
      mass: 26.03729
    }, {
      nominal: 27,
      mass: 27.04772
    }, {
      nominal: 28,
      mass: 28.05591
    }],
    symbol: 'O',
    mass: 15.999404924318277,
    name: 'Oxygen',
    monoisotopicMass: 15.99491461957
  }, {
    number: 9,
    isotopes: [{
      nominal: 14,
      mass: 14.034315
    }, {
      nominal: 15,
      mass: 15.018043
    }, {
      nominal: 16,
      mass: 16.0114657
    }, {
      nominal: 17,
      mass: 17.00209524
    }, {
      nominal: 18,
      mass: 18.00093733
    }, {
      nominal: 19,
      mass: 18.99840316273,
      abundance: 1
    }, {
      nominal: 20,
      mass: 19.999981252
    }, {
      nominal: 21,
      mass: 20.9999489
    }, {
      nominal: 22,
      mass: 22.002999
    }, {
      nominal: 23,
      mass: 23.003557
    }, {
      nominal: 24,
      mass: 24.008115
    }, {
      nominal: 25,
      mass: 25.012199
    }, {
      nominal: 26,
      mass: 26.020038
    }, {
      nominal: 27,
      mass: 27.02644
    }, {
      nominal: 28,
      mass: 28.03534
    }, {
      nominal: 29,
      mass: 29.04254
    }, {
      nominal: 30,
      mass: 30.05165
    }, {
      nominal: 31,
      mass: 31.05971
    }],
    symbol: 'F',
    mass: 18.99840316273,
    name: 'Fluorine',
    monoisotopicMass: 18.99840316273
  }, {
    number: 10,
    isotopes: [{
      nominal: 16,
      mass: 16.02575
    }, {
      nominal: 17,
      mass: 17.01771396
    }, {
      nominal: 18,
      mass: 18.0057087
    }, {
      nominal: 19,
      mass: 19.00188091
    }, {
      nominal: 20,
      mass: 19.9924401762,
      abundance: 0.9048
    }, {
      nominal: 21,
      mass: 20.993846685,
      abundance: 0.0027
    }, {
      nominal: 22,
      mass: 21.991385114,
      abundance: 0.0925
    }, {
      nominal: 23,
      mass: 22.99446691
    }, {
      nominal: 24,
      mass: 23.99361065
    }, {
      nominal: 25,
      mass: 24.997789
    }, {
      nominal: 26,
      mass: 26.000515
    }, {
      nominal: 27,
      mass: 27.007553
    }, {
      nominal: 28,
      mass: 28.01212
    }, {
      nominal: 29,
      mass: 29.01975
    }, {
      nominal: 30,
      mass: 30.02473
    }, {
      nominal: 31,
      mass: 31.0331
    }, {
      nominal: 32,
      mass: 32.03972
    }, {
      nominal: 33,
      mass: 33.04938
    }, {
      nominal: 34,
      mass: 34.05673
    }],
    symbol: 'Ne',
    mass: 20.18004638052026,
    name: 'Neon',
    monoisotopicMass: 19.9924401762
  }, {
    number: 11,
    isotopes: [{
      nominal: 18,
      mass: 18.02688
    }, {
      nominal: 19,
      mass: 19.01388
    }, {
      nominal: 20,
      mass: 20.0073544
    }, {
      nominal: 21,
      mass: 20.99765469
    }, {
      nominal: 22,
      mass: 21.99443741
    }, {
      nominal: 23,
      mass: 22.989769282,
      abundance: 1
    }, {
      nominal: 24,
      mass: 23.99096295
    }, {
      nominal: 25,
      mass: 24.989954
    }, {
      nominal: 26,
      mass: 25.9926346
    }, {
      nominal: 27,
      mass: 26.9940765
    }, {
      nominal: 28,
      mass: 27.998939
    }, {
      nominal: 29,
      mass: 29.0028771
    }, {
      nominal: 30,
      mass: 30.0090979
    }, {
      nominal: 31,
      mass: 31.013163
    }, {
      nominal: 32,
      mass: 32.02019
    }, {
      nominal: 33,
      mass: 33.02573
    }, {
      nominal: 34,
      mass: 34.03359
    }, {
      nominal: 35,
      mass: 35.04062
    }, {
      nominal: 36,
      mass: 36.04929
    }, {
      nominal: 37,
      mass: 37.05705
    }],
    symbol: 'Na',
    mass: 22.989769282,
    name: 'Sodium',
    monoisotopicMass: 22.989769282
  }, {
    number: 12,
    isotopes: [{
      nominal: 19,
      mass: 19.034169
    }, {
      nominal: 20,
      mass: 20.01885
    }, {
      nominal: 21,
      mass: 21.011716
    }, {
      nominal: 22,
      mass: 21.99957065
    }, {
      nominal: 23,
      mass: 22.99412421
    }, {
      nominal: 24,
      mass: 23.985041697,
      abundance: 0.7899
    }, {
      nominal: 25,
      mass: 24.985836976,
      abundance: 0.1
    }, {
      nominal: 26,
      mass: 25.982592968,
      abundance: 0.1101
    }, {
      nominal: 27,
      mass: 26.984340624
    }, {
      nominal: 28,
      mass: 27.9838767
    }, {
      nominal: 29,
      mass: 28.988617
    }, {
      nominal: 30,
      mass: 29.9904629
    }, {
      nominal: 31,
      mass: 30.996648
    }, {
      nominal: 32,
      mass: 31.9991102
    }, {
      nominal: 33,
      mass: 33.0053271
    }, {
      nominal: 34,
      mass: 34.008935
    }, {
      nominal: 35,
      mass: 35.01679
    }, {
      nominal: 36,
      mass: 36.02188
    }, {
      nominal: 37,
      mass: 37.03037
    }, {
      nominal: 38,
      mass: 38.03658
    }, {
      nominal: 39,
      mass: 39.04538
    }, {
      nominal: 40,
      mass: 40.05218
    }],
    symbol: 'Mg',
    mass: 24.3050516198371,
    name: 'Magnesium',
    monoisotopicMass: 23.985041697
  }, {
    number: 13,
    isotopes: [{
      nominal: 21,
      mass: 21.02897
    }, {
      nominal: 22,
      mass: 22.01954
    }, {
      nominal: 23,
      mass: 23.00724435
    }, {
      nominal: 24,
      mass: 23.9999489
    }, {
      nominal: 25,
      mass: 24.9904281
    }, {
      nominal: 26,
      mass: 25.986891904
    }, {
      nominal: 27,
      mass: 26.98153853,
      abundance: 1
    }, {
      nominal: 28,
      mass: 27.98191021
    }, {
      nominal: 29,
      mass: 28.9804565
    }, {
      nominal: 30,
      mass: 29.98296
    }, {
      nominal: 31,
      mass: 30.983945
    }, {
      nominal: 32,
      mass: 31.988085
    }, {
      nominal: 33,
      mass: 32.990909
    }, {
      nominal: 34,
      mass: 33.996705
    }, {
      nominal: 35,
      mass: 34.999764
    }, {
      nominal: 36,
      mass: 36.00639
    }, {
      nominal: 37,
      mass: 37.01053
    }, {
      nominal: 38,
      mass: 38.0174
    }, {
      nominal: 39,
      mass: 39.02254
    }, {
      nominal: 40,
      mass: 40.03003
    }, {
      nominal: 41,
      mass: 41.03638
    }, {
      nominal: 42,
      mass: 42.04384
    }, {
      nominal: 43,
      mass: 43.05147
    }],
    symbol: 'Al',
    mass: 26.98153853,
    name: 'Aluminium',
    monoisotopicMass: 26.98153853
  }, {
    number: 14,
    isotopes: [{
      nominal: 22,
      mass: 22.03579
    }, {
      nominal: 23,
      mass: 23.02544
    }, {
      nominal: 24,
      mass: 24.011535
    }, {
      nominal: 25,
      mass: 25.004109
    }, {
      nominal: 26,
      mass: 25.99233384
    }, {
      nominal: 27,
      mass: 26.98670481
    }, {
      nominal: 28,
      mass: 27.97692653465,
      abundance: 0.92223
    }, {
      nominal: 29,
      mass: 28.9764946649,
      abundance: 0.04685
    }, {
      nominal: 30,
      mass: 29.973770136,
      abundance: 0.03092
    }, {
      nominal: 31,
      mass: 30.975363194
    }, {
      nominal: 32,
      mass: 31.97415154
    }, {
      nominal: 33,
      mass: 32.97797696
    }, {
      nominal: 34,
      mass: 33.978576
    }, {
      nominal: 35,
      mass: 34.984583
    }, {
      nominal: 36,
      mass: 35.986695
    }, {
      nominal: 37,
      mass: 36.992921
    }, {
      nominal: 38,
      mass: 37.995523
    }, {
      nominal: 39,
      mass: 39.002491
    }, {
      nominal: 40,
      mass: 40.00583
    }, {
      nominal: 41,
      mass: 41.01301
    }, {
      nominal: 42,
      mass: 42.01778
    }, {
      nominal: 43,
      mass: 43.0248
    }, {
      nominal: 44,
      mass: 44.03061
    }, {
      nominal: 45,
      mass: 45.03995
    }],
    symbol: 'Si',
    mass: 28.085498705705955,
    name: 'Silicon',
    monoisotopicMass: 27.97692653465
  }, {
    number: 15,
    isotopes: [{
      nominal: 24,
      mass: 24.03577
    }, {
      nominal: 25,
      mass: 25.02119
    }, {
      nominal: 26,
      mass: 26.01178
    }, {
      nominal: 27,
      mass: 26.999224
    }, {
      nominal: 28,
      mass: 27.9923266
    }, {
      nominal: 29,
      mass: 28.98180079
    }, {
      nominal: 30,
      mass: 29.97831375
    }, {
      nominal: 31,
      mass: 30.97376199842,
      abundance: 1
    }, {
      nominal: 32,
      mass: 31.973907643
    }, {
      nominal: 33,
      mass: 32.9717257
    }, {
      nominal: 34,
      mass: 33.97364589
    }, {
      nominal: 35,
      mass: 34.9733141
    }, {
      nominal: 36,
      mass: 35.97826
    }, {
      nominal: 37,
      mass: 36.979607
    }, {
      nominal: 38,
      mass: 37.984252
    }, {
      nominal: 39,
      mass: 38.986227
    }, {
      nominal: 40,
      mass: 39.99133
    }, {
      nominal: 41,
      mass: 40.994654
    }, {
      nominal: 42,
      mass: 42.00108
    }, {
      nominal: 43,
      mass: 43.00502
    }, {
      nominal: 44,
      mass: 44.01121
    }, {
      nominal: 45,
      mass: 45.01645
    }, {
      nominal: 46,
      mass: 46.02446
    }, {
      nominal: 47,
      mass: 47.03139
    }],
    symbol: 'P',
    mass: 30.97376199842,
    name: 'Phosphorus',
    monoisotopicMass: 30.97376199842
  }, {
    number: 16,
    isotopes: [{
      nominal: 26,
      mass: 26.02907
    }, {
      nominal: 27,
      mass: 27.01828
    }, {
      nominal: 28,
      mass: 28.00437
    }, {
      nominal: 29,
      mass: 28.996611
    }, {
      nominal: 30,
      mass: 29.98490703
    }, {
      nominal: 31,
      mass: 30.97955701
    }, {
      nominal: 32,
      mass: 31.9720711744,
      abundance: 0.9499
    }, {
      nominal: 33,
      mass: 32.9714589098,
      abundance: 0.0075
    }, {
      nominal: 34,
      mass: 33.967867004,
      abundance: 0.0425
    }, {
      nominal: 35,
      mass: 34.96903231
    }, {
      nominal: 36,
      mass: 35.96708071,
      abundance: 0.0001
    }, {
      nominal: 37,
      mass: 36.97112551
    }, {
      nominal: 38,
      mass: 37.9711633
    }, {
      nominal: 39,
      mass: 38.975134
    }, {
      nominal: 40,
      mass: 39.9754826
    }, {
      nominal: 41,
      mass: 40.9795935
    }, {
      nominal: 42,
      mass: 41.9810651
    }, {
      nominal: 43,
      mass: 42.9869076
    }, {
      nominal: 44,
      mass: 43.9901188
    }, {
      nominal: 45,
      mass: 44.99572
    }, {
      nominal: 46,
      mass: 46.00004
    }, {
      nominal: 47,
      mass: 47.00795
    }, {
      nominal: 48,
      mass: 48.0137
    }, {
      nominal: 49,
      mass: 49.02276
    }],
    symbol: 'S',
    mass: 32.06478740612706,
    name: 'Sulfur',
    monoisotopicMass: 31.9720711744
  }, {
    number: 17,
    isotopes: [{
      nominal: 28,
      mass: 28.02954
    }, {
      nominal: 29,
      mass: 29.01478
    }, {
      nominal: 30,
      mass: 30.00477
    }, {
      nominal: 31,
      mass: 30.992414
    }, {
      nominal: 32,
      mass: 31.98568464
    }, {
      nominal: 33,
      mass: 32.97745199
    }, {
      nominal: 34,
      mass: 33.973762485
    }, {
      nominal: 35,
      mass: 34.968852682,
      abundance: 0.7576
    }, {
      nominal: 36,
      mass: 35.968306809
    }, {
      nominal: 37,
      mass: 36.965902602,
      abundance: 0.2424
    }, {
      nominal: 38,
      mass: 37.96801044
    }, {
      nominal: 39,
      mass: 38.9680082
    }, {
      nominal: 40,
      mass: 39.970415
    }, {
      nominal: 41,
      mass: 40.970685
    }, {
      nominal: 42,
      mass: 41.97325
    }, {
      nominal: 43,
      mass: 42.97389
    }, {
      nominal: 44,
      mass: 43.97787
    }, {
      nominal: 45,
      mass: 44.98029
    }, {
      nominal: 46,
      mass: 45.98517
    }, {
      nominal: 47,
      mass: 46.98916
    }, {
      nominal: 48,
      mass: 47.99564
    }, {
      nominal: 49,
      mass: 49.00123
    }, {
      nominal: 50,
      mass: 50.00905
    }, {
      nominal: 51,
      mass: 51.01554
    }],
    symbol: 'Cl',
    mass: 35.452937582608,
    name: 'Chlorine',
    monoisotopicMass: 34.968852682
  }, {
    number: 18,
    isotopes: [{
      nominal: 30,
      mass: 30.02307
    }, {
      nominal: 31,
      mass: 31.01212
    }, {
      nominal: 32,
      mass: 31.9976378
    }, {
      nominal: 33,
      mass: 32.98992555
    }, {
      nominal: 34,
      mass: 33.98027009
    }, {
      nominal: 35,
      mass: 34.97525759
    }, {
      nominal: 36,
      mass: 35.967545105,
      abundance: 0.003336
    }, {
      nominal: 37,
      mass: 36.96677633
    }, {
      nominal: 38,
      mass: 37.96273211,
      abundance: 0.000629
    }, {
      nominal: 39,
      mass: 38.964313
    }, {
      nominal: 40,
      mass: 39.9623831237,
      abundance: 0.996035
    }, {
      nominal: 41,
      mass: 40.96450057
    }, {
      nominal: 42,
      mass: 41.9630457
    }, {
      nominal: 43,
      mass: 42.9656361
    }, {
      nominal: 44,
      mass: 43.9649238
    }, {
      nominal: 45,
      mass: 44.96803973
    }, {
      nominal: 46,
      mass: 45.968083
    }, {
      nominal: 47,
      mass: 46.972935
    }, {
      nominal: 48,
      mass: 47.97591
    }, {
      nominal: 49,
      mass: 48.9819
    }, {
      nominal: 50,
      mass: 49.98613
    }, {
      nominal: 51,
      mass: 50.9937
    }, {
      nominal: 52,
      mass: 51.99896
    }, {
      nominal: 53,
      mass: 53.00729
    }],
    symbol: 'Ar',
    mass: 39.947798563582005,
    name: 'Argon',
    monoisotopicMass: 39.9623831237
  }, {
    number: 19,
    isotopes: [{
      nominal: 32,
      mass: 32.02265
    }, {
      nominal: 33,
      mass: 33.00756
    }, {
      nominal: 34,
      mass: 33.99869
    }, {
      nominal: 35,
      mass: 34.98800541
    }, {
      nominal: 36,
      mass: 35.98130201
    }, {
      nominal: 37,
      mass: 36.97337589
    }, {
      nominal: 38,
      mass: 37.96908112
    }, {
      nominal: 39,
      mass: 38.9637064864,
      abundance: 0.932581
    }, {
      nominal: 40,
      mass: 39.963998166,
      abundance: 0.000117
    }, {
      nominal: 41,
      mass: 40.9618252579,
      abundance: 0.067302
    }, {
      nominal: 42,
      mass: 41.96240231
    }, {
      nominal: 43,
      mass: 42.9607347
    }, {
      nominal: 44,
      mass: 43.96158699
    }, {
      nominal: 45,
      mass: 44.96069149
    }, {
      nominal: 46,
      mass: 45.96198159
    }, {
      nominal: 47,
      mass: 46.9616616
    }, {
      nominal: 48,
      mass: 47.96534119
    }, {
      nominal: 49,
      mass: 48.96821075
    }, {
      nominal: 50,
      mass: 49.97238
    }, {
      nominal: 51,
      mass: 50.975828
    }, {
      nominal: 52,
      mass: 51.98224
    }, {
      nominal: 53,
      mass: 52.98746
    }, {
      nominal: 54,
      mass: 53.99463
    }, {
      nominal: 55,
      mass: 55.00076
    }, {
      nominal: 56,
      mass: 56.00851
    }],
    symbol: 'K',
    mass: 39.098300910086,
    name: 'Potassium',
    monoisotopicMass: 38.9637064864
  }, {
    number: 20,
    isotopes: [{
      nominal: 34,
      mass: 34.01487
    }, {
      nominal: 35,
      mass: 35.00514
    }, {
      nominal: 36,
      mass: 35.993074
    }, {
      nominal: 37,
      mass: 36.98589785
    }, {
      nominal: 38,
      mass: 37.97631922
    }, {
      nominal: 39,
      mass: 38.97071081
    }, {
      nominal: 40,
      mass: 39.962590863,
      abundance: 0.96941
    }, {
      nominal: 41,
      mass: 40.96227792
    }, {
      nominal: 42,
      mass: 41.95861783,
      abundance: 0.00647
    }, {
      nominal: 43,
      mass: 42.95876644,
      abundance: 0.00135
    }, {
      nominal: 44,
      mass: 43.95548156,
      abundance: 0.02086
    }, {
      nominal: 45,
      mass: 44.95618635
    }, {
      nominal: 46,
      mass: 45.953689,
      abundance: 0.00004
    }, {
      nominal: 47,
      mass: 46.9545424
    }, {
      nominal: 48,
      mass: 47.95252276,
      abundance: 0.00187
    }, {
      nominal: 49,
      mass: 48.95566274
    }, {
      nominal: 50,
      mass: 49.9574992
    }, {
      nominal: 51,
      mass: 50.960989
    }, {
      nominal: 52,
      mass: 51.963217
    }, {
      nominal: 53,
      mass: 52.96945
    }, {
      nominal: 54,
      mass: 53.9734
    }, {
      nominal: 55,
      mass: 54.9803
    }, {
      nominal: 56,
      mass: 55.98508
    }, {
      nominal: 57,
      mass: 56.99262
    }, {
      nominal: 58,
      mass: 57.99794
    }],
    symbol: 'Ca',
    mass: 40.078022511017735,
    name: 'Calcium',
    monoisotopicMass: 39.962590863
  }, {
    number: 21,
    isotopes: [{
      nominal: 36,
      mass: 36.01648
    }, {
      nominal: 37,
      mass: 37.00374
    }, {
      nominal: 38,
      mass: 37.99512
    }, {
      nominal: 39,
      mass: 38.984785
    }, {
      nominal: 40,
      mass: 39.9779673
    }, {
      nominal: 41,
      mass: 40.969251105
    }, {
      nominal: 42,
      mass: 41.96551653
    }, {
      nominal: 43,
      mass: 42.9611505
    }, {
      nominal: 44,
      mass: 43.9594029
    }, {
      nominal: 45,
      mass: 44.95590828,
      abundance: 1
    }, {
      nominal: 46,
      mass: 45.95516826
    }, {
      nominal: 47,
      mass: 46.9524037
    }, {
      nominal: 48,
      mass: 47.9522236
    }, {
      nominal: 49,
      mass: 48.9500146
    }, {
      nominal: 50,
      mass: 49.952176
    }, {
      nominal: 51,
      mass: 50.953592
    }, {
      nominal: 52,
      mass: 51.95688
    }, {
      nominal: 53,
      mass: 52.95909
    }, {
      nominal: 54,
      mass: 53.96393
    }, {
      nominal: 55,
      mass: 54.96782
    }, {
      nominal: 56,
      mass: 55.97345
    }, {
      nominal: 57,
      mass: 56.97777
    }, {
      nominal: 58,
      mass: 57.98403
    }, {
      nominal: 59,
      mass: 58.98894
    }, {
      nominal: 60,
      mass: 59.99565
    }, {
      nominal: 61,
      mass: 61.001
    }],
    symbol: 'Sc',
    mass: 44.95590828,
    name: 'Scandium',
    monoisotopicMass: 44.95590828
  }, {
    number: 22,
    isotopes: [{
      nominal: 38,
      mass: 38.01145
    }, {
      nominal: 39,
      mass: 39.00236
    }, {
      nominal: 40,
      mass: 39.9905
    }, {
      nominal: 41,
      mass: 40.983148
    }, {
      nominal: 42,
      mass: 41.97304903
    }, {
      nominal: 43,
      mass: 42.9685225
    }, {
      nominal: 44,
      mass: 43.95968995
    }, {
      nominal: 45,
      mass: 44.95812198
    }, {
      nominal: 46,
      mass: 45.95262772,
      abundance: 0.0825
    }, {
      nominal: 47,
      mass: 46.95175879,
      abundance: 0.0744
    }, {
      nominal: 48,
      mass: 47.94794198,
      abundance: 0.7372
    }, {
      nominal: 49,
      mass: 48.94786568,
      abundance: 0.0541
    }, {
      nominal: 50,
      mass: 49.94478689,
      abundance: 0.0518
    }, {
      nominal: 51,
      mass: 50.94661065
    }, {
      nominal: 52,
      mass: 51.946893
    }, {
      nominal: 53,
      mass: 52.94973
    }, {
      nominal: 54,
      mass: 53.95105
    }, {
      nominal: 55,
      mass: 54.95527
    }, {
      nominal: 56,
      mass: 55.95791
    }, {
      nominal: 57,
      mass: 56.96364
    }, {
      nominal: 58,
      mass: 57.9666
    }, {
      nominal: 59,
      mass: 58.97247
    }, {
      nominal: 60,
      mass: 59.97603
    }, {
      nominal: 61,
      mass: 60.98245
    }, {
      nominal: 62,
      mass: 61.98651
    }, {
      nominal: 63,
      mass: 62.99375
    }],
    symbol: 'Ti',
    mass: 47.866744962721995,
    name: 'Titanium',
    monoisotopicMass: 47.94794198
  }, {
    number: 23,
    isotopes: [{
      nominal: 40,
      mass: 40.01276
    }, {
      nominal: 41,
      mass: 41.00021
    }, {
      nominal: 42,
      mass: 41.99182
    }, {
      nominal: 43,
      mass: 42.980766
    }, {
      nominal: 44,
      mass: 43.97411
    }, {
      nominal: 45,
      mass: 44.9657748
    }, {
      nominal: 46,
      mass: 45.96019878
    }, {
      nominal: 47,
      mass: 46.95490491
    }, {
      nominal: 48,
      mass: 47.9522522
    }, {
      nominal: 49,
      mass: 48.9485118
    }, {
      nominal: 50,
      mass: 49.94715601,
      abundance: 0.0025
    }, {
      nominal: 51,
      mass: 50.94395704,
      abundance: 0.9975
    }, {
      nominal: 52,
      mass: 51.94477301
    }, {
      nominal: 53,
      mass: 52.9443367
    }, {
      nominal: 54,
      mass: 53.946439
    }, {
      nominal: 55,
      mass: 54.94724
    }, {
      nominal: 56,
      mass: 55.95048
    }, {
      nominal: 57,
      mass: 56.95252
    }, {
      nominal: 58,
      mass: 57.95672
    }, {
      nominal: 59,
      mass: 58.95939
    }, {
      nominal: 60,
      mass: 59.96431
    }, {
      nominal: 61,
      mass: 60.96725
    }, {
      nominal: 62,
      mass: 61.97265
    }, {
      nominal: 63,
      mass: 62.97639
    }, {
      nominal: 64,
      mass: 63.98264
    }, {
      nominal: 65,
      mass: 64.9875
    }, {
      nominal: 66,
      mass: 65.99398
    }],
    symbol: 'V',
    mass: 50.941465037425004,
    name: 'Vanadium',
    monoisotopicMass: 50.94395704
  }, {
    number: 24,
    isotopes: [{
      nominal: 42,
      mass: 42.0067
    }, {
      nominal: 43,
      mass: 42.99753
    }, {
      nominal: 44,
      mass: 43.98536
    }, {
      nominal: 45,
      mass: 44.97905
    }, {
      nominal: 46,
      mass: 45.968359
    }, {
      nominal: 47,
      mass: 46.9628974
    }, {
      nominal: 48,
      mass: 47.9540291
    }, {
      nominal: 49,
      mass: 48.9513333
    }, {
      nominal: 50,
      mass: 49.94604183,
      abundance: 0.04345
    }, {
      nominal: 51,
      mass: 50.94476502
    }, {
      nominal: 52,
      mass: 51.94050623,
      abundance: 0.83789
    }, {
      nominal: 53,
      mass: 52.94064815,
      abundance: 0.09501
    }, {
      nominal: 54,
      mass: 53.93887916,
      abundance: 0.02365
    }, {
      nominal: 55,
      mass: 54.94083843
    }, {
      nominal: 56,
      mass: 55.9406531
    }, {
      nominal: 57,
      mass: 56.943613
    }, {
      nominal: 58,
      mass: 57.94435
    }, {
      nominal: 59,
      mass: 58.94859
    }, {
      nominal: 60,
      mass: 59.95008
    }, {
      nominal: 61,
      mass: 60.95442
    }, {
      nominal: 62,
      mass: 61.9561
    }, {
      nominal: 63,
      mass: 62.96165
    }, {
      nominal: 64,
      mass: 63.96408
    }, {
      nominal: 65,
      mass: 64.96996
    }, {
      nominal: 66,
      mass: 65.97366
    }, {
      nominal: 67,
      mass: 66.98016
    }, {
      nominal: 68,
      mass: 67.98403
    }],
    symbol: 'Cr',
    mass: 51.9961317554337,
    name: 'Chromium',
    monoisotopicMass: 51.94050623
  }, {
    number: 25,
    isotopes: [{
      nominal: 44,
      mass: 44.00715
    }, {
      nominal: 45,
      mass: 44.99449
    }, {
      nominal: 46,
      mass: 45.98609
    }, {
      nominal: 47,
      mass: 46.975775
    }, {
      nominal: 48,
      mass: 47.96852
    }, {
      nominal: 49,
      mass: 48.959595
    }, {
      nominal: 50,
      mass: 49.95423778
    }, {
      nominal: 51,
      mass: 50.94820847
    }, {
      nominal: 52,
      mass: 51.9455639
    }, {
      nominal: 53,
      mass: 52.94128889
    }, {
      nominal: 54,
      mass: 53.9403576
    }, {
      nominal: 55,
      mass: 54.93804391,
      abundance: 1
    }, {
      nominal: 56,
      mass: 55.93890369
    }, {
      nominal: 57,
      mass: 56.9382861
    }, {
      nominal: 58,
      mass: 57.9400666
    }, {
      nominal: 59,
      mass: 58.9403911
    }, {
      nominal: 60,
      mass: 59.9431366
    }, {
      nominal: 61,
      mass: 60.9444525
    }, {
      nominal: 62,
      mass: 61.94795
    }, {
      nominal: 63,
      mass: 62.9496647
    }, {
      nominal: 64,
      mass: 63.9538494
    }, {
      nominal: 65,
      mass: 64.9560198
    }, {
      nominal: 66,
      mass: 65.960547
    }, {
      nominal: 67,
      mass: 66.96424
    }, {
      nominal: 68,
      mass: 67.96962
    }, {
      nominal: 69,
      mass: 68.97366
    }, {
      nominal: 70,
      mass: 69.97937
    }, {
      nominal: 71,
      mass: 70.98368
    }],
    symbol: 'Mn',
    mass: 54.93804391,
    name: 'Manganese',
    monoisotopicMass: 54.93804391
  }, {
    number: 26,
    isotopes: [{
      nominal: 45,
      mass: 45.01442
    }, {
      nominal: 46,
      mass: 46.00063
    }, {
      nominal: 47,
      mass: 46.99185
    }, {
      nominal: 48,
      mass: 47.98023
    }, {
      nominal: 49,
      mass: 48.973429
    }, {
      nominal: 50,
      mass: 49.962975
    }, {
      nominal: 51,
      mass: 50.956841
    }, {
      nominal: 52,
      mass: 51.9481131
    }, {
      nominal: 53,
      mass: 52.9453064
    }, {
      nominal: 54,
      mass: 53.93960899,
      abundance: 0.05845
    }, {
      nominal: 55,
      mass: 54.93829199
    }, {
      nominal: 56,
      mass: 55.93493633,
      abundance: 0.91754
    }, {
      nominal: 57,
      mass: 56.93539284,
      abundance: 0.02119
    }, {
      nominal: 58,
      mass: 57.93327443,
      abundance: 0.00282
    }, {
      nominal: 59,
      mass: 58.93487434
    }, {
      nominal: 60,
      mass: 59.9340711
    }, {
      nominal: 61,
      mass: 60.9367462
    }, {
      nominal: 62,
      mass: 61.9367918
    }, {
      nominal: 63,
      mass: 62.9402727
    }, {
      nominal: 64,
      mass: 63.9409878
    }, {
      nominal: 65,
      mass: 64.9450115
    }, {
      nominal: 66,
      mass: 65.94625
    }, {
      nominal: 67,
      mass: 66.95054
    }, {
      nominal: 68,
      mass: 67.95295
    }, {
      nominal: 69,
      mass: 68.95807
    }, {
      nominal: 70,
      mass: 69.96102
    }, {
      nominal: 71,
      mass: 70.96672
    }, {
      nominal: 72,
      mass: 71.96983
    }, {
      nominal: 73,
      mass: 72.97572
    }, {
      nominal: 74,
      mass: 73.97935
    }],
    symbol: 'Fe',
    mass: 55.845144433865904,
    name: 'Iron',
    monoisotopicMass: 55.93493633
  }, {
    number: 27,
    isotopes: [{
      nominal: 47,
      mass: 47.01057
    }, {
      nominal: 48,
      mass: 48.00093
    }, {
      nominal: 49,
      mass: 48.98891
    }, {
      nominal: 50,
      mass: 49.98091
    }, {
      nominal: 51,
      mass: 50.970647
    }, {
      nominal: 52,
      mass: 51.96351
    }, {
      nominal: 53,
      mass: 52.9542041
    }, {
      nominal: 54,
      mass: 53.94845987
    }, {
      nominal: 55,
      mass: 54.9419972
    }, {
      nominal: 56,
      mass: 55.9398388
    }, {
      nominal: 57,
      mass: 56.93629057
    }, {
      nominal: 58,
      mass: 57.9357521
    }, {
      nominal: 59,
      mass: 58.93319429,
      abundance: 1
    }, {
      nominal: 60,
      mass: 59.9338163
    }, {
      nominal: 61,
      mass: 60.93247662
    }, {
      nominal: 62,
      mass: 61.934059
    }, {
      nominal: 63,
      mass: 62.9336
    }, {
      nominal: 64,
      mass: 63.935811
    }, {
      nominal: 65,
      mass: 64.9364621
    }, {
      nominal: 66,
      mass: 65.939443
    }, {
      nominal: 67,
      mass: 66.9406096
    }, {
      nominal: 68,
      mass: 67.94426
    }, {
      nominal: 69,
      mass: 68.94614
    }, {
      nominal: 70,
      mass: 69.94963
    }, {
      nominal: 71,
      mass: 70.95237
    }, {
      nominal: 72,
      mass: 71.95729
    }, {
      nominal: 73,
      mass: 72.96039
    }, {
      nominal: 74,
      mass: 73.96515
    }, {
      nominal: 75,
      mass: 74.96876
    }, {
      nominal: 76,
      mass: 75.97413
    }],
    symbol: 'Co',
    mass: 58.93319429,
    name: 'Cobalt',
    monoisotopicMass: 58.93319429
  }, {
    number: 28,
    isotopes: [{
      nominal: 48,
      mass: 48.01769
    }, {
      nominal: 49,
      mass: 49.0077
    }, {
      nominal: 50,
      mass: 49.99474
    }, {
      nominal: 51,
      mass: 50.98611
    }, {
      nominal: 52,
      mass: 51.9748
    }, {
      nominal: 53,
      mass: 52.96819
    }, {
      nominal: 54,
      mass: 53.957892
    }, {
      nominal: 55,
      mass: 54.95133063
    }, {
      nominal: 56,
      mass: 55.94212855
    }, {
      nominal: 57,
      mass: 56.93979218
    }, {
      nominal: 58,
      mass: 57.93534241,
      abundance: 0.68077
    }, {
      nominal: 59,
      mass: 58.9343462
    }, {
      nominal: 60,
      mass: 59.93078588,
      abundance: 0.26223
    }, {
      nominal: 61,
      mass: 60.93105557,
      abundance: 0.011399
    }, {
      nominal: 62,
      mass: 61.92834537,
      abundance: 0.036346
    }, {
      nominal: 63,
      mass: 62.92966963
    }, {
      nominal: 64,
      mass: 63.92796682,
      abundance: 0.009255
    }, {
      nominal: 65,
      mass: 64.93008517
    }, {
      nominal: 66,
      mass: 65.9291393
    }, {
      nominal: 67,
      mass: 66.9315694
    }, {
      nominal: 68,
      mass: 67.9318688
    }, {
      nominal: 69,
      mass: 68.9356103
    }, {
      nominal: 70,
      mass: 69.9364313
    }, {
      nominal: 71,
      mass: 70.940519
    }, {
      nominal: 72,
      mass: 71.9417859
    }, {
      nominal: 73,
      mass: 72.9462067
    }, {
      nominal: 74,
      mass: 73.94798
    }, {
      nominal: 75,
      mass: 74.9525
    }, {
      nominal: 76,
      mass: 75.95533
    }, {
      nominal: 77,
      mass: 76.96055
    }, {
      nominal: 78,
      mass: 77.96336
    }, {
      nominal: 79,
      mass: 78.97025
    }],
    symbol: 'Ni',
    mass: 58.69334710994765,
    name: 'Nickel',
    monoisotopicMass: 57.93534241
  }, {
    number: 29,
    isotopes: [{
      nominal: 52,
      mass: 51.99671
    }, {
      nominal: 53,
      mass: 52.98459
    }, {
      nominal: 54,
      mass: 53.97666
    }, {
      nominal: 55,
      mass: 54.96604
    }, {
      nominal: 56,
      mass: 55.95895
    }, {
      nominal: 57,
      mass: 56.9492125
    }, {
      nominal: 58,
      mass: 57.94453305
    }, {
      nominal: 59,
      mass: 58.93949748
    }, {
      nominal: 60,
      mass: 59.9373645
    }, {
      nominal: 61,
      mass: 60.9334576
    }, {
      nominal: 62,
      mass: 61.93259541
    }, {
      nominal: 63,
      mass: 62.92959772,
      abundance: 0.6915
    }, {
      nominal: 64,
      mass: 63.92976434
    }, {
      nominal: 65,
      mass: 64.9277897,
      abundance: 0.3085
    }, {
      nominal: 66,
      mass: 65.92886903
    }, {
      nominal: 67,
      mass: 66.9277303
    }, {
      nominal: 68,
      mass: 67.9296109
    }, {
      nominal: 69,
      mass: 68.9294293
    }, {
      nominal: 70,
      mass: 69.9323921
    }, {
      nominal: 71,
      mass: 70.9326768
    }, {
      nominal: 72,
      mass: 71.9358203
    }, {
      nominal: 73,
      mass: 72.9366744
    }, {
      nominal: 74,
      mass: 73.9398749
    }, {
      nominal: 75,
      mass: 74.9415226
    }, {
      nominal: 76,
      mass: 75.945275
    }, {
      nominal: 77,
      mass: 76.94792
    }, {
      nominal: 78,
      mass: 77.95223
    }, {
      nominal: 79,
      mass: 78.95502
    }, {
      nominal: 80,
      mass: 79.96089
    }, {
      nominal: 81,
      mass: 80.96587
    }, {
      nominal: 82,
      mass: 81.97244
    }],
    symbol: 'Cu',
    mass: 63.54603994583,
    name: 'Copper',
    monoisotopicMass: 62.92959772
  }, {
    number: 30,
    isotopes: [{
      nominal: 54,
      mass: 53.99204
    }, {
      nominal: 55,
      mass: 54.98398
    }, {
      nominal: 56,
      mass: 55.97254
    }, {
      nominal: 57,
      mass: 56.96506
    }, {
      nominal: 58,
      mass: 57.954591
    }, {
      nominal: 59,
      mass: 58.94931266
    }, {
      nominal: 60,
      mass: 59.9418421
    }, {
      nominal: 61,
      mass: 60.939507
    }, {
      nominal: 62,
      mass: 61.93433397
    }, {
      nominal: 63,
      mass: 62.9332115
    }, {
      nominal: 64,
      mass: 63.92914201,
      abundance: 0.4917
    }, {
      nominal: 65,
      mass: 64.92924077
    }, {
      nominal: 66,
      mass: 65.92603381,
      abundance: 0.2773
    }, {
      nominal: 67,
      mass: 66.92712775,
      abundance: 0.0404
    }, {
      nominal: 68,
      mass: 67.92484455,
      abundance: 0.1845
    }, {
      nominal: 69,
      mass: 68.9265507
    }, {
      nominal: 70,
      mass: 69.9253192,
      abundance: 0.0061
    }, {
      nominal: 71,
      mass: 70.9277196
    }, {
      nominal: 72,
      mass: 71.9268428
    }, {
      nominal: 73,
      mass: 72.9295826
    }, {
      nominal: 74,
      mass: 73.9294073
    }, {
      nominal: 75,
      mass: 74.9328402
    }, {
      nominal: 76,
      mass: 75.933115
    }, {
      nominal: 77,
      mass: 76.9368872
    }, {
      nominal: 78,
      mass: 77.9382892
    }, {
      nominal: 79,
      mass: 78.9426381
    }, {
      nominal: 80,
      mass: 79.9445529
    }, {
      nominal: 81,
      mass: 80.9504026
    }, {
      nominal: 82,
      mass: 81.95426
    }, {
      nominal: 83,
      mass: 82.96056
    }, {
      nominal: 84,
      mass: 83.96521
    }, {
      nominal: 85,
      mass: 84.97226
    }],
    symbol: 'Zn',
    mass: 65.37778252952499,
    name: 'Zinc',
    monoisotopicMass: 63.92914201
  }, {
    number: 31,
    isotopes: [{
      nominal: 56,
      mass: 55.99536
    }, {
      nominal: 57,
      mass: 56.9832
    }, {
      nominal: 58,
      mass: 57.97478
    }, {
      nominal: 59,
      mass: 58.96353
    }, {
      nominal: 60,
      mass: 59.95729
    }, {
      nominal: 61,
      mass: 60.949399
    }, {
      nominal: 62,
      mass: 61.94419025
    }, {
      nominal: 63,
      mass: 62.9392942
    }, {
      nominal: 64,
      mass: 63.9368404
    }, {
      nominal: 65,
      mass: 64.93273459
    }, {
      nominal: 66,
      mass: 65.9315894
    }, {
      nominal: 67,
      mass: 66.9282025
    }, {
      nominal: 68,
      mass: 67.9279805
    }, {
      nominal: 69,
      mass: 68.9255735,
      abundance: 0.60108
    }, {
      nominal: 70,
      mass: 69.9260219
    }, {
      nominal: 71,
      mass: 70.92470258,
      abundance: 0.39892
    }, {
      nominal: 72,
      mass: 71.92636747
    }, {
      nominal: 73,
      mass: 72.9251747
    }, {
      nominal: 74,
      mass: 73.9269457
    }, {
      nominal: 75,
      mass: 74.9265002
    }, {
      nominal: 76,
      mass: 75.9288276
    }, {
      nominal: 77,
      mass: 76.9291543
    }, {
      nominal: 78,
      mass: 77.9316088
    }, {
      nominal: 79,
      mass: 78.9328523
    }, {
      nominal: 80,
      mass: 79.9364208
    }, {
      nominal: 81,
      mass: 80.9381338
    }, {
      nominal: 82,
      mass: 81.9431765
    }, {
      nominal: 83,
      mass: 82.9471203
    }, {
      nominal: 84,
      mass: 83.95246
    }, {
      nominal: 85,
      mass: 84.95699
    }, {
      nominal: 86,
      mass: 85.96301
    }, {
      nominal: 87,
      mass: 86.96824
    }],
    symbol: 'Ga',
    mass: 69.7230660725936,
    name: 'Gallium',
    monoisotopicMass: 68.9255735
  }, {
    number: 32,
    isotopes: [{
      nominal: 58,
      mass: 57.99172
    }, {
      nominal: 59,
      mass: 58.98249
    }, {
      nominal: 60,
      mass: 59.97036
    }, {
      nominal: 61,
      mass: 60.96379
    }, {
      nominal: 62,
      mass: 61.95502
    }, {
      nominal: 63,
      mass: 62.949628
    }, {
      nominal: 64,
      mass: 63.9416899
    }, {
      nominal: 65,
      mass: 64.9393681
    }, {
      nominal: 66,
      mass: 65.9338621
    }, {
      nominal: 67,
      mass: 66.9327339
    }, {
      nominal: 68,
      mass: 67.9280953
    }, {
      nominal: 69,
      mass: 68.9279645
    }, {
      nominal: 70,
      mass: 69.92424875,
      abundance: 0.2057
    }, {
      nominal: 71,
      mass: 70.92495233
    }, {
      nominal: 72,
      mass: 71.922075826,
      abundance: 0.2745
    }, {
      nominal: 73,
      mass: 72.923458956,
      abundance: 0.0775
    }, {
      nominal: 74,
      mass: 73.921177761,
      abundance: 0.365
    }, {
      nominal: 75,
      mass: 74.92285837
    }, {
      nominal: 76,
      mass: 75.921402726,
      abundance: 0.0773
    }, {
      nominal: 77,
      mass: 76.923549843
    }, {
      nominal: 78,
      mass: 77.9228529
    }, {
      nominal: 79,
      mass: 78.92536
    }, {
      nominal: 80,
      mass: 79.9253508
    }, {
      nominal: 81,
      mass: 80.9288329
    }, {
      nominal: 82,
      mass: 81.929774
    }, {
      nominal: 83,
      mass: 82.9345391
    }, {
      nominal: 84,
      mass: 83.9375751
    }, {
      nominal: 85,
      mass: 84.9429697
    }, {
      nominal: 86,
      mass: 85.94658
    }, {
      nominal: 87,
      mass: 86.95268
    }, {
      nominal: 88,
      mass: 87.95691
    }, {
      nominal: 89,
      mass: 88.96379
    }, {
      nominal: 90,
      mass: 89.96863
    }],
    symbol: 'Ge',
    mass: 72.6275501646868,
    name: 'Germanium',
    monoisotopicMass: 73.921177761
  }, {
    number: 33,
    isotopes: [{
      nominal: 60,
      mass: 59.99388
    }, {
      nominal: 61,
      mass: 60.98112
    }, {
      nominal: 62,
      mass: 61.97361
    }, {
      nominal: 63,
      mass: 62.9639
    }, {
      nominal: 64,
      mass: 63.95743
    }, {
      nominal: 65,
      mass: 64.949611
    }, {
      nominal: 66,
      mass: 65.9441488
    }, {
      nominal: 67,
      mass: 66.93925111
    }, {
      nominal: 68,
      mass: 67.9367741
    }, {
      nominal: 69,
      mass: 68.932246
    }, {
      nominal: 70,
      mass: 69.930926
    }, {
      nominal: 71,
      mass: 70.9271138
    }, {
      nominal: 72,
      mass: 71.9267523
    }, {
      nominal: 73,
      mass: 72.9238291
    }, {
      nominal: 74,
      mass: 73.9239286
    }, {
      nominal: 75,
      mass: 74.92159457,
      abundance: 1
    }, {
      nominal: 76,
      mass: 75.92239202
    }, {
      nominal: 77,
      mass: 76.9206476
    }, {
      nominal: 78,
      mass: 77.921828
    }, {
      nominal: 79,
      mass: 78.9209484
    }, {
      nominal: 80,
      mass: 79.9224746
    }, {
      nominal: 81,
      mass: 80.9221323
    }, {
      nominal: 82,
      mass: 81.9247412
    }, {
      nominal: 83,
      mass: 82.9252069
    }, {
      nominal: 84,
      mass: 83.9293033
    }, {
      nominal: 85,
      mass: 84.9321637
    }, {
      nominal: 86,
      mass: 85.9367015
    }, {
      nominal: 87,
      mass: 86.9402917
    }, {
      nominal: 88,
      mass: 87.94555
    }, {
      nominal: 89,
      mass: 88.94976
    }, {
      nominal: 90,
      mass: 89.95563
    }, {
      nominal: 91,
      mass: 90.96039
    }, {
      nominal: 92,
      mass: 91.96674
    }],
    symbol: 'As',
    mass: 74.92159457,
    name: 'Arsenic',
    monoisotopicMass: 74.92159457
  }, {
    number: 34,
    isotopes: [{
      nominal: 64,
      mass: 63.97109
    }, {
      nominal: 65,
      mass: 64.9644
    }, {
      nominal: 66,
      mass: 65.95559
    }, {
      nominal: 67,
      mass: 66.949994
    }, {
      nominal: 68,
      mass: 67.94182524
    }, {
      nominal: 69,
      mass: 68.9394148
    }, {
      nominal: 70,
      mass: 69.9335155
    }, {
      nominal: 71,
      mass: 70.9322094
    }, {
      nominal: 72,
      mass: 71.9271405
    }, {
      nominal: 73,
      mass: 72.9267549
    }, {
      nominal: 74,
      mass: 73.922475934,
      abundance: 0.0089
    }, {
      nominal: 75,
      mass: 74.92252287
    }, {
      nominal: 76,
      mass: 75.919213704,
      abundance: 0.0937
    }, {
      nominal: 77,
      mass: 76.919914154,
      abundance: 0.0763
    }, {
      nominal: 78,
      mass: 77.91730928,
      abundance: 0.2377
    }, {
      nominal: 79,
      mass: 78.91849929
    }, {
      nominal: 80,
      mass: 79.9165218,
      abundance: 0.4961
    }, {
      nominal: 81,
      mass: 80.917993
    }, {
      nominal: 82,
      mass: 81.9166995,
      abundance: 0.0873
    }, {
      nominal: 83,
      mass: 82.9191186
    }, {
      nominal: 84,
      mass: 83.9184668
    }, {
      nominal: 85,
      mass: 84.9222608
    }, {
      nominal: 86,
      mass: 85.9243117
    }, {
      nominal: 87,
      mass: 86.9286886
    }, {
      nominal: 88,
      mass: 87.9314175
    }, {
      nominal: 89,
      mass: 88.9366691
    }, {
      nominal: 90,
      mass: 89.9401
    }, {
      nominal: 91,
      mass: 90.94596
    }, {
      nominal: 92,
      mass: 91.94984
    }, {
      nominal: 93,
      mass: 92.95629
    }, {
      nominal: 94,
      mass: 93.96049
    }, {
      nominal: 95,
      mass: 94.9673
    }],
    symbol: 'Se',
    mass: 78.95938855701361,
    name: 'Selenium',
    monoisotopicMass: 79.9165218
  }, {
    number: 35,
    isotopes: [{
      nominal: 67,
      mass: 66.96465
    }, {
      nominal: 68,
      mass: 67.95873
    }, {
      nominal: 69,
      mass: 68.950497
    }, {
      nominal: 70,
      mass: 69.944792
    }, {
      nominal: 71,
      mass: 70.9393422
    }, {
      nominal: 72,
      mass: 71.9365886
    }, {
      nominal: 73,
      mass: 72.9316715
    }, {
      nominal: 74,
      mass: 73.9299102
    }, {
      nominal: 75,
      mass: 74.9258105
    }, {
      nominal: 76,
      mass: 75.924542
    }, {
      nominal: 77,
      mass: 76.9213792
    }, {
      nominal: 78,
      mass: 77.9211459
    }, {
      nominal: 79,
      mass: 78.9183376,
      abundance: 0.5069
    }, {
      nominal: 80,
      mass: 79.9185298
    }, {
      nominal: 81,
      mass: 80.9162897,
      abundance: 0.4931
    }, {
      nominal: 82,
      mass: 81.9168032
    }, {
      nominal: 83,
      mass: 82.9151756
    }, {
      nominal: 84,
      mass: 83.916496
    }, {
      nominal: 85,
      mass: 84.9156458
    }, {
      nominal: 86,
      mass: 85.9188054
    }, {
      nominal: 87,
      mass: 86.920674
    }, {
      nominal: 88,
      mass: 87.9240833
    }, {
      nominal: 89,
      mass: 88.9267046
    }, {
      nominal: 90,
      mass: 89.9312928
    }, {
      nominal: 91,
      mass: 90.9343986
    }, {
      nominal: 92,
      mass: 91.9396316
    }, {
      nominal: 93,
      mass: 92.94313
    }, {
      nominal: 94,
      mass: 93.9489
    }, {
      nominal: 95,
      mass: 94.95301
    }, {
      nominal: 96,
      mass: 95.95903
    }, {
      nominal: 97,
      mass: 96.96344
    }, {
      nominal: 98,
      mass: 97.96946
    }],
    symbol: 'Br',
    mass: 79.90352778050999,
    name: 'Bromine',
    monoisotopicMass: 78.9183376
  }, {
    number: 36,
    isotopes: [{
      nominal: 69,
      mass: 68.96518
    }, {
      nominal: 70,
      mass: 69.95604
    }, {
      nominal: 71,
      mass: 70.95027
    }, {
      nominal: 72,
      mass: 71.9420924
    }, {
      nominal: 73,
      mass: 72.9392892
    }, {
      nominal: 74,
      mass: 73.933084
    }, {
      nominal: 75,
      mass: 74.9309457
    }, {
      nominal: 76,
      mass: 75.9259103
    }, {
      nominal: 77,
      mass: 76.92467
    }, {
      nominal: 78,
      mass: 77.92036494,
      abundance: 0.00355
    }, {
      nominal: 79,
      mass: 78.9200829
    }, {
      nominal: 80,
      mass: 79.91637808,
      abundance: 0.02286
    }, {
      nominal: 81,
      mass: 80.9165912
    }, {
      nominal: 82,
      mass: 81.91348273,
      abundance: 0.11593
    }, {
      nominal: 83,
      mass: 82.91412716,
      abundance: 0.115
    }, {
      nominal: 84,
      mass: 83.9114977282,
      abundance: 0.56987
    }, {
      nominal: 85,
      mass: 84.9125273
    }, {
      nominal: 86,
      mass: 85.9106106269,
      abundance: 0.17279
    }, {
      nominal: 87,
      mass: 86.91335476
    }, {
      nominal: 88,
      mass: 87.9144479
    }, {
      nominal: 89,
      mass: 88.9178355
    }, {
      nominal: 90,
      mass: 89.9195279
    }, {
      nominal: 91,
      mass: 90.9238063
    }, {
      nominal: 92,
      mass: 91.9261731
    }, {
      nominal: 93,
      mass: 92.9311472
    }, {
      nominal: 94,
      mass: 93.93414
    }, {
      nominal: 95,
      mass: 94.939711
    }, {
      nominal: 96,
      mass: 95.943017
    }, {
      nominal: 97,
      mass: 96.94909
    }, {
      nominal: 98,
      mass: 97.95243
    }, {
      nominal: 99,
      mass: 98.95839
    }, {
      nominal: 100,
      mass: 99.96237
    }, {
      nominal: 101,
      mass: 100.96873
    }],
    symbol: 'Kr',
    mass: 83.7979999953261,
    name: 'Krypton',
    monoisotopicMass: 83.9114977282
  }, {
    number: 37,
    isotopes: [{
      nominal: 71,
      mass: 70.96532
    }, {
      nominal: 72,
      mass: 71.95908
    }, {
      nominal: 73,
      mass: 72.95053
    }, {
      nominal: 74,
      mass: 73.9442659
    }, {
      nominal: 75,
      mass: 74.9385732
    }, {
      nominal: 76,
      mass: 75.935073
    }, {
      nominal: 77,
      mass: 76.9304016
    }, {
      nominal: 78,
      mass: 77.9281419
    }, {
      nominal: 79,
      mass: 78.9239899
    }, {
      nominal: 80,
      mass: 79.9225164
    }, {
      nominal: 81,
      mass: 80.9189939
    }, {
      nominal: 82,
      mass: 81.918209
    }, {
      nominal: 83,
      mass: 82.9151142
    }, {
      nominal: 84,
      mass: 83.9143752
    }, {
      nominal: 85,
      mass: 84.9117897379,
      abundance: 0.7217
    }, {
      nominal: 86,
      mass: 85.91116743
    }, {
      nominal: 87,
      mass: 86.909180531,
      abundance: 0.2783
    }, {
      nominal: 88,
      mass: 87.91131559
    }, {
      nominal: 89,
      mass: 88.9122783
    }, {
      nominal: 90,
      mass: 89.9147985
    }, {
      nominal: 91,
      mass: 90.9165372
    }, {
      nominal: 92,
      mass: 91.9197284
    }, {
      nominal: 93,
      mass: 92.9220393
    }, {
      nominal: 94,
      mass: 93.9263948
    }, {
      nominal: 95,
      mass: 94.92926
    }, {
      nominal: 96,
      mass: 95.9341334
    }, {
      nominal: 97,
      mass: 96.9371771
    }, {
      nominal: 98,
      mass: 97.9416869
    }, {
      nominal: 99,
      mass: 98.94503
    }, {
      nominal: 100,
      mass: 99.95003
    }, {
      nominal: 101,
      mass: 100.95404
    }, {
      nominal: 102,
      mass: 101.95952
    }, {
      nominal: 103,
      mass: 102.96392
    }],
    symbol: 'Rb',
    mass: 85.46766359561973,
    name: 'Rubidium',
    monoisotopicMass: 84.9117897379
  }, {
    number: 38,
    isotopes: [{
      nominal: 73,
      mass: 72.9657
    }, {
      nominal: 74,
      mass: 73.95617
    }, {
      nominal: 75,
      mass: 74.94995
    }, {
      nominal: 76,
      mass: 75.941763
    }, {
      nominal: 77,
      mass: 76.9379455
    }, {
      nominal: 78,
      mass: 77.93218
    }, {
      nominal: 79,
      mass: 78.9297077
    }, {
      nominal: 80,
      mass: 79.9245175
    }, {
      nominal: 81,
      mass: 80.9232114
    }, {
      nominal: 82,
      mass: 81.9183999
    }, {
      nominal: 83,
      mass: 82.9175544
    }, {
      nominal: 84,
      mass: 83.9134191,
      abundance: 0.0056
    }, {
      nominal: 85,
      mass: 84.912932
    }, {
      nominal: 86,
      mass: 85.9092606,
      abundance: 0.0986
    }, {
      nominal: 87,
      mass: 86.9088775,
      abundance: 0.07
    }, {
      nominal: 88,
      mass: 87.9056125,
      abundance: 0.8258
    }, {
      nominal: 89,
      mass: 88.9074511
    }, {
      nominal: 90,
      mass: 89.90773
    }, {
      nominal: 91,
      mass: 90.9101954
    }, {
      nominal: 92,
      mass: 91.9110382
    }, {
      nominal: 93,
      mass: 92.9140242
    }, {
      nominal: 94,
      mass: 93.9153556
    }, {
      nominal: 95,
      mass: 94.9193529
    }, {
      nominal: 96,
      mass: 95.9217066
    }, {
      nominal: 97,
      mass: 96.926374
    }, {
      nominal: 98,
      mass: 97.9286888
    }, {
      nominal: 99,
      mass: 98.9328907
    }, {
      nominal: 100,
      mass: 99.93577
    }, {
      nominal: 101,
      mass: 100.940352
    }, {
      nominal: 102,
      mass: 101.943791
    }, {
      nominal: 103,
      mass: 102.94909
    }, {
      nominal: 104,
      mass: 103.95265
    }, {
      nominal: 105,
      mass: 104.95855
    }, {
      nominal: 106,
      mass: 105.96265
    }, {
      nominal: 107,
      mass: 106.96897
    }],
    symbol: 'Sr',
    mass: 87.61664446962,
    name: 'Strontium',
    monoisotopicMass: 87.9056125
  }, {
    number: 39,
    isotopes: [{
      nominal: 76,
      mass: 75.95856
    }, {
      nominal: 77,
      mass: 76.949781
    }, {
      nominal: 78,
      mass: 77.94361
    }, {
      nominal: 79,
      mass: 78.93735
    }, {
      nominal: 80,
      mass: 79.9343561
    }, {
      nominal: 81,
      mass: 80.9294556
    }, {
      nominal: 82,
      mass: 81.9269314
    }, {
      nominal: 83,
      mass: 82.922485
    }, {
      nominal: 84,
      mass: 83.9206721
    }, {
      nominal: 85,
      mass: 84.916433
    }, {
      nominal: 86,
      mass: 85.914886
    }, {
      nominal: 87,
      mass: 86.9108761
    }, {
      nominal: 88,
      mass: 87.9095016
    }, {
      nominal: 89,
      mass: 88.9058403,
      abundance: 1
    }, {
      nominal: 90,
      mass: 89.9071439
    }, {
      nominal: 91,
      mass: 90.9072974
    }, {
      nominal: 92,
      mass: 91.9089451
    }, {
      nominal: 93,
      mass: 92.909578
    }, {
      nominal: 94,
      mass: 93.9115906
    }, {
      nominal: 95,
      mass: 94.9128161
    }, {
      nominal: 96,
      mass: 95.9158968
    }, {
      nominal: 97,
      mass: 96.9182741
    }, {
      nominal: 98,
      mass: 97.9223821
    }, {
      nominal: 99,
      mass: 98.924148
    }, {
      nominal: 100,
      mass: 99.927715
    }, {
      nominal: 101,
      mass: 100.9301477
    }, {
      nominal: 102,
      mass: 101.9343277
    }, {
      nominal: 103,
      mass: 102.937243
    }, {
      nominal: 104,
      mass: 103.94196
    }, {
      nominal: 105,
      mass: 104.94544
    }, {
      nominal: 106,
      mass: 105.95056
    }, {
      nominal: 107,
      mass: 106.95452
    }, {
      nominal: 108,
      mass: 107.95996
    }, {
      nominal: 109,
      mass: 108.96436
    }],
    symbol: 'Y',
    mass: 88.9058403,
    name: 'Yttrium',
    monoisotopicMass: 88.9058403
  }, {
    number: 40,
    isotopes: [{
      nominal: 78,
      mass: 77.95566
    }, {
      nominal: 79,
      mass: 78.94948
    }, {
      nominal: 80,
      mass: 79.9404
    }, {
      nominal: 81,
      mass: 80.93731
    }, {
      nominal: 82,
      mass: 81.93135
    }, {
      nominal: 83,
      mass: 82.9292421
    }, {
      nominal: 84,
      mass: 83.9233269
    }, {
      nominal: 85,
      mass: 84.9214444
    }, {
      nominal: 86,
      mass: 85.9162972
    }, {
      nominal: 87,
      mass: 86.914818
    }, {
      nominal: 88,
      mass: 87.9102213
    }, {
      nominal: 89,
      mass: 88.9088814
    }, {
      nominal: 90,
      mass: 89.9046977,
      abundance: 0.5145
    }, {
      nominal: 91,
      mass: 90.9056396,
      abundance: 0.1122
    }, {
      nominal: 92,
      mass: 91.9050347,
      abundance: 0.1715
    }, {
      nominal: 93,
      mass: 92.9064699
    }, {
      nominal: 94,
      mass: 93.9063108,
      abundance: 0.1738
    }, {
      nominal: 95,
      mass: 94.9080385
    }, {
      nominal: 96,
      mass: 95.9082714,
      abundance: 0.028
    }, {
      nominal: 97,
      mass: 96.9109512
    }, {
      nominal: 98,
      mass: 97.9127289
    }, {
      nominal: 99,
      mass: 98.916667
    }, {
      nominal: 100,
      mass: 99.9180006
    }, {
      nominal: 101,
      mass: 100.921448
    }, {
      nominal: 102,
      mass: 101.9231409
    }, {
      nominal: 103,
      mass: 102.927191
    }, {
      nominal: 104,
      mass: 103.929436
    }, {
      nominal: 105,
      mass: 104.934008
    }, {
      nominal: 106,
      mass: 105.93676
    }, {
      nominal: 107,
      mass: 106.94174
    }, {
      nominal: 108,
      mass: 107.94487
    }, {
      nominal: 109,
      mass: 108.95041
    }, {
      nominal: 110,
      mass: 109.95396
    }, {
      nominal: 111,
      mass: 110.95968
    }, {
      nominal: 112,
      mass: 111.9637
    }],
    symbol: 'Zr',
    mass: 91.22364159706,
    name: 'Zirconium',
    monoisotopicMass: 89.9046977
  }, {
    number: 41,
    isotopes: [{
      nominal: 81,
      mass: 80.9496
    }, {
      nominal: 82,
      mass: 81.94396
    }, {
      nominal: 83,
      mass: 82.93729
    }, {
      nominal: 84,
      mass: 83.93449
    }, {
      nominal: 85,
      mass: 84.9288458
    }, {
      nominal: 86,
      mass: 85.9257828
    }, {
      nominal: 87,
      mass: 86.9206937
    }, {
      nominal: 88,
      mass: 87.918222
    }, {
      nominal: 89,
      mass: 88.913445
    }, {
      nominal: 90,
      mass: 89.9112584
    }, {
      nominal: 91,
      mass: 90.9069897
    }, {
      nominal: 92,
      mass: 91.9071881
    }, {
      nominal: 93,
      mass: 92.906373,
      abundance: 1
    }, {
      nominal: 94,
      mass: 93.9072788
    }, {
      nominal: 95,
      mass: 94.9068324
    }, {
      nominal: 96,
      mass: 95.9080973
    }, {
      nominal: 97,
      mass: 96.9080959
    }, {
      nominal: 98,
      mass: 97.9103265
    }, {
      nominal: 99,
      mass: 98.911613
    }, {
      nominal: 100,
      mass: 99.9143276
    }, {
      nominal: 101,
      mass: 100.9153103
    }, {
      nominal: 102,
      mass: 101.9180772
    }, {
      nominal: 103,
      mass: 102.9194572
    }, {
      nominal: 104,
      mass: 103.9228925
    }, {
      nominal: 105,
      mass: 104.9249465
    }, {
      nominal: 106,
      mass: 105.9289317
    }, {
      nominal: 107,
      mass: 106.9315937
    }, {
      nominal: 108,
      mass: 107.9360748
    }, {
      nominal: 109,
      mass: 108.93922
    }, {
      nominal: 110,
      mass: 109.94403
    }, {
      nominal: 111,
      mass: 110.94753
    }, {
      nominal: 112,
      mass: 111.95247
    }, {
      nominal: 113,
      mass: 112.95651
    }, {
      nominal: 114,
      mass: 113.96201
    }, {
      nominal: 115,
      mass: 114.96634
    }],
    symbol: 'Nb',
    mass: 92.906373,
    name: 'Niobium',
    monoisotopicMass: 92.906373
  }, {
    number: 42,
    isotopes: [{
      nominal: 83,
      mass: 82.94988
    }, {
      nominal: 84,
      mass: 83.94149
    }, {
      nominal: 85,
      mass: 84.938261
    }, {
      nominal: 86,
      mass: 85.9311748
    }, {
      nominal: 87,
      mass: 86.9281962
    }, {
      nominal: 88,
      mass: 87.9219678
    }, {
      nominal: 89,
      mass: 88.9194682
    }, {
      nominal: 90,
      mass: 89.9139309
    }, {
      nominal: 91,
      mass: 90.9117453
    }, {
      nominal: 92,
      mass: 91.90680796,
      abundance: 0.1453
    }, {
      nominal: 93,
      mass: 92.90680958
    }, {
      nominal: 94,
      mass: 93.9050849,
      abundance: 0.0915
    }, {
      nominal: 95,
      mass: 94.90583877,
      abundance: 0.1584
    }, {
      nominal: 96,
      mass: 95.90467612,
      abundance: 0.1667
    }, {
      nominal: 97,
      mass: 96.90601812,
      abundance: 0.096
    }, {
      nominal: 98,
      mass: 97.90540482,
      abundance: 0.2439
    }, {
      nominal: 99,
      mass: 98.90770851
    }, {
      nominal: 100,
      mass: 99.9074718,
      abundance: 0.0982
    }, {
      nominal: 101,
      mass: 100.9103414
    }, {
      nominal: 102,
      mass: 101.9102834
    }, {
      nominal: 103,
      mass: 102.913079
    }, {
      nominal: 104,
      mass: 103.9137344
    }, {
      nominal: 105,
      mass: 104.916969
    }, {
      nominal: 106,
      mass: 105.918259
    }, {
      nominal: 107,
      mass: 106.922106
    }, {
      nominal: 108,
      mass: 107.924033
    }, {
      nominal: 109,
      mass: 108.928424
    }, {
      nominal: 110,
      mass: 109.930704
    }, {
      nominal: 111,
      mass: 110.935654
    }, {
      nominal: 112,
      mass: 111.93831
    }, {
      nominal: 113,
      mass: 112.94335
    }, {
      nominal: 114,
      mass: 113.94653
    }, {
      nominal: 115,
      mass: 114.95196
    }, {
      nominal: 116,
      mass: 115.95545
    }, {
      nominal: 117,
      mass: 116.96117
    }],
    symbol: 'Mo',
    mass: 95.959788541188,
    name: 'Molybdenum',
    monoisotopicMass: 97.90540482
  }, {
    number: 43,
    isotopes: [{
      nominal: 85,
      mass: 84.95058
    }, {
      nominal: 86,
      mass: 85.94493
    }, {
      nominal: 87,
      mass: 86.9380672
    }, {
      nominal: 88,
      mass: 87.93378
    }, {
      nominal: 89,
      mass: 88.9276487
    }, {
      nominal: 90,
      mass: 89.9240739
    }, {
      nominal: 91,
      mass: 90.9184254
    }, {
      nominal: 92,
      mass: 91.9152698
    }, {
      nominal: 93,
      mass: 92.910246
    }, {
      nominal: 94,
      mass: 93.9096536
    }, {
      nominal: 95,
      mass: 94.9076536
    }, {
      nominal: 96,
      mass: 95.907868
    }, {
      nominal: 97,
      mass: 96.9063667
    }, {
      nominal: 98,
      mass: 97.9072124
    }, {
      nominal: 99,
      mass: 98.9062508
    }, {
      nominal: 100,
      mass: 99.9076539
    }, {
      nominal: 101,
      mass: 100.907309
    }, {
      nominal: 102,
      mass: 101.9092097
    }, {
      nominal: 103,
      mass: 102.909176
    }, {
      nominal: 104,
      mass: 103.911425
    }, {
      nominal: 105,
      mass: 104.911655
    }, {
      nominal: 106,
      mass: 105.914358
    }, {
      nominal: 107,
      mass: 106.9154606
    }, {
      nominal: 108,
      mass: 107.9184957
    }, {
      nominal: 109,
      mass: 108.920256
    }, {
      nominal: 110,
      mass: 109.923744
    }, {
      nominal: 111,
      mass: 110.925901
    }, {
      nominal: 112,
      mass: 111.9299458
    }, {
      nominal: 113,
      mass: 112.932569
    }, {
      nominal: 114,
      mass: 113.93691
    }, {
      nominal: 115,
      mass: 114.93998
    }, {
      nominal: 116,
      mass: 115.94476
    }, {
      nominal: 117,
      mass: 116.94806
    }, {
      nominal: 118,
      mass: 117.95299
    }, {
      nominal: 119,
      mass: 118.95666
    }, {
      nominal: 120,
      mass: 119.96187
    }],
    symbol: 'Tc',
    mass: null,
    name: 'Technetium'
  }, {
    number: 44,
    isotopes: [{
      nominal: 87,
      mass: 86.95069
    }, {
      nominal: 88,
      mass: 87.9416
    }, {
      nominal: 89,
      mass: 88.93762
    }, {
      nominal: 90,
      mass: 89.9303444
    }, {
      nominal: 91,
      mass: 90.9267419
    }, {
      nominal: 92,
      mass: 91.9202344
    }, {
      nominal: 93,
      mass: 92.9171044
    }, {
      nominal: 94,
      mass: 93.9113429
    }, {
      nominal: 95,
      mass: 94.910406
    }, {
      nominal: 96,
      mass: 95.90759025,
      abundance: 0.0554
    }, {
      nominal: 97,
      mass: 96.9075471
    }, {
      nominal: 98,
      mass: 97.9052868,
      abundance: 0.0187
    }, {
      nominal: 99,
      mass: 98.9059341,
      abundance: 0.1276
    }, {
      nominal: 100,
      mass: 99.9042143,
      abundance: 0.126
    }, {
      nominal: 101,
      mass: 100.9055769,
      abundance: 0.1706
    }, {
      nominal: 102,
      mass: 101.9043441,
      abundance: 0.3155
    }, {
      nominal: 103,
      mass: 102.9063186
    }, {
      nominal: 104,
      mass: 103.9054275,
      abundance: 0.1862
    }, {
      nominal: 105,
      mass: 104.9077476
    }, {
      nominal: 106,
      mass: 105.9073291
    }, {
      nominal: 107,
      mass: 106.909972
    }, {
      nominal: 108,
      mass: 107.910188
    }, {
      nominal: 109,
      mass: 108.913326
    }, {
      nominal: 110,
      mass: 109.9140407
    }, {
      nominal: 111,
      mass: 110.91757
    }, {
      nominal: 112,
      mass: 111.918809
    }, {
      nominal: 113,
      mass: 112.922844
    }, {
      nominal: 114,
      mass: 113.9246136
    }, {
      nominal: 115,
      mass: 114.92882
    }, {
      nominal: 116,
      mass: 115.9312192
    }, {
      nominal: 117,
      mass: 116.9361
    }, {
      nominal: 118,
      mass: 117.93853
    }, {
      nominal: 119,
      mass: 118.94357
    }, {
      nominal: 120,
      mass: 119.94631
    }, {
      nominal: 121,
      mass: 120.95164
    }, {
      nominal: 122,
      mass: 121.95447
    }, {
      nominal: 123,
      mass: 122.95989
    }, {
      nominal: 124,
      mass: 123.96305
    }],
    symbol: 'Ru',
    mass: 101.06494013916,
    name: 'Ruthenium',
    monoisotopicMass: 101.9043441
  }, {
    number: 45,
    isotopes: [{
      nominal: 89,
      mass: 88.95058
    }, {
      nominal: 90,
      mass: 89.94422
    }, {
      nominal: 91,
      mass: 90.93688
    }, {
      nominal: 92,
      mass: 91.9323677
    }, {
      nominal: 93,
      mass: 92.9259128
    }, {
      nominal: 94,
      mass: 93.9217305
    }, {
      nominal: 95,
      mass: 94.9158979
    }, {
      nominal: 96,
      mass: 95.914453
    }, {
      nominal: 97,
      mass: 96.911329
    }, {
      nominal: 98,
      mass: 97.910708
    }, {
      nominal: 99,
      mass: 98.9081282
    }, {
      nominal: 100,
      mass: 99.908117
    }, {
      nominal: 101,
      mass: 100.9061606
    }, {
      nominal: 102,
      mass: 101.9068374
    }, {
      nominal: 103,
      mass: 102.905498,
      abundance: 1
    }, {
      nominal: 104,
      mass: 103.9066492
    }, {
      nominal: 105,
      mass: 104.9056885
    }, {
      nominal: 106,
      mass: 105.9072868
    }, {
      nominal: 107,
      mass: 106.906748
    }, {
      nominal: 108,
      mass: 107.908714
    }, {
      nominal: 109,
      mass: 108.9087488
    }, {
      nominal: 110,
      mass: 109.911079
    }, {
      nominal: 111,
      mass: 110.9116423
    }, {
      nominal: 112,
      mass: 111.914403
    }, {
      nominal: 113,
      mass: 112.9154393
    }, {
      nominal: 114,
      mass: 113.918718
    }, {
      nominal: 115,
      mass: 114.9203116
    }, {
      nominal: 116,
      mass: 115.924059
    }, {
      nominal: 117,
      mass: 116.9260354
    }, {
      nominal: 118,
      mass: 117.93034
    }, {
      nominal: 119,
      mass: 118.932557
    }, {
      nominal: 120,
      mass: 119.93686
    }, {
      nominal: 121,
      mass: 120.93942
    }, {
      nominal: 122,
      mass: 121.94399
    }, {
      nominal: 123,
      mass: 122.94685
    }, {
      nominal: 124,
      mass: 123.95151
    }, {
      nominal: 125,
      mass: 124.95469
    }, {
      nominal: 126,
      mass: 125.95946
    }],
    symbol: 'Rh',
    mass: 102.905498,
    name: 'Rhodium',
    monoisotopicMass: 102.905498
  }, {
    number: 46,
    isotopes: [{
      nominal: 91,
      mass: 90.95032
    }, {
      nominal: 92,
      mass: 91.94088
    }, {
      nominal: 93,
      mass: 92.93651
    }, {
      nominal: 94,
      mass: 93.9290376
    }, {
      nominal: 95,
      mass: 94.9248898
    }, {
      nominal: 96,
      mass: 95.9182151
    }, {
      nominal: 97,
      mass: 96.916472
    }, {
      nominal: 98,
      mass: 97.9126983
    }, {
      nominal: 99,
      mass: 98.9117748
    }, {
      nominal: 100,
      mass: 99.908505
    }, {
      nominal: 101,
      mass: 100.9082864
    }, {
      nominal: 102,
      mass: 101.9056022,
      abundance: 0.0102
    }, {
      nominal: 103,
      mass: 102.9060809
    }, {
      nominal: 104,
      mass: 103.9040305,
      abundance: 0.1114
    }, {
      nominal: 105,
      mass: 104.9050796,
      abundance: 0.2233
    }, {
      nominal: 106,
      mass: 105.9034804,
      abundance: 0.2733
    }, {
      nominal: 107,
      mass: 106.9051282
    }, {
      nominal: 108,
      mass: 107.9038916,
      abundance: 0.2646
    }, {
      nominal: 109,
      mass: 108.9059504
    }, {
      nominal: 110,
      mass: 109.9051722,
      abundance: 0.1172
    }, {
      nominal: 111,
      mass: 110.90768968
    }, {
      nominal: 112,
      mass: 111.9073297
    }, {
      nominal: 113,
      mass: 112.910261
    }, {
      nominal: 114,
      mass: 113.9103686
    }, {
      nominal: 115,
      mass: 114.913659
    }, {
      nominal: 116,
      mass: 115.914297
    }, {
      nominal: 117,
      mass: 116.9179547
    }, {
      nominal: 118,
      mass: 117.9190667
    }, {
      nominal: 119,
      mass: 118.9233402
    }, {
      nominal: 120,
      mass: 119.9245511
    }, {
      nominal: 121,
      mass: 120.9289503
    }, {
      nominal: 122,
      mass: 121.930632
    }, {
      nominal: 123,
      mass: 122.93514
    }, {
      nominal: 124,
      mass: 123.93714
    }, {
      nominal: 125,
      mass: 124.94179
    }, {
      nominal: 126,
      mass: 125.94416
    }, {
      nominal: 127,
      mass: 126.94907
    }, {
      nominal: 128,
      mass: 127.95183
    }],
    symbol: 'Pd',
    mass: 106.41532750734,
    name: 'Palladium',
    monoisotopicMass: 105.9034804
  }, {
    number: 47,
    isotopes: [{
      nominal: 93,
      mass: 92.95033
    }, {
      nominal: 94,
      mass: 93.94373
    }, {
      nominal: 95,
      mass: 94.93602
    }, {
      nominal: 96,
      mass: 95.930744
    }, {
      nominal: 97,
      mass: 96.92397
    }, {
      nominal: 98,
      mass: 97.92156
    }, {
      nominal: 99,
      mass: 98.9176458
    }, {
      nominal: 100,
      mass: 99.9161154
    }, {
      nominal: 101,
      mass: 100.912684
    }, {
      nominal: 102,
      mass: 101.9117047
    }, {
      nominal: 103,
      mass: 102.9089631
    }, {
      nominal: 104,
      mass: 103.9086239
    }, {
      nominal: 105,
      mass: 104.9065256
    }, {
      nominal: 106,
      mass: 105.9066636
    }, {
      nominal: 107,
      mass: 106.9050916,
      abundance: 0.51839
    }, {
      nominal: 108,
      mass: 107.9059503
    }, {
      nominal: 109,
      mass: 108.9047553,
      abundance: 0.48161
    }, {
      nominal: 110,
      mass: 109.9061102
    }, {
      nominal: 111,
      mass: 110.9052959
    }, {
      nominal: 112,
      mass: 111.9070486
    }, {
      nominal: 113,
      mass: 112.906573
    }, {
      nominal: 114,
      mass: 113.908823
    }, {
      nominal: 115,
      mass: 114.908767
    }, {
      nominal: 116,
      mass: 115.9113868
    }, {
      nominal: 117,
      mass: 116.911774
    }, {
      nominal: 118,
      mass: 117.9145955
    }, {
      nominal: 119,
      mass: 118.91557
    }, {
      nominal: 120,
      mass: 119.9187848
    }, {
      nominal: 121,
      mass: 120.920125
    }, {
      nominal: 122,
      mass: 121.923664
    }, {
      nominal: 123,
      mass: 122.925337
    }, {
      nominal: 124,
      mass: 123.92893
    }, {
      nominal: 125,
      mass: 124.93105
    }, {
      nominal: 126,
      mass: 125.93475
    }, {
      nominal: 127,
      mass: 126.93711
    }, {
      nominal: 128,
      mass: 127.94106
    }, {
      nominal: 129,
      mass: 128.94395
    }, {
      nominal: 130,
      mass: 129.9507
    }],
    symbol: 'Ag',
    mass: 107.868149634557,
    name: 'Silver',
    monoisotopicMass: 106.9050916
  }, {
    number: 48,
    isotopes: [{
      nominal: 95,
      mass: 94.94994
    }, {
      nominal: 96,
      mass: 95.94034
    }, {
      nominal: 97,
      mass: 96.9351
    }, {
      nominal: 98,
      mass: 97.927389
    }, {
      nominal: 99,
      mass: 98.9249258
    }, {
      nominal: 100,
      mass: 99.9203488
    }, {
      nominal: 101,
      mass: 100.9185862
    }, {
      nominal: 102,
      mass: 101.914482
    }, {
      nominal: 103,
      mass: 102.9134165
    }, {
      nominal: 104,
      mass: 103.9098564
    }, {
      nominal: 105,
      mass: 104.9094639
    }, {
      nominal: 106,
      mass: 105.9064599,
      abundance: 0.0125
    }, {
      nominal: 107,
      mass: 106.9066121
    }, {
      nominal: 108,
      mass: 107.9041834,
      abundance: 0.0089
    }, {
      nominal: 109,
      mass: 108.9049867
    }, {
      nominal: 110,
      mass: 109.90300661,
      abundance: 0.1249
    }, {
      nominal: 111,
      mass: 110.90418287,
      abundance: 0.128
    }, {
      nominal: 112,
      mass: 111.90276287,
      abundance: 0.2413
    }, {
      nominal: 113,
      mass: 112.90440813,
      abundance: 0.1222
    }, {
      nominal: 114,
      mass: 113.90336509,
      abundance: 0.2873
    }, {
      nominal: 115,
      mass: 114.90543751
    }, {
      nominal: 116,
      mass: 115.90476315,
      abundance: 0.0749
    }, {
      nominal: 117,
      mass: 116.907226
    }, {
      nominal: 118,
      mass: 117.906922
    }, {
      nominal: 119,
      mass: 118.909847
    }, {
      nominal: 120,
      mass: 119.9098681
    }, {
      nominal: 121,
      mass: 120.9129637
    }, {
      nominal: 122,
      mass: 121.9134591
    }, {
      nominal: 123,
      mass: 122.9168925
    }, {
      nominal: 124,
      mass: 123.9176574
    }, {
      nominal: 125,
      mass: 124.9212576
    }, {
      nominal: 126,
      mass: 125.9224291
    }, {
      nominal: 127,
      mass: 126.926472
    }, {
      nominal: 128,
      mass: 127.9278129
    }, {
      nominal: 129,
      mass: 128.93182
    }, {
      nominal: 130,
      mass: 129.93394
    }, {
      nominal: 131,
      mass: 130.9406
    }, {
      nominal: 132,
      mass: 131.94604
    }, {
      nominal: 133,
      mass: 132.95285
    }],
    symbol: 'Cd',
    mass: 112.411557818268,
    name: 'Cadmium',
    monoisotopicMass: 113.90336509
  }, {
    number: 49,
    isotopes: [{
      nominal: 97,
      mass: 96.94934
    }, {
      nominal: 98,
      mass: 97.94214
    }, {
      nominal: 99,
      mass: 98.93411
    }, {
      nominal: 100,
      mass: 99.93096
    }, {
      nominal: 101,
      mass: 100.92634
    }, {
      nominal: 102,
      mass: 101.9241071
    }, {
      nominal: 103,
      mass: 102.9198819
    }, {
      nominal: 104,
      mass: 103.9182145
    }, {
      nominal: 105,
      mass: 104.914502
    }, {
      nominal: 106,
      mass: 105.913464
    }, {
      nominal: 107,
      mass: 106.91029
    }, {
      nominal: 108,
      mass: 107.9096935
    }, {
      nominal: 109,
      mass: 108.9071514
    }, {
      nominal: 110,
      mass: 109.90717
    }, {
      nominal: 111,
      mass: 110.9051085
    }, {
      nominal: 112,
      mass: 111.9055377
    }, {
      nominal: 113,
      mass: 112.90406184,
      abundance: 0.0429
    }, {
      nominal: 114,
      mass: 113.90491791
    }, {
      nominal: 115,
      mass: 114.903878776,
      abundance: 0.9571
    }, {
      nominal: 116,
      mass: 115.90525999
    }, {
      nominal: 117,
      mass: 116.9045157
    }, {
      nominal: 118,
      mass: 117.9063566
    }, {
      nominal: 119,
      mass: 118.9058507
    }, {
      nominal: 120,
      mass: 119.907967
    }, {
      nominal: 121,
      mass: 120.907851
    }, {
      nominal: 122,
      mass: 121.910281
    }, {
      nominal: 123,
      mass: 122.910434
    }, {
      nominal: 124,
      mass: 123.913182
    }, {
      nominal: 125,
      mass: 124.913605
    }, {
      nominal: 126,
      mass: 125.916507
    }, {
      nominal: 127,
      mass: 126.917446
    }, {
      nominal: 128,
      mass: 127.9204
    }, {
      nominal: 129,
      mass: 128.9218053
    }, {
      nominal: 130,
      mass: 129.924977
    }, {
      nominal: 131,
      mass: 130.9269715
    }, {
      nominal: 132,
      mass: 131.933001
    }, {
      nominal: 133,
      mass: 132.93831
    }, {
      nominal: 134,
      mass: 133.94454
    }, {
      nominal: 135,
      mass: 134.95005
    }],
    symbol: 'In',
    mass: 114.81808662944559,
    name: 'Indium',
    monoisotopicMass: 114.903878776
  }, {
    number: 50,
    isotopes: [{
      nominal: 99,
      mass: 98.94853
    }, {
      nominal: 100,
      mass: 99.9385
    }, {
      nominal: 101,
      mass: 100.93526
    }, {
      nominal: 102,
      mass: 101.93029
    }, {
      nominal: 103,
      mass: 102.928105
    }, {
      nominal: 104,
      mass: 103.9231052
    }, {
      nominal: 105,
      mass: 104.9212684
    }, {
      nominal: 106,
      mass: 105.9169574
    }, {
      nominal: 107,
      mass: 106.9157137
    }, {
      nominal: 108,
      mass: 107.9118943
    }, {
      nominal: 109,
      mass: 108.9112921
    }, {
      nominal: 110,
      mass: 109.907845
    }, {
      nominal: 111,
      mass: 110.9077401
    }, {
      nominal: 112,
      mass: 111.90482387,
      abundance: 0.0097
    }, {
      nominal: 113,
      mass: 112.9051757
    }, {
      nominal: 114,
      mass: 113.9027827,
      abundance: 0.0066
    }, {
      nominal: 115,
      mass: 114.903344699,
      abundance: 0.0034
    }, {
      nominal: 116,
      mass: 115.9017428,
      abundance: 0.1454
    }, {
      nominal: 117,
      mass: 116.90295398,
      abundance: 0.0768
    }, {
      nominal: 118,
      mass: 117.90160657,
      abundance: 0.2422
    }, {
      nominal: 119,
      mass: 118.90331117,
      abundance: 0.0859
    }, {
      nominal: 120,
      mass: 119.90220163,
      abundance: 0.3258
    }, {
      nominal: 121,
      mass: 120.9042426
    }, {
      nominal: 122,
      mass: 121.9034438,
      abundance: 0.0463
    }, {
      nominal: 123,
      mass: 122.9057252
    }, {
      nominal: 124,
      mass: 123.9052766,
      abundance: 0.0579
    }, {
      nominal: 125,
      mass: 124.9077864
    }, {
      nominal: 126,
      mass: 125.907659
    }, {
      nominal: 127,
      mass: 126.91039
    }, {
      nominal: 128,
      mass: 127.910507
    }, {
      nominal: 129,
      mass: 128.913465
    }, {
      nominal: 130,
      mass: 129.9139738
    }, {
      nominal: 131,
      mass: 130.917045
    }, {
      nominal: 132,
      mass: 131.9178267
    }, {
      nominal: 133,
      mass: 132.9239134
    }, {
      nominal: 134,
      mass: 133.9286821
    }, {
      nominal: 135,
      mass: 134.9349086
    }, {
      nominal: 136,
      mass: 135.93999
    }, {
      nominal: 137,
      mass: 136.94655
    }, {
      nominal: 138,
      mass: 137.95184
    }],
    symbol: 'Sn',
    mass: 118.71011259301059,
    name: 'Tin',
    monoisotopicMass: 119.90220163
  }, {
    number: 51,
    isotopes: [{
      nominal: 103,
      mass: 102.93969
    }, {
      nominal: 104,
      mass: 103.93648
    }, {
      nominal: 105,
      mass: 104.931276
    }, {
      nominal: 106,
      mass: 105.928638
    }, {
      nominal: 107,
      mass: 106.9241506
    }, {
      nominal: 108,
      mass: 107.9222267
    }, {
      nominal: 109,
      mass: 108.9181411
    }, {
      nominal: 110,
      mass: 109.9168543
    }, {
      nominal: 111,
      mass: 110.9132182
    }, {
      nominal: 112,
      mass: 111.9124
    }, {
      nominal: 113,
      mass: 112.909375
    }, {
      nominal: 114,
      mass: 113.90929
    }, {
      nominal: 115,
      mass: 114.906598
    }, {
      nominal: 116,
      mass: 115.9067931
    }, {
      nominal: 117,
      mass: 116.9048415
    }, {
      nominal: 118,
      mass: 117.9055321
    }, {
      nominal: 119,
      mass: 118.9039455
    }, {
      nominal: 120,
      mass: 119.9050794
    }, {
      nominal: 121,
      mass: 120.903812,
      abundance: 0.5721
    }, {
      nominal: 122,
      mass: 121.9051699
    }, {
      nominal: 123,
      mass: 122.9042132,
      abundance: 0.4279
    }, {
      nominal: 124,
      mass: 123.905935
    }, {
      nominal: 125,
      mass: 124.905253
    }, {
      nominal: 126,
      mass: 125.907253
    }, {
      nominal: 127,
      mass: 126.9069243
    }, {
      nominal: 128,
      mass: 127.909146
    }, {
      nominal: 129,
      mass: 128.909147
    }, {
      nominal: 130,
      mass: 129.911662
    }, {
      nominal: 131,
      mass: 130.9119888
    }, {
      nominal: 132,
      mass: 131.9145077
    }, {
      nominal: 133,
      mass: 132.9152732
    }, {
      nominal: 134,
      mass: 133.9205357
    }, {
      nominal: 135,
      mass: 134.9251851
    }, {
      nominal: 136,
      mass: 135.9307459
    }, {
      nominal: 137,
      mass: 136.93555
    }, {
      nominal: 138,
      mass: 137.94145
    }, {
      nominal: 139,
      mass: 138.94655
    }, {
      nominal: 140,
      mass: 139.95283
    }],
    symbol: 'Sb',
    mass: 121.75978367348,
    name: 'Antimony',
    monoisotopicMass: 120.903812
  }, {
    number: 52,
    isotopes: [{
      nominal: 105,
      mass: 104.9433
    }, {
      nominal: 106,
      mass: 105.9375
    }, {
      nominal: 107,
      mass: 106.935012
    }, {
      nominal: 108,
      mass: 107.9293805
    }, {
      nominal: 109,
      mass: 108.9273045
    }, {
      nominal: 110,
      mass: 109.9224581
    }, {
      nominal: 111,
      mass: 110.9210006
    }, {
      nominal: 112,
      mass: 111.9167279
    }, {
      nominal: 113,
      mass: 112.915891
    }, {
      nominal: 114,
      mass: 113.912089
    }, {
      nominal: 115,
      mass: 114.911902
    }, {
      nominal: 116,
      mass: 115.90846
    }, {
      nominal: 117,
      mass: 116.908646
    }, {
      nominal: 118,
      mass: 117.905854
    }, {
      nominal: 119,
      mass: 118.9064071
    }, {
      nominal: 120,
      mass: 119.9040593,
      abundance: 0.0009
    }, {
      nominal: 121,
      mass: 120.904944
    }, {
      nominal: 122,
      mass: 121.9030435,
      abundance: 0.0255
    }, {
      nominal: 123,
      mass: 122.9042698,
      abundance: 0.0089
    }, {
      nominal: 124,
      mass: 123.9028171,
      abundance: 0.0474
    }, {
      nominal: 125,
      mass: 124.9044299,
      abundance: 0.0707
    }, {
      nominal: 126,
      mass: 125.9033109,
      abundance: 0.1884
    }, {
      nominal: 127,
      mass: 126.9052257
    }, {
      nominal: 128,
      mass: 127.90446128,
      abundance: 0.3174
    }, {
      nominal: 129,
      mass: 128.90659646
    }, {
      nominal: 130,
      mass: 129.906222748,
      abundance: 0.3408
    }, {
      nominal: 131,
      mass: 130.908522213
    }, {
      nominal: 132,
      mass: 131.9085467
    }, {
      nominal: 133,
      mass: 132.9109688
    }, {
      nominal: 134,
      mass: 133.911394
    }, {
      nominal: 135,
      mass: 134.9165557
    }, {
      nominal: 136,
      mass: 135.9201006
    }, {
      nominal: 137,
      mass: 136.9255989
    }, {
      nominal: 138,
      mass: 137.9294722
    }, {
      nominal: 139,
      mass: 138.9353672
    }, {
      nominal: 140,
      mass: 139.939499
    }, {
      nominal: 141,
      mass: 140.9458
    }, {
      nominal: 142,
      mass: 141.95022
    }, {
      nominal: 143,
      mass: 142.95676
    }],
    symbol: 'Te',
    mass: 127.6031264846604,
    name: 'Tellurium',
    monoisotopicMass: 129.906222748
  }, {
    number: 53,
    isotopes: [{
      nominal: 107,
      mass: 106.94678
    }, {
      nominal: 108,
      mass: 107.94348
    }, {
      nominal: 109,
      mass: 108.9380853
    }, {
      nominal: 110,
      mass: 109.935089
    }, {
      nominal: 111,
      mass: 110.9302692
    }, {
      nominal: 112,
      mass: 111.928005
    }, {
      nominal: 113,
      mass: 112.9236501
    }, {
      nominal: 114,
      mass: 113.92185
    }, {
      nominal: 115,
      mass: 114.918048
    }, {
      nominal: 116,
      mass: 115.91681
    }, {
      nominal: 117,
      mass: 116.913648
    }, {
      nominal: 118,
      mass: 117.913074
    }, {
      nominal: 119,
      mass: 118.910074
    }, {
      nominal: 120,
      mass: 119.910087
    }, {
      nominal: 121,
      mass: 120.9074051
    }, {
      nominal: 122,
      mass: 121.9075888
    }, {
      nominal: 123,
      mass: 122.9055885
    }, {
      nominal: 124,
      mass: 123.906209
    }, {
      nominal: 125,
      mass: 124.9046294
    }, {
      nominal: 126,
      mass: 125.9056233
    }, {
      nominal: 127,
      mass: 126.9044719,
      abundance: 1
    }, {
      nominal: 128,
      mass: 127.9058086
    }, {
      nominal: 129,
      mass: 128.9049837
    }, {
      nominal: 130,
      mass: 129.9066702
    }, {
      nominal: 131,
      mass: 130.9061263
    }, {
      nominal: 132,
      mass: 131.9079935
    }, {
      nominal: 133,
      mass: 132.907797
    }, {
      nominal: 134,
      mass: 133.9097588
    }, {
      nominal: 135,
      mass: 134.9100488
    }, {
      nominal: 136,
      mass: 135.914604
    }, {
      nominal: 137,
      mass: 136.9180282
    }, {
      nominal: 138,
      mass: 137.9227264
    }, {
      nominal: 139,
      mass: 138.926506
    }, {
      nominal: 140,
      mass: 139.93173
    }, {
      nominal: 141,
      mass: 140.93569
    }, {
      nominal: 142,
      mass: 141.9412
    }, {
      nominal: 143,
      mass: 142.94565
    }, {
      nominal: 144,
      mass: 143.95139
    }, {
      nominal: 145,
      mass: 144.95605
    }],
    symbol: 'I',
    mass: 126.9044719,
    name: 'Iodine',
    monoisotopicMass: 126.9044719
  }, {
    number: 54,
    isotopes: [{
      nominal: 109,
      mass: 108.95043
    }, {
      nominal: 110,
      mass: 109.94426
    }, {
      nominal: 111,
      mass: 110.941607
    }, {
      nominal: 112,
      mass: 111.935559
    }, {
      nominal: 113,
      mass: 112.9332217
    }, {
      nominal: 114,
      mass: 113.92798
    }, {
      nominal: 115,
      mass: 114.926294
    }, {
      nominal: 116,
      mass: 115.921581
    }, {
      nominal: 117,
      mass: 116.920359
    }, {
      nominal: 118,
      mass: 117.916179
    }, {
      nominal: 119,
      mass: 118.915411
    }, {
      nominal: 120,
      mass: 119.911784
    }, {
      nominal: 121,
      mass: 120.911453
    }, {
      nominal: 122,
      mass: 121.908368
    }, {
      nominal: 123,
      mass: 122.908482
    }, {
      nominal: 124,
      mass: 123.905892,
      abundance: 0.000952
    }, {
      nominal: 125,
      mass: 124.9063944
    }, {
      nominal: 126,
      mass: 125.9042983,
      abundance: 0.00089
    }, {
      nominal: 127,
      mass: 126.9051829
    }, {
      nominal: 128,
      mass: 127.903531,
      abundance: 0.019102
    }, {
      nominal: 129,
      mass: 128.9047808611,
      abundance: 0.264006
    }, {
      nominal: 130,
      mass: 129.903509349,
      abundance: 0.04071
    }, {
      nominal: 131,
      mass: 130.90508406,
      abundance: 0.212324
    }, {
      nominal: 132,
      mass: 131.9041550856,
      abundance: 0.269086
    }, {
      nominal: 133,
      mass: 132.9059108
    }, {
      nominal: 134,
      mass: 133.90539466,
      abundance: 0.104357
    }, {
      nominal: 135,
      mass: 134.9072278
    }, {
      nominal: 136,
      mass: 135.907214484,
      abundance: 0.088573
    }, {
      nominal: 137,
      mass: 136.91155778
    }, {
      nominal: 138,
      mass: 137.9141463
    }, {
      nominal: 139,
      mass: 138.9187922
    }, {
      nominal: 140,
      mass: 139.9216458
    }, {
      nominal: 141,
      mass: 140.9267872
    }, {
      nominal: 142,
      mass: 141.9299731
    }, {
      nominal: 143,
      mass: 142.9353696
    }, {
      nominal: 144,
      mass: 143.9389451
    }, {
      nominal: 145,
      mass: 144.94472
    }, {
      nominal: 146,
      mass: 145.948518
    }, {
      nominal: 147,
      mass: 146.95426
    }, {
      nominal: 148,
      mass: 147.95813
    }],
    symbol: 'Xe',
    mass: 131.29276144779053,
    name: 'Xenon',
    monoisotopicMass: 131.9041550856
  }, {
    number: 55,
    isotopes: [{
      nominal: 112,
      mass: 111.950309
    }, {
      nominal: 113,
      mass: 112.9444291
    }, {
      nominal: 114,
      mass: 113.941296
    }, {
      nominal: 115,
      mass: 114.93591
    }, {
      nominal: 116,
      mass: 115.93337
    }, {
      nominal: 117,
      mass: 116.928617
    }, {
      nominal: 118,
      mass: 117.92656
    }, {
      nominal: 119,
      mass: 118.922377
    }, {
      nominal: 120,
      mass: 119.920677
    }, {
      nominal: 121,
      mass: 120.917227
    }, {
      nominal: 122,
      mass: 121.916108
    }, {
      nominal: 123,
      mass: 122.912996
    }, {
      nominal: 124,
      mass: 123.9122578
    }, {
      nominal: 125,
      mass: 124.909728
    }, {
      nominal: 126,
      mass: 125.909446
    }, {
      nominal: 127,
      mass: 126.9074174
    }, {
      nominal: 128,
      mass: 127.9077487
    }, {
      nominal: 129,
      mass: 128.9060657
    }, {
      nominal: 130,
      mass: 129.9067093
    }, {
      nominal: 131,
      mass: 130.9054649
    }, {
      nominal: 132,
      mass: 131.9064339
    }, {
      nominal: 133,
      mass: 132.905451961,
      abundance: 1
    }, {
      nominal: 134,
      mass: 133.906718503
    }, {
      nominal: 135,
      mass: 134.905977
    }, {
      nominal: 136,
      mass: 135.9073114
    }, {
      nominal: 137,
      mass: 136.90708923
    }, {
      nominal: 138,
      mass: 137.9110171
    }, {
      nominal: 139,
      mass: 138.9133638
    }, {
      nominal: 140,
      mass: 139.9172831
    }, {
      nominal: 141,
      mass: 140.9200455
    }, {
      nominal: 142,
      mass: 141.924296
    }, {
      nominal: 143,
      mass: 142.927349
    }, {
      nominal: 144,
      mass: 143.932076
    }, {
      nominal: 145,
      mass: 144.935527
    }, {
      nominal: 146,
      mass: 145.940344
    }, {
      nominal: 147,
      mass: 146.944156
    }, {
      nominal: 148,
      mass: 147.94923
    }, {
      nominal: 149,
      mass: 148.95302
    }, {
      nominal: 150,
      mass: 149.95833
    }, {
      nominal: 151,
      mass: 150.96258
    }],
    symbol: 'Cs',
    mass: 132.905451961,
    name: 'Caesium',
    monoisotopicMass: 132.905451961
  }, {
    number: 56,
    isotopes: [{
      nominal: 114,
      mass: 113.95066
    }, {
      nominal: 115,
      mass: 114.94737
    }, {
      nominal: 116,
      mass: 115.94128
    }, {
      nominal: 117,
      mass: 116.93814
    }, {
      nominal: 118,
      mass: 117.93306
    }, {
      nominal: 119,
      mass: 118.93066
    }, {
      nominal: 120,
      mass: 119.92605
    }, {
      nominal: 121,
      mass: 120.92405
    }, {
      nominal: 122,
      mass: 121.919904
    }, {
      nominal: 123,
      mass: 122.918781
    }, {
      nominal: 124,
      mass: 123.915094
    }, {
      nominal: 125,
      mass: 124.914472
    }, {
      nominal: 126,
      mass: 125.91125
    }, {
      nominal: 127,
      mass: 126.911091
    }, {
      nominal: 128,
      mass: 127.908342
    }, {
      nominal: 129,
      mass: 128.908681
    }, {
      nominal: 130,
      mass: 129.9063207,
      abundance: 0.00106
    }, {
      nominal: 131,
      mass: 130.906941
    }, {
      nominal: 132,
      mass: 131.9050611,
      abundance: 0.00101
    }, {
      nominal: 133,
      mass: 132.9060074
    }, {
      nominal: 134,
      mass: 133.90450818,
      abundance: 0.02417
    }, {
      nominal: 135,
      mass: 134.90568838,
      abundance: 0.06592
    }, {
      nominal: 136,
      mass: 135.90457573,
      abundance: 0.07854
    }, {
      nominal: 137,
      mass: 136.90582714,
      abundance: 0.11232
    }, {
      nominal: 138,
      mass: 137.905247,
      abundance: 0.71698
    }, {
      nominal: 139,
      mass: 138.9088411
    }, {
      nominal: 140,
      mass: 139.9106057
    }, {
      nominal: 141,
      mass: 140.9144033
    }, {
      nominal: 142,
      mass: 141.9164324
    }, {
      nominal: 143,
      mass: 142.9206253
    }, {
      nominal: 144,
      mass: 143.9229549
    }, {
      nominal: 145,
      mass: 144.9275184
    }, {
      nominal: 146,
      mass: 145.930284
    }, {
      nominal: 147,
      mass: 146.935304
    }, {
      nominal: 148,
      mass: 147.938171
    }, {
      nominal: 149,
      mass: 148.94308
    }, {
      nominal: 150,
      mass: 149.94605
    }, {
      nominal: 151,
      mass: 150.95127
    }, {
      nominal: 152,
      mass: 151.95481
    }, {
      nominal: 153,
      mass: 152.96036
    }],
    symbol: 'Ba',
    mass: 137.3268916286322,
    name: 'Barium',
    monoisotopicMass: 137.905247
  }, {
    number: 57,
    isotopes: [{
      nominal: 116,
      mass: 115.9563
    }, {
      nominal: 117,
      mass: 116.94999
    }, {
      nominal: 118,
      mass: 117.94673
    }, {
      nominal: 119,
      mass: 118.94099
    }, {
      nominal: 120,
      mass: 119.93807
    }, {
      nominal: 121,
      mass: 120.93315
    }, {
      nominal: 122,
      mass: 121.93071
    }, {
      nominal: 123,
      mass: 122.9263
    }, {
      nominal: 124,
      mass: 123.924574
    }, {
      nominal: 125,
      mass: 124.920816
    }, {
      nominal: 126,
      mass: 125.919513
    }, {
      nominal: 127,
      mass: 126.916375
    }, {
      nominal: 128,
      mass: 127.915592
    }, {
      nominal: 129,
      mass: 128.912694
    }, {
      nominal: 130,
      mass: 129.912369
    }, {
      nominal: 131,
      mass: 130.91007
    }, {
      nominal: 132,
      mass: 131.910119
    }, {
      nominal: 133,
      mass: 132.908218
    }, {
      nominal: 134,
      mass: 133.908514
    }, {
      nominal: 135,
      mass: 134.906984
    }, {
      nominal: 136,
      mass: 135.907635
    }, {
      nominal: 137,
      mass: 136.9064504
    }, {
      nominal: 138,
      mass: 137.9071149,
      abundance: 0.0008881
    }, {
      nominal: 139,
      mass: 138.9063563,
      abundance: 0.9991119
    }, {
      nominal: 140,
      mass: 139.9094806
    }, {
      nominal: 141,
      mass: 140.910966
    }, {
      nominal: 142,
      mass: 141.9140909
    }, {
      nominal: 143,
      mass: 142.9160795
    }, {
      nominal: 144,
      mass: 143.919646
    }, {
      nominal: 145,
      mass: 144.921808
    }, {
      nominal: 146,
      mass: 145.925875
    }, {
      nominal: 147,
      mass: 146.928418
    }, {
      nominal: 148,
      mass: 147.932679
    }, {
      nominal: 149,
      mass: 148.93535
    }, {
      nominal: 150,
      mass: 149.93947
    }, {
      nominal: 151,
      mass: 150.94232
    }, {
      nominal: 152,
      mass: 151.94682
    }, {
      nominal: 153,
      mass: 152.95036
    }, {
      nominal: 154,
      mass: 153.95517
    }, {
      nominal: 155,
      mass: 154.95901
    }],
    symbol: 'La',
    mass: 138.90546887371266,
    name: 'Lanthanum',
    monoisotopicMass: 138.9063563
  }, {
    number: 58,
    isotopes: [{
      nominal: 119,
      mass: 118.95271
    }, {
      nominal: 120,
      mass: 119.94654
    }, {
      nominal: 121,
      mass: 120.94335
    }, {
      nominal: 122,
      mass: 121.93787
    }, {
      nominal: 123,
      mass: 122.93528
    }, {
      nominal: 124,
      mass: 123.93031
    }, {
      nominal: 125,
      mass: 124.92844
    }, {
      nominal: 126,
      mass: 125.923971
    }, {
      nominal: 127,
      mass: 126.922727
    }, {
      nominal: 128,
      mass: 127.918911
    }, {
      nominal: 129,
      mass: 128.918102
    }, {
      nominal: 130,
      mass: 129.914736
    }, {
      nominal: 131,
      mass: 130.914429
    }, {
      nominal: 132,
      mass: 131.911464
    }, {
      nominal: 133,
      mass: 132.91152
    }, {
      nominal: 134,
      mass: 133.908928
    }, {
      nominal: 135,
      mass: 134.909161
    }, {
      nominal: 136,
      mass: 135.90712921,
      abundance: 0.00185
    }, {
      nominal: 137,
      mass: 136.90776236
    }, {
      nominal: 138,
      mass: 137.905991,
      abundance: 0.00251
    }, {
      nominal: 139,
      mass: 138.9066551
    }, {
      nominal: 140,
      mass: 139.9054431,
      abundance: 0.8845
    }, {
      nominal: 141,
      mass: 140.9082807
    }, {
      nominal: 142,
      mass: 141.9092504,
      abundance: 0.11114
    }, {
      nominal: 143,
      mass: 142.9123921
    }, {
      nominal: 144,
      mass: 143.9136529
    }, {
      nominal: 145,
      mass: 144.917265
    }, {
      nominal: 146,
      mass: 145.918802
    }, {
      nominal: 147,
      mass: 146.9226899
    }, {
      nominal: 148,
      mass: 147.924424
    }, {
      nominal: 149,
      mass: 148.928427
    }, {
      nominal: 150,
      mass: 149.930384
    }, {
      nominal: 151,
      mass: 150.934272
    }, {
      nominal: 152,
      mass: 151.9366
    }, {
      nominal: 153,
      mass: 152.94093
    }, {
      nominal: 154,
      mass: 153.9438
    }, {
      nominal: 155,
      mass: 154.94855
    }, {
      nominal: 156,
      mass: 155.95183
    }, {
      nominal: 157,
      mass: 156.95705
    }],
    symbol: 'Ce',
    mass: 140.1157307378545,
    name: 'Cerium',
    monoisotopicMass: 139.9054431
  }, {
    number: 59,
    isotopes: [{
      nominal: 121,
      mass: 120.95532
    }, {
      nominal: 122,
      mass: 121.95175
    }, {
      nominal: 123,
      mass: 122.94596
    }, {
      nominal: 124,
      mass: 123.94294
    }, {
      nominal: 125,
      mass: 124.9377
    }, {
      nominal: 126,
      mass: 125.93524
    }, {
      nominal: 127,
      mass: 126.93071
    }, {
      nominal: 128,
      mass: 127.928791
    }, {
      nominal: 129,
      mass: 128.925095
    }, {
      nominal: 130,
      mass: 129.92359
    }, {
      nominal: 131,
      mass: 130.920235
    }, {
      nominal: 132,
      mass: 131.919255
    }, {
      nominal: 133,
      mass: 132.916331
    }, {
      nominal: 134,
      mass: 133.915697
    }, {
      nominal: 135,
      mass: 134.913112
    }, {
      nominal: 136,
      mass: 135.912677
    }, {
      nominal: 137,
      mass: 136.9106792
    }, {
      nominal: 138,
      mass: 137.910754
    }, {
      nominal: 139,
      mass: 138.9089408
    }, {
      nominal: 140,
      mass: 139.9090803
    }, {
      nominal: 141,
      mass: 140.9076576,
      abundance: 1
    }, {
      nominal: 142,
      mass: 141.9100496
    }, {
      nominal: 143,
      mass: 142.9108228
    }, {
      nominal: 144,
      mass: 143.9133109
    }, {
      nominal: 145,
      mass: 144.9145182
    }, {
      nominal: 146,
      mass: 145.91768
    }, {
      nominal: 147,
      mass: 146.919008
    }, {
      nominal: 148,
      mass: 147.92213
    }, {
      nominal: 149,
      mass: 148.923736
    }, {
      nominal: 150,
      mass: 149.9266765
    }, {
      nominal: 151,
      mass: 150.928309
    }, {
      nominal: 152,
      mass: 151.931553
    }, {
      nominal: 153,
      mass: 152.933904
    }, {
      nominal: 154,
      mass: 153.93753
    }, {
      nominal: 155,
      mass: 154.940509
    }, {
      nominal: 156,
      mass: 155.94464
    }, {
      nominal: 157,
      mass: 156.94789
    }, {
      nominal: 158,
      mass: 157.95241
    }, {
      nominal: 159,
      mass: 158.95589
    }],
    symbol: 'Pr',
    mass: 140.9076576,
    name: 'Praseodymium',
    monoisotopicMass: 140.9076576
  }, {
    number: 60,
    isotopes: [{
      nominal: 124,
      mass: 123.9522
    }, {
      nominal: 125,
      mass: 124.9489
    }, {
      nominal: 126,
      mass: 125.94311
    }, {
      nominal: 127,
      mass: 126.94038
    }, {
      nominal: 128,
      mass: 127.93525
    }, {
      nominal: 129,
      mass: 128.9331
    }, {
      nominal: 130,
      mass: 129.928506
    }, {
      nominal: 131,
      mass: 130.927248
    }, {
      nominal: 132,
      mass: 131.923321
    }, {
      nominal: 133,
      mass: 132.922348
    }, {
      nominal: 134,
      mass: 133.91879
    }, {
      nominal: 135,
      mass: 134.918181
    }, {
      nominal: 136,
      mass: 135.914976
    }, {
      nominal: 137,
      mass: 136.914562
    }, {
      nominal: 138,
      mass: 137.91195
    }, {
      nominal: 139,
      mass: 138.911954
    }, {
      nominal: 140,
      mass: 139.90955
    }, {
      nominal: 141,
      mass: 140.9096147
    }, {
      nominal: 142,
      mass: 141.907729,
      abundance: 0.27152
    }, {
      nominal: 143,
      mass: 142.90982,
      abundance: 0.12174
    }, {
      nominal: 144,
      mass: 143.910093,
      abundance: 0.23798
    }, {
      nominal: 145,
      mass: 144.9125793,
      abundance: 0.08293
    }, {
      nominal: 146,
      mass: 145.9131226,
      abundance: 0.17189
    }, {
      nominal: 147,
      mass: 146.9161061
    }, {
      nominal: 148,
      mass: 147.9168993,
      abundance: 0.05756
    }, {
      nominal: 149,
      mass: 148.9201548
    }, {
      nominal: 150,
      mass: 149.9209022,
      abundance: 0.05638
    }, {
      nominal: 151,
      mass: 150.9238403
    }, {
      nominal: 152,
      mass: 151.924692
    }, {
      nominal: 153,
      mass: 152.927718
    }, {
      nominal: 154,
      mass: 153.92948
    }, {
      nominal: 155,
      mass: 154.9331357
    }, {
      nominal: 156,
      mass: 155.93508
    }, {
      nominal: 157,
      mass: 156.939386
    }, {
      nominal: 158,
      mass: 157.94197
    }, {
      nominal: 159,
      mass: 158.94653
    }, {
      nominal: 160,
      mass: 159.9494
    }, {
      nominal: 161,
      mass: 160.95428
    }],
    symbol: 'Nd',
    mass: 144.241596031827,
    name: 'Neodymium',
    monoisotopicMass: 141.907729
  }, {
    number: 61,
    isotopes: [{
      nominal: 126,
      mass: 125.95792
    }, {
      nominal: 127,
      mass: 126.95192
    }, {
      nominal: 128,
      mass: 127.9487
    }, {
      nominal: 129,
      mass: 128.94323
    }, {
      nominal: 130,
      mass: 129.94053
    }, {
      nominal: 131,
      mass: 130.93567
    }, {
      nominal: 132,
      mass: 131.93384
    }, {
      nominal: 133,
      mass: 132.929782
    }, {
      nominal: 134,
      mass: 133.928353
    }, {
      nominal: 135,
      mass: 134.924823
    }, {
      nominal: 136,
      mass: 135.923585
    }, {
      nominal: 137,
      mass: 136.92048
    }, {
      nominal: 138,
      mass: 137.919548
    }, {
      nominal: 139,
      mass: 138.9168
    }, {
      nominal: 140,
      mass: 139.91604
    }, {
      nominal: 141,
      mass: 140.913555
    }, {
      nominal: 142,
      mass: 141.91289
    }, {
      nominal: 143,
      mass: 142.9109383
    }, {
      nominal: 144,
      mass: 143.9125964
    }, {
      nominal: 145,
      mass: 144.9127559
    }, {
      nominal: 146,
      mass: 145.9147024
    }, {
      nominal: 147,
      mass: 146.915145
    }, {
      nominal: 148,
      mass: 147.9174819
    }, {
      nominal: 149,
      mass: 148.9183423
    }, {
      nominal: 150,
      mass: 149.920991
    }, {
      nominal: 151,
      mass: 150.9212175
    }, {
      nominal: 152,
      mass: 151.923506
    }, {
      nominal: 153,
      mass: 152.9241567
    }, {
      nominal: 154,
      mass: 153.926472
    }, {
      nominal: 155,
      mass: 154.928137
    }, {
      nominal: 156,
      mass: 155.9311175
    }, {
      nominal: 157,
      mass: 156.9331214
    }, {
      nominal: 158,
      mass: 157.936565
    }, {
      nominal: 159,
      mass: 158.939287
    }, {
      nominal: 160,
      mass: 159.9431
    }, {
      nominal: 161,
      mass: 160.94607
    }, {
      nominal: 162,
      mass: 161.95022
    }, {
      nominal: 163,
      mass: 162.95357
    }],
    symbol: 'Pm',
    mass: null,
    name: 'Promethium'
  }, {
    number: 62,
    isotopes: [{
      nominal: 128,
      mass: 127.95842
    }, {
      nominal: 129,
      mass: 128.95476
    }, {
      nominal: 130,
      mass: 129.949
    }, {
      nominal: 131,
      mass: 130.94618
    }, {
      nominal: 132,
      mass: 131.94087
    }, {
      nominal: 133,
      mass: 132.93856
    }, {
      nominal: 134,
      mass: 133.93411
    }, {
      nominal: 135,
      mass: 134.93252
    }, {
      nominal: 136,
      mass: 135.928276
    }, {
      nominal: 137,
      mass: 136.926971
    }, {
      nominal: 138,
      mass: 137.923244
    }, {
      nominal: 139,
      mass: 138.922297
    }, {
      nominal: 140,
      mass: 139.918995
    }, {
      nominal: 141,
      mass: 140.9184816
    }, {
      nominal: 142,
      mass: 141.9152044
    }, {
      nominal: 143,
      mass: 142.9146353
    }, {
      nominal: 144,
      mass: 143.9120065,
      abundance: 0.0307
    }, {
      nominal: 145,
      mass: 144.9134173
    }, {
      nominal: 146,
      mass: 145.913047
    }, {
      nominal: 147,
      mass: 146.9149044,
      abundance: 0.1499
    }, {
      nominal: 148,
      mass: 147.9148292,
      abundance: 0.1124
    }, {
      nominal: 149,
      mass: 148.9171921,
      abundance: 0.1382
    }, {
      nominal: 150,
      mass: 149.9172829,
      abundance: 0.0738
    }, {
      nominal: 151,
      mass: 150.9199398
    }, {
      nominal: 152,
      mass: 151.9197397,
      abundance: 0.2675
    }, {
      nominal: 153,
      mass: 152.9221047
    }, {
      nominal: 154,
      mass: 153.9222169,
      abundance: 0.2275
    }, {
      nominal: 155,
      mass: 154.9246477
    }, {
      nominal: 156,
      mass: 155.925536
    }, {
      nominal: 157,
      mass: 156.9284187
    }, {
      nominal: 158,
      mass: 157.929951
    }, {
      nominal: 159,
      mass: 158.9332172
    }, {
      nominal: 160,
      mass: 159.9353353
    }, {
      nominal: 161,
      mass: 160.9391602
    }, {
      nominal: 162,
      mass: 161.94146
    }, {
      nominal: 163,
      mass: 162.94555
    }, {
      nominal: 164,
      mass: 163.94836
    }, {
      nominal: 165,
      mass: 164.95297
    }],
    symbol: 'Sm',
    mass: 150.36635571193,
    name: 'Samarium',
    monoisotopicMass: 151.9197397
  }, {
    number: 63,
    isotopes: [{
      nominal: 130,
      mass: 129.96369
    }, {
      nominal: 131,
      mass: 130.95784
    }, {
      nominal: 132,
      mass: 131.95467
    }, {
      nominal: 133,
      mass: 132.94929
    }, {
      nominal: 134,
      mass: 133.9464
    }, {
      nominal: 135,
      mass: 134.94187
    }, {
      nominal: 136,
      mass: 135.93962
    }, {
      nominal: 137,
      mass: 136.93546
    }, {
      nominal: 138,
      mass: 137.933709
    }, {
      nominal: 139,
      mass: 138.929792
    }, {
      nominal: 140,
      mass: 139.928088
    }, {
      nominal: 141,
      mass: 140.924932
    }, {
      nominal: 142,
      mass: 141.923442
    }, {
      nominal: 143,
      mass: 142.920299
    }, {
      nominal: 144,
      mass: 143.91882
    }, {
      nominal: 145,
      mass: 144.9162726
    }, {
      nominal: 146,
      mass: 145.917211
    }, {
      nominal: 147,
      mass: 146.9167527
    }, {
      nominal: 148,
      mass: 147.918089
    }, {
      nominal: 149,
      mass: 148.9179378
    }, {
      nominal: 150,
      mass: 149.9197077
    }, {
      nominal: 151,
      mass: 150.9198578,
      abundance: 0.4781
    }, {
      nominal: 152,
      mass: 151.9217522
    }, {
      nominal: 153,
      mass: 152.921238,
      abundance: 0.5219
    }, {
      nominal: 154,
      mass: 153.922987
    }, {
      nominal: 155,
      mass: 154.9229011
    }, {
      nominal: 156,
      mass: 155.9247605
    }, {
      nominal: 157,
      mass: 156.9254334
    }, {
      nominal: 158,
      mass: 157.927799
    }, {
      nominal: 159,
      mass: 158.9291001
    }, {
      nominal: 160,
      mass: 159.931851
    }, {
      nominal: 161,
      mass: 160.933664
    }, {
      nominal: 162,
      mass: 161.936989
    }, {
      nominal: 163,
      mass: 162.939196
    }, {
      nominal: 164,
      mass: 163.94274
    }, {
      nominal: 165,
      mass: 164.94559
    }, {
      nominal: 166,
      mass: 165.94962
    }, {
      nominal: 167,
      mass: 166.95289
    }],
    symbol: 'Eu',
    mass: 151.96437812637998,
    name: 'Europium',
    monoisotopicMass: 152.921238
  }, {
    number: 64,
    isotopes: [{
      nominal: 133,
      mass: 132.96133
    }, {
      nominal: 134,
      mass: 133.95566
    }, {
      nominal: 135,
      mass: 134.95245
    }, {
      nominal: 136,
      mass: 135.9473
    }, {
      nominal: 137,
      mass: 136.94502
    }, {
      nominal: 138,
      mass: 137.94025
    }, {
      nominal: 139,
      mass: 138.93813
    }, {
      nominal: 140,
      mass: 139.933674
    }, {
      nominal: 141,
      mass: 140.932126
    }, {
      nominal: 142,
      mass: 141.928116
    }, {
      nominal: 143,
      mass: 142.92675
    }, {
      nominal: 144,
      mass: 143.922963
    }, {
      nominal: 145,
      mass: 144.921713
    }, {
      nominal: 146,
      mass: 145.9183188
    }, {
      nominal: 147,
      mass: 146.9191014
    }, {
      nominal: 148,
      mass: 147.9181215
    }, {
      nominal: 149,
      mass: 148.9193481
    }, {
      nominal: 150,
      mass: 149.9186644
    }, {
      nominal: 151,
      mass: 150.920356
    }, {
      nominal: 152,
      mass: 151.9197995,
      abundance: 0.002
    }, {
      nominal: 153,
      mass: 152.921758
    }, {
      nominal: 154,
      mass: 153.9208741,
      abundance: 0.0218
    }, {
      nominal: 155,
      mass: 154.9226305,
      abundance: 0.148
    }, {
      nominal: 156,
      mass: 155.9221312,
      abundance: 0.2047
    }, {
      nominal: 157,
      mass: 156.9239686,
      abundance: 0.1565
    }, {
      nominal: 158,
      mass: 157.9241123,
      abundance: 0.2484
    }, {
      nominal: 159,
      mass: 158.926397
    }, {
      nominal: 160,
      mass: 159.9270624,
      abundance: 0.2186
    }, {
      nominal: 161,
      mass: 160.9296775
    }, {
      nominal: 162,
      mass: 161.930993
    }, {
      nominal: 163,
      mass: 162.9341769
    }, {
      nominal: 164,
      mass: 163.93583
    }, {
      nominal: 165,
      mass: 164.93936
    }, {
      nominal: 166,
      mass: 165.94146
    }, {
      nominal: 167,
      mass: 166.94545
    }, {
      nominal: 168,
      mass: 167.94808
    }, {
      nominal: 169,
      mass: 168.9526
    }],
    symbol: 'Gd',
    mass: 157.25213064687998,
    name: 'Gadolinium',
    monoisotopicMass: 157.9241123
  }, {
    number: 65,
    isotopes: [{
      nominal: 135,
      mass: 134.96476
    }, {
      nominal: 136,
      mass: 135.96129
    }, {
      nominal: 137,
      mass: 136.95602
    }, {
      nominal: 138,
      mass: 137.95312
    }, {
      nominal: 139,
      mass: 138.94833
    }, {
      nominal: 140,
      mass: 139.94581
    }, {
      nominal: 141,
      mass: 140.94145
    }, {
      nominal: 142,
      mass: 141.93928
    }, {
      nominal: 143,
      mass: 142.935137
    }, {
      nominal: 144,
      mass: 143.933045
    }, {
      nominal: 145,
      mass: 144.92882
    }, {
      nominal: 146,
      mass: 145.927253
    }, {
      nominal: 147,
      mass: 146.9240548
    }, {
      nominal: 148,
      mass: 147.924282
    }, {
      nominal: 149,
      mass: 148.9232535
    }, {
      nominal: 150,
      mass: 149.9236649
    }, {
      nominal: 151,
      mass: 150.9231096
    }, {
      nominal: 152,
      mass: 151.924083
    }, {
      nominal: 153,
      mass: 152.9234424
    }, {
      nominal: 154,
      mass: 153.924685
    }, {
      nominal: 155,
      mass: 154.923511
    }, {
      nominal: 156,
      mass: 155.9247552
    }, {
      nominal: 157,
      mass: 156.924033
    }, {
      nominal: 158,
      mass: 157.9254209
    }, {
      nominal: 159,
      mass: 158.9253547,
      abundance: 1
    }, {
      nominal: 160,
      mass: 159.9271756
    }, {
      nominal: 161,
      mass: 160.9275778
    }, {
      nominal: 162,
      mass: 161.929495
    }, {
      nominal: 163,
      mass: 162.9306547
    }, {
      nominal: 164,
      mass: 163.93336
    }, {
      nominal: 165,
      mass: 164.93498
    }, {
      nominal: 166,
      mass: 165.93786
    }, {
      nominal: 167,
      mass: 166.93996
    }, {
      nominal: 168,
      mass: 167.9434
    }, {
      nominal: 169,
      mass: 168.94597
    }, {
      nominal: 170,
      mass: 169.94984
    }, {
      nominal: 171,
      mass: 170.95273
    }],
    symbol: 'Tb',
    mass: 158.9253547,
    name: 'Terbium',
    monoisotopicMass: 158.9253547
  }, {
    number: 66,
    isotopes: [{
      nominal: 138,
      mass: 137.9625
    }, {
      nominal: 139,
      mass: 138.95959
    }, {
      nominal: 140,
      mass: 139.95402
    }, {
      nominal: 141,
      mass: 140.95128
    }, {
      nominal: 142,
      mass: 141.94619
    }, {
      nominal: 143,
      mass: 142.943994
    }, {
      nominal: 144,
      mass: 143.9392695
    }, {
      nominal: 145,
      mass: 144.937474
    }, {
      nominal: 146,
      mass: 145.9328445
    }, {
      nominal: 147,
      mass: 146.9310827
    }, {
      nominal: 148,
      mass: 147.927157
    }, {
      nominal: 149,
      mass: 148.927322
    }, {
      nominal: 150,
      mass: 149.9255933
    }, {
      nominal: 151,
      mass: 150.9261916
    }, {
      nominal: 152,
      mass: 151.9247253
    }, {
      nominal: 153,
      mass: 152.9257724
    }, {
      nominal: 154,
      mass: 153.9244293
    }, {
      nominal: 155,
      mass: 154.925759
    }, {
      nominal: 156,
      mass: 155.9242847,
      abundance: 0.00056
    }, {
      nominal: 157,
      mass: 156.9254707
    }, {
      nominal: 158,
      mass: 157.9244159,
      abundance: 0.00095
    }, {
      nominal: 159,
      mass: 158.925747
    }, {
      nominal: 160,
      mass: 159.9252046,
      abundance: 0.02329
    }, {
      nominal: 161,
      mass: 160.9269405,
      abundance: 0.18889
    }, {
      nominal: 162,
      mass: 161.9268056,
      abundance: 0.25475
    }, {
      nominal: 163,
      mass: 162.9287383,
      abundance: 0.24896
    }, {
      nominal: 164,
      mass: 163.9291819,
      abundance: 0.2826
    }, {
      nominal: 165,
      mass: 164.9317105
    }, {
      nominal: 166,
      mass: 165.9328139
    }, {
      nominal: 167,
      mass: 166.935661
    }, {
      nominal: 168,
      mass: 167.93713
    }, {
      nominal: 169,
      mass: 168.94031
    }, {
      nominal: 170,
      mass: 169.94239
    }, {
      nominal: 171,
      mass: 170.94612
    }, {
      nominal: 172,
      mass: 171.94846
    }, {
      nominal: 173,
      mass: 172.95283
    }],
    symbol: 'Dy',
    mass: 162.499472819424,
    name: 'Dysprosium',
    monoisotopicMass: 163.9291819
  }, {
    number: 67,
    isotopes: [{
      nominal: 140,
      mass: 139.96859
    }, {
      nominal: 141,
      mass: 140.96311
    }, {
      nominal: 142,
      mass: 141.96001
    }, {
      nominal: 143,
      mass: 142.95486
    }, {
      nominal: 144,
      mass: 143.9521097
    }, {
      nominal: 145,
      mass: 144.9472674
    }, {
      nominal: 146,
      mass: 145.9449935
    }, {
      nominal: 147,
      mass: 146.9401423
    }, {
      nominal: 148,
      mass: 147.937744
    }, {
      nominal: 149,
      mass: 148.933803
    }, {
      nominal: 150,
      mass: 149.933498
    }, {
      nominal: 151,
      mass: 150.9316983
    }, {
      nominal: 152,
      mass: 151.931724
    }, {
      nominal: 153,
      mass: 152.9302064
    }, {
      nominal: 154,
      mass: 153.9306068
    }, {
      nominal: 155,
      mass: 154.929104
    }, {
      nominal: 156,
      mass: 155.929706
    }, {
      nominal: 157,
      mass: 156.928254
    }, {
      nominal: 158,
      mass: 157.928946
    }, {
      nominal: 159,
      mass: 158.9277197
    }, {
      nominal: 160,
      mass: 159.928737
    }, {
      nominal: 161,
      mass: 160.9278615
    }, {
      nominal: 162,
      mass: 161.9291023
    }, {
      nominal: 163,
      mass: 162.928741
    }, {
      nominal: 164,
      mass: 163.9302403
    }, {
      nominal: 165,
      mass: 164.9303288,
      abundance: 1
    }, {
      nominal: 166,
      mass: 165.9322909
    }, {
      nominal: 167,
      mass: 166.9331385
    }, {
      nominal: 168,
      mass: 167.935522
    }, {
      nominal: 169,
      mass: 168.936878
    }, {
      nominal: 170,
      mass: 169.939625
    }, {
      nominal: 171,
      mass: 170.94147
    }, {
      nominal: 172,
      mass: 171.94473
    }, {
      nominal: 173,
      mass: 172.94702
    }, {
      nominal: 174,
      mass: 173.95095
    }, {
      nominal: 175,
      mass: 174.95362
    }],
    symbol: 'Ho',
    mass: 164.9303288,
    name: 'Holmium',
    monoisotopicMass: 164.9303288
  }, {
    number: 68,
    isotopes: [{
      nominal: 142,
      mass: 141.9701
    }, {
      nominal: 143,
      mass: 142.96662
    }, {
      nominal: 144,
      mass: 143.9607
    }, {
      nominal: 145,
      mass: 144.95805
    }, {
      nominal: 146,
      mass: 145.9524184
    }, {
      nominal: 147,
      mass: 146.949964
    }, {
      nominal: 148,
      mass: 147.944735
    }, {
      nominal: 149,
      mass: 148.942306
    }, {
      nominal: 150,
      mass: 149.937916
    }, {
      nominal: 151,
      mass: 150.937449
    }, {
      nominal: 152,
      mass: 151.935057
    }, {
      nominal: 153,
      mass: 152.93508
    }, {
      nominal: 154,
      mass: 153.9327908
    }, {
      nominal: 155,
      mass: 154.9332159
    }, {
      nominal: 156,
      mass: 155.931067
    }, {
      nominal: 157,
      mass: 156.931949
    }, {
      nominal: 158,
      mass: 157.929893
    }, {
      nominal: 159,
      mass: 158.9306918
    }, {
      nominal: 160,
      mass: 159.929077
    }, {
      nominal: 161,
      mass: 160.9300046
    }, {
      nominal: 162,
      mass: 161.9287884,
      abundance: 0.00139
    }, {
      nominal: 163,
      mass: 162.9300408
    }, {
      nominal: 164,
      mass: 163.9292088,
      abundance: 0.01601
    }, {
      nominal: 165,
      mass: 164.9307345
    }, {
      nominal: 166,
      mass: 165.9302995,
      abundance: 0.33503
    }, {
      nominal: 167,
      mass: 166.9320546,
      abundance: 0.22869
    }, {
      nominal: 168,
      mass: 167.9323767,
      abundance: 0.26978
    }, {
      nominal: 169,
      mass: 168.9345968
    }, {
      nominal: 170,
      mass: 169.9354702,
      abundance: 0.1491
    }, {
      nominal: 171,
      mass: 170.9380357
    }, {
      nominal: 172,
      mass: 171.9393619
    }, {
      nominal: 173,
      mass: 172.9424
    }, {
      nominal: 174,
      mass: 173.94423
    }, {
      nominal: 175,
      mass: 174.94777
    }, {
      nominal: 176,
      mass: 175.94994
    }, {
      nominal: 177,
      mass: 176.95399
    }],
    symbol: 'Er',
    mass: 167.259082649669,
    name: 'Erbium',
    monoisotopicMass: 165.9302995
  }, {
    number: 69,
    isotopes: [{
      nominal: 144,
      mass: 143.97628
    }, {
      nominal: 145,
      mass: 144.97039
    }, {
      nominal: 146,
      mass: 145.96684
    }, {
      nominal: 147,
      mass: 146.9613799
    }, {
      nominal: 148,
      mass: 147.958384
    }, {
      nominal: 149,
      mass: 148.95289
    }, {
      nominal: 150,
      mass: 149.95009
    }, {
      nominal: 151,
      mass: 150.945488
    }, {
      nominal: 152,
      mass: 151.944422
    }, {
      nominal: 153,
      mass: 152.94204
    }, {
      nominal: 154,
      mass: 153.94157
    }, {
      nominal: 155,
      mass: 154.93921
    }, {
      nominal: 156,
      mass: 155.938992
    }, {
      nominal: 157,
      mass: 156.936944
    }, {
      nominal: 158,
      mass: 157.93698
    }, {
      nominal: 159,
      mass: 158.934975
    }, {
      nominal: 160,
      mass: 159.935263
    }, {
      nominal: 161,
      mass: 160.933549
    }, {
      nominal: 162,
      mass: 161.934002
    }, {
      nominal: 163,
      mass: 162.9326592
    }, {
      nominal: 164,
      mass: 163.933544
    }, {
      nominal: 165,
      mass: 164.9324431
    }, {
      nominal: 166,
      mass: 165.933561
    }, {
      nominal: 167,
      mass: 166.9328562
    }, {
      nominal: 168,
      mass: 167.9341774
    }, {
      nominal: 169,
      mass: 168.9342179,
      abundance: 1
    }, {
      nominal: 170,
      mass: 169.935806
    }, {
      nominal: 171,
      mass: 170.9364339
    }, {
      nominal: 172,
      mass: 171.9384055
    }, {
      nominal: 173,
      mass: 172.9396084
    }, {
      nominal: 174,
      mass: 173.942173
    }, {
      nominal: 175,
      mass: 174.943841
    }, {
      nominal: 176,
      mass: 175.947
    }, {
      nominal: 177,
      mass: 176.94904
    }, {
      nominal: 178,
      mass: 177.95264
    }, {
      nominal: 179,
      mass: 178.95534
    }],
    symbol: 'Tm',
    mass: 168.9342179,
    name: 'Thulium',
    monoisotopicMass: 168.9342179
  }, {
    number: 70,
    isotopes: [{
      nominal: 148,
      mass: 147.96758
    }, {
      nominal: 149,
      mass: 148.96436
    }, {
      nominal: 150,
      mass: 149.95852
    }, {
      nominal: 151,
      mass: 150.9554
    }, {
      nominal: 152,
      mass: 151.95027
    }, {
      nominal: 153,
      mass: 152.94932
    }, {
      nominal: 154,
      mass: 153.946396
    }, {
      nominal: 155,
      mass: 154.945783
    }, {
      nominal: 156,
      mass: 155.942825
    }, {
      nominal: 157,
      mass: 156.942645
    }, {
      nominal: 158,
      mass: 157.9398705
    }, {
      nominal: 159,
      mass: 158.940055
    }, {
      nominal: 160,
      mass: 159.937557
    }, {
      nominal: 161,
      mass: 160.937907
    }, {
      nominal: 162,
      mass: 161.935774
    }, {
      nominal: 163,
      mass: 162.93634
    }, {
      nominal: 164,
      mass: 163.934495
    }, {
      nominal: 165,
      mass: 164.93527
    }, {
      nominal: 166,
      mass: 165.9338747
    }, {
      nominal: 167,
      mass: 166.934953
    }, {
      nominal: 168,
      mass: 167.9338896,
      abundance: 0.00123
    }, {
      nominal: 169,
      mass: 168.9351825
    }, {
      nominal: 170,
      mass: 169.9347664,
      abundance: 0.02982
    }, {
      nominal: 171,
      mass: 170.9363302,
      abundance: 0.1409
    }, {
      nominal: 172,
      mass: 171.9363859,
      abundance: 0.2168
    }, {
      nominal: 173,
      mass: 172.9382151,
      abundance: 0.16103
    }, {
      nominal: 174,
      mass: 173.9388664,
      abundance: 0.32026
    }, {
      nominal: 175,
      mass: 174.9412808
    }, {
      nominal: 176,
      mass: 175.9425764,
      abundance: 0.12996
    }, {
      nominal: 177,
      mass: 176.9452656
    }, {
      nominal: 178,
      mass: 177.946651
    }, {
      nominal: 179,
      mass: 178.95004
    }, {
      nominal: 180,
      mass: 179.95212
    }, {
      nominal: 181,
      mass: 180.95589
    }],
    symbol: 'Yb',
    mass: 173.05415016631702,
    name: 'Ytterbium',
    monoisotopicMass: 173.9388664
  }, {
    number: 71,
    isotopes: [{
      nominal: 150,
      mass: 149.97355
    }, {
      nominal: 151,
      mass: 150.96768
    }, {
      nominal: 152,
      mass: 151.96412
    }, {
      nominal: 153,
      mass: 152.95875
    }, {
      nominal: 154,
      mass: 153.95736
    }, {
      nominal: 155,
      mass: 154.954321
    }, {
      nominal: 156,
      mass: 155.953033
    }, {
      nominal: 157,
      mass: 156.950127
    }, {
      nominal: 158,
      mass: 157.949316
    }, {
      nominal: 159,
      mass: 158.946636
    }, {
      nominal: 160,
      mass: 159.946033
    }, {
      nominal: 161,
      mass: 160.943572
    }, {
      nominal: 162,
      mass: 161.943283
    }, {
      nominal: 163,
      mass: 162.941179
    }, {
      nominal: 164,
      mass: 163.941339
    }, {
      nominal: 165,
      mass: 164.939407
    }, {
      nominal: 166,
      mass: 165.939859
    }, {
      nominal: 167,
      mass: 166.93827
    }, {
      nominal: 168,
      mass: 167.938736
    }, {
      nominal: 169,
      mass: 168.9376441
    }, {
      nominal: 170,
      mass: 169.938478
    }, {
      nominal: 171,
      mass: 170.937917
    }, {
      nominal: 172,
      mass: 171.9390891
    }, {
      nominal: 173,
      mass: 172.938934
    }, {
      nominal: 174,
      mass: 173.9403409
    }, {
      nominal: 175,
      mass: 174.9407752,
      abundance: 0.97401
    }, {
      nominal: 176,
      mass: 175.9426897,
      abundance: 0.02599
    }, {
      nominal: 177,
      mass: 176.9437615
    }, {
      nominal: 178,
      mass: 177.945958
    }, {
      nominal: 179,
      mass: 178.9473309
    }, {
      nominal: 180,
      mass: 179.949888
    }, {
      nominal: 181,
      mass: 180.95191
    }, {
      nominal: 182,
      mass: 181.95504
    }, {
      nominal: 183,
      mass: 182.957363
    }, {
      nominal: 184,
      mass: 183.96091
    }, {
      nominal: 185,
      mass: 184.96362
    }],
    symbol: 'Lu',
    mass: 174.96681495785498,
    name: 'Lutetium',
    monoisotopicMass: 174.9407752
  }, {
    number: 72,
    isotopes: [{
      nominal: 153,
      mass: 152.97069
    }, {
      nominal: 154,
      mass: 153.96486
    }, {
      nominal: 155,
      mass: 154.96311
    }, {
      nominal: 156,
      mass: 155.95935
    }, {
      nominal: 157,
      mass: 156.95824
    }, {
      nominal: 158,
      mass: 157.954801
    }, {
      nominal: 159,
      mass: 158.953996
    }, {
      nominal: 160,
      mass: 159.950691
    }, {
      nominal: 161,
      mass: 160.950278
    }, {
      nominal: 162,
      mass: 161.9472148
    }, {
      nominal: 163,
      mass: 162.947113
    }, {
      nominal: 164,
      mass: 163.944371
    }, {
      nominal: 165,
      mass: 164.944567
    }, {
      nominal: 166,
      mass: 165.94218
    }, {
      nominal: 167,
      mass: 166.9426
    }, {
      nominal: 168,
      mass: 167.940568
    }, {
      nominal: 169,
      mass: 168.941259
    }, {
      nominal: 170,
      mass: 169.939609
    }, {
      nominal: 171,
      mass: 170.940492
    }, {
      nominal: 172,
      mass: 171.93945
    }, {
      nominal: 173,
      mass: 172.940513
    }, {
      nominal: 174,
      mass: 173.9400461,
      abundance: 0.0016
    }, {
      nominal: 175,
      mass: 174.9415092
    }, {
      nominal: 176,
      mass: 175.9414076,
      abundance: 0.0526
    }, {
      nominal: 177,
      mass: 176.9432277,
      abundance: 0.186
    }, {
      nominal: 178,
      mass: 177.9437058,
      abundance: 0.2728
    }, {
      nominal: 179,
      mass: 178.9458232,
      abundance: 0.1362
    }, {
      nominal: 180,
      mass: 179.946557,
      abundance: 0.3508
    }, {
      nominal: 181,
      mass: 180.9491083
    }, {
      nominal: 182,
      mass: 181.9505612
    }, {
      nominal: 183,
      mass: 182.95353
    }, {
      nominal: 184,
      mass: 183.955446
    }, {
      nominal: 185,
      mass: 184.958862
    }, {
      nominal: 186,
      mass: 185.960897
    }, {
      nominal: 187,
      mass: 186.96477
    }, {
      nominal: 188,
      mass: 187.96685
    }, {
      nominal: 189,
      mass: 188.97084
    }],
    symbol: 'Hf',
    mass: 178.4849787234,
    name: 'Hafnium',
    monoisotopicMass: 179.946557
  }, {
    number: 73,
    isotopes: [{
      nominal: 155,
      mass: 154.97424
    }, {
      nominal: 156,
      mass: 155.97203
    }, {
      nominal: 157,
      mass: 156.96818
    }, {
      nominal: 158,
      mass: 157.96654
    }, {
      nominal: 159,
      mass: 158.963023
    }, {
      nominal: 160,
      mass: 159.961488
    }, {
      nominal: 161,
      mass: 160.958452
    }, {
      nominal: 162,
      mass: 161.957294
    }, {
      nominal: 163,
      mass: 162.954337
    }, {
      nominal: 164,
      mass: 163.953534
    }, {
      nominal: 165,
      mass: 164.950781
    }, {
      nominal: 166,
      mass: 165.950512
    }, {
      nominal: 167,
      mass: 166.948093
    }, {
      nominal: 168,
      mass: 167.948047
    }, {
      nominal: 169,
      mass: 168.946011
    }, {
      nominal: 170,
      mass: 169.946175
    }, {
      nominal: 171,
      mass: 170.944476
    }, {
      nominal: 172,
      mass: 171.944895
    }, {
      nominal: 173,
      mass: 172.94375
    }, {
      nominal: 174,
      mass: 173.944454
    }, {
      nominal: 175,
      mass: 174.943737
    }, {
      nominal: 176,
      mass: 175.944857
    }, {
      nominal: 177,
      mass: 176.9444795
    }, {
      nominal: 178,
      mass: 177.945678
    }, {
      nominal: 179,
      mass: 178.9459366
    }, {
      nominal: 180,
      mass: 179.9474648,
      abundance: 0.0001201
    }, {
      nominal: 181,
      mass: 180.9479958,
      abundance: 0.9998799
    }, {
      nominal: 182,
      mass: 181.9501519
    }, {
      nominal: 183,
      mass: 182.9513726
    }, {
      nominal: 184,
      mass: 183.954008
    }, {
      nominal: 185,
      mass: 184.955559
    }, {
      nominal: 186,
      mass: 185.958551
    }, {
      nominal: 187,
      mass: 186.960386
    }, {
      nominal: 188,
      mass: 187.963916
    }, {
      nominal: 189,
      mass: 188.96583
    }, {
      nominal: 190,
      mass: 189.96939
    }, {
      nominal: 191,
      mass: 190.97156
    }, {
      nominal: 192,
      mass: 191.97514
    }],
    symbol: 'Ta',
    mass: 180.9478756362269,
    name: 'Tantalum',
    monoisotopicMass: 180.9479958
  }, {
    number: 74,
    isotopes: [{
      nominal: 157,
      mass: 156.97884
    }, {
      nominal: 158,
      mass: 157.97456
    }, {
      nominal: 159,
      mass: 158.97264
    }, {
      nominal: 160,
      mass: 159.96846
    }, {
      nominal: 161,
      mass: 160.9672
    }, {
      nominal: 162,
      mass: 161.963499
    }, {
      nominal: 163,
      mass: 162.962524
    }, {
      nominal: 164,
      mass: 163.958961
    }, {
      nominal: 165,
      mass: 164.958281
    }, {
      nominal: 166,
      mass: 165.955031
    }, {
      nominal: 167,
      mass: 166.954805
    }, {
      nominal: 168,
      mass: 167.951806
    }, {
      nominal: 169,
      mass: 168.951779
    }, {
      nominal: 170,
      mass: 169.949232
    }, {
      nominal: 171,
      mass: 170.949451
    }, {
      nominal: 172,
      mass: 171.947292
    }, {
      nominal: 173,
      mass: 172.947689
    }, {
      nominal: 174,
      mass: 173.946079
    }, {
      nominal: 175,
      mass: 174.946717
    }, {
      nominal: 176,
      mass: 175.945634
    }, {
      nominal: 177,
      mass: 176.946643
    }, {
      nominal: 178,
      mass: 177.945883
    }, {
      nominal: 179,
      mass: 178.947077
    }, {
      nominal: 180,
      mass: 179.9467108,
      abundance: 0.0012
    }, {
      nominal: 181,
      mass: 180.9481978
    }, {
      nominal: 182,
      mass: 181.94820394,
      abundance: 0.265
    }, {
      nominal: 183,
      mass: 182.95022275,
      abundance: 0.1431
    }, {
      nominal: 184,
      mass: 183.95093092,
      abundance: 0.3064
    }, {
      nominal: 185,
      mass: 184.95341897
    }, {
      nominal: 186,
      mass: 185.9543628,
      abundance: 0.2843
    }, {
      nominal: 187,
      mass: 186.9571588
    }, {
      nominal: 188,
      mass: 187.9584862
    }, {
      nominal: 189,
      mass: 188.961763
    }, {
      nominal: 190,
      mass: 189.963091
    }, {
      nominal: 191,
      mass: 190.966531
    }, {
      nominal: 192,
      mass: 191.96817
    }, {
      nominal: 193,
      mass: 192.97178
    }, {
      nominal: 194,
      mass: 193.97367
    }],
    symbol: 'W',
    mass: 183.841777550513,
    name: 'Tungsten',
    monoisotopicMass: 183.95093092
  }, {
    number: 75,
    isotopes: [{
      nominal: 159,
      mass: 158.98418
    }, {
      nominal: 160,
      mass: 159.98182
    }, {
      nominal: 161,
      mass: 160.97757
    }, {
      nominal: 162,
      mass: 161.97584
    }, {
      nominal: 163,
      mass: 162.97208
    }, {
      nominal: 164,
      mass: 163.970453
    }, {
      nominal: 165,
      mass: 164.967103
    }, {
      nominal: 166,
      mass: 165.965761
    }, {
      nominal: 167,
      mass: 166.962595
    }, {
      nominal: 168,
      mass: 167.961573
    }, {
      nominal: 169,
      mass: 168.958766
    }, {
      nominal: 170,
      mass: 169.95822
    }, {
      nominal: 171,
      mass: 170.955716
    }, {
      nominal: 172,
      mass: 171.95542
    }, {
      nominal: 173,
      mass: 172.953243
    }, {
      nominal: 174,
      mass: 173.953115
    }, {
      nominal: 175,
      mass: 174.951381
    }, {
      nominal: 176,
      mass: 175.951623
    }, {
      nominal: 177,
      mass: 176.950328
    }, {
      nominal: 178,
      mass: 177.950989
    }, {
      nominal: 179,
      mass: 178.949989
    }, {
      nominal: 180,
      mass: 179.950792
    }, {
      nominal: 181,
      mass: 180.950058
    }, {
      nominal: 182,
      mass: 181.95121
    }, {
      nominal: 183,
      mass: 182.9508196
    }, {
      nominal: 184,
      mass: 183.9525228
    }, {
      nominal: 185,
      mass: 184.9529545,
      abundance: 0.374
    }, {
      nominal: 186,
      mass: 185.9549856
    }, {
      nominal: 187,
      mass: 186.9557501,
      abundance: 0.626
    }, {
      nominal: 188,
      mass: 187.9581115
    }, {
      nominal: 189,
      mass: 188.959226
    }, {
      nominal: 190,
      mass: 189.961744
    }, {
      nominal: 191,
      mass: 190.963122
    }, {
      nominal: 192,
      mass: 191.966088
    }, {
      nominal: 193,
      mass: 192.967541
    }, {
      nominal: 194,
      mass: 193.97076
    }, {
      nominal: 195,
      mass: 194.97254
    }, {
      nominal: 196,
      mass: 195.9758
    }, {
      nominal: 197,
      mass: 196.97799
    }, {
      nominal: 198,
      mass: 197.9816
    }],
    symbol: 'Re',
    mass: 186.20670454560002,
    name: 'Rhenium',
    monoisotopicMass: 186.9557501
  }, {
    number: 76,
    isotopes: [{
      nominal: 161,
      mass: 160.98903
    }, {
      nominal: 162,
      mass: 161.98443
    }, {
      nominal: 163,
      mass: 162.98241
    }, {
      nominal: 164,
      mass: 163.97802
    }, {
      nominal: 165,
      mass: 164.9766
    }, {
      nominal: 166,
      mass: 165.972692
    }, {
      nominal: 167,
      mass: 166.971549
    }, {
      nominal: 168,
      mass: 167.967808
    }, {
      nominal: 169,
      mass: 168.967018
    }, {
      nominal: 170,
      mass: 169.963578
    }, {
      nominal: 171,
      mass: 170.963174
    }, {
      nominal: 172,
      mass: 171.960017
    }, {
      nominal: 173,
      mass: 172.959808
    }, {
      nominal: 174,
      mass: 173.957064
    }, {
      nominal: 175,
      mass: 174.956945
    }, {
      nominal: 176,
      mass: 175.954806
    }, {
      nominal: 177,
      mass: 176.954966
    }, {
      nominal: 178,
      mass: 177.953254
    }, {
      nominal: 179,
      mass: 178.953817
    }, {
      nominal: 180,
      mass: 179.952375
    }, {
      nominal: 181,
      mass: 180.953247
    }, {
      nominal: 182,
      mass: 181.95211
    }, {
      nominal: 183,
      mass: 182.953125
    }, {
      nominal: 184,
      mass: 183.9524885,
      abundance: 0.0002
    }, {
      nominal: 185,
      mass: 184.9540417
    }, {
      nominal: 186,
      mass: 185.953835,
      abundance: 0.0159
    }, {
      nominal: 187,
      mass: 186.9557474,
      abundance: 0.0196
    }, {
      nominal: 188,
      mass: 187.9558352,
      abundance: 0.1324
    }, {
      nominal: 189,
      mass: 188.9581442,
      abundance: 0.1615
    }, {
      nominal: 190,
      mass: 189.9584437,
      abundance: 0.2626
    }, {
      nominal: 191,
      mass: 190.9609264
    }, {
      nominal: 192,
      mass: 191.961477,
      abundance: 0.4078
    }, {
      nominal: 193,
      mass: 192.9641479
    }, {
      nominal: 194,
      mass: 193.9651772
    }, {
      nominal: 195,
      mass: 194.968318
    }, {
      nominal: 196,
      mass: 195.969641
    }, {
      nominal: 197,
      mass: 196.97283
    }, {
      nominal: 198,
      mass: 197.97441
    }, {
      nominal: 199,
      mass: 198.97801
    }, {
      nominal: 200,
      mass: 199.97984
    }, {
      nominal: 201,
      mass: 200.98364
    }, {
      nominal: 202,
      mass: 201.98595
    }],
    symbol: 'Os',
    mass: 190.22485962823998,
    name: 'Osmium',
    monoisotopicMass: 191.961477
  }, {
    number: 77,
    isotopes: [{
      nominal: 164,
      mass: 163.99191
    }, {
      nominal: 165,
      mass: 164.9875
    }, {
      nominal: 166,
      mass: 165.98566
    }, {
      nominal: 167,
      mass: 166.981666
    }, {
      nominal: 168,
      mass: 167.979907
    }, {
      nominal: 169,
      mass: 168.976298
    }, {
      nominal: 170,
      mass: 169.974922
    }, {
      nominal: 171,
      mass: 170.97164
    }, {
      nominal: 172,
      mass: 171.970607
    }, {
      nominal: 173,
      mass: 172.967506
    }, {
      nominal: 174,
      mass: 173.966861
    }, {
      nominal: 175,
      mass: 174.96415
    }, {
      nominal: 176,
      mass: 175.96365
    }, {
      nominal: 177,
      mass: 176.961301
    }, {
      nominal: 178,
      mass: 177.961082
    }, {
      nominal: 179,
      mass: 178.95912
    }, {
      nominal: 180,
      mass: 179.959229
    }, {
      nominal: 181,
      mass: 180.957625
    }, {
      nominal: 182,
      mass: 181.958076
    }, {
      nominal: 183,
      mass: 182.95684
    }, {
      nominal: 184,
      mass: 183.957476
    }, {
      nominal: 185,
      mass: 184.956698
    }, {
      nominal: 186,
      mass: 185.957944
    }, {
      nominal: 187,
      mass: 186.957542
    }, {
      nominal: 188,
      mass: 187.958828
    }, {
      nominal: 189,
      mass: 188.958715
    }, {
      nominal: 190,
      mass: 189.9605412
    }, {
      nominal: 191,
      mass: 190.9605893,
      abundance: 0.373
    }, {
      nominal: 192,
      mass: 191.9626002
    }, {
      nominal: 193,
      mass: 192.9629216,
      abundance: 0.627
    }, {
      nominal: 194,
      mass: 193.9650735
    }, {
      nominal: 195,
      mass: 194.9659747
    }, {
      nominal: 196,
      mass: 195.968397
    }, {
      nominal: 197,
      mass: 196.969655
    }, {
      nominal: 198,
      mass: 197.97228
    }, {
      nominal: 199,
      mass: 198.973805
    }, {
      nominal: 200,
      mass: 199.9768
    }, {
      nominal: 201,
      mass: 200.97864
    }, {
      nominal: 202,
      mass: 201.98199
    }, {
      nominal: 203,
      mass: 202.98423
    }, {
      nominal: 204,
      mass: 203.9896
    }],
    symbol: 'Ir',
    mass: 192.2160516521,
    name: 'Iridium',
    monoisotopicMass: 192.9629216
  }, {
    number: 78,
    isotopes: [{
      nominal: 166,
      mass: 165.99486
    }, {
      nominal: 167,
      mass: 166.99269
    }, {
      nominal: 168,
      mass: 167.98813
    }, {
      nominal: 169,
      mass: 168.98657
    }, {
      nominal: 170,
      mass: 169.982496
    }, {
      nominal: 171,
      mass: 170.981245
    }, {
      nominal: 172,
      mass: 171.977351
    }, {
      nominal: 173,
      mass: 172.976443
    }, {
      nominal: 174,
      mass: 173.97282
    }, {
      nominal: 175,
      mass: 174.97241
    }, {
      nominal: 176,
      mass: 175.968938
    }, {
      nominal: 177,
      mass: 176.96847
    }, {
      nominal: 178,
      mass: 177.96565
    }, {
      nominal: 179,
      mass: 178.965359
    }, {
      nominal: 180,
      mass: 179.963032
    }, {
      nominal: 181,
      mass: 180.963098
    }, {
      nominal: 182,
      mass: 181.961172
    }, {
      nominal: 183,
      mass: 182.961597
    }, {
      nominal: 184,
      mass: 183.959915
    }, {
      nominal: 185,
      mass: 184.960614
    }, {
      nominal: 186,
      mass: 185.959351
    }, {
      nominal: 187,
      mass: 186.960617
    }, {
      nominal: 188,
      mass: 187.9593889
    }, {
      nominal: 189,
      mass: 188.960831
    }, {
      nominal: 190,
      mass: 189.9599297,
      abundance: 0.00012
    }, {
      nominal: 191,
      mass: 190.9616729
    }, {
      nominal: 192,
      mass: 191.9610387,
      abundance: 0.00782
    }, {
      nominal: 193,
      mass: 192.9629824
    }, {
      nominal: 194,
      mass: 193.9626809,
      abundance: 0.3286
    }, {
      nominal: 195,
      mass: 194.9647917,
      abundance: 0.3378
    }, {
      nominal: 196,
      mass: 195.96495209,
      abundance: 0.2521
    }, {
      nominal: 197,
      mass: 196.96734069
    }, {
      nominal: 198,
      mass: 197.9678949,
      abundance: 0.07356
    }, {
      nominal: 199,
      mass: 198.9705952
    }, {
      nominal: 200,
      mass: 199.971443
    }, {
      nominal: 201,
      mass: 200.974513
    }, {
      nominal: 202,
      mass: 201.975639
    }, {
      nominal: 203,
      mass: 202.97893
    }, {
      nominal: 204,
      mass: 203.98076
    }, {
      nominal: 205,
      mass: 204.98608
    }, {
      nominal: 206,
      mass: 205.98966
    }],
    symbol: 'Pt',
    mass: 195.084456864931,
    name: 'Platinum',
    monoisotopicMass: 194.9647917
  }, {
    number: 79,
    isotopes: [{
      nominal: 169,
      mass: 168.99808
    }, {
      nominal: 170,
      mass: 169.99597
    }, {
      nominal: 171,
      mass: 170.991876
    }, {
      nominal: 172,
      mass: 171.989942
    }, {
      nominal: 173,
      mass: 172.986241
    }, {
      nominal: 174,
      mass: 173.984717
    }, {
      nominal: 175,
      mass: 174.981304
    }, {
      nominal: 176,
      mass: 175.98025
    }, {
      nominal: 177,
      mass: 176.97687
    }, {
      nominal: 178,
      mass: 177.976032
    }, {
      nominal: 179,
      mass: 178.973174
    }, {
      nominal: 180,
      mass: 179.972523
    }, {
      nominal: 181,
      mass: 180.970079
    }, {
      nominal: 182,
      mass: 181.969618
    }, {
      nominal: 183,
      mass: 182.967591
    }, {
      nominal: 184,
      mass: 183.967452
    }, {
      nominal: 185,
      mass: 184.96579
    }, {
      nominal: 186,
      mass: 185.965953
    }, {
      nominal: 187,
      mass: 186.964543
    }, {
      nominal: 188,
      mass: 187.965349
    }, {
      nominal: 189,
      mass: 188.963948
    }, {
      nominal: 190,
      mass: 189.964698
    }, {
      nominal: 191,
      mass: 190.963702
    }, {
      nominal: 192,
      mass: 191.964814
    }, {
      nominal: 193,
      mass: 192.9641373
    }, {
      nominal: 194,
      mass: 193.9654178
    }, {
      nominal: 195,
      mass: 194.9650352
    }, {
      nominal: 196,
      mass: 195.9665699
    }, {
      nominal: 197,
      mass: 196.96656879,
      abundance: 1
    }, {
      nominal: 198,
      mass: 197.96824242
    }, {
      nominal: 199,
      mass: 198.96876528
    }, {
      nominal: 200,
      mass: 199.970756
    }, {
      nominal: 201,
      mass: 200.9716575
    }, {
      nominal: 202,
      mass: 201.973856
    }, {
      nominal: 203,
      mass: 202.9751544
    }, {
      nominal: 204,
      mass: 203.97783
    }, {
      nominal: 205,
      mass: 204.97985
    }, {
      nominal: 206,
      mass: 205.98474
    }, {
      nominal: 207,
      mass: 206.9884
    }, {
      nominal: 208,
      mass: 207.99345
    }, {
      nominal: 209,
      mass: 208.99735
    }, {
      nominal: 210,
      mass: 210.0025
    }],
    symbol: 'Au',
    mass: 196.96656879,
    name: 'Gold',
    monoisotopicMass: 196.96656879
  }, {
    number: 80,
    isotopes: [{
      nominal: 171,
      mass: 171.00353
    }, {
      nominal: 172,
      mass: 171.99881
    }, {
      nominal: 173,
      mass: 172.99709
    }, {
      nominal: 174,
      mass: 173.992865
    }, {
      nominal: 175,
      mass: 174.991441
    }, {
      nominal: 176,
      mass: 175.987361
    }, {
      nominal: 177,
      mass: 176.986277
    }, {
      nominal: 178,
      mass: 177.982484
    }, {
      nominal: 179,
      mass: 178.981831
    }, {
      nominal: 180,
      mass: 179.97826
    }, {
      nominal: 181,
      mass: 180.977819
    }, {
      nominal: 182,
      mass: 181.974689
    }, {
      nominal: 183,
      mass: 182.9744448
    }, {
      nominal: 184,
      mass: 183.971714
    }, {
      nominal: 185,
      mass: 184.971899
    }, {
      nominal: 186,
      mass: 185.969362
    }, {
      nominal: 187,
      mass: 186.969814
    }, {
      nominal: 188,
      mass: 187.967567
    }, {
      nominal: 189,
      mass: 188.968195
    }, {
      nominal: 190,
      mass: 189.966323
    }, {
      nominal: 191,
      mass: 190.967157
    }, {
      nominal: 192,
      mass: 191.965635
    }, {
      nominal: 193,
      mass: 192.966653
    }, {
      nominal: 194,
      mass: 193.9654491
    }, {
      nominal: 195,
      mass: 194.966721
    }, {
      nominal: 196,
      mass: 195.9658326,
      abundance: 0.0015
    }, {
      nominal: 197,
      mass: 196.9672128
    }, {
      nominal: 198,
      mass: 197.9667686,
      abundance: 0.0997
    }, {
      nominal: 199,
      mass: 198.96828064,
      abundance: 0.1687
    }, {
      nominal: 200,
      mass: 199.96832659,
      abundance: 0.231
    }, {
      nominal: 201,
      mass: 200.97030284,
      abundance: 0.1318
    }, {
      nominal: 202,
      mass: 201.9706434,
      abundance: 0.2986
    }, {
      nominal: 203,
      mass: 202.9728728
    }, {
      nominal: 204,
      mass: 203.97349398,
      abundance: 0.0687
    }, {
      nominal: 205,
      mass: 204.9760734
    }, {
      nominal: 206,
      mass: 205.977514
    }, {
      nominal: 207,
      mass: 206.9823
    }, {
      nominal: 208,
      mass: 207.985759
    }, {
      nominal: 209,
      mass: 208.99072
    }, {
      nominal: 210,
      mass: 209.99424
    }, {
      nominal: 211,
      mass: 210.99933
    }, {
      nominal: 212,
      mass: 212.00296
    }, {
      nominal: 213,
      mass: 213.00823
    }, {
      nominal: 214,
      mass: 214.012
    }, {
      nominal: 215,
      mass: 215.0174
    }, {
      nominal: 216,
      mass: 216.02132
    }],
    symbol: 'Hg',
    mass: 200.59916703455602,
    name: 'Mercury',
    monoisotopicMass: 201.9706434
  }, {
    number: 81,
    isotopes: [{
      nominal: 176,
      mass: 176.000624
    }, {
      nominal: 177,
      mass: 176.996431
    }, {
      nominal: 178,
      mass: 177.99485
    }, {
      nominal: 179,
      mass: 178.991111
    }, {
      nominal: 180,
      mass: 179.990057
    }, {
      nominal: 181,
      mass: 180.98626
    }, {
      nominal: 182,
      mass: 181.985713
    }, {
      nominal: 183,
      mass: 182.982193
    }, {
      nominal: 184,
      mass: 183.981886
    }, {
      nominal: 185,
      mass: 184.978789
    }, {
      nominal: 186,
      mass: 185.978651
    }, {
      nominal: 187,
      mass: 186.9759063
    }, {
      nominal: 188,
      mass: 187.976021
    }, {
      nominal: 189,
      mass: 188.973588
    }, {
      nominal: 190,
      mass: 189.973828
    }, {
      nominal: 191,
      mass: 190.9717842
    }, {
      nominal: 192,
      mass: 191.972225
    }, {
      nominal: 193,
      mass: 192.970502
    }, {
      nominal: 194,
      mass: 193.971081
    }, {
      nominal: 195,
      mass: 194.969774
    }, {
      nominal: 196,
      mass: 195.970481
    }, {
      nominal: 197,
      mass: 196.969576
    }, {
      nominal: 198,
      mass: 197.970483
    }, {
      nominal: 199,
      mass: 198.969877
    }, {
      nominal: 200,
      mass: 199.9709633
    }, {
      nominal: 201,
      mass: 200.970822
    }, {
      nominal: 202,
      mass: 201.972102
    }, {
      nominal: 203,
      mass: 202.9723446,
      abundance: 0.2952
    }, {
      nominal: 204,
      mass: 203.9738639
    }, {
      nominal: 205,
      mass: 204.9744278,
      abundance: 0.7048
    }, {
      nominal: 206,
      mass: 205.9761106
    }, {
      nominal: 207,
      mass: 206.9774197
    }, {
      nominal: 208,
      mass: 207.982019
    }, {
      nominal: 209,
      mass: 208.9853594
    }, {
      nominal: 210,
      mass: 209.990074
    }, {
      nominal: 211,
      mass: 210.993475
    }, {
      nominal: 212,
      mass: 211.99834
    }, {
      nominal: 213,
      mass: 213.001915
    }, {
      nominal: 214,
      mass: 214.00694
    }, {
      nominal: 215,
      mass: 215.01064
    }, {
      nominal: 216,
      mass: 216.0158
    }, {
      nominal: 217,
      mass: 217.01966
    }, {
      nominal: 218,
      mass: 218.02479
    }],
    symbol: 'Tl',
    mass: 204.38341283936,
    name: 'Thallium',
    monoisotopicMass: 204.9744278
  }, {
    number: 82,
    isotopes: [{
      nominal: 178,
      mass: 178.003831
    }, {
      nominal: 179,
      mass: 179.002201
    }, {
      nominal: 180,
      mass: 179.997928
    }, {
      nominal: 181,
      mass: 180.996653
    }, {
      nominal: 182,
      mass: 181.992672
    }, {
      nominal: 183,
      mass: 182.991872
    }, {
      nominal: 184,
      mass: 183.988136
    }, {
      nominal: 185,
      mass: 184.98761
    }, {
      nominal: 186,
      mass: 185.984238
    }, {
      nominal: 187,
      mass: 186.9839109
    }, {
      nominal: 188,
      mass: 187.980875
    }, {
      nominal: 189,
      mass: 188.980807
    }, {
      nominal: 190,
      mass: 189.978082
    }, {
      nominal: 191,
      mass: 190.978276
    }, {
      nominal: 192,
      mass: 191.975775
    }, {
      nominal: 193,
      mass: 192.976173
    }, {
      nominal: 194,
      mass: 193.974012
    }, {
      nominal: 195,
      mass: 194.974543
    }, {
      nominal: 196,
      mass: 195.972774
    }, {
      nominal: 197,
      mass: 196.9734312
    }, {
      nominal: 198,
      mass: 197.972034
    }, {
      nominal: 199,
      mass: 198.972913
    }, {
      nominal: 200,
      mass: 199.971819
    }, {
      nominal: 201,
      mass: 200.972883
    }, {
      nominal: 202,
      mass: 201.972152
    }, {
      nominal: 203,
      mass: 202.9733911
    }, {
      nominal: 204,
      mass: 203.973044,
      abundance: 0.014
    }, {
      nominal: 205,
      mass: 204.9744822
    }, {
      nominal: 206,
      mass: 205.9744657,
      abundance: 0.241
    }, {
      nominal: 207,
      mass: 206.9758973,
      abundance: 0.221
    }, {
      nominal: 208,
      mass: 207.9766525,
      abundance: 0.524
    }, {
      nominal: 209,
      mass: 208.9810905
    }, {
      nominal: 210,
      mass: 209.9841889
    }, {
      nominal: 211,
      mass: 210.9887371
    }, {
      nominal: 212,
      mass: 211.9918977
    }, {
      nominal: 213,
      mass: 212.9965629
    }, {
      nominal: 214,
      mass: 213.9998059
    }, {
      nominal: 215,
      mass: 215.00474
    }, {
      nominal: 216,
      mass: 216.00803
    }, {
      nominal: 217,
      mass: 217.01314
    }, {
      nominal: 218,
      mass: 218.01659
    }, {
      nominal: 219,
      mass: 219.02177
    }, {
      nominal: 220,
      mass: 220.02541
    }],
    symbol: 'Pb',
    mass: 207.216908063,
    name: 'Lead',
    monoisotopicMass: 207.9766525
  }, {
    number: 83,
    isotopes: [{
      nominal: 184,
      mass: 184.001275
    }, {
      nominal: 185,
      mass: 184.9976
    }, {
      nominal: 186,
      mass: 185.996644
    }, {
      nominal: 187,
      mass: 186.993147
    }, {
      nominal: 188,
      mass: 187.992287
    }, {
      nominal: 189,
      mass: 188.989195
    }, {
      nominal: 190,
      mass: 189.988622
    }, {
      nominal: 191,
      mass: 190.9857866
    }, {
      nominal: 192,
      mass: 191.985469
    }, {
      nominal: 193,
      mass: 192.98296
    }, {
      nominal: 194,
      mass: 193.982785
    }, {
      nominal: 195,
      mass: 194.9806488
    }, {
      nominal: 196,
      mass: 195.980667
    }, {
      nominal: 197,
      mass: 196.9788651
    }, {
      nominal: 198,
      mass: 197.979206
    }, {
      nominal: 199,
      mass: 198.977673
    }, {
      nominal: 200,
      mass: 199.978131
    }, {
      nominal: 201,
      mass: 200.97701
    }, {
      nominal: 202,
      mass: 201.977734
    }, {
      nominal: 203,
      mass: 202.976893
    }, {
      nominal: 204,
      mass: 203.9778361
    }, {
      nominal: 205,
      mass: 204.9773867
    }, {
      nominal: 206,
      mass: 205.9784993
    }, {
      nominal: 207,
      mass: 206.978471
    }, {
      nominal: 208,
      mass: 207.9797425
    }, {
      nominal: 209,
      mass: 208.9803991,
      abundance: 1
    }, {
      nominal: 210,
      mass: 209.9841207
    }, {
      nominal: 211,
      mass: 210.9872697
    }, {
      nominal: 212,
      mass: 211.991286
    }, {
      nominal: 213,
      mass: 212.9943851
    }, {
      nominal: 214,
      mass: 213.998712
    }, {
      nominal: 215,
      mass: 215.00177
    }, {
      nominal: 216,
      mass: 216.006306
    }, {
      nominal: 217,
      mass: 217.009372
    }, {
      nominal: 218,
      mass: 218.014188
    }, {
      nominal: 219,
      mass: 219.01748
    }, {
      nominal: 220,
      mass: 220.02235
    }, {
      nominal: 221,
      mass: 221.02587
    }, {
      nominal: 222,
      mass: 222.03078
    }, {
      nominal: 223,
      mass: 223.0345
    }, {
      nominal: 224,
      mass: 224.03947
    }],
    symbol: 'Bi',
    mass: 208.9803991,
    name: 'Bismuth',
    monoisotopicMass: 208.9803991
  }, {
    number: 84,
    isotopes: [{
      nominal: 186,
      mass: 186.004393
    }, {
      nominal: 187,
      mass: 187.003041
    }, {
      nominal: 188,
      mass: 187.999416
    }, {
      nominal: 189,
      mass: 188.998473
    }, {
      nominal: 190,
      mass: 189.995101
    }, {
      nominal: 191,
      mass: 190.9945585
    }, {
      nominal: 192,
      mass: 191.991336
    }, {
      nominal: 193,
      mass: 192.991026
    }, {
      nominal: 194,
      mass: 193.988186
    }, {
      nominal: 195,
      mass: 194.988126
    }, {
      nominal: 196,
      mass: 195.985526
    }, {
      nominal: 197,
      mass: 196.98566
    }, {
      nominal: 198,
      mass: 197.983389
    }, {
      nominal: 199,
      mass: 198.983667
    }, {
      nominal: 200,
      mass: 199.981799
    }, {
      nominal: 201,
      mass: 200.9822598
    }, {
      nominal: 202,
      mass: 201.980758
    }, {
      nominal: 203,
      mass: 202.9814161
    }, {
      nominal: 204,
      mass: 203.98031
    }, {
      nominal: 205,
      mass: 204.981203
    }, {
      nominal: 206,
      mass: 205.980474
    }, {
      nominal: 207,
      mass: 206.9815938
    }, {
      nominal: 208,
      mass: 207.9812461
    }, {
      nominal: 209,
      mass: 208.9824308
    }, {
      nominal: 210,
      mass: 209.9828741
    }, {
      nominal: 211,
      mass: 210.9866536
    }, {
      nominal: 212,
      mass: 211.9888684
    }, {
      nominal: 213,
      mass: 212.9928576
    }, {
      nominal: 214,
      mass: 213.9952017
    }, {
      nominal: 215,
      mass: 214.9994201
    }, {
      nominal: 216,
      mass: 216.0019152
    }, {
      nominal: 217,
      mass: 217.0063182
    }, {
      nominal: 218,
      mass: 218.0089735
    }, {
      nominal: 219,
      mass: 219.013614
    }, {
      nominal: 220,
      mass: 220.016386
    }, {
      nominal: 221,
      mass: 221.021228
    }, {
      nominal: 222,
      mass: 222.02414
    }, {
      nominal: 223,
      mass: 223.02907
    }, {
      nominal: 224,
      mass: 224.03211
    }, {
      nominal: 225,
      mass: 225.03707
    }, {
      nominal: 226,
      mass: 226.04031
    }, {
      nominal: 227,
      mass: 227.04539
    }],
    symbol: 'Po',
    mass: null,
    name: 'Polonium'
  }, {
    number: 85,
    isotopes: [{
      nominal: 191,
      mass: 191.004148
    }, {
      nominal: 192,
      mass: 192.003152
    }, {
      nominal: 193,
      mass: 192.999927
    }, {
      nominal: 194,
      mass: 193.999236
    }, {
      nominal: 195,
      mass: 194.9962685
    }, {
      nominal: 196,
      mass: 195.9958
    }, {
      nominal: 197,
      mass: 196.993189
    }, {
      nominal: 198,
      mass: 197.992784
    }, {
      nominal: 199,
      mass: 198.9905277
    }, {
      nominal: 200,
      mass: 199.990351
    }, {
      nominal: 201,
      mass: 200.9884171
    }, {
      nominal: 202,
      mass: 201.98863
    }, {
      nominal: 203,
      mass: 202.986943
    }, {
      nominal: 204,
      mass: 203.987251
    }, {
      nominal: 205,
      mass: 204.986076
    }, {
      nominal: 206,
      mass: 205.986657
    }, {
      nominal: 207,
      mass: 206.9858
    }, {
      nominal: 208,
      mass: 207.9866133
    }, {
      nominal: 209,
      mass: 208.9861702
    }, {
      nominal: 210,
      mass: 209.9871479
    }, {
      nominal: 211,
      mass: 210.9874966
    }, {
      nominal: 212,
      mass: 211.9907377
    }, {
      nominal: 213,
      mass: 212.992937
    }, {
      nominal: 214,
      mass: 213.9963721
    }, {
      nominal: 215,
      mass: 214.9986528
    }, {
      nominal: 216,
      mass: 216.0024236
    }, {
      nominal: 217,
      mass: 217.0047192
    }, {
      nominal: 218,
      mass: 218.008695
    }, {
      nominal: 219,
      mass: 219.0111618
    }, {
      nominal: 220,
      mass: 220.015433
    }, {
      nominal: 221,
      mass: 221.018017
    }, {
      nominal: 222,
      mass: 222.022494
    }, {
      nominal: 223,
      mass: 223.025151
    }, {
      nominal: 224,
      mass: 224.029749
    }, {
      nominal: 225,
      mass: 225.03263
    }, {
      nominal: 226,
      mass: 226.03716
    }, {
      nominal: 227,
      mass: 227.04024
    }, {
      nominal: 228,
      mass: 228.04475
    }, {
      nominal: 229,
      mass: 229.04812
    }],
    symbol: 'At',
    mass: null,
    name: 'Astatine'
  }, {
    number: 86,
    isotopes: [{
      nominal: 193,
      mass: 193.009708
    }, {
      nominal: 194,
      mass: 194.006144
    }, {
      nominal: 195,
      mass: 195.005422
    }, {
      nominal: 196,
      mass: 196.002116
    }, {
      nominal: 197,
      mass: 197.001585
    }, {
      nominal: 198,
      mass: 197.998679
    }, {
      nominal: 199,
      mass: 198.99839
    }, {
      nominal: 200,
      mass: 199.99569
    }, {
      nominal: 201,
      mass: 200.995628
    }, {
      nominal: 202,
      mass: 201.993264
    }, {
      nominal: 203,
      mass: 202.993388
    }, {
      nominal: 204,
      mass: 203.99143
    }, {
      nominal: 205,
      mass: 204.991719
    }, {
      nominal: 206,
      mass: 205.990214
    }, {
      nominal: 207,
      mass: 206.9907303
    }, {
      nominal: 208,
      mass: 207.989635
    }, {
      nominal: 209,
      mass: 208.990415
    }, {
      nominal: 210,
      mass: 209.9896891
    }, {
      nominal: 211,
      mass: 210.9906011
    }, {
      nominal: 212,
      mass: 211.9907039
    }, {
      nominal: 213,
      mass: 212.9938831
    }, {
      nominal: 214,
      mass: 213.995363
    }, {
      nominal: 215,
      mass: 214.9987459
    }, {
      nominal: 216,
      mass: 216.0002719
    }, {
      nominal: 217,
      mass: 217.003928
    }, {
      nominal: 218,
      mass: 218.0056016
    }, {
      nominal: 219,
      mass: 219.0094804
    }, {
      nominal: 220,
      mass: 220.0113941
    }, {
      nominal: 221,
      mass: 221.0155371
    }, {
      nominal: 222,
      mass: 222.0175782
    }, {
      nominal: 223,
      mass: 223.0218893
    }, {
      nominal: 224,
      mass: 224.024096
    }, {
      nominal: 225,
      mass: 225.028486
    }, {
      nominal: 226,
      mass: 226.030861
    }, {
      nominal: 227,
      mass: 227.035304
    }, {
      nominal: 228,
      mass: 228.037835
    }, {
      nominal: 229,
      mass: 229.042257
    }, {
      nominal: 230,
      mass: 230.04514
    }, {
      nominal: 231,
      mass: 231.04987
    }],
    symbol: 'Rn',
    mass: null,
    name: 'Radon'
  }, {
    number: 87,
    isotopes: [{
      nominal: 199,
      mass: 199.007259
    }, {
      nominal: 200,
      mass: 200.006586
    }, {
      nominal: 201,
      mass: 201.003867
    }, {
      nominal: 202,
      mass: 202.00332
    }, {
      nominal: 203,
      mass: 203.0009407
    }, {
      nominal: 204,
      mass: 204.000652
    }, {
      nominal: 205,
      mass: 204.9985939
    }, {
      nominal: 206,
      mass: 205.998666
    }, {
      nominal: 207,
      mass: 206.996946
    }, {
      nominal: 208,
      mass: 207.997138
    }, {
      nominal: 209,
      mass: 208.995955
    }, {
      nominal: 210,
      mass: 209.996422
    }, {
      nominal: 211,
      mass: 210.995556
    }, {
      nominal: 212,
      mass: 211.9962257
    }, {
      nominal: 213,
      mass: 212.996186
    }, {
      nominal: 214,
      mass: 213.9989713
    }, {
      nominal: 215,
      mass: 215.0003418
    }, {
      nominal: 216,
      mass: 216.0031899
    }, {
      nominal: 217,
      mass: 217.0046323
    }, {
      nominal: 218,
      mass: 218.0075787
    }, {
      nominal: 219,
      mass: 219.0092524
    }, {
      nominal: 220,
      mass: 220.0123277
    }, {
      nominal: 221,
      mass: 221.0142552
    }, {
      nominal: 222,
      mass: 222.017552
    }, {
      nominal: 223,
      mass: 223.019736
    }, {
      nominal: 224,
      mass: 224.023398
    }, {
      nominal: 225,
      mass: 225.025573
    }, {
      nominal: 226,
      mass: 226.029566
    }, {
      nominal: 227,
      mass: 227.031869
    }, {
      nominal: 228,
      mass: 228.035823
    }, {
      nominal: 229,
      mass: 229.038298
    }, {
      nominal: 230,
      mass: 230.042416
    }, {
      nominal: 231,
      mass: 231.045158
    }, {
      nominal: 232,
      mass: 232.04937
    }, {
      nominal: 233,
      mass: 233.05264
    }],
    symbol: 'Fr',
    mass: null,
    name: 'Francium'
  }, {
    number: 88,
    isotopes: [{
      nominal: 201,
      mass: 201.01271
    }, {
      nominal: 202,
      mass: 202.00976
    }, {
      nominal: 203,
      mass: 203.009304
    }, {
      nominal: 204,
      mass: 204.006492
    }, {
      nominal: 205,
      mass: 205.006268
    }, {
      nominal: 206,
      mass: 206.003828
    }, {
      nominal: 207,
      mass: 207.003799
    }, {
      nominal: 208,
      mass: 208.001841
    }, {
      nominal: 209,
      mass: 209.00199
    }, {
      nominal: 210,
      mass: 210.000494
    }, {
      nominal: 211,
      mass: 211.0008932
    }, {
      nominal: 212,
      mass: 211.999787
    }, {
      nominal: 213,
      mass: 213.000384
    }, {
      nominal: 214,
      mass: 214.0000997
    }, {
      nominal: 215,
      mass: 215.0027204
    }, {
      nominal: 216,
      mass: 216.0035334
    }, {
      nominal: 217,
      mass: 217.0063207
    }, {
      nominal: 218,
      mass: 218.007141
    }, {
      nominal: 219,
      mass: 219.0100855
    }, {
      nominal: 220,
      mass: 220.0110259
    }, {
      nominal: 221,
      mass: 221.0139177
    }, {
      nominal: 222,
      mass: 222.0153748
    }, {
      nominal: 223,
      mass: 223.0185023
    }, {
      nominal: 224,
      mass: 224.020212
    }, {
      nominal: 225,
      mass: 225.0236119
    }, {
      nominal: 226,
      mass: 226.0254103
    }, {
      nominal: 227,
      mass: 227.0291783
    }, {
      nominal: 228,
      mass: 228.0310707
    }, {
      nominal: 229,
      mass: 229.034942
    }, {
      nominal: 230,
      mass: 230.037055
    }, {
      nominal: 231,
      mass: 231.041027
    }, {
      nominal: 232,
      mass: 232.0434753
    }, {
      nominal: 233,
      mass: 233.047582
    }, {
      nominal: 234,
      mass: 234.050342
    }, {
      nominal: 235,
      mass: 235.05497
    }],
    symbol: 'Ra',
    mass: null,
    name: 'Radium'
  }, {
    number: 89,
    isotopes: [{
      nominal: 206,
      mass: 206.014452
    }, {
      nominal: 207,
      mass: 207.011966
    }, {
      nominal: 208,
      mass: 208.01155
    }, {
      nominal: 209,
      mass: 209.009495
    }, {
      nominal: 210,
      mass: 210.009436
    }, {
      nominal: 211,
      mass: 211.007732
    }, {
      nominal: 212,
      mass: 212.007813
    }, {
      nominal: 213,
      mass: 213.006609
    }, {
      nominal: 214,
      mass: 214.006918
    }, {
      nominal: 215,
      mass: 215.006475
    }, {
      nominal: 216,
      mass: 216.008743
    }, {
      nominal: 217,
      mass: 217.009344
    }, {
      nominal: 218,
      mass: 218.011642
    }, {
      nominal: 219,
      mass: 219.012421
    }, {
      nominal: 220,
      mass: 220.0147549
    }, {
      nominal: 221,
      mass: 221.015592
    }, {
      nominal: 222,
      mass: 222.0178442
    }, {
      nominal: 223,
      mass: 223.0191377
    }, {
      nominal: 224,
      mass: 224.0217232
    }, {
      nominal: 225,
      mass: 225.02323
    }, {
      nominal: 226,
      mass: 226.0260984
    }, {
      nominal: 227,
      mass: 227.0277523
    }, {
      nominal: 228,
      mass: 228.0310215
    }, {
      nominal: 229,
      mass: 229.032956
    }, {
      nominal: 230,
      mass: 230.036327
    }, {
      nominal: 231,
      mass: 231.038393
    }, {
      nominal: 232,
      mass: 232.042034
    }, {
      nominal: 233,
      mass: 233.044346
    }, {
      nominal: 234,
      mass: 234.048139
    }, {
      nominal: 235,
      mass: 235.05084
    }, {
      nominal: 236,
      mass: 236.054988
    }, {
      nominal: 237,
      mass: 237.05827
    }],
    symbol: 'Ac',
    mass: null,
    name: 'Actinium'
  }, {
    number: 90,
    isotopes: [{
      nominal: 208,
      mass: 208.0179
    }, {
      nominal: 209,
      mass: 209.017753
    }, {
      nominal: 210,
      mass: 210.015094
    }, {
      nominal: 211,
      mass: 211.014929
    }, {
      nominal: 212,
      mass: 212.012988
    }, {
      nominal: 213,
      mass: 213.013009
    }, {
      nominal: 214,
      mass: 214.0115
    }, {
      nominal: 215,
      mass: 215.0117248
    }, {
      nominal: 216,
      mass: 216.011056
    }, {
      nominal: 217,
      mass: 217.013117
    }, {
      nominal: 218,
      mass: 218.013276
    }, {
      nominal: 219,
      mass: 219.015537
    }, {
      nominal: 220,
      mass: 220.015748
    }, {
      nominal: 221,
      mass: 221.018184
    }, {
      nominal: 222,
      mass: 222.018469
    }, {
      nominal: 223,
      mass: 223.0208119
    }, {
      nominal: 224,
      mass: 224.021464
    }, {
      nominal: 225,
      mass: 225.0239514
    }, {
      nominal: 226,
      mass: 226.0249034
    }, {
      nominal: 227,
      mass: 227.0277042
    }, {
      nominal: 228,
      mass: 228.0287413
    }, {
      nominal: 229,
      mass: 229.0317627
    }, {
      nominal: 230,
      mass: 230.0331341
    }, {
      nominal: 231,
      mass: 231.0363046
    }, {
      nominal: 232,
      mass: 232.0380558,
      abundance: 1
    }, {
      nominal: 233,
      mass: 233.0415823
    }, {
      nominal: 234,
      mass: 234.0436014
    }, {
      nominal: 235,
      mass: 235.047255
    }, {
      nominal: 236,
      mass: 236.049657
    }, {
      nominal: 237,
      mass: 237.053629
    }, {
      nominal: 238,
      mass: 238.0565
    }, {
      nominal: 239,
      mass: 239.06077
    }],
    symbol: 'Th',
    mass: 232.0380558,
    name: 'Thorium',
    monoisotopicMass: 232.0380558
  }, {
    number: 91,
    isotopes: [{
      nominal: 212,
      mass: 212.023203
    }, {
      nominal: 213,
      mass: 213.021109
    }, {
      nominal: 214,
      mass: 214.020918
    }, {
      nominal: 215,
      mass: 215.019183
    }, {
      nominal: 216,
      mass: 216.019109
    }, {
      nominal: 217,
      mass: 217.018325
    }, {
      nominal: 218,
      mass: 218.020059
    }, {
      nominal: 219,
      mass: 219.019904
    }, {
      nominal: 220,
      mass: 220.021705
    }, {
      nominal: 221,
      mass: 221.021875
    }, {
      nominal: 222,
      mass: 222.023784
    }, {
      nominal: 223,
      mass: 223.023963
    }, {
      nominal: 224,
      mass: 224.0256176
    }, {
      nominal: 225,
      mass: 225.026131
    }, {
      nominal: 226,
      mass: 226.027948
    }, {
      nominal: 227,
      mass: 227.0288054
    }, {
      nominal: 228,
      mass: 228.0310517
    }, {
      nominal: 229,
      mass: 229.0320972
    }, {
      nominal: 230,
      mass: 230.034541
    }, {
      nominal: 231,
      mass: 231.0358842,
      abundance: 1
    }, {
      nominal: 232,
      mass: 232.0385917
    }, {
      nominal: 233,
      mass: 233.0402472
    }, {
      nominal: 234,
      mass: 234.0433072
    }, {
      nominal: 235,
      mass: 235.045399
    }, {
      nominal: 236,
      mass: 236.048668
    }, {
      nominal: 237,
      mass: 237.051023
    }, {
      nominal: 238,
      mass: 238.054637
    }, {
      nominal: 239,
      mass: 239.05726
    }, {
      nominal: 240,
      mass: 240.06098
    }, {
      nominal: 241,
      mass: 241.06408
    }],
    symbol: 'Pa',
    mass: 231.0358842,
    name: 'Protactinium',
    monoisotopicMass: 231.0358842
  }, {
    number: 92,
    isotopes: [{
      nominal: 217,
      mass: 217.02466
    }, {
      nominal: 218,
      mass: 218.023523
    }, {
      nominal: 219,
      mass: 219.024999
    }, {
      nominal: 220,
      mass: 220.02462
    }, {
      nominal: 221,
      mass: 221.02628
    }, {
      nominal: 222,
      mass: 222.026
    }, {
      nominal: 223,
      mass: 223.027739
    }, {
      nominal: 224,
      mass: 224.027605
    }, {
      nominal: 225,
      mass: 225.029391
    }, {
      nominal: 226,
      mass: 226.029339
    }, {
      nominal: 227,
      mass: 227.031157
    }, {
      nominal: 228,
      mass: 228.031371
    }, {
      nominal: 229,
      mass: 229.0335063
    }, {
      nominal: 230,
      mass: 230.0339401
    }, {
      nominal: 231,
      mass: 231.0362939
    }, {
      nominal: 232,
      mass: 232.0371563
    }, {
      nominal: 233,
      mass: 233.0396355
    }, {
      nominal: 234,
      mass: 234.0409523,
      abundance: 0.000054
    }, {
      nominal: 235,
      mass: 235.0439301,
      abundance: 0.007204
    }, {
      nominal: 236,
      mass: 236.0455682
    }, {
      nominal: 237,
      mass: 237.0487304
    }, {
      nominal: 238,
      mass: 238.0507884,
      abundance: 0.992742
    }, {
      nominal: 239,
      mass: 239.0542935
    }, {
      nominal: 240,
      mass: 240.0565934
    }, {
      nominal: 241,
      mass: 241.06033
    }, {
      nominal: 242,
      mass: 242.06293
    }, {
      nominal: 243,
      mass: 243.06699
    }],
    symbol: 'U',
    mass: 238.0289104616574,
    name: 'Uranium',
    monoisotopicMass: 238.0507884
  }, {
    number: 93,
    isotopes: [{
      nominal: 219,
      mass: 219.03143
    }, {
      nominal: 220,
      mass: 220.03254
    }, {
      nominal: 221,
      mass: 221.03204
    }, {
      nominal: 222,
      mass: 222.0333
    }, {
      nominal: 223,
      mass: 223.03285
    }, {
      nominal: 224,
      mass: 224.03422
    }, {
      nominal: 225,
      mass: 225.033911
    }, {
      nominal: 226,
      mass: 226.035188
    }, {
      nominal: 227,
      mass: 227.034957
    }, {
      nominal: 228,
      mass: 228.036067
    }, {
      nominal: 229,
      mass: 229.036264
    }, {
      nominal: 230,
      mass: 230.037828
    }, {
      nominal: 231,
      mass: 231.038245
    }, {
      nominal: 232,
      mass: 232.04011
    }, {
      nominal: 233,
      mass: 233.040741
    }, {
      nominal: 234,
      mass: 234.0428953
    }, {
      nominal: 235,
      mass: 235.0440635
    }, {
      nominal: 236,
      mass: 236.04657
    }, {
      nominal: 237,
      mass: 237.0481736
    }, {
      nominal: 238,
      mass: 238.0509466
    }, {
      nominal: 239,
      mass: 239.0529392
    }, {
      nominal: 240,
      mass: 240.056165
    }, {
      nominal: 241,
      mass: 241.058253
    }, {
      nominal: 242,
      mass: 242.06164
    }, {
      nominal: 243,
      mass: 243.06428
    }, {
      nominal: 244,
      mass: 244.06785
    }, {
      nominal: 245,
      mass: 245.0708
    }],
    symbol: 'Np',
    mass: null,
    name: 'Neptunium'
  }, {
    number: 94,
    isotopes: [{
      nominal: 228,
      mass: 228.038732
    }, {
      nominal: 229,
      mass: 229.040144
    }, {
      nominal: 230,
      mass: 230.03965
    }, {
      nominal: 231,
      mass: 231.041102
    }, {
      nominal: 232,
      mass: 232.041185
    }, {
      nominal: 233,
      mass: 233.042998
    }, {
      nominal: 234,
      mass: 234.0433174
    }, {
      nominal: 235,
      mass: 235.045286
    }, {
      nominal: 236,
      mass: 236.0460581
    }, {
      nominal: 237,
      mass: 237.0484098
    }, {
      nominal: 238,
      mass: 238.0495601
    }, {
      nominal: 239,
      mass: 239.0521636
    }, {
      nominal: 240,
      mass: 240.0538138
    }, {
      nominal: 241,
      mass: 241.0568517
    }, {
      nominal: 242,
      mass: 242.0587428
    }, {
      nominal: 243,
      mass: 243.0620036
    }, {
      nominal: 244,
      mass: 244.0642053
    }, {
      nominal: 245,
      mass: 245.067826
    }, {
      nominal: 246,
      mass: 246.070205
    }, {
      nominal: 247,
      mass: 247.07419
    }],
    symbol: 'Pu',
    mass: null,
    name: 'Plutonium'
  }, {
    number: 95,
    isotopes: [{
      nominal: 230,
      mass: 230.04609
    }, {
      nominal: 231,
      mass: 231.04556
    }, {
      nominal: 232,
      mass: 232.04645
    }, {
      nominal: 233,
      mass: 233.04644
    }, {
      nominal: 234,
      mass: 234.04773
    }, {
      nominal: 235,
      mass: 235.047908
    }, {
      nominal: 236,
      mass: 236.04943
    }, {
      nominal: 237,
      mass: 237.049996
    }, {
      nominal: 238,
      mass: 238.051985
    }, {
      nominal: 239,
      mass: 239.0530247
    }, {
      nominal: 240,
      mass: 240.0553
    }, {
      nominal: 241,
      mass: 241.0568293
    }, {
      nominal: 242,
      mass: 242.0595494
    }, {
      nominal: 243,
      mass: 243.0613813
    }, {
      nominal: 244,
      mass: 244.0642851
    }, {
      nominal: 245,
      mass: 245.0664548
    }, {
      nominal: 246,
      mass: 246.069775
    }, {
      nominal: 247,
      mass: 247.07209
    }, {
      nominal: 248,
      mass: 248.07575
    }, {
      nominal: 249,
      mass: 249.07848
    }],
    symbol: 'Am',
    name: 'Americium',
    mass: null
  }, {
    number: 96,
    isotopes: [{
      nominal: 232,
      mass: 232.04982
    }, {
      nominal: 233,
      mass: 233.05077
    }, {
      nominal: 234,
      mass: 234.05016
    }, {
      nominal: 235,
      mass: 235.05154
    }, {
      nominal: 236,
      mass: 236.051374
    }, {
      nominal: 237,
      mass: 237.052869
    }, {
      nominal: 238,
      mass: 238.053081
    }, {
      nominal: 239,
      mass: 239.05491
    }, {
      nominal: 240,
      mass: 240.0555297
    }, {
      nominal: 241,
      mass: 241.0576532
    }, {
      nominal: 242,
      mass: 242.058836
    }, {
      nominal: 243,
      mass: 243.0613893
    }, {
      nominal: 244,
      mass: 244.0627528
    }, {
      nominal: 245,
      mass: 245.0654915
    }, {
      nominal: 246,
      mass: 246.0672238
    }, {
      nominal: 247,
      mass: 247.0703541
    }, {
      nominal: 248,
      mass: 248.0723499
    }, {
      nominal: 249,
      mass: 249.0759548
    }, {
      nominal: 250,
      mass: 250.078358
    }, {
      nominal: 251,
      mass: 251.082286
    }, {
      nominal: 252,
      mass: 252.08487
    }],
    symbol: 'Cm',
    name: 'Curium',
    mass: null
  }, {
    number: 97,
    isotopes: [{
      nominal: 234,
      mass: 234.05727
    }, {
      nominal: 235,
      mass: 235.05658
    }, {
      nominal: 236,
      mass: 236.05748
    }, {
      nominal: 237,
      mass: 237.0571
    }, {
      nominal: 238,
      mass: 238.0582
    }, {
      nominal: 239,
      mass: 239.05824
    }, {
      nominal: 240,
      mass: 240.05976
    }, {
      nominal: 241,
      mass: 241.06016
    }, {
      nominal: 242,
      mass: 242.06198
    }, {
      nominal: 243,
      mass: 243.0630078
    }, {
      nominal: 244,
      mass: 244.065181
    }, {
      nominal: 245,
      mass: 245.0663618
    }, {
      nominal: 246,
      mass: 246.068673
    }, {
      nominal: 247,
      mass: 247.0703073
    }, {
      nominal: 248,
      mass: 248.073088
    }, {
      nominal: 249,
      mass: 249.0749877
    }, {
      nominal: 250,
      mass: 250.0783167
    }, {
      nominal: 251,
      mass: 251.080762
    }, {
      nominal: 252,
      mass: 252.08431
    }, {
      nominal: 253,
      mass: 253.08688
    }, {
      nominal: 254,
      mass: 254.0906
    }],
    symbol: 'Bk',
    name: 'Berkelium',
    mass: null
  }, {
    number: 98,
    isotopes: [{
      nominal: 237,
      mass: 237.062198
    }, {
      nominal: 238,
      mass: 238.06149
    }, {
      nominal: 239,
      mass: 239.06253
    }, {
      nominal: 240,
      mass: 240.062256
    }, {
      nominal: 241,
      mass: 241.06369
    }, {
      nominal: 242,
      mass: 242.063754
    }, {
      nominal: 243,
      mass: 243.06548
    }, {
      nominal: 244,
      mass: 244.0660008
    }, {
      nominal: 245,
      mass: 245.0680487
    }, {
      nominal: 246,
      mass: 246.0688055
    }, {
      nominal: 247,
      mass: 247.070965
    }, {
      nominal: 248,
      mass: 248.0721851
    }, {
      nominal: 249,
      mass: 249.0748539
    }, {
      nominal: 250,
      mass: 250.0764062
    }, {
      nominal: 251,
      mass: 251.0795886
    }, {
      nominal: 252,
      mass: 252.0816272
    }, {
      nominal: 253,
      mass: 253.0851345
    }, {
      nominal: 254,
      mass: 254.087324
    }, {
      nominal: 255,
      mass: 255.09105
    }, {
      nominal: 256,
      mass: 256.09344
    }],
    symbol: 'Cf',
    name: 'Californium',
    mass: null
  }, {
    number: 99,
    isotopes: [{
      nominal: 239,
      mass: 239.06823
    }, {
      nominal: 240,
      mass: 240.06892
    }, {
      nominal: 241,
      mass: 241.06856
    }, {
      nominal: 242,
      mass: 242.06957
    }, {
      nominal: 243,
      mass: 243.06951
    }, {
      nominal: 244,
      mass: 244.07088
    }, {
      nominal: 245,
      mass: 245.07125
    }, {
      nominal: 246,
      mass: 246.0729
    }, {
      nominal: 247,
      mass: 247.073622
    }, {
      nominal: 248,
      mass: 248.075471
    }, {
      nominal: 249,
      mass: 249.076411
    }, {
      nominal: 250,
      mass: 250.07861
    }, {
      nominal: 251,
      mass: 251.0799936
    }, {
      nominal: 252,
      mass: 252.08298
    }, {
      nominal: 253,
      mass: 253.0848257
    }, {
      nominal: 254,
      mass: 254.0880222
    }, {
      nominal: 255,
      mass: 255.090275
    }, {
      nominal: 256,
      mass: 256.0936
    }, {
      nominal: 257,
      mass: 257.09598
    }, {
      nominal: 258,
      mass: 258.09952
    }],
    symbol: 'Es',
    name: 'Einsteinium',
    mass: null
  }, {
    number: 100,
    isotopes: [{
      nominal: 241,
      mass: 241.07421
    }, {
      nominal: 242,
      mass: 242.07343
    }, {
      nominal: 243,
      mass: 243.07446
    }, {
      nominal: 244,
      mass: 244.07404
    }, {
      nominal: 245,
      mass: 245.07535
    }, {
      nominal: 246,
      mass: 246.07535
    }, {
      nominal: 247,
      mass: 247.07694
    }, {
      nominal: 248,
      mass: 248.0771865
    }, {
      nominal: 249,
      mass: 249.0789275
    }, {
      nominal: 250,
      mass: 250.079521
    }, {
      nominal: 251,
      mass: 251.08154
    }, {
      nominal: 252,
      mass: 252.0824671
    }, {
      nominal: 253,
      mass: 253.0851846
    }, {
      nominal: 254,
      mass: 254.0868544
    }, {
      nominal: 255,
      mass: 255.089964
    }, {
      nominal: 256,
      mass: 256.0917745
    }, {
      nominal: 257,
      mass: 257.0951061
    }, {
      nominal: 258,
      mass: 258.09708
    }, {
      nominal: 259,
      mass: 259.1006
    }, {
      nominal: 260,
      mass: 260.10281
    }],
    symbol: 'Fm',
    name: 'Fermium',
    mass: null
  }, {
    number: 101,
    isotopes: [{
      nominal: 245,
      mass: 245.08081
    }, {
      nominal: 246,
      mass: 246.08171
    }, {
      nominal: 247,
      mass: 247.08152
    }, {
      nominal: 248,
      mass: 248.08282
    }, {
      nominal: 249,
      mass: 249.08291
    }, {
      nominal: 250,
      mass: 250.08441
    }, {
      nominal: 251,
      mass: 251.084774
    }, {
      nominal: 252,
      mass: 252.08643
    }, {
      nominal: 253,
      mass: 253.087144
    }, {
      nominal: 254,
      mass: 254.08959
    }, {
      nominal: 255,
      mass: 255.0910841
    }, {
      nominal: 256,
      mass: 256.09389
    }, {
      nominal: 257,
      mass: 257.0955424
    }, {
      nominal: 258,
      mass: 258.0984315
    }, {
      nominal: 259,
      mass: 259.10051
    }, {
      nominal: 260,
      mass: 260.10365
    }, {
      nominal: 261,
      mass: 261.10583
    }, {
      nominal: 262,
      mass: 262.1091
    }],
    symbol: 'Md',
    name: 'Mendelevium',
    mass: null
  }, {
    number: 102,
    isotopes: [{
      nominal: 248,
      mass: 248.08655
    }, {
      nominal: 249,
      mass: 249.0878
    }, {
      nominal: 250,
      mass: 250.08756
    }, {
      nominal: 251,
      mass: 251.08894
    }, {
      nominal: 252,
      mass: 252.088967
    }, {
      nominal: 253,
      mass: 253.0905641
    }, {
      nominal: 254,
      mass: 254.090956
    }, {
      nominal: 255,
      mass: 255.093191
    }, {
      nominal: 256,
      mass: 256.0942829
    }, {
      nominal: 257,
      mass: 257.0968878
    }, {
      nominal: 258,
      mass: 258.09821
    }, {
      nominal: 259,
      mass: 259.10103
    }, {
      nominal: 260,
      mass: 260.10264
    }, {
      nominal: 261,
      mass: 261.1057
    }, {
      nominal: 262,
      mass: 262.10746
    }, {
      nominal: 263,
      mass: 263.11071
    }, {
      nominal: 264,
      mass: 264.11273
    }],
    symbol: 'No',
    name: 'Nobelium',
    mass: null
  }, {
    number: 103,
    isotopes: [{
      nominal: 251,
      mass: 251.09418
    }, {
      nominal: 252,
      mass: 252.09526
    }, {
      nominal: 253,
      mass: 253.09509
    }, {
      nominal: 254,
      mass: 254.09648
    }, {
      nominal: 255,
      mass: 255.096562
    }, {
      nominal: 256,
      mass: 256.098494
    }, {
      nominal: 257,
      mass: 257.099418
    }, {
      nominal: 258,
      mass: 258.10176
    }, {
      nominal: 259,
      mass: 259.102902
    }, {
      nominal: 260,
      mass: 260.1055
    }, {
      nominal: 261,
      mass: 261.10688
    }, {
      nominal: 262,
      mass: 262.10961
    }, {
      nominal: 263,
      mass: 263.11136
    }, {
      nominal: 264,
      mass: 264.1142
    }, {
      nominal: 265,
      mass: 265.11619
    }, {
      nominal: 266,
      mass: 266.11983
    }],
    symbol: 'Lr',
    name: 'Lawrencium',
    mass: null
  }, {
    number: 104,
    isotopes: [{
      nominal: 253,
      mass: 253.10044
    }, {
      nominal: 254,
      mass: 254.10005
    }, {
      nominal: 255,
      mass: 255.10127
    }, {
      nominal: 256,
      mass: 256.101152
    }, {
      nominal: 257,
      mass: 257.102918
    }, {
      nominal: 258,
      mass: 258.103428
    }, {
      nominal: 259,
      mass: 259.105596
    }, {
      nominal: 260,
      mass: 260.10644
    }, {
      nominal: 261,
      mass: 261.108773
    }, {
      nominal: 262,
      mass: 262.10992
    }, {
      nominal: 263,
      mass: 263.11249
    }, {
      nominal: 264,
      mass: 264.11388
    }, {
      nominal: 265,
      mass: 265.11668
    }, {
      nominal: 266,
      mass: 266.11817
    }, {
      nominal: 267,
      mass: 267.12179
    }, {
      nominal: 268,
      mass: 268.12397
    }],
    symbol: 'Rf',
    name: 'Rutherfordium',
    mass: null
  }, {
    number: 105,
    isotopes: [{
      nominal: 255,
      mass: 255.10707
    }, {
      nominal: 256,
      mass: 256.10789
    }, {
      nominal: 257,
      mass: 257.10758
    }, {
      nominal: 258,
      mass: 258.10928
    }, {
      nominal: 259,
      mass: 259.109492
    }, {
      nominal: 260,
      mass: 260.1113
    }, {
      nominal: 261,
      mass: 261.11192
    }, {
      nominal: 262,
      mass: 262.11407
    }, {
      nominal: 263,
      mass: 263.11499
    }, {
      nominal: 264,
      mass: 264.11741
    }, {
      nominal: 265,
      mass: 265.11861
    }, {
      nominal: 266,
      mass: 266.12103
    }, {
      nominal: 267,
      mass: 267.12247
    }, {
      nominal: 268,
      mass: 268.12567
    }, {
      nominal: 269,
      mass: 269.12791
    }, {
      nominal: 270,
      mass: 270.13136
    }],
    symbol: 'Db',
    name: 'Dubnium',
    mass: null
  }, {
    number: 106,
    isotopes: [{
      nominal: 258,
      mass: 258.11298
    }, {
      nominal: 259,
      mass: 259.1144
    }, {
      nominal: 260,
      mass: 260.114384
    }, {
      nominal: 261,
      mass: 261.115949
    }, {
      nominal: 262,
      mass: 262.116337
    }, {
      nominal: 263,
      mass: 263.11829
    }, {
      nominal: 264,
      mass: 264.11893
    }, {
      nominal: 265,
      mass: 265.12109
    }, {
      nominal: 266,
      mass: 266.12198
    }, {
      nominal: 267,
      mass: 267.12436
    }, {
      nominal: 268,
      mass: 268.12539
    }, {
      nominal: 269,
      mass: 269.12863
    }, {
      nominal: 270,
      mass: 270.13043
    }, {
      nominal: 271,
      mass: 271.13393
    }, {
      nominal: 272,
      mass: 272.13589
    }, {
      nominal: 273,
      mass: 273.13958
    }],
    symbol: 'Sg',
    name: 'Seaborgium',
    mass: null
  }, {
    number: 107,
    isotopes: [{
      nominal: 260,
      mass: 260.12166
    }, {
      nominal: 261,
      mass: 261.12145
    }, {
      nominal: 262,
      mass: 262.12297
    }, {
      nominal: 263,
      mass: 263.12292
    }, {
      nominal: 264,
      mass: 264.12459
    }, {
      nominal: 265,
      mass: 265.12491
    }, {
      nominal: 266,
      mass: 266.12679
    }, {
      nominal: 267,
      mass: 267.1275
    }, {
      nominal: 268,
      mass: 268.12969
    }, {
      nominal: 269,
      mass: 269.13042
    }, {
      nominal: 270,
      mass: 270.13336
    }, {
      nominal: 271,
      mass: 271.13526
    }, {
      nominal: 272,
      mass: 272.13826
    }, {
      nominal: 273,
      mass: 273.14024
    }, {
      nominal: 274,
      mass: 274.14355
    }, {
      nominal: 275,
      mass: 275.14567
    }],
    symbol: 'Bh',
    name: 'Bohrium',
    mass: null
  }, {
    number: 108,
    isotopes: [{
      nominal: 263,
      mass: 263.12852
    }, {
      nominal: 264,
      mass: 264.128357
    }, {
      nominal: 265,
      mass: 265.129793
    }, {
      nominal: 266,
      mass: 266.130046
    }, {
      nominal: 267,
      mass: 267.13167
    }, {
      nominal: 268,
      mass: 268.13186
    }, {
      nominal: 269,
      mass: 269.13375
    }, {
      nominal: 270,
      mass: 270.13429
    }, {
      nominal: 271,
      mass: 271.13717
    }, {
      nominal: 272,
      mass: 272.1385
    }, {
      nominal: 273,
      mass: 273.14168
    }, {
      nominal: 274,
      mass: 274.1433
    }, {
      nominal: 275,
      mass: 275.14667
    }, {
      nominal: 276,
      mass: 276.14846
    }, {
      nominal: 277,
      mass: 277.1519
    }],
    symbol: 'Hs',
    name: 'Hassium',
    mass: null
  }, {
    number: 109,
    isotopes: [{
      nominal: 265,
      mass: 265.136
    }, {
      nominal: 266,
      mass: 266.13737
    }, {
      nominal: 267,
      mass: 267.13719
    }, {
      nominal: 268,
      mass: 268.13865
    }, {
      nominal: 269,
      mass: 269.13882
    }, {
      nominal: 270,
      mass: 270.14033
    }, {
      nominal: 271,
      mass: 271.14074
    }, {
      nominal: 272,
      mass: 272.14341
    }, {
      nominal: 273,
      mass: 273.1444
    }, {
      nominal: 274,
      mass: 274.14724
    }, {
      nominal: 275,
      mass: 275.14882
    }, {
      nominal: 276,
      mass: 276.15159
    }, {
      nominal: 277,
      mass: 277.15327
    }, {
      nominal: 278,
      mass: 278.15631
    }, {
      nominal: 279,
      mass: 279.15808
    }],
    symbol: 'Mt',
    name: 'Meitnerium',
    mass: null
  }, {
    number: 110,
    isotopes: [{
      nominal: 267,
      mass: 267.14377
    }, {
      nominal: 268,
      mass: 268.14348
    }, {
      nominal: 269,
      mass: 269.144752
    }, {
      nominal: 270,
      mass: 270.144584
    }, {
      nominal: 271,
      mass: 271.14595
    }, {
      nominal: 272,
      mass: 272.14602
    }, {
      nominal: 273,
      mass: 273.14856
    }, {
      nominal: 274,
      mass: 274.14941
    }, {
      nominal: 275,
      mass: 275.15203
    }, {
      nominal: 276,
      mass: 276.15303
    }, {
      nominal: 277,
      mass: 277.15591
    }, {
      nominal: 278,
      mass: 278.15704
    }, {
      nominal: 279,
      mass: 279.1601
    }, {
      nominal: 280,
      mass: 280.16131
    }, {
      nominal: 281,
      mass: 281.16451
    }],
    symbol: 'Ds',
    name: 'Darmstadtium',
    mass: null
  }, {
    number: 111,
    isotopes: [{
      nominal: 272,
      mass: 272.15327
    }, {
      nominal: 273,
      mass: 273.15313
    }, {
      nominal: 274,
      mass: 274.15525
    }, {
      nominal: 275,
      mass: 275.15594
    }, {
      nominal: 276,
      mass: 276.15833
    }, {
      nominal: 277,
      mass: 277.15907
    }, {
      nominal: 278,
      mass: 278.16149
    }, {
      nominal: 279,
      mass: 279.16272
    }, {
      nominal: 280,
      mass: 280.16514
    }, {
      nominal: 281,
      mass: 281.16636
    }, {
      nominal: 282,
      mass: 282.16912
    }, {
      nominal: 283,
      mass: 283.17054
    }],
    symbol: 'Rg',
    name: 'Roentgenium',
    mass: null
  }, {
    number: 112,
    isotopes: [{
      nominal: 276,
      mass: 276.16141
    }, {
      nominal: 277,
      mass: 277.16364
    }, {
      nominal: 278,
      mass: 278.16416
    }, {
      nominal: 279,
      mass: 279.16654
    }, {
      nominal: 280,
      mass: 280.16715
    }, {
      nominal: 281,
      mass: 281.16975
    }, {
      nominal: 282,
      mass: 282.1705
    }, {
      nominal: 283,
      mass: 283.17327
    }, {
      nominal: 284,
      mass: 284.17416
    }, {
      nominal: 285,
      mass: 285.17712
    }],
    symbol: 'Cn',
    name: 'Copernicium',
    mass: null
  }, {
    number: 113,
    isotopes: [{
      nominal: 278,
      mass: 278.17058
    }, {
      nominal: 279,
      mass: 279.17095
    }, {
      nominal: 280,
      mass: 280.17293
    }, {
      nominal: 281,
      mass: 281.17348
    }, {
      nominal: 282,
      mass: 282.17567
    }, {
      nominal: 283,
      mass: 283.17657
    }, {
      nominal: 284,
      mass: 284.17873
    }, {
      nominal: 285,
      mass: 285.17973
    }, {
      nominal: 286,
      mass: 286.18221
    }, {
      nominal: 287,
      mass: 287.18339
    }],
    symbol: 'Nh',
    name: 'Nihonium',
    mass: null
  }, {
    number: 114,
    isotopes: [{
      nominal: 285,
      mass: 285.18364
    }, {
      nominal: 286,
      mass: 286.18423
    }, {
      nominal: 287,
      mass: 287.18678
    }, {
      nominal: 288,
      mass: 288.18757
    }, {
      nominal: 289,
      mass: 289.19042
    }],
    symbol: 'Fl',
    name: 'Flerovium',
    mass: null
  }, {
    number: 115,
    isotopes: [{
      nominal: 287,
      mass: 287.1907
    }, {
      nominal: 288,
      mass: 288.19274
    }, {
      nominal: 289,
      mass: 289.19363
    }, {
      nominal: 290,
      mass: 290.19598
    }, {
      nominal: 291,
      mass: 291.19707
    }],
    symbol: 'Mc',
    name: 'Moscovium',
    mass: null
  }, {
    number: 116,
    isotopes: [{
      nominal: 289,
      mass: 289.19816
    }, {
      nominal: 290,
      mass: 290.19864
    }, {
      nominal: 291,
      mass: 291.20108
    }, {
      nominal: 292,
      mass: 292.20174
    }, {
      nominal: 293,
      mass: 293.20449
    }],
    symbol: 'Lv',
    name: 'Livermorium',
    mass: null
  }, {
    number: 117,
    isotopes: [{
      nominal: 291,
      mass: 291.20553
    }, {
      nominal: 292,
      mass: 292.20746
    }, {
      nominal: 293,
      mass: 293.20824
    }, {
      nominal: 294,
      mass: 294.21046
    }],
    symbol: 'Ts',
    name: 'Teennessine',
    mass: null
  }, {
    number: 118,
    isotopes: [{
      nominal: 293,
      mass: 293.21356
    }, {
      nominal: 294,
      mass: 294.21392
    }, {
      nominal: 295,
      mass: 295.21624
    }],
    symbol: 'Og',
    name: 'Oganesson',
    mass: null
  }];

  const elements$2 = elementsAndIsotopes.map(element => ({
    number: element.number,
    symbol: element.symbol,
    mass: element.mass,
    name: element.name,
    monoisotopicMass: element.monoisotopicMass
  }));

  const elementsAndIsotopesObject = {};
  elementsAndIsotopes.forEach(element => {
    elementsAndIsotopesObject[element.symbol] = element;
  });

  const elementsAndStableIsotopes = JSON.parse(JSON.stringify(elementsAndIsotopes));
  elementsAndStableIsotopes.forEach(element => {
    element.isotopes = element.isotopes.filter(i => i.abundance > 0);
  });

  const elementsAndStableIsotopesObject = {};
  elementsAndStableIsotopes.forEach(element => {
    elementsAndStableIsotopesObject[element.symbol] = element;
  });

  const elementsObject = {};
  elements$2.forEach(element => {
    elementsObject[element.symbol] = element;
  });

  const stableIsotopesObject = {};
  for (const element of elementsAndIsotopes) {
    let abundance = 0;
    let mostAbundant = 0;
    for (const isotope of element.isotopes) {
      if (isotope.abundance > abundance) {
        abundance = isotope.abundance;
        mostAbundant = isotope.nominal;
      }
    }
    for (const isotope of element.isotopes) {
      if (isotope.abundance === 0) continue;
      const entry = {
        name: element.name,
        mass: isotope.mass,
        symbol: element.symbol
      };
      if (isotope.nominal === mostAbundant) {
        entry.mostAbundant = true;
      }
      stableIsotopesObject[isotope.nominal + element.symbol] = entry;
    }
  }

  const isotopesObject = {};
  Object.keys(elementsAndIsotopesObject).forEach(key => {
    let e = elementsAndIsotopesObject[key];
    e.isotopes.forEach(i => {
      isotopesObject[i.nominal + key] = {
        abundance: i.abundance,
        mass: i.mass
      };
    });
  });

  const unsaturationsObject = {
    O: 0,
    N: 1,
    H: -1,
    Na: -1,
    K: -1,
    Li: -1,
    Ca: -2,
    C: 2,
    F: -1,
    Si: 2,
    Cl: -1,
    Br: -1,
    I: -1,
    S: 0,
    P: 1
  };

  const elements$1 = Object.keys(elementsObject).sort((a, b) => b.length - a.length);

  /**
   * Ensure that the mf has been entered with capital letters and not only lowercase
   * If there is only lowercase we try to capitalize the mf
   * @param {string} mf
   */

  function ensureCase(mf) {
    for (let i = 0; i < mf.length; i++) {
      if (mf.charCodeAt(i) > 64 && mf.charCodeAt(i) < 91) {
        return mf;
      }
    }
    let parts = mf.replace(/([a-z]*)([^a-z]*)/g, '$1 $2 ').split(/ +/);
    for (let i = 0; i < parts.length; i++) {
      if (parts[i].match(/^[a-z]$/)) {
        parts[i] = parts[i].toUpperCase();
      } else if (parts[i].match(/^[a-z]+$/)) {
        let newPart = '';
        for (let j = 0; j < parts[i].length; j++) {
          let two = parts[i].substr(j, 2);
          let one = parts[i].charAt(j).toUpperCase();
          if (['c', 'h', 'o', 'n'].includes(two.charAt(0)) && ['h', 'o', 'n'].includes(two.charAt(1))) {
            newPart += two.toUpperCase();
            j++;
          } else {
            two = two.charAt(0).toUpperCase() + two.charAt(1);
            if (elements$1.includes(two)) {
              newPart += two;
              j++;
            } else if (elements$1.includes(one)) {
              newPart += one;
            } else {
              return mf;
            }
          }
        }
        parts[i] = newPart;
      }
    }
    return parts.join('');
  }

  function getIsotopeRatioInfo(value) {
    let result = {
      mass: 0,
      monoisotopicMass: 0
    };
    let element = elementsAndStableIsotopesObject[value.atom];
    if (!element) throw new Error(`Element not found: ${value.atom}`);
    let isotopesArray = element.isotopes;
    let ratios = normalize$3(value.ratio);
    let max = Math.max(...ratios);
    if (ratios.length > isotopesArray.length) {
      throw new Error(`the number of specified ratios is bigger that the number of stable isotopes: ${value.atom}`);
    }
    for (let i = 0; i < ratios.length; i++) {
      result.mass += ratios[i] * isotopesArray[i].mass;
      if (max === ratios[i] && result.monoisotopicMass === 0) {
        result.monoisotopicMass = isotopesArray[i].mass;
      }
    }
    return result;
  }
  function normalize$3(array) {
    let sum = array.reduce((prev, current) => prev + current, 0);
    return array.map(a => a / sum);
  }

  /**
   *
   * @param {*} parts
   * @param {*} [options={}]
   */
  function getEA(parts) {
    let results = {};
    for (let part of parts) {
      for (let line of part) {
        switch (line.kind) {
          case Kind.ISOTOPE:
            {
              let isotope = isotopesObject[line.value.isotope + line.value.atom];
              if (!isotope) {
                throw new Error(`Unknown isotope: ${line.value.isotope}${line.value.atom}`);
              }
              addMass(results, line.value.atom, isotope.mass * line.multiplier);
              break;
            }
          case Kind.ISOTOPE_RATIO:
            {
              let isotopeRatioInfo = getIsotopeRatioInfo(line.value);
              addMass(results, line.value.atom, isotopeRatioInfo.mass * line.multiplier);
              break;
            }
          case Kind.ATOM:
            {
              let element = elementsObject[line.value];
              if (!element) {
                element = groupsObject[line.value];
                if (!element) throw Error(`Unknown element: ${line.value}`);
                // need to explode group ????
              }

              addMass(results, line.value, element.mass * line.multiplier);
              break;
            }
          case Kind.CHARGE:
            break;
          default:
            throw new Error('partToMF unhandled Kind: ', line.kind);
        }
      }
    }
    let eas = [];
    let sum = 0;
    for (let key in results) {
      sum += results[key];
      eas.push({
        element: key,
        mass: results[key]
      });
    }
    eas.forEach(ea => {
      ea.ratio = ea.mass / sum;
    });
    return eas;
  }
  function addMass(results, atom, mass) {
    if (!results[atom]) results[atom] = 0;
    results[atom] += mass;
  }

  /**
   *
   * @param {*} parts
   * @param {*} [options={}]
   */
  function getElements(parts) {
    const elements = [];
    for (const part of parts) {
      for (const line of part) {
        let number = line.multiplier;
        switch (line.kind) {
          case Kind.ATOM:
            {
              let symbol = line.value;
              let element = elementsObject[symbol];
              if (!element) {
                throw new Error(`element unknown: ${symbol} - ${line}`);
              }
              addElement(elements, {
                symbol,
                number
              });
              break;
            }
          case Kind.ISOTOPE:
            {
              let element = elementsAndIsotopesObject[line.value.atom];
              if (!element) {
                throw new Error(`element unknown: ${part.value.atom} - ${line}`);
              }
              let isotope = element.isotopes.filter(a => a.nominal === line.value.isotope)[0];
              if (!isotope) {
                throw new Error(`isotope unknown: ${line.value.isotope} - ${line}`);
              }
              addElement(elements, {
                symbol: line.value.atom,
                number,
                isotope: line.value.isotope
              });
              break;
            }
          default:
            throw new Error(`unknown type: ${line.kind}`);
        }
      }
    }
    return elements;
  }
  function addElement(elements, newElement) {
    for (let element of elements) {
      if (element.symbol === newElement.symbol && element.isotope === newElement.isotope) {
        element.number += newElement.number;
        return;
      }
    }
    elements.push(newElement);
  }

  /**
   * Convert a MF part to an array of atoms
   * This procedure will suppress the isotopes !
   * This is mainly used to make queries
   */

  function partToAtoms(part) {
    let atoms = {};
    for (let line of part) {
      switch (line.kind) {
        case Kind.ISOTOPE:
          if (!atoms[line.value.atom]) atoms[line.value.atom] = 0;
          atoms[line.value.atom] += line.multiplier;
          break;
        case Kind.ISOTOPE_RATIO:
          if (!atoms[line.value.atom]) atoms[line.value.atom] = 0;
          atoms[line.value.atom] += line.multiplier;
          break;
        case Kind.ATOM:
          if (!atoms[line.value]) atoms[line.value] = 0;
          atoms[line.value] += line.multiplier;
          break;
        case Kind.CHARGE:
          break;
        case Kind.ANCHOR:
          break;
        default:
          throw new Error('partToMF unhandled Kind: ', line.kind);
      }
    }
    return atoms;
  }

  function partToMF(part) {
    let options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
    let mf = [];
    for (let line of part) {
      switch (line.kind) {
        case Kind.ISOTOPE:
          if (line.multiplier !== 0) {
            mf.push(`[${line.value.isotope}${line.value.atom}]${line.multiplier !== 1 ? line.multiplier : ''}`);
          }
          break;
        case Kind.ISOTOPE_RATIO:
          if (line.multiplier !== 0) {
            mf.push(`${line.value.atom}{${line.value.ratio.join(',')}}${line.multiplier !== 1 ? line.multiplier : ''}`);
          }
          break;
        case Kind.ATOM:
          if (line.multiplier !== 0) {
            mf.push(line.value + (line.multiplier !== 1 ? line.multiplier : ''));
          }
          break;
        case Kind.CHARGE:
          if (line.value === 0 || options.neutral) break;
          mf.push(`(${line.value > 0 ? `+${line.value}` : line.value})`);
          break;
      }
    }
    return mf.join('');
  }

  /**
   *
   * @param {*} parts
   * @param {*} [options={}]
   */
  function getInfo$1(parts) {
    let options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
    let {
      customUnsaturations = {}
    } = options;
    if (parts.length === 0) return {};
    if (parts.length === 1) {
      return getProcessedPart$1(parts[0], customUnsaturations);
    }
    let result = {
      parts: []
    };
    for (let part of parts) {
      result.parts.push(getProcessedPart$1(part, customUnsaturations));
    }
    result.monoisotopicMass = 0;
    result.mass = 0;
    result.charge = 0;
    result.mf = result.parts.map(a => a.mf).join('.');
    result.parts.forEach(a => {
      result.mass += a.mass;
      result.monoisotopicMass += a.monoisotopicMass;
      result.charge += a.charge;
    });
    return result;
  }
  function getProcessedPart$1(part, customUnsaturations) {
    let currentPart = {
      mass: 0,
      monoisotopicMass: 0,
      charge: 0,
      mf: '',
      atoms: partToAtoms(part)
    };
    let unsaturation = 0;
    let validUnsaturation = true;
    currentPart.mf = partToMF(part);
    for (let line of part) {
      let currentElement = '';
      switch (line.kind) {
        case Kind.ATOM:
          {
            currentElement = line.value;
            let element = elementsAndIsotopesObject[line.value];

            // todo should we have a kind GROUP ?
            if (!element) {
              element = groupsObject[line.value];
              if (!element) throw Error(`Unknown element: ${line.value}`);
              if (!customUnsaturations[line.value]) {
                customUnsaturations[line.value] = element.unsaturation;
              }
            }
            if (!element) throw new Error(`Unknown element: ${line.value}`);
            currentPart.monoisotopicMass += element.monoisotopicMass * line.multiplier;
            currentPart.mass += element.mass * line.multiplier;
            break;
          }
        case Kind.ISOTOPE:
          {
            currentElement = line.value.atom;
            let isotope = isotopesObject[line.value.isotope + line.value.atom];
            if (!isotope) {
              throw new Error(`Unknown isotope: ${line.value.isotope}${line.value.atom}`);
            }
            currentPart.monoisotopicMass += isotope.mass * line.multiplier;
            currentPart.mass += isotope.mass * line.multiplier;
            break;
          }
        case Kind.ISOTOPE_RATIO:
          {
            currentElement = line.value.atom;
            let isotopeRatioInfo = getIsotopeRatioInfo(line.value);
            currentPart.monoisotopicMass += isotopeRatioInfo.monoisotopicMass * line.multiplier;
            currentPart.mass += isotopeRatioInfo.mass * line.multiplier;
            break;
          }
        case Kind.CHARGE:
          currentPart.charge = line.value;
          if (validUnsaturation) {
            unsaturation -= line.value;
          }
          break;
        default:
          throw new Error('Unimplemented Kind in getInfo', line.kind);
      }
      if (currentElement) {
        if (customUnsaturations[currentElement] !== undefined) {
          unsaturation += customUnsaturations[currentElement] * line.multiplier;
        } else if (unsaturationsObject[currentElement] !== undefined) {
          unsaturation += unsaturationsObject[currentElement] * line.multiplier;
        } else {
          validUnsaturation = false;
        }
      }
    }

    // need to calculate the observedMonoisotopicMass
    if (currentPart.charge) {
      currentPart.observedMonoisotopicMass = (currentPart.monoisotopicMass - currentPart.charge * ELECTRON_MASS) / Math.abs(currentPart.charge);
    }
    if (validUnsaturation) {
      currentPart.unsaturation = unsaturation / 2 + 1;
    }
    return currentPart;
  }

  /**
   *
   * @param {*} parts
   * @param {*} options
   */
  function getIsotopesInfo(parts) {
    if (parts.length === 0) return [];
    if (parts.length > 1) {
      throw new Error('getIsotopesInfo can not be applied on multipart MF');
    }
    return getProcessedPart(parts[0]);
  }
  function getProcessedPart(part) {
    let result = {
      charge: 0,
      isotopes: []
    };
    for (let line of part) {
      switch (line.kind) {
        case Kind.ISOTOPE:
          {
            let isotope = isotopesObject[line.value.isotope + line.value.atom];
            if (!isotope) {
              throw Error('unknown isotope:', line.value.atom, line.value.isotope);
            }
            result.isotopes.push({
              atom: `[${line.value.isotope}${line.value.atom}]`,
              number: line.multiplier,
              distribution: [{
                x: isotope.mass,
                y: 1
              }]
            });
            break;
          }
        case Kind.ISOTOPE_RATIO:
          {
            let element = elementsAndStableIsotopesObject[line.value.atom];
            if (!element) throw new Error('unknown element:', line.value);
            let distribution = getDistribution(element.isotopes, line.value.ratio);
            result.isotopes.push({
              atom: `${line.value.atom}{${line.value.ratio.join(',')}}`,
              number: line.multiplier,
              distribution
            });
          }
          break;
        case Kind.ATOM:
          {
            let element = elementsAndStableIsotopesObject[line.value];
            if (!element) throw new Error('unknown element:', line.value);
            result.isotopes.push({
              atom: line.value,
              number: line.multiplier,
              distribution: element.isotopes.map(e => ({
                x: e.mass,
                y: e.abundance
              }))
            });
            break;
          }
        case Kind.CHARGE:
          result.charge += line.value;
          break;
        default:
          throw new Error('partToMF unhandled Kind: ', line.kind);
      }
    }
    return result;
  }
  function getDistribution(isotopesArray, ratio) {
    let ratios = normalize$2(ratio);
    let result = [];
    if (ratios.length > isotopesArray.length) {
      throw new Error(`the number of specified ratios is bigger that the number of stable isotopes: ${isotopesObject}`);
    }
    for (let i = 0; i < ratios.length; i++) {
      result.push({
        x: isotopesArray[i].mass,
        y: ratios[i]
      });
    }
    return result;
  }
  function normalize$2(array) {
    let sum = array.reduce((prev, current) => prev + current, 0);
    return array.map(a => a / sum);
  }

  /**
   * Converts an array of mf elements to an array of formatting information
   * @param {Array<Object>} result of the parse method
   */

  function partsToDisplay(parts) {
    let lines = [];
    for (let part of parts) {
      if (lines.length > 0) lines.push({
        kind: Kind.SALT,
        value: '•'
      });
      for (let partLine of part) {
        lines.push(partLine);
        if (partLine.multiplier) {
          lines.push({
            kind: Kind.MULTIPLIER,
            value: partLine.multiplier
          });
        }
      }
    }
    return toDisplay(lines);
  }

  function partsToMF(parts, options) {
    let mf = [];
    for (let part of parts) {
      mf.push(partToMF(part, options));
    }
    return mf.join(' . ');
  }

  /**
   * Implementation of the Hill system for sorting atoms
   * https://en.wikipedia.org/wiki/Chemical_formula#Hill_system
   * @param {string} a - first atom to compare
   * @param {string} b - second atom to compare
   * @returns
   */

  function atomSorter(a, b) {
    if (a === b) return 0;
    if (a === 'C') return -1;
    if (b === 'C') return 1;
    if (a === 'H') return -1;
    if (b === 'H') return 1;
    if (a < b) return -1;
    return 1;
  }

  /**
   *
   * @param {*} lines
   * @param {object} [options={}]
   * @param {boolean} [options.expand=true] - Should we expand the groupsObject
   */

  function toParts(lines) {
    let options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
    const {
      expand: shouldExpandgroupsObject = true
    } = options;
    let parts = [];
    let currentPart = createNewPart();
    let previousKind = Kind.BEGIN;
    parts.push(currentPart);
    for (let line of lines) {
      switch (line.kind) {
        case Kind.ATOM:
        case Kind.ISOTOPE_RATIO:
        case Kind.ISOTOPE:
        case Kind.CHARGE:
          currentPart.lines.push({
            ...line,
            multiplier: 1
          });
          break;
        case Kind.OPENING_PARENTHESIS:
          openingParenthesis(currentPart);
          break;
        case Kind.CLOSING_PARENTHESIS:
          closingParenthesis(currentPart);
          break;
        case Kind.PRE_MULTIPLIER:
          preMultiplier(currentPart, line);
          break;
        case Kind.MULTIPLIER:
          postMultiplier(currentPart, line.value, previousKind);
          break;
        case Kind.SALT:
          globalPartMultiplier(currentPart);
          currentPart = createNewPart();
          parts.push(currentPart);
          break;
        case Kind.ANCHOR:
          // we ignore anchors to create the parts and canonized MF
          break;
        case Kind.COMMENT:
          // we ignore comments to create the parts and canonized MF
          break;
        case Kind.TEXT:
          break;
        default:
          throw new Error(`Can not process mf having: ${line.kind}`);
      }
      previousKind = line.kind;
    }
    globalPartMultiplier(currentPart);
    if (shouldExpandgroupsObject) expandgroupsObject(parts);
    return combineAtomsIsotopesCharges(parts);
  }
  function createNewPart() {
    let currentMultiplier = {
      value: 1,
      fromIndex: 0
    };
    return {
      lines: [],
      multipliers: [currentMultiplier],
      currentMultiplier
    };
  }
  function openingParenthesis(currentPart) {
    currentPart.currentMultiplier = {
      value: 1,
      fromIndex: currentPart.lines.length
    };
    currentPart.multipliers.push(currentPart.currentMultiplier);
  }
  function closingParenthesis(currentPart) {
    currentPart.currentMultiplier = currentPart.multipliers.pop();
    if (currentPart.currentMultiplier !== 1) {
      for (let i = currentPart.currentMultiplier.fromIndex; i < currentPart.lines.length; i++) {
        currentPart.lines[i].multiplier *= currentPart.currentMultiplier.value;
      }
    }
  }
  function preMultiplier(currentPart, line) {
    currentPart.currentMultiplier.value *= line.value;
  }
  function globalPartMultiplier(currentPart) {
    for (let i = currentPart.multipliers[0].fromIndex; i < currentPart.lines.length; i++) {
      currentPart.lines[i].multiplier *= currentPart.multipliers[0].value;
    }
  }
  function postMultiplier(currentPart, value, previousKind) {
    if (previousKind === Kind.CLOSING_PARENTHESIS) {
      // need to apply to everything till the previous parenthesis
      for (let i = currentPart.currentMultiplier.fromIndex; i < currentPart.lines.length; i++) {
        currentPart.lines[i].multiplier *= value;
      }
    } else {
      // just applies to the previous element
      currentPart.lines[currentPart.lines.length - 1].multiplier *= value;
    }
  }
  function expandgroupsObject(parts) {
    for (let part of parts) {
      let expanded = false;
      for (let i = 0; i < part.lines.length; i++) {
        let line = part.lines[i];
        if (line.kind === Kind.ATOM) {
          let group = groupsObject[line.value];
          if (group) {
            expanded = true;
            for (let element of group.elements) {
              if (element.isotope) {
                part.lines.push({
                  kind: 'isotope',
                  value: {
                    atom: element.symbol,
                    isotope: element.isotope
                  },
                  multiplier: line.multiplier * element.number
                });
              } else {
                part.lines.push({
                  kind: 'atom',
                  value: element.symbol,
                  multiplier: line.multiplier * element.number
                });
              }
            }
            part.lines[i] = undefined;
          }
        }
      }
      if (expanded) part.lines = part.lines.filter(a => a);
    }
  }
  function combineAtomsIsotopesCharges(parts) {
    let results = [];
    for (let part of parts) {
      let result = [];
      results.push(result);
      calculateAndSortKeys(part);
      let currentKey = '';
      for (let key of part.keys) {
        if (key.key === Kind.CHARGE) {
          if (currentKey !== key.key) {
            result.push({
              kind: Kind.CHARGE,
              value: key.value.value * key.value.multiplier
            });
          } else {
            result[result.length - 1].value += key.value.value * key.value.multiplier;
          }
        } else if (currentKey !== key.key) {
          result.push(key.value);
        } else {
          result[result.length - 1].multiplier += key.value.multiplier;
        }
        currentKey = key.key;
      }
      result.sort((a, b) => {
        if (a.kind === Kind.CHARGE) return 1;
        if (b.kind === Kind.CHARGE) return -1;
        let atomA = a.kind === Kind.ATOM ? a.value : a.value.atom;
        let atomB = b.kind === Kind.ATOM ? b.value : b.value.atom;
        if (atomA !== atomB) return atomSorter(atomA, atomB);
        // same atome but some isotopes ...
        if (a.kind === Kind.ATOM) return -1;
        if (b.kind === Kind.ATOM) return 1;
        if (a.kind === Kind.ISOTOPE) return -1;
        if (b.kind === Kind.ISOTOPE) return 1;
        if (a.kind === Kind.ISOTOPE_RATIO) return -1;
        if (b.kind === Kind.ISOTOPE_RATIO) return 1;
        return 0;
      });
    }
    return results;
  }
  function calculateAndSortKeys(part) {
    part.keys = [];
    for (let line of part.lines) {
      part.keys.push({
        key: getKey(line),
        value: line
      });
    }
    part.keys.sort((a, b) => stringComparator(a.key, b.key));
  }
  function getKey(line) {
    let key = [line.kind];
    switch (line.kind) {
      case Kind.CHARGE:
        break;
      default:
        if (typeof line.value === 'string') {
          key.push(line.value);
        } else {
          for (let prop of Object.keys(line.value).sort()) {
            key.push(line.value[prop]);
          }
        }
    }
    return key.join('-');
  }
  function stringComparator(a, b) {
    if (a < b) return -1;
    if (a > b) return 1;
    return 0;
  }

  function toText(lines) {
    let text = [];
    for (let line of lines) {
      switch (line.kind) {
        case Format.SUBSCRIPT:
          {
            const value = String(line.value);
            for (let i = 0; i < value.length; i++) {
              const char = value[i];
              if (subscript[char]) {
                text.push(subscript[char]);
              } else {
                throw new Error(`Subscript problem with: ${char}`);
              }
            }
          }
          break;
        case Format.SUPERSCRIPT:
          {
            const value = String(line.value);
            for (let i = 0; i < value.length; i++) {
              const char = value[i];
              if (superscript[char]) {
                text.push(superscript[char]);
              } else {
                throw new Error(`Superscript problem with: ${char}`);
              }
            }
            break;
          }
        case Format.SUPERIMPOSE:
          {
            const under = String(line.under);
            for (let i = 0; i < under.length; i++) {
              const char = under[i];
              if (subscript[char]) {
                text.push(subscript[char]);
              } else {
                throw new Error(`Subscript problem with: ${char}`);
              }
            }
            const over = String(line.over);
            for (let i = 0; i < over.length; i++) {
              const char = over[i];
              if (superscript[char]) {
                text.push(superscript[char]);
              } else {
                throw new Error(`Superscript problem with: ${char}`);
              }
            }
            break;
          }
        default:
          text.push(line.value);
      }
    }
    return text.join('');
  }

  /**
   * Class allowing to deal with molecular formula and derived information
   */
  class MF {
    constructor(mf) {
      let options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
      if (options.ensureCase) {
        mf = ensureCase(mf);
      }
      this.parsed = parse(mf);
      this.cache = {};
    }
    toDisplay() {
      if (!this.cache.displayed) this.cache.displayed = toDisplay(this.parsed);
      return this.cache.displayed;
    }
    toHtml() {
      if (!this.cache.html) {
        this.toDisplay();
        this.cache.html = toHtml(this.cache.displayed);
      }
      return this.cache.html;
    }
    toText() {
      if (!this.cache.text) {
        this.toDisplay();
        this.cache.text = toText(this.cache.displayed);
      }
      return this.cache.text;
    }
    toCanonicText() {
      if (!this.cache.canonicText) {
        this.cache.canonicText = new MF(this.toMF()).toText(this.cache.displayed);
      }
      return this.cache.canonicText;
    }
    toParts(options) {
      if (!this.cache.parts) {
        this.cache.parts = toParts(this.parsed, options);
      }
      return this.cache.parts;
    }

    /**
     * Returns an object with the global MF, global charge, monoisotopic mass and mass
     * as well as the same information for all the parts
     * @param {object} [options={}] options
     */
    getInfo() {
      let options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
      if (!this.cache.info) {
        this.toParts();
        this.cache.info = getInfo$1(this.cache.parts, options);
      }
      return this.cache.info;
    }

    /**
     * Returns an object with the elemental analysis
     */
    getEA() {
      if (!this.cache.ea) {
        this.toParts();
        this.cache.ea = getEA(this.cache.parts);
      }
      return this.cache.ea;
    }

    /**
     * Get the different elements for each part
     * @returns an array
     */
    getElements() {
      if (!this.cache.elements) {
        this.toParts();
        this.cache.elements = getElements(this.cache.parts);
      }
      return this.cache.elements;
    }

    /**
     * Returns an array with each atom and isotopic composition
     */
    getIsotopesInfo() {
      if (!this.cache.isotopesInfo) {
        this.toParts();
        this.cache.isotopesInfo = getIsotopesInfo(this.cache.parts);
      }
      return this.cache.isotopesInfo;
    }

    /**
     * Get a canonized MF
     */
    toMF() {
      if (!this.cache.mf) {
        this.toParts();
        this.cache.mf = partsToMF(this.cache.parts);
      }
      return this.cache.mf;
    }

    /**
     * Get a canonized MF
     */
    toNeutralMF() {
      if (!this.cache.neutralMF) {
        this.toParts();
        this.cache.neutralMF = partsToMF(this.cache.parts, {
          neutral: true
        });
      }
      return this.cache.neutralMF;
    }
    canonize() {
      this.toParts();
      this.cache.displayed = partsToDisplay(this.cache.parts);
      this.cache.html = undefined;
    }
  }

  function getMsem(em, charge) {
    if (charge > 0) {
      return em / charge - ELECTRON_MASS;
    } else if (charge < 0) {
      return em / (charge * -1) + ELECTRON_MASS;
    } else {
      return 0;
    }
  }

  /**
   * Returns an object containing:
   * {ms: {em, charge, ionization}, ionization: {}}
   * We return the ionization in order to know which one has been selected
   */

  function getMsInfo(entry) {
    let options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
    const {
      allowNeutralMolecules,
      ionization = {
        mf: '',
        em: 0,
        charge: 0
      },
      forceIonization = false,
      targetMass
    } = options;
    let realIonization = ionization;
    if (!forceIonization && entry.ionization && entry.ionization.mf !== '') {
      realIonization = entry.ionization;
    }
    let ms = {
      ionization: realIonization.mf,
      em: 0,
      charge: entry.charge + realIonization.charge
    };
    if (ms.charge !== 0) {
      ms.em = getMsem(entry.em + realIonization.em, ms.charge);
    } else if (allowNeutralMolecules) {
      ms.em = entry.em + realIonization.em;
    }
    if (targetMass) {
      ms.delta = targetMass - ms.em;
      ms.ppm = (targetMass - ms.em) / ms.em * 1e6;
    }
    return {
      ms,
      ionization: realIonization
    };
  }

  function mfDiff(mfString1, mfString2) {
    let mf1 = new MF(mfString1).getInfo().atoms;
    let mf2 = new MF(mfString2).getInfo().atoms;
    let atoms = Object.keys(mf1);
    Object.keys(mf2).forEach(atom => {
      if (!atoms.includes(atom)) atoms.push(atom);
    });
    let mf = '';
    for (let atom of atoms) {
      let diff = (mf1[atom] || 0) - (mf2[atom] || 0);
      if (diff) mf += atom + diff;
    }
    return new MF(mf).toMF();
  }

  function processRange(string, comment) {
    let options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
    const {
      limit
    } = options;
    let results = [];
    let parts = string.split(/(-?[0-9]+--?[0-9]+)/).filter(v => v); // remove empty parts

    let position = -1;
    let mfs = [];
    for (let i = 0; i < parts.length; i++) {
      let part = parts[i];
      if (!~part.search(/-?[0-9]--?[0-9]/)) {
        position++;
        mfs[position] = {
          mf: part,
          min: 1,
          max: 1
        };
      } else {
        let min = part.replace(/^(-?[0-9]*)-(-?[0-9]*)/, '$1') >> 0;
        let max = part.replace(/^(-?[0-9]*)-(-?[0-9]*)/, '$2') >> 0;
        mfs[position].min = Math.min(min, max);
        mfs[position].max = Math.max(min, max);
      }
    }
    let currents = new Array(mfs.length);
    for (let i = 0; i < currents.length; i++) {
      currents[i] = mfs[i].min;
    }
    position = 0;
    while (position < currents.length) {
      if (currents[position] < mfs[position].max) {
        results.push(getMF(mfs, currents, comment));
        currents[position]++;
        for (let i = 0; i < position; i++) {
          currents[i] = mfs[i].min;
        }
        position = 0;
      } else {
        position++;
      }
      if (results.length > limit) {
        throw Error(`processRange generates to many fragments (over ${limit})`);
      }
    }
    results.push(getMF(mfs, currents, comment));
    return results;
  }
  function getMF(mfs, currents, comment) {
    let mf = '';
    for (let i = 0; i < mfs.length; i++) {
      if (currents[i] === 0) {
        // TODO we need to remove from currents[i] till we reach another part of the MF
        mf += removeMFLastPart(mfs[i].mf);
      } else {
        mf += mfs[i].mf;
        if (currents[i] !== 1) {
          mf += currents[i];
        }
      }
    }
    if (comment) mf += `$${comment}`;
    return mf;
  }

  /*
     Allows to remove the last part of a MF. Useful when you have something with '0' times.
     C10H -> C10
     C10((Me)N) -> C10
     C10Ala -> C10
     C10Ala((Me)N) -> C10Ala
     */
  function removeMFLastPart(mf) {
    let parenthesis = 0;
    let start = true;
    for (let i = mf.length - 1; i >= 0; i--) {
      let ascii = mf.charCodeAt(i);
      if (ascii > 96 && ascii < 123) {
        // lowercase
        if (!start && !parenthesis) {
          return mf.substr(0, i + 1);
        }
      } else if (ascii > 64 && ascii < 91) {
        // uppercase
        if (!start && !parenthesis) {
          return mf.substr(0, i + 1);
        }
        start = false;
      } else if (ascii === 40) {
        // (
        parenthesis--;
        if (!parenthesis) return mf.substr(0, i);
      } else if (ascii === 41) {
        // )
        parenthesis++;
      } else {
        start = false;
        if (!parenthesis) return mf.substr(0, i + 1);
      }
    }
    return '';
  }

  function preprocessIonizations() {
    let ionizationsString = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : '';
    if (Array.isArray(ionizationsString)) return ionizationsString;
    let ionizations = ionizationsString.split(/ *[.,;\t\r\n]+ */);

    // it is allowed to have ranges in Ionizations. We need to explode them.

    let results = [];
    for (let ionization of ionizations) {
      let parts = processRange(ionization);
      for (let part of parts) {
        let info = new MF(part).getInfo();
        results.push({
          mf: part,
          em: info.monoisotopicMass,
          charge: info.charge,
          atoms: info.atoms
        });
      }
    }
    return results;
  }

  function preprocessRanges(ranges) {
    ranges = JSON.parse(JSON.stringify(ranges));
    if (typeof ranges === 'string') {
      // need to convert to ranges
      let parsed = parse(ranges.replace(/[\r\n\t ]/g, ''));
      let newRanges = [];
      let current = {
        mf: '',
        min: 1,
        max: 1
      };

      // example ClBr2(CH2)0-2NO
      // the idea is that has long as we don't have a range we don't really care
      // there is a limitation is that the range has to be first level of parenthesis
      let parenthesisLevel = 0;
      let currentMF = ''; // start at an atom first level or a parenthesis
      for (let item of parsed) {
        switch (item.kind) {
          case Kind.ATOM:
            if (parenthesisLevel === 0 && currentMF) {
              current.mf += currentMF;
              currentMF = '';
            }
            currentMF += item.value;
            break;
          case Kind.ISOTOPE:
            if (parenthesisLevel === 0 && currentMF) {
              current.mf += currentMF;
              currentMF = '';
            }
            currentMF += `[${item.value.isotope}${item.value.atom}]`;
            break;
          case Kind.MULTIPLIER:
            if (parenthesisLevel === 0 && currentMF) {
              current.mf += currentMF + item.value;
              currentMF = '';
            } else {
              currentMF += item.value;
            }
            break;
          case Kind.MULTIPLIER_RANGE:
            if (parenthesisLevel !== 0) {
              throw new Error('Range multiplier can only be at the first level');
            }
            newRanges.push({
              mf: currentMF,
              min: item.value.from,
              max: item.value.to
            });
            currentMF = '';
            break;
          case Kind.OPENING_PARENTHESIS:
            parenthesisLevel++;
            currentMF += '(';
            break;
          case Kind.CLOSING_PARENTHESIS:
            parenthesisLevel--;
            currentMF += ')';
            break;
          default:
            throw Error(`can not preprocess ${ranges}`);
        }
      }
      if (currentMF) {
        current.mf += currentMF;
      }
      if (current.mf) {
        newRanges.push(current);
      }
      ranges = newRanges;
    }
    let possibilities = [];
    for (let i = 0; i < ranges.length; i++) {
      let range = ranges[i];
      let min = range.min === undefined ? 0 : range.min;
      let max = range.max === undefined ? 1 : range.max;
      let possibility = {
        mf: range.mf,
        originalMinCount: min,
        // value defined by the user
        originalMaxCount: max,
        // value defined by the user
        currentMinCount: min,
        currentMaxCount: max,
        currentCount: min,
        currentMonoisotopicMass: 0,
        currentCharge: 0,
        currentUnsaturation: 0,
        initialOrder: i,
        minInnerMass: 0,
        maxInnerMass: 0,
        minInnerCharge: 0,
        maxInnerCharge: 0,
        minCharge: 0,
        maxCharge: 0,
        minMass: 0,
        maxMass: 0,
        innerCharge: false
      };
      possibilities.push(possibility);
      let info = new MF(range.mf).getInfo();
      possibility.em = range.em || info.monoisotopicMass;
      possibility.charge = range.charge || info.charge;
      possibility.unsaturation = range.unsaturation === undefined ? (info.unsaturation - 1) * 2 : range.unsaturation;
      possibility.atoms = info.atoms;
      if (possibility.mf !== info.mf) {
        possibility.isGroup = true;
      }
    }
    possibilities = possibilities.filter(r => r.originalMinCount !== 0 || r.originalMaxCount !== 0);

    // we will sort the way we analyse the data
    // 1. The one possibility parameter
    // 2. The charged part
    // 3. Decreasing em
    possibilities.sort((a, b) => {
      if (a.originalMinCount === a.originalMaxCount) return -1; // should be in front, they are 'static'
      if (b.originalMinCount === b.originalMaxCount) return 1;
      if (a.charge && b.charge) {
        if (Math.abs(a.charge) > Math.abs(b.charge)) return -1;
        if (Math.abs(a.charge) < Math.abs(b.charge)) return 1;
        return b.em - a.em;
      }
      if (a.charge) return -1;
      if (b.charge) return 1;
      return b.em - a.em;
    });

    // we calculate couple of fixed values

    for (let i = 0; i < possibilities.length; i++) {
      for (let j = i; j < possibilities.length; j++) {
        let possibility = possibilities[j];
        if (possibility.em > 0) {
          possibilities[i].minMass += possibility.em * possibility.originalMinCount;
          possibilities[i].maxMass += possibility.em * possibility.originalMaxCount;
        } else {
          possibilities[i].minMass += possibility.em * possibility.originalMaxCount;
          possibilities[i].maxMass += possibility.em * possibility.originalMinCount;
        }
        if (possibility.charge > 0) {
          possibilities[i].minCharge += possibility.charge * possibility.originalMinCount;
          possibilities[i].maxCharge += possibility.charge * possibility.originalMaxCount;
        } else {
          possibilities[i].minCharge += possibility.charge * possibility.originalMaxCount;
          possibilities[i].maxCharge += possibility.charge * possibility.originalMinCount;
        }
      }
    }
    for (let i = 0; i < possibilities.length; i++) {
      if (i < possibilities.length - 1) {
        let possibility = possibilities[i];
        let innerPossibility = possibilities[i + 1];
        possibility.minInnerMass = innerPossibility.minMass;
        possibility.maxInnerMass = innerPossibility.maxMass;
        possibility.minInnerCharge = innerPossibility.minCharge;
        possibility.maxInnerCharge = innerPossibility.maxCharge;
        if (possibility.minInnerCharge || possibility.maxInnerCharge) {
          possibility.innerCharge = true;
        }
      }
    }
    return possibilities;
  }

  function getRangesForFragment(ranges) {
    ranges = JSON.parse(JSON.stringify(ranges));
    if (typeof ranges === 'string') {
      // need to convert to ranges
      let parsed = parse(ranges.replace(/[\r\n\t ]/g, ''));
      let newRanges = [];

      // example ClBr2(CH2)0-2NO
      // the idea is that has long as we don't have a range we don't really care
      // there is a limitation is that the range has to be first level of parenthesis
      let parenthesisLevel = 0;
      let currentMF = ''; // start at an atom first level or a parenthesis
      for (let item of parsed) {
        switch (item.kind) {
          case Kind.ATOM:
            if (parenthesisLevel === 0 && currentMF) {
              newRanges.push({
                mf: currentMF
              });
              currentMF = '';
            }
            currentMF += item.value;
            break;
          case Kind.ISOTOPE:
            if (parenthesisLevel === 0 && currentMF) {
              newRanges.push({
                mf: currentMF
              });
              currentMF = '';
            }
            currentMF += `[${item.value.isotope}${item.value.atom}]`;
            break;
          case Kind.MULTIPLIER:
            if (parenthesisLevel === 0 && currentMF) {
              newRanges.push({
                mf: currentMF,
                max: item.value
              });
              currentMF = '';
            } else {
              currentMF += item.value;
            }
            break;
          case Kind.MULTIPLIER_RANGE:
            if (parenthesisLevel !== 0) {
              throw new Error('Range multiplier can only be at the first level');
            }
            newRanges.push({
              mf: currentMF,
              min: item.value.from,
              max: item.value.to
            });
            currentMF = '';
            break;
          case Kind.OPENING_PARENTHESIS:
            if (parenthesisLevel === 0 && currentMF) {
              newRanges.push({
                mf: currentMF
              });
              currentMF = '';
            }
            parenthesisLevel++;
            currentMF += '(';
            break;
          case Kind.CLOSING_PARENTHESIS:
            parenthesisLevel--;
            currentMF += ')';
            break;
          default:
            throw Error(`can not preprocess ${ranges}`);
        }
      }
      if (currentMF) {
        newRanges.push({
          mf: currentMF
        });
      }
      ranges = newRanges;
    }
    let possibilities = [];
    for (const range of ranges) {
      if (range.max === 0) continue;
      let max = range.max === undefined ? 1 : range.max;
      possibilities.push(`${range.mf}0-${max}`);
    }
    return possibilities.join(' ');
  }

  const mfLosses = {};
  ['Amp', 'Tmp', 'Cmp', 'Gmp', 'Ump'].forEach(nucleotide => {
    mfLosses[nucleotide] = {
      code: nucleotide.charAt(0),
      diff: mfDiff('Rmp', nucleotide)
    };
  });
  ['Damp', 'Dtmp', 'Dcmp', 'Dgmp', 'Dump'].forEach(nucleotide => {
    mfLosses[nucleotide] = {
      code: nucleotide.charAt(1).toUpperCase(),
      diff: mfDiff('Drmp', nucleotide)
    };
  });
  function baseLoss(nucleotide) {
    // any residue can loose a base
    let results = [];
    for (let key in mfLosses) {
      const base = mfLosses[key];
      if (nucleotide.includes(key)) {
        results.push(`${nucleotide}(${base.diff})$${base.code}*`);
      }
    }
    return results;
  }

  function addFiveTermBaseLoss(mfs, fiveTerm, i, options) {
    if (!options.abcdBaseLoss) return;
    let loss = baseLoss(fiveTerm);
    loss.forEach(mf => {
      if (options.a) {
        mfs.push(`${mf}`.replace('$', `O-1H-1$a${i} `));
      }
      if (options.b) {
        mfs.push(`${mf}`.replace('$', `H-1$b${i} `));
      }
      if (options.c) {
        mfs.push(`${mf}`.replace('$', `PO2$c${i} `));
      }
      if (options.d) {
        mfs.push(`${mf}`.replace('$', `PO3H2$d${i} `));
      }
    });
  }

  // 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

  function addInternalTerm(mfs, internal, ter3, ter5) {
    let options = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : {};
    if (options.aw) {
      // without base loss
      mfs.push(`HO${internal}O-1H-1$w${ter3}:a${ter5}`); // A W
    }

    if (options.bw) {
      // without base loss
      mfs.push(`HO${internal}H$w${ter3}:b${ter5}`); // B W
    }

    if (options.abw) {
      // with base loss
      let fragment = furanThreeTerm(internal);
      mfs.push(`HO${fragment}$w${ter3}:a${ter5}-B`); // A minus base - W
    }

    if (options.aby) {
      // with base loss
      let fragment = furanThreeTerm(internal);
      mfs.push(`O-2P-1${fragment}$y${ter3}:a${ter5}-B`); // A minus base - Y
    }
  }

  function addThreeTerm(mfs, threeTerm, i, options) {
    if (options.w) mfs.push(`HO${threeTerm}$w${i}`); // neutral ok
    if (options.x) mfs.push(`H-1${threeTerm}$x${i}`); // neutral ok
    if (options.y) mfs.push(`O-2P-1${threeTerm}$y${i}`); // neutral ok
    if (options.z) mfs.push(`O-3H-2P-1${threeTerm}$z${i}`); // neutral ok
    if (options.zch2) mfs.push(`O-3H-4C-1P-1${threeTerm}$z${i}-CH2`); // TODO to confirm
  }

  function addThreeTermBaseLoss(mfs, threeTerm, i, options) {
    if (!options.wxyzBaseLoss) return;
    let loss = baseLoss(threeTerm);
    loss.forEach(mf => {
      if (options.w) {
        mfs.push(`HO${mf}`.replace('$', `$w${i} `));
      }
      if (options.x) {
        mfs.push(`H-1${mf}`.replace('$', `$x${i} `));
      }
      if (options.y) {
        mfs.push(`O-2P-1${mf}`.replace('$', `$y${i} `));
      }
      if (options.z) {
        mfs.push(`O-3H-1P-1(+)${mf}`.replace('$', `$z${i} `));
      }
    });
  }

  function generateFragments(mf, options) {
    if (options === undefined) {
      options = {
        a: false,
        ab: false,
        b: false,
        c: false,
        d: false,
        dh2o: false,
        w: false,
        x: false,
        y: false,
        z: false,
        zch2: false,
        aw: false,
        bw: false,
        abw: false,
        aby: false,
        abcdBaseLoss: false,
        wxyzBaseLoss: false
      };
    }
    let mfs = [];
    // need to allow 0-9 to deal with neutral loss
    let mfparts = mf.replace(/([a-z)0-9])([A-Z][a-z](?=[a-z]))/g, '$1 $2').split(/ /);
    let fiveTerm = '';
    let threeTerm = '';
    if (mfparts[0].startsWith('(')) {
      fiveTerm += mfparts[0];
      mfparts = mfparts.splice(1);
    }
    if (mfparts[mfparts.length - 1].includes('(')) {
      threeTerm += mfparts[mfparts.length - 1].replace(/^[^()]*/, '');
      mfparts[mfparts.length - 1] = mfparts[mfparts.length - 1].replace(/\(.*/, '');
    }
    for (let ter5 = 1; ter5 < mfparts.length; ter5++) {
      fiveTerm += mfparts[ter5 - 1];
      threeTerm = mfparts[mfparts.length - ter5] + threeTerm;
      addFiveTerm(mfs, fiveTerm, ter5, options);
      addFiveTermBaseLoss(mfs, fiveTerm, ter5, options);
      addThreeTerm(mfs, threeTerm, ter5, options);
      addThreeTermBaseLoss(mfs, threeTerm, ter5, options);
    }
    for (let i = 1; i < mfparts.length - 1; i++) {
      let internal = '';
      for (let j = i; j < mfparts.length - 1; j++) {
        internal += mfparts[j];
        if (j > i) {
          addInternalTerm(mfs, internal, mfparts.length - i, j + 1, options);
        }
      }
    }
    return mfs;
  }

  var NucleotidePkg = /*#__PURE__*/Object.freeze({
    __proto__: null,
    sequenceToMF: sequenceToMF$1,
    generateFragments: generateFragments,
    furanThreeTerm: furanThreeTerm,
    baseLoss: baseLoss
  });

  // SOURCE: https://en.wikipedia.org/wiki/Amino_acid
  // Link for UTF8 code for modified: https://codepoints.net/search?sc=Grek
  const aminoAcids = [
  // Standard amino acids
  {
    name: 'Alanine',
    aa3: 'Ala',
    aa1: 'A',
    sc: {
      type: 'hydrophobic'
    },
    pKaC: 2.33,
    pKaN: 9.71
  }, {
    name: 'Arginine',
    aa3: 'Arg',
    aa1: 'R',
    sc: {
      type: 'positive',
      pKa: 12.1
    },
    pKaC: 2.03,
    pKaN: 9.0
  }, {
    name: 'Asparagine',
    aa3: 'Asn',
    aa1: 'N',
    sc: {
      type: 'polar'
    },
    pKaC: 2.13,
    pKaN: 9.05
  }, {
    name: 'Aspartic acid',
    aa3: 'Asp',
    aa1: 'D',
    sc: {
      type: 'negative',
      pKa: 3.71
    },
    pKaC: 1.95,
    pKaN: 9.66
  }, {
    name: 'Cysteine',
    aa3: 'Cys',
    aa1: 'C',
    sc: {
      type: 'special',
      pKa: 8.14
    },
    pKaC: 1.91,
    pKaN: 10.28
  }, {
    name: 'Glutamic acid',
    aa3: 'Glu',
    aa1: 'E',
    sc: {
      type: 'negative',
      pKa: 4.15
    },
    pKaC: 2.16,
    pKaN: 9.58
  }, {
    name: 'Glutamine',
    aa3: 'Gln',
    aa1: 'Q',
    sc: {
      type: 'polar'
    },
    pKaC: 2.18,
    pKaN: 9.0
  }, {
    name: 'Glycine',
    aa3: 'Gly',
    aa1: 'G',
    sc: {
      type: 'special'
    },
    pKaC: 2.34,
    pKaN: 9.58
  }, {
    name: 'Histidine',
    aa3: 'His',
    aa1: 'H',
    sc: {
      type: 'positive',
      pKa: 6.04
    },
    pKaC: 1.7,
    pKaN: 9.09
  }, {
    name: 'Isoleucine',
    aa3: 'Ile',
    aa1: 'I',
    sc: {
      type: 'hydrophobic'
    },
    pKaC: 2.26,
    pKaN: 9.6
  }, {
    name: 'Leucine',
    aa3: 'Leu',
    aa1: 'L',
    sc: {
      type: 'hydrophobic'
    },
    pKaC: 2.32,
    pKaN: 9.58
  }, {
    name: 'Lysine',
    aa3: 'Lys',
    aa1: 'K',
    sc: {
      type: 'positive',
      pKa: 10.67
    },
    pKaC: 2.15,
    pKaN: 9.16
  }, {
    name: 'Methionine',
    aa3: 'Met',
    aa1: 'M',
    sc: {
      type: 'hydrophobic'
    },
    pKaC: 2.16,
    pKaN: 9.08
  }, {
    name: 'Phenylalanine',
    aa3: 'Phe',
    aa1: 'F',
    sc: {
      type: 'hydrophobic'
    },
    pKaC: 2.18,
    pKaN: 9.09
  }, {
    name: 'Proline',
    aa3: 'Pro',
    aa1: 'P',
    sc: {
      type: 'special'
    },
    pKaC: 1.95,
    pKaN: 10.47
  }, {
    name: 'Serine',
    aa3: 'Ser',
    aa1: 'S',
    sc: {
      type: 'polar'
    },
    pKaC: 2.13,
    pKaN: 9.05
  }, {
    name: 'Threonine',
    aa3: 'Thr',
    aa1: 'T',
    sc: {
      type: 'polar'
    },
    pKaC: 2.2,
    pKaN: 8.96
  }, {
    name: 'Tryptophan',
    aa3: 'Trp',
    aa1: 'W',
    sc: {
      type: 'hydrophobic'
    },
    pKaC: 2.38,
    pKaN: 9.34
  }, {
    name: 'Tyrosine',
    aa3: 'Tyr',
    aa1: 'Y',
    sc: {
      type: 'hydrophobic'
    },
    pKaC: 2.24,
    pKaN: 9.04
  }, {
    name: 'Valine',
    aa3: 'Val',
    aa1: 'V',
    sc: {
      type: 'hydrophobic'
    },
    pKaC: 2.27,
    pKaN: 9.52
  },
  // Additional
  {
    name: 'Selenocysteine',
    aa3: 'Sec',
    aa1: 'U'
  }, {
    name: 'Pyrrolysine',
    aa3: 'Pyl',
    aa1: 'O'
  },
  // Ambiguous
  {
    name: 'Asparagine or aspartic acid',
    aa3: 'Asx',
    aa1: 'B'
  }, {
    name: 'Glutamine or glutamic acid',
    aa3: 'Glx',
    aa1: 'Z'
  }, {
    name: 'Leucine or isoleucine',
    aa3: 'Xle',
    aa1: 'J'
  }, {
    name: 'Unspecified or unknown',
    aa3: 'Xaa',
    aa1: 'X'
  }];

  // we will convert the data to an object to be much faster
  let aaObject = {};
  for (let i = 0; i < aminoAcids.length; i++) {
    aaObject[aminoAcids[i].aa3] = aminoAcids[i];
  }
  function calculateCharge$1(aas) {
    let pH = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 7.0;
    let combined = combine(aas);
    if (!combined) return;
    let charge = calculateForPh(combined, pH);
    return Math.round(charge * 1000) / 1000;
  }

  // this methods required an array of aas

  function calculateIEP$1(aas) {
    let combined = combine(aas);
    if (!combined) return;
    let first = 0;
    let last = 14;
    let current = 14;
    let previous = 0;
    let currentCharge;
    while (Math.abs(current - previous) > 0.0001) {
      previous = current;
      current = (last + first) / 2;
      currentCharge = calculateForPh(combined, current);
      if (currentCharge > 0) {
        first = current;
      } else if (currentCharge < 0) {
        last = current;
      } else {
        previous = current;
      }
    }
    return Math.round(current * 1000) / 1000;
  }
  function calculateChart(aas) {
    let combined = combine(aas);
    if (!combined) return;
    let y = [];
    let x = [];
    let yAbs = [];
    for (let i = 0; i <= 14; i = i + 0.01) {
      let charge = calculateForPh(combined, i);
      x.push(i);
      y.push(charge);
      yAbs.push(Math.abs(charge));
    }
    combined.x = x;
    combined.y = y;
    combined.yAbs = yAbs;
    return combined;
  }
  function calculateForPh(combined, pH) {
    let total = 0;
    total += 1 / (1 + 10 ** (pH - combined.first));
    total += -1 / (1 + 10 ** (combined.last - pH));
    for (let key in combined.acid) {
      total += -combined.acid[key] / (1 + 10 ** (aaObject[key].sc.pKa - pH));
    }
    for (let key in combined.basic) {
      total += combined.basic[key] / (1 + 10 ** (pH - aaObject[key].sc.pKa));
    }
    return total;
  }

  // we will combine the amino acids
  function combine(aas) {
    let combined = {};
    if (aaObject[aas[0]]) {
      combined.first = aaObject[aas[0]].pKaN;
    } else {
      return;
    }
    if (aaObject[aas[aas.length - 1]]) {
      combined.last = aaObject[aas[aas.length - 1]].pKaC;
    } else {
      return;
    }
    combined.basic = {};
    combined.acid = {};
    for (let i = 0; i < aas.length; i++) {
      let currentAA = aas[i];
      if (!aaObject[currentAA]) return;
      if (aaObject[currentAA].sc && aaObject[currentAA].sc.type) {
        if (aaObject[currentAA].sc.type === 'positive') {
          if (!combined.basic[currentAA]) {
            combined.basic[currentAA] = 0;
          }
          combined.basic[currentAA]++;
        } else if (aaObject[currentAA].sc.type === 'negative') {
          if (!combined.acid[currentAA]) {
            combined.acid[currentAA] = 0;
          }
          combined.acid[currentAA]++;
        }
      }
    }
    return combined;
  }

  /*
   We can generate a color based on iep
   0 -> 7 means that at pH 7 it is charged negatively (blue)
   7 -> 14 means that at pH7 it is charged positively (red)
   */
  function getColor(iep) {
    if (iep < 7) {
      if (iep < 3) iep = 3;
      let white = Math.round(255 - (7 - iep) * (200 / 4));
      return `rgb(${white},${white},255)`;
    } else if (iep > 7) {
      if (iep > 11) iep = 11;
      let white = Math.round(255 - (iep - 7) * (200 / 4));
      return `rgb(255,${white},${white})`;
    }
    return 'rgb(255,255,255)';
  }

  function splitPeptide(sequence) {
    let aas = sequence.replace(/([A-Z])/g, ' $1').split(/ /);
    let begin = 0;
    while (aas[begin] === '' || aas[begin] === 'H') {
      begin++;
    }
    let end = aas.length - 1;
    while (aas[end] === 'O' || aas[end] === 'H') {
      end--;
    }
    aas = aas.slice(begin, end + 1);
    return aas;
  }

  /**
   * Add neutral loss on natural amino acids
   * @param {*} mf
   * @returns
   */
  function allowNeutralLoss(mf) {
    if (Array.isArray(mf)) {
      for (let i = 0; i < mf.length; i++) {
        mf[i] = allowOneNeutralLoss(mf[i]);
      }
      return mf;
    } else {
      return allowOneNeutralLoss(mf);
    }
  }
  function allowOneNeutralLoss(mf) {
    mf = mf.replace(/(Ser|Thr|Asp|Glu)(?!\()/g, '$1(H-2O-1)0-1');
    mf = mf.replace(/(Arg|Lys|Asn|Gln)(?!\()/g, '$1(N-1H-3)0-1');
    return mf;
  }

  function getAA(code) {
    if (code.length === 1) {
      for (let i = 0; i < aminoAcids.length; i++) {
        if (aminoAcids[i].aa1 === code) {
          return aminoAcids[i];
        }
      }
    }
    if (code.length === 3) {
      for (let i = 0; i < aminoAcids.length; i++) {
        if (aminoAcids[i].aa3 === code) {
          return aminoAcids[i];
        }
      }
    }
  }

  // SOURCE: https://en.wikipedia.org/wiki/Amino_acid

  function chargePeptide(mf) {
    let options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
    if (options.pH === undefined) options.pH = 0;
    if (Array.isArray(mf)) {
      for (let i = 0; i < mf.length; i++) {
        mf[i] = chargeOnePeptide(mf[i], options);
      }
      return mf;
    } else {
      return chargeOnePeptide(mf, options);
    }
  }
  function chargeOnePeptide(mf, options) {
    let pH = options.pH;
    // we will allow to charge the peptide at a specific pH

    // first amino acids (N-terminal)
    if (mf.match(/^H[A-Z][a-z]{2}/)) {
      let firstAA = mf.replace(/^H([A-Z][a-z]{2}).*/, '$1');
      if (getAA(firstAA) && pH < getAA(firstAA).pKaN) {
        mf = mf.replace(/^H([^+])/, 'H+H$1');
      }
    }

    // last amino acids (C-terminal)
    if (mf.match(/[A-Z][a-z]{2}OH$/)) {
      let lastAA = mf.replace(/.*([A-Z][a-z]{2})OH$/, '$1');
      if (getAA(lastAA) && pH > getAA(lastAA).pKaC) {
        mf = mf.replace(/OH$/, 'O-');
      }
    }

    // basic AA
    if (pH < getAA('Arg').sc.pKa) mf = mf.replace(/(Arg)(?!\()/g, '$1(H+)');
    if (pH < getAA('His').sc.pKa) mf = mf.replace(/(His)(?!\()/g, '$1(H+)');
    if (pH < getAA('Lys').sc.pKa) mf = mf.replace(/(Lys)(?!\()/g, '$1(H+)');

    // acid AA
    if (pH > getAA('Asp').sc.pKa) mf = mf.replace(/(Asp)(?!\()/g, '$1(H-1-)');
    if (pH > getAA('Glu').sc.pKa) mf = mf.replace(/(Glu)(?!\()/g, '$1(H-1-)');
    if (pH > getAA('Cys').sc.pKa) mf = mf.replace(/(Cys)(?!\()/g, '$1(H-1-)');
    return mf;
  }

  function sequenceToMF(mf) {
    // 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
    // it could be a multiline mf !
    // if it is a multiline we could make some "tricks" ...

    let newMF = mf;
    // SEQRES   1 B  256  MET PRO VAL GLU ILE THR VAL LYS GLU LEU LEU GLU ALA
    // SEQRES   2 B  256  GLY VAL HIS PHE GLY HIS GLU ARG LYS ARG TRP ASN PRO
    // or
    // MET PRO VAL GLU ILE THR VAL LYS GLU LEU LEU GLU ALA
    // GLY VAL HIS PHE GLY HIS GLU ARG LYS ARG TRP ASN PRO
    if (mf.search(/[A-Z]{3} [A-Z]{3} [A-Z]{3}/) > -1) {
      // this is a PDB !
      let tmpmf = mf.replace(/[\r\n]+/g, ' ');
      tmpmf = tmpmf.replace(/(SEQRES|[0-9]+| [A-Z] | [0-9A-Z]{4-50})/g, '');
      // we need to correct the uppercase / lowercase
      let parts = tmpmf.split(' ');
      newMF = 'H';
      for (let i = 0; i < parts.length; i++) {
        newMF += parts[i].substr(0, 1) + parts[i].substr(1).toLowerCase();
      }
      newMF += 'OH';
    } else if (mf.includes('(') && isOneLetterCode(mf)) {
      // we expect one letter code with modification
      newMF = '';
      let nTerminal = 'H';
      let cTerminal = 'OH';
      let parenthesisCounter = 0;
      for (let i = 0; i < mf.length; i++) {
        let currentSymbol = mf[i];
        if (currentSymbol === '(' || currentSymbol === ')' || parenthesisCounter > 0) {
          if (currentSymbol === '(') {
            parenthesisCounter++;
            if (i === 0) nTerminal = '';
          }
          if (currentSymbol === ')') {
            parenthesisCounter--;
            if (i === mf.length - 1) cTerminal = '';
          }
          newMF += currentSymbol;
          continue;
        }
        newMF += convertAA1To3(currentSymbol);
      }
      newMF = nTerminal + newMF + cTerminal;
    } else if (mf.search(/[A-Z]{3}/) > -1 && mf.search(/[a-zA-Z][a-z0-9]/) === -1) {
      // UNIPROT
      //   370        380        390        400        410        420
      //GFKPNLRKTF VSGLFRESCG AHFYRGVDVK PFYIKKPVDN LFALMLILNR LRGWGVVGGM
      //
      //    430        440        450        460        470        480
      //SDPRLYKVWV RLSSQVPSMF FGGTDLAADY YVVSPPTAVS VYTKTPYGRL LADTRTSGFR
      // We remove all the number, all the spaces, etc
      newMF = `H${convertAA1To3(newMF.replace(/[^A-Z]/g, ''))}OH`;
    }
    return newMF;
  }
  function convertAA1To3(mf) {
    let newmf = '';
    for (let i = 0; i < mf.length; i++) {
      newmf += aa1To3(mf.charAt(i));
    }
    return newmf;
  }
  function aa1To3(code) {
    for (let i = 0; i < aminoAcids.length; i++) {
      if (aminoAcids[i].aa1 === code) {
        return aminoAcids[i].aa3;
      }
    }
    throw new Error(`Invalid 1 letter code: ${code}`);
  }

  // mf can contain as well parenthesis. We need to check if it is not yet a correct molecular formula
  function isOneLetterCode(mf) {
    let parenthesisLevel = 0;
    for (let char of mf) {
      if (parenthesisLevel === 0 && char.match(/[a-z]/)) return false;
      if (char === '(') parenthesisLevel++;
      if (char === ')') parenthesisLevel--;
    }
    return true;
  }

  function generatePeptideFragments(mf, options) {
    if (options === undefined) {
      options = {
        a: false,
        b: true,
        c: false,
        x: false,
        y: true,
        z: false,
        i: false,
        ya: false,
        yb: false,
        yc: false,
        zc: false
      };
    }
    options.maxInternal = options.maxInternal || Number.MAX_VALUE;
    options.minInternal = options.minInternal || 0;
    let mfs = [];
    // need to allow 0-9 to deal with neutral loss
    let mfparts = mf.replace(/([a-z)0-9])([A-Z][a-z](?=[a-z]))/g, '$1 $2').split(/ /);
    let nTerm = '';
    let cTerm = '';
    if (mfparts[0].startsWith('(')) {
      nTerm += mfparts[0];
      mfparts = mfparts.splice(1);
    }
    if (mfparts[mfparts.length - 1].includes('(')) {
      cTerm += mfparts[mfparts.length - 1].replace(/^[^()]*/, '');
      mfparts[mfparts.length - 1] = mfparts[mfparts.length - 1].replace(/\(.*/, '');
    }
    for (let i = 1; i < mfparts.length; i++) {
      nTerm += mfparts[i - 1];
      cTerm = mfparts[mfparts.length - i] + cTerm;
      addNTerm(mfs, nTerm, i, options);
      addCTerm(mfs, cTerm, i, options);
      if (options.i) mfs.push(`${mfparts[i]}HC-1O-1(+1)$i:${mfparts[i]}`);
      if (options.ya || options.yb || options.yc || options.zc) {
        // we have double fragmentations
        for (let j = i + 1; j < Math.min(mfparts.length, options.maxInternal + i + 1); j++) {
          let iTerm = '';
          if (j - i >= options.minInternal) {
            for (let k = i; k < j; k++) {
              iTerm += mfparts[k];
            }
            addITerm(mfs, iTerm, mfparts.length - i, j, options);
          }
        }
      }
    }

    // todo does this make sense ??? I think we should remote those 3 lines
    if (mfs.length === 0) {
      mfs = mfs.concat([mf]);
    }
    return mfs;
  }
  function addNTerm(mfs, nTerm, i, options) {
    if (options.a) mfs.push(`${nTerm}C-1O-1(+1)$a${i}`);
    if (options.b) mfs.push(`${nTerm}(+1)$b${i}`);
    if (options.c) mfs.push(`${nTerm}NH3(+1)$c${i}`);
  }
  function addITerm(mfs, iTerm, i, j, options) {
    if (options.ya) mfs.push(`H${iTerm}C-1O-1(+1)$a${j}y${i}`);
    if (options.yb) mfs.push(`H${iTerm}(+1)$b${j}y${i}`);
    if (options.yc) mfs.push(`H${iTerm}NH3(+1)$c${j}y${i}`);
    if (options.zc) mfs.push(`N-1${iTerm}NH3(+1)$c${j}z${i}`);
  }
  function addCTerm(mfs, cTerm, i, options) {
    if (options.x) mfs.push(`CO(+1)${cTerm}$x${i}`);
    if (options.y) mfs.push(`H2(+1)${cTerm}$y${i}`);
    if (options.z) mfs.push(`N-1H-1(+1)${cTerm}$z${i}`);
  }

  /*
  Iotuibs:
  * minMissed (default: 0)
  * maxMissed (default: 0)
  * minResidue: 0;
  * maxResidue: infinity
  * enzyme: chymotrypsin, trypsin, glucph4, glucph8, thermolysin, cyanogenbromide : Mandatory, no default value !
   */

  function digestPeptide(sequence) {
    let options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
    sequence = sequence.replace(/^H([^a-z])/, '$1').replace(/OH$/, '');
    options.enzyme = options.enzyme || 'trypsin';
    if (options.minMissed === undefined) options.minMissed = 0;
    if (options.maxMissed === undefined) options.maxMissed = 0;
    if (options.minResidue === undefined) options.minResidue = 0;
    if (options.maxResidue === undefined) options.maxResidue = Number.MAX_VALUE;
    let regexp = getRegexp(options.enzyme);
    let fragments = sequence.replace(regexp, '$1 ').split(/ /).filter(entry => entry);
    {
      let from = 0;
      for (let i = 0; i < fragments.length; i++) {
        let nbResidue = fragments[i].replace(/([A-Z][a-z][a-z])/g, ' $1').split(/ /).filter(entry => entry).length;
        fragments[i] = {
          sequence: fragments[i],
          nbResidue,
          from,
          to: from + nbResidue - 1
        };
        from += nbResidue;
      }
    }
    let results = [];
    for (let i = 0; i < fragments.length - options.minMissed; i++) {
      for (let j = options.minMissed; j <= Math.min(options.maxMissed, fragments.length - i - 1); j++) {
        let fragment = '';
        let nbResidue = 0;
        for (let k = i; k <= i + j; k++) {
          fragment += fragments[k].sequence;
          nbResidue += fragments[k].nbResidue;
        }
        let from = fragments[i].from + 1;
        let to = fragments[i + j].to + 1;
        if (fragment && nbResidue >= options.minResidue && nbResidue <= options.maxResidue) {
          results.push(`H${fragment}OH$D${from}>${to}`);
        }
      }
    }
    return results;
  }
  function getRegexp(enzyme) {
    switch (enzyme.toLowerCase().replace(/[^a-z0-9]/g, '')) {
      case 'chymotrypsin':
        return /(Phe|Tyr|Trp)(?!Pro)/g;
      case 'trypsin':
        return /(Lys|Arg)(?!Pro)/g;
      case 'lysc':
        return /(Lys)(?!Pro)/g;
      case 'glucph4':
        return /(Glu)(?!Pro|Glu)/g;
      case 'glucph8':
        return /(Asp|Glu)(?!Pro|Glu)/g;
      case 'thermolysin':
        // N-term of  Leu, Phe, Val, Ile, Ala, Met
        return /()(?=Ile|Leu|Val|Ala|Met|Phe)/g;
      case 'cyanogenbromide':
        return /(Met)/g;
      case 'any':
        return /()(?=[A-Z][a-z][a-z])/g;
      default:
        throw new Error(`Digestion enzyme: ${enzyme} is unknown`);
    }
  }

  function getInfo() {
    return aminoAcids;
  }

  // sequence should be in the "right" format like HAlaGlyProOH

  function calculateIEP(sequence) {
    let aas = splitPeptide(sequence);
    let result = calculateIEP$1(aas);
    return result;
  }
  function calculateIEPChart(sequence) {
    let aas = splitPeptide(sequence);
    let result = calculateChart(aas);
    return result;
  }
  function getColorForIEP(iep) {
    return getColor(iep);
  }
  function calculateCharge(sequence, ph) {
    let aas = splitPeptide(sequence);
    return calculateCharge$1(aas, ph);
  }

  var PeptidePkg = /*#__PURE__*/Object.freeze({
    __proto__: null,
    getInfo: getInfo,
    calculateIEP: calculateIEP,
    calculateIEPChart: calculateIEPChart,
    getColorForIEP: getColorForIEP,
    calculateCharge: calculateCharge,
    allowNeutralLoss: allowNeutralLoss,
    chargePeptide: chargePeptide,
    sequenceToMF: sequenceToMF,
    generatePeptideFragments: generatePeptideFragments,
    digestPeptide: digestPeptide,
    splitPeptide: splitPeptide
  });

  const ALTERNATIVES = ['', '¹', '²', '³', '⁴', '⁵', '⁶', '⁷', '⁸', '⁹'];
  const SYMBOLS = ['Θ', 'Δ', 'Λ', 'Φ', 'Ω', 'Γ', 'Χ'];
  let currentSymbol = 0;

  /**
   * Code that allows to split a sequence of amino acids or nucleotides natural or non natural
   * @param {string} [sequence]
   * @param {object} [options={}]
   * @param {string} [options.kind] - peptide, rna, ds-dna or dna. Default if contains U: rna, otherwise ds-dna
   * @param {string} [options.fivePrime=monophosphate] - alcohol, monophosphate, diphosphate, triphosphate
   * @param {string} [options.circular=false]
   */

  function appendResidues(data, sequence) {
    let options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
    const {
      kind = 'peptide'
    } = options;
    currentSymbol = 0;
    // we normalize the sequence to 3 letter codes

    if (kind === 'peptide') {
      sequence = sequenceToMF(sequence);
    } else {
      sequence = sequenceToMF$1(sequence, options);
    }
    const result = {
      begin: '',
      end: '',
      residues: []
    };
    const STATE_BEGIN = 0;
    const STATE_MIDDLE = 1;
    const STATE_END = 2;
    let parenthesisLevel = 0;
    let state = STATE_BEGIN; // as long as we don't have an uppercase followed by 2 lowercases
    for (let i = 0; i < sequence.length; i++) {
      let currentChar = sequence.charAt(i);
      let nextChar = i < sequence.length - 1 ? sequence.charAt(i + 1) : '';
      let nextNextChar = i < sequence.length - 2 ? sequence.charAt(i + 2) : '';
      if (state === STATE_BEGIN && currentChar.match(/[A-Z]/) && nextChar.match(/[a-z]/) && nextNextChar.match(/[a-z]/) && parenthesisLevel === 0) {
        state = STATE_MIDDLE;
      }
      if (state === STATE_MIDDLE && !sequence.substring(i).match(/[A-Z][a-z][a-z]/) && !currentChar.match(/[a-z]/) && parenthesisLevel === 0) {
        state = STATE_END;
      } else if (currentChar.match(/[A-Z]/) && nextChar.match(/[a-z]/) && nextNextChar.match(/[a-z]/) && parenthesisLevel === 0) {
        result.residues.push('');
      }
      switch (state) {
        case STATE_BEGIN:
          result.begin = result.begin + currentChar;
          break;
        case STATE_MIDDLE:
          result.residues[result.residues.length - 1] = result.residues[result.residues.length - 1] + currentChar;
          break;
        case STATE_END:
          result.end = result.end + currentChar;
          break;
      }
      if (currentChar === '(') {
        parenthesisLevel++;
      } else if (currentChar === ')') {
        parenthesisLevel--;
      }
    }

    // we process all the residues
    let alternatives = {};
    let replacements = {};
    for (let i = 0; i < result.residues.length; i++) {
      let label = result.residues[i];
      let residue = {
        value: label,
        results: {
          begin: [],
          end: []
        }
      };
      residue.fromBegin = i + 1;
      residue.fromEnd = result.residues.length - i;
      residue.kind = 'residue';
      if (label.includes('(')) {
        getModifiedReplacement(label, residue, alternatives, replacements);
      } else if (groupsObject[label] && groupsObject[label].oneLetter) {
        residue.label = groupsObject[label].oneLetter;
      } else {
        getUnknownReplacement(label, residue, replacements);
      }
      result.residues[i] = residue;
    }
    result.begin = removeStartEndParenthesis(result.begin);
    result.end = removeStartEndParenthesis(result.end);
    if (result.begin.length > 2) {
      let label = options.kind === 'peptide' ? 'Nter' : "5'";
      replacements[result.begin] = {
        label
      };
      result.begin = label;
    }
    if (result.end.length > 2) {
      let label = options.kind === 'peptide' ? 'Cter' : "3'";
      replacements[result.end] = {
        label
      };
      result.end = label;
    }
    result.begin = {
      label: result.begin,
      kind: 'begin'
    };
    result.end = {
      label: result.end,
      kind: 'end'
    };
    result.alternatives = alternatives;
    result.replacements = replacements;
    result.all = [result.begin].concat(result.residues, [result.end]);
    result.all.forEach(entry => {
      entry.info = {
        nbOver: 0,
        nbUnder: 0
      };
    });
    data.residues = result;
  }
  function getUnknownReplacement(unknownResidue, residue, replacements) {
    if (!replacements[unknownResidue]) {
      replacements[unknownResidue] = {
        label: SYMBOLS[currentSymbol] || '?',
        id: unknownResidue
      };
    }
    currentSymbol++;
    residue.replaced = true;
    residue.label = replacements[unknownResidue].label;
  }
  function getModifiedReplacement(modifiedResidue, residue, alternatives, replacements) {
    if (!replacements[modifiedResidue]) {
      let position = modifiedResidue.indexOf('(');
      let residueCode = modifiedResidue.substring(0, position);
      let modification = removeStartEndParenthesis(modifiedResidue.substring(position));
      if (groupsObject[residueCode] && groupsObject[residueCode].alternativeOneLetter) {
        let alternativeOneLetter = groupsObject[residueCode].alternativeOneLetter;
        if (!alternatives[alternativeOneLetter]) {
          alternatives[alternativeOneLetter] = {
            count: 1
          };
        } else {
          alternatives[alternativeOneLetter].count++;
        }
        replacements[modifiedResidue] = {
          label: ALTERNATIVES[alternatives[alternativeOneLetter].count - 1] + alternativeOneLetter,
          residue: residueCode,
          modification
        };
      } else {
        getUnknownReplacement(modifiedResidue, residue, replacements);
      }
    }
    residue.replaced = true;
    residue.label = replacements[modifiedResidue].label;
  }
  function removeStartEndParenthesis(mf) {
    if (mf[0] === '(' && mf[mf.length - 1] === ')') {
      return mf.substring(1, mf.length - 1);
    }
    return mf;
  }

  const methods$1 = {};
  const names = [];
  function registerMethods(name, m) {
    if (Array.isArray(name)) {
      for (const _name of name) {
        registerMethods(_name, m);
      }
      return;
    }
    if (typeof name === 'object') {
      for (const _name in name) {
        registerMethods(_name, name[_name]);
      }
      return;
    }
    addMethodNames(Object.getOwnPropertyNames(m));
    methods$1[name] = Object.assign(methods$1[name] || {}, m);
  }
  function getMethodsFor(name) {
    return methods$1[name] || {};
  }
  function getMethodNames() {
    return [...new Set(names)];
  }
  function addMethodNames(_names) {
    names.push(..._names);
  }

  // Map function
  function map(array, block) {
    let i;
    const il = array.length;
    const result = [];
    for (i = 0; i < il; i++) {
      result.push(block(array[i]));
    }
    return result;
  } // Filter function

  function filter(array, block) {
    let i;
    const il = array.length;
    const result = [];
    for (i = 0; i < il; i++) {
      if (block(array[i])) {
        result.push(array[i]);
      }
    }
    return result;
  } // Degrees to radians

  function radians(d) {
    return d % 360 * Math.PI / 180;
  } // Radians to degrees

  function camelCase(s) {
    return s.toLowerCase().replace(/-(.)/g, function (m, g) {
      return g.toUpperCase();
    });
  } // Convert camel cased string to dash separated

  function unCamelCase(s) {
    return s.replace(/([A-Z])/g, function (m, g) {
      return '-' + g.toLowerCase();
    });
  } // Capitalize first letter of a string

  function capitalize(s) {
    return s.charAt(0).toUpperCase() + s.slice(1);
  } // Calculate proportional width and height values when necessary

  function proportionalSize(element, width, height, box) {
    if (width == null || height == null) {
      box = box || element.bbox();
      if (width == null) {
        width = box.width / box.height * height;
      } else if (height == null) {
        height = box.height / box.width * width;
      }
    }
    return {
      width: width,
      height: height
    };
  }
  /**
   * This function adds support for string origins.
   * It searches for an origin in o.origin o.ox and o.originX.
   * This way, origin: {x: 'center', y: 50} can be passed as well as ox: 'center', oy: 50
  **/

  function getOrigin(o, element) {
    const origin = o.origin; // First check if origin is in ox or originX

    let ox = o.ox != null ? o.ox : o.originX != null ? o.originX : 'center';
    let oy = o.oy != null ? o.oy : o.originY != null ? o.originY : 'center'; // Then check if origin was used and overwrite in that case

    if (origin != null) {
      [ox, oy] = Array.isArray(origin) ? origin : typeof origin === 'object' ? [origin.x, origin.y] : [origin, origin];
    } // Make sure to only call bbox when actually needed

    const condX = typeof ox === 'string';
    const condY = typeof oy === 'string';
    if (condX || condY) {
      const {
        height,
        width,
        x,
        y
      } = element.bbox(); // And only overwrite if string was passed for this specific axis

      if (condX) {
        ox = ox.includes('left') ? x : ox.includes('right') ? x + width : x + width / 2;
      }
      if (condY) {
        oy = oy.includes('top') ? y : oy.includes('bottom') ? y + height : y + height / 2;
      }
    } // Return the origin as it is if it wasn't a string

    return [ox, oy];
  }

  // Default namespaces
  const svg = 'http://www.w3.org/2000/svg';
  const html = 'http://www.w3.org/1999/xhtml';
  const xmlns = 'http://www.w3.org/2000/xmlns/';
  const xlink = 'http://www.w3.org/1999/xlink';
  const svgjs = 'http://svgjs.dev/svgjs';
  const globals = {
    window: typeof window === 'undefined' ? null : window,
    document: typeof document === 'undefined' ? null : document
  };
  class Base {// constructor (node/*, {extensions = []} */) {
    //   // this.tags = []
    //   //
    //   // for (let extension of extensions) {
    //   //   extension.setup.call(this, node)
    //   //   this.tags.push(extension.name)
    //   // }
    // }
  }
  const elements = {};
  const root = '___SYMBOL___ROOT___'; // Method for element creation

  function create(name) {
    let ns = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : svg;
    // create element
    return globals.document.createElementNS(ns, name);
  }
  function makeInstance(element) {
    let isHTML = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
    if (element instanceof Base) return element;
    if (typeof element === 'object') {
      return adopter(element);
    }
    if (element == null) {
      return new elements[root]();
    }
    if (typeof element === 'string' && element.charAt(0) !== '<') {
      return adopter(globals.document.querySelector(element));
    } // Make sure, that HTML elements are created with the correct namespace

    const wrapper = isHTML ? globals.document.createElement('div') : create('svg');
    wrapper.innerHTML = element; // We can use firstChild here because we know,
    // that the first char is < and thus an element

    element = adopter(wrapper.firstChild); // make sure, that element doesnt have its wrapper attached

    wrapper.removeChild(wrapper.firstChild);
    return element;
  }
  function nodeOrNew(name, node) {
    return node instanceof globals.window.Node ? node : create(name);
  } // Adopt existing svg elements

  function adopt(node) {
    // check for presence of node
    if (!node) return null; // make sure a node isn't already adopted

    if (node.instance instanceof Base) return node.instance;
    if (node.nodeName === '#document-fragment') {
      return new elements.Fragment(node);
    } // initialize variables

    let className = capitalize(node.nodeName || 'Dom'); // Make sure that gradients are adopted correctly

    if (className === 'LinearGradient' || className === 'RadialGradient') {
      className = 'Gradient'; // Fallback to Dom if element is not known
    } else if (!elements[className]) {
      className = 'Dom';
    }
    return new elements[className](node);
  }
  let adopter = adopt;
  function register(element) {
    let name = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : element.name;
    let asRoot = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;
    elements[name] = element;
    if (asRoot) elements[root] = element;
    addMethodNames(Object.getOwnPropertyNames(element.prototype));
    return element;
  }
  function getClass(name) {
    return elements[name];
  } // Element id sequence

  let did = 1000; // Get next named element id

  function eid(name) {
    return 'Svgjs' + capitalize(name) + did++;
  } // Deep new id assignment

  function assignNewId(node) {
    // do the same for SVG child nodes as well
    for (let i = node.children.length - 1; i >= 0; i--) {
      assignNewId(node.children[i]);
    }
    if (node.id) {
      node.id = eid(node.nodeName);
      return node;
    }
    return node;
  } // Method for extending objects

  function extend(modules, methods) {
    let key, i;
    modules = Array.isArray(modules) ? modules : [modules];
    for (i = modules.length - 1; i >= 0; i--) {
      for (key in methods) {
        modules[i].prototype[key] = methods[key];
      }
    }
  }
  function wrapWithAttrCheck(fn) {
    return function () {
      for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
        args[_key] = arguments[_key];
      }
      const o = args[args.length - 1];
      if (o && o.constructor === Object && !(o instanceof Array)) {
        return fn.apply(this, args.slice(0, -1)).attr(o);
      } else {
        return fn.apply(this, args);
      }
    };
  }
  function siblings() {
    return this.parent().children();
  } // Get the current position siblings

  function position() {
    return this.parent().index(this);
  } // Get the next element (will return null if there is none)

  function next() {
    return this.siblings()[this.position() + 1];
  } // Get the next element (will return null if there is none)

  function prev() {
    return this.siblings()[this.position() - 1];
  } // Send given element one step forward

  function forward() {
    const i = this.position();
    const p = this.parent(); // move node one step forward

    p.add(this.remove(), i + 1);
    return this;
  } // Send given element one step backward

  function backward() {
    const i = this.position();
    const p = this.parent();
    p.add(this.remove(), i ? i - 1 : 0);
    return this;
  } // Send given element all the way to the front

  function front() {
    const p = this.parent(); // Move node forward

    p.add(this.remove());
    return this;
  } // Send given element all the way to the back

  function back() {
    const p = this.parent(); // Move node back

    p.add(this.remove(), 0);
    return this;
  } // Inserts a given element before the targeted element

  function before(element) {
    element = makeInstance(element);
    element.remove();
    const i = this.position();
    this.parent().add(element, i);
    return this;
  } // Inserts a given element after the targeted element

  function after(element) {
    element = makeInstance(element);
    element.remove();
    const i = this.position();
    this.parent().add(element, i + 1);
    return this;
  }
  function insertBefore(element) {
    element = makeInstance(element);
    element.before(this);
    return this;
  }
  function insertAfter(element) {
    element = makeInstance(element);
    element.after(this);
    return this;
  }
  registerMethods('Dom', {
    siblings,
    position,
    next,
    prev,
    forward,
    backward,
    front,
    back,
    before,
    after,
    insertBefore,
    insertAfter
  });

  // Parse unit value
  const numberAndUnit = /^([+-]?(\d+(\.\d*)?|\.\d+)(e[+-]?\d+)?)([a-z%]*)$/i; // Parse hex value

  const hex = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i; // Parse rgb value

  const rgb = /rgb\((\d+),(\d+),(\d+)\)/; // Parse reference id

  const reference = /(#[a-z_][a-z0-9\-_]*)/i; // splits a transformation chain

  const transforms = /\)\s*,?\s*/; // Whitespace

  const whitespace = /\s/g; // Test hex value

  const isHex = /^#[a-f0-9]{3}$|^#[a-f0-9]{6}$/i; // Test rgb value

  const isRgb = /^rgb\(/; // Test for blank string

  const isBlank = /^(\s+)?$/; // Test for numeric string

  const isNumber = /^[+-]?(\d+(\.\d*)?|\.\d+)(e[+-]?\d+)?$/i; // Test for image url

  const isImage = /\.(jpg|jpeg|png|gif|svg)(\?[^=]+.*)?/i; // split at whitespace and comma

  const delimiter = /[\s,]+/; // Test for path letter

  const isPathLetter = /[MLHVCSQTAZ]/i;
  function classes() {
    const attr = this.attr('class');
    return attr == null ? [] : attr.trim().split(delimiter);
  } // Return true if class exists on the node, false otherwise

  function hasClass(name) {
    return this.classes().indexOf(name) !== -1;
  } // Add class to the node

  function addClass(name) {
    if (!this.hasClass(name)) {
      const array = this.classes();
      array.push(name);
      this.attr('class', array.join(' '));
    }
    return this;
  } // Remove class from the node

  function removeClass(name) {
    if (this.hasClass(name)) {
      this.attr('class', this.classes().filter(function (c) {
        return c !== name;
      }).join(' '));
    }
    return this;
  } // Toggle the presence of a class on the node

  function toggleClass(name) {
    return this.hasClass(name) ? this.removeClass(name) : this.addClass(name);
  }
  registerMethods('Dom', {
    classes,
    hasClass,
    addClass,
    removeClass,
    toggleClass
  });
  function css(style, val) {
    const ret = {};
    if (arguments.length === 0) {
      // get full style as object
      this.node.style.cssText.split(/\s*;\s*/).filter(function (el) {
        return !!el.length;
      }).forEach(function (el) {
        const t = el.split(/\s*:\s*/);
        ret[t[0]] = t[1];
      });
      return ret;
    }
    if (arguments.length < 2) {
      // get style properties as array
      if (Array.isArray(style)) {
        for (const name of style) {
          const cased = camelCase(name);
          ret[cased] = this.node.style[cased];
        }
        return ret;
      } // get style for property

      if (typeof style === 'string') {
        return this.node.style[camelCase(style)];
      } // set styles in object

      if (typeof style === 'object') {
        for (const name in style) {
          // set empty string if null/undefined/'' was given
          this.node.style[camelCase(name)] = style[name] == null || isBlank.test(style[name]) ? '' : style[name];
        }
      }
    } // set style for property

    if (arguments.length === 2) {
      this.node.style[camelCase(style)] = val == null || isBlank.test(val) ? '' : val;
    }
    return this;
  } // Show element

  function show() {
    return this.css('display', '');
  } // Hide element

  function hide() {
    return this.css('display', 'none');
  } // Is element visible?

  function visible() {
    return this.css('display') !== 'none';
  }
  registerMethods('Dom', {
    css,
    show,
    hide,
    visible
  });
  function data(a, v, r) {
    if (a == null) {
      // get an object of attributes
      return this.data(map(filter(this.node.attributes, el => el.nodeName.indexOf('data-') === 0), el => el.nodeName.slice(5)));
    } else if (a instanceof Array) {
      const data = {};
      for (const key of a) {
        data[key] = this.data(key);
      }
      return data;
    } else if (typeof a === 'object') {
      for (v in a) {
        this.data(v, a[v]);
      }
    } else if (arguments.length < 2) {
      try {
        return JSON.parse(this.attr('data-' + a));
      } catch (e) {
        return this.attr('data-' + a);
      }
    } else {
      this.attr('data-' + a, v === null ? null : r === true || typeof v === 'string' || typeof v === 'number' ? v : JSON.stringify(v));
    }
    return this;
  }
  registerMethods('Dom', {
    data
  });
  function remember(k, v) {
    // remember every item in an object individually
    if (typeof arguments[0] === 'object') {
      for (const key in k) {
        this.remember(key, k[key]);
      }
    } else if (arguments.length === 1) {
      // retrieve memory
      return this.memory()[k];
    } else {
      // store memory
      this.memory()[k] = v;
    }
    return this;
  } // Erase a given memory

  function forget() {
    if (arguments.length === 0) {
      this._memory = {};
    } else {
      for (let i = arguments.length - 1; i >= 0; i--) {
        delete this.memory()[arguments[i]];
      }
    }
    return this;
  } // This triggers creation of a new hidden class which is not performant
  // However, this function is not rarely used so it will not happen frequently
  // Return local memory object

  function memory() {
    return this._memory = this._memory || {};
  }
  registerMethods('Dom', {
    remember,
    forget,
    memory
  });
  function sixDigitHex(hex) {
    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;
  }
  function componentHex(component) {
    const integer = Math.round(component);
    const bounded = Math.max(0, Math.min(255, integer));
    const hex = bounded.toString(16);
    return hex.length === 1 ? '0' + hex : hex;
  }
  function is(object, space) {
    for (let i = space.length; i--;) {
      if (object[space[i]] == null) {
        return false;
      }
    }
    return true;
  }
  function getParameters(a, b) {
    const params = is(a, 'rgb') ? {
      _a: a.r,
      _b: a.g,
      _c: a.b,
      _d: 0,
      space: 'rgb'
    } : is(a, 'xyz') ? {
      _a: a.x,
      _b: a.y,
      _c: a.z,
      _d: 0,
      space: 'xyz'
    } : is(a, 'hsl') ? {
      _a: a.h,
      _b: a.s,
      _c: a.l,
      _d: 0,
      space: 'hsl'
    } : is(a, 'lab') ? {
      _a: a.l,
      _b: a.a,
      _c: a.b,
      _d: 0,
      space: 'lab'
    } : is(a, 'lch') ? {
      _a: a.l,
      _b: a.c,
      _c: a.h,
      _d: 0,
      space: 'lch'
    } : is(a, 'cmyk') ? {
      _a: a.c,
      _b: a.m,
      _c: a.y,
      _d: a.k,
      space: 'cmyk'
    } : {
      _a: 0,
      _b: 0,
      _c: 0,
      space: 'rgb'
    };
    params.space = b || params.space;
    return params;
  }
  function cieSpace(space) {
    if (space === 'lab' || space === 'xyz' || space === 'lch') {
      return true;
    } else {
      return false;
    }
  }
  function hueToRgb(p, q, t) {
    if (t < 0) t += 1;
    if (t > 1) t -= 1;
    if (t < 1 / 6) return p + (q - p) * 6 * t;
    if (t < 1 / 2) return q;
    if (t < 2 / 3) return p + (q - p) * (2 / 3 - t) * 6;
    return p;
  }
  class Color {
    constructor() {
      this.init(...arguments);
    } // Test if given value is a color

    static isColor(color) {
      return color && (color instanceof Color || this.isRgb(color) || this.test(color));
    } // Test if given value is an rgb object

    static isRgb(color) {
      return color && typeof color.r === 'number' && typeof color.g === 'number' && typeof color.b === 'number';
    }
    /*
    Generating random colors
    */

    static random() {
      let mode = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 'vibrant';
      let t = arguments.length > 1 ? arguments[1] : undefined;
      // Get the math modules
      const {
        random,
        round,
        sin,
        PI: pi
      } = Math; // Run the correct generator

      if (mode === 'vibrant') {
        const l = (81 - 57) * random() + 57;
        const c = (83 - 45) * random() + 45;
        const h = 360 * random();
        const color = new Color(l, c, h, 'lch');
        return color;
      } else if (mode === 'sine') {
        t = t == null ? random() : t;
        const r = round(80 * sin(2 * pi * t / 0.5 + 0.01) + 150);
        const g = round(50 * sin(2 * pi * t / 0.5 + 4.6) + 200);
        const b = round(100 * sin(2 * pi * t / 0.5 + 2.3) + 150);
        const color = new Color(r, g, b);
        return color;
      } else if (mode === 'pastel') {
        const l = (94 - 86) * random() + 86;
        const c = (26 - 9) * random() + 9;
        const h = 360 * random();
        const color = new Color(l, c, h, 'lch');
        return color;
      } else if (mode === 'dark') {
        const l = 10 + 10 * random();
        const c = (125 - 75) * random() + 86;
        const h = 360 * random();
        const color = new Color(l, c, h, 'lch');
        return color;
      } else if (mode === 'rgb') {
        const r = 255 * random();
        const g = 255 * random();
        const b = 255 * random();
        const color = new Color(r, g, b);
        return color;
      } else if (mode === 'lab') {
        const l = 100 * random();
        const a = 256 * random() - 128;
        const b = 256 * random() - 128;
        const color = new Color(l, a, b, 'lab');
        return color;
      } else if (mode === 'grey') {
        const grey = 255 * random();
        const color = new Color(grey, grey, grey);
        return color;
      } else {
        throw new Error('Unsupported random color mode');
      }
    } // Test if given value is a color string

    static test(color) {
      return typeof color === 'string' && (isHex.test(color) || isRgb.test(color));
    }
    cmyk() {
      // Get the rgb values for the current color
      const {
        _a,
        _b,
        _c
      } = this.rgb();
      const [r, g, b] = [_a, _b, _c].map(v => v / 255); // Get the cmyk values in an unbounded format

      const k = Math.min(1 - r, 1 - g, 1 - b);
      if (k === 1) {
        // Catch the black case
        return new Color(0, 0, 0, 1, 'cmyk');
      }
      const c = (1 - r - k) / (1 - k);
      const m = (1 - g - k) / (1 - k);
      const y = (1 - b - k) / (1 - k); // Construct the new color

      const color = new Color(c, m, y, k, 'cmyk');
      return color;
    }
    hsl() {
      // Get the rgb values
      const {
        _a,
        _b,
        _c
      } = this.rgb();
      const [r, g, b] = [_a, _b, _c].map(v => v / 255); // Find the maximum and minimum values to get the lightness

      const max = Math.max(r, g, b);
      const min = Math.min(r, g, b);
      const l = (max + min) / 2; // If the r, g, v values are identical then we are grey

      const isGrey = max === min; // Calculate the hue and saturation

      const delta = max - min;
      const s = isGrey ? 0 : l > 0.5 ? delta / (2 - max - min) : delta / (max + min);
      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

      const color = new Color(360 * h, 100 * s, 100 * l, 'hsl');
      return color;
    }
    init() {
      let a = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 0;
      let b = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;
      let c = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 0;
      let d = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : 0;
      let space = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : 'rgb';
      // This catches the case when a falsy value is passed like ''
      a = !a ? 0 : a; // Reset all values in case the init function is rerun with new color space

      if (this.space) {
        for (const component in this.space) {
          delete this[this.space[component]];
        }
      }
      if (typeof a === 'number') {
        // Allow for the case that we don't need d...
        space = typeof d === 'string' ? d : space;
        d = typeof d === 'string' ? 0 : d; // Assign the values straight to the color

        Object.assign(this, {
          _a: a,
          _b: b,
          _c: c,
          _d: d,
          space
        }); // If the user gave us an array, make the color from it
      } else if (a instanceof Array) {
        this.space = b || (typeof a[3] === 'string' ? a[3] : a[4]) || 'rgb';
        Object.assign(this, {
          _a: a[0],
          _b: a[1],
          _c: a[2],
          _d: a[3] || 0
        });
      } else if (a instanceof Object) {
        // Set the object up and assign its values directly
        const values = getParameters(a, b);
        Object.assign(this, values);
      } else if (typeof a === 'string') {
        if (isRgb.test(a)) {
          const noWhitespace = a.replace(whitespace, '');
          const [_a, _b, _c] = rgb.exec(noWhitespace).slice(1, 4).map(v => parseInt(v));
          Object.assign(this, {
            _a,
            _b,
            _c,
            _d: 0,
            space: 'rgb'
          });
        } else if (isHex.test(a)) {
          const hexParse = v => parseInt(v, 16);
          const [, _a, _b, _c] = hex.exec(sixDigitHex(a)).map(hexParse);
          Object.assign(this, {
            _a,
            _b,
            _c,
            _d: 0,
            space: 'rgb'
          });
        } else throw Error('Unsupported string format, can\'t construct Color');
      } // Now add the components as a convenience

      const {
        _a,
        _b,
        _c,
        _d
      } = this;
      const components = this.space === 'rgb' ? {
        r: _a,
        g: _b,
        b: _c
      } : this.space === 'xyz' ? {
        x: _a,
        y: _b,
        z: _c
      } : this.space === 'hsl' ? {
        h: _a,
        s: _b,
        l: _c
      } : this.space === 'lab' ? {
        l: _a,
        a: _b,
        b: _c
      } : this.space === 'lch' ? {
        l: _a,
        c: _b,
        h: _c
      } : this.space === 'cmyk' ? {
        c: _a,
        m: _b,
        y: _c,
        k: _d
      } : {};
      Object.assign(this, components);
    }
    lab() {
      // Get the xyz color
      const {
        x,
        y,
        z
      } = this.xyz(); // Get the lab components

      const l = 116 * y - 16;
      const a = 500 * (x - y);
      const b = 200 * (y - z); // Construct and return a new color

      const color = new Color(l, a, b, 'lab');
      return color;
    }
    lch() {
      // Get the lab color directly
      const {
        l,
        a,
        b
      } = this.lab(); // Get the chromaticity and the hue using polar coordinates

      const c = Math.sqrt(a ** 2 + b ** 2);
      let h = 180 * Math.atan2(b, a) / Math.PI;
      if (h < 0) {
        h *= -1;
        h = 360 - h;
      } // Make a new color and return it

      const color = new Color(l, c, h, 'lch');
      return color;
    }
    /*
    Conversion Methods
    */

    rgb() {
      if (this.space === 'rgb') {
        return this;
      } else if (cieSpace(this.space)) {
        // Convert to the xyz color space
        let {
          x,
          y,
          z
        } = this;
        if (this.space === 'lab' || this.space === 'lch') {
          // Get the values in the lab space
          let {
            l,
            a,
            b
          } = this;
          if (this.space === 'lch') {
            const {
              c,
              h
            } = this;
            const dToR = Math.PI / 180;
            a = c * Math.cos(dToR * h);
            b = c * Math.sin(dToR * h);
          } // Undo the nonlinear function

          const yL = (l + 16) / 116;
          const xL = a / 500 + yL;
          const zL = yL - b / 200; // Get the xyz values

          const ct = 16 / 116;
          const mx = 0.008856;
          const nm = 7.787;
          x = 0.95047 * (xL ** 3 > mx ? xL ** 3 : (xL - ct) / nm);
          y = 1.00000 * (yL ** 3 > mx ? yL ** 3 : (yL - ct) / nm);
          z = 1.08883 * (zL ** 3 > mx ? zL ** 3 : (zL - ct) / nm);
        } // Convert xyz to unbounded rgb values

        const rU = x * 3.2406 + y * -1.5372 + z * -0.4986;
        const gU = x * -0.9689 + y * 1.8758 + z * 0.0415;
        const bU = x * 0.0557 + y * -0.2040 + z * 1.0570; // Convert the values to true rgb values

        const pow = Math.pow;
        const bd = 0.0031308;
        const r = rU > bd ? 1.055 * pow(rU, 1 / 2.4) - 0.055 : 12.92 * rU;
        const g = gU > bd ? 1.055 * pow(gU, 1 / 2.4) - 0.055 : 12.92 * gU;
        const b = bU > bd ? 1.055 * pow(bU, 1 / 2.4) - 0.055 : 12.92 * bU; // Make and return the color

        const color = new Color(255 * r, 255 * g, 255 * b);
        return color;
      } else if (this.space === 'hsl') {
        // https://bgrins.github.io/TinyColor/docs/tinycolor.html
        // Get the current hsl values
        let {
          h,
          s,
          l
        } = this;
        h /= 360;
        s /= 100;
        l /= 100; // If we are grey, then just make the color directly

        if (s === 0) {
          l *= 255;
          const color = new Color(l, l, l);
          return color;
        } // TODO I have no idea what this does :D If you figure it out, tell me!

        const q = l < 0.5 ? l * (1 + s) : l + s - l * s;
        const p = 2 * l - q; // Get the rgb values

        const r = 255 * hueToRgb(p, q, h + 1 / 3);
        const g = 255 * hueToRgb(p, q, h);
        const b = 255 * hueToRgb(p, q, h - 1 / 3); // Make a new color

        const color = new Color(r, g, b);
        return color;
      } else if (this.space === 'cmyk') {
        // https://gist.github.com/felipesabino/5066336
        // Get the normalised cmyk values
        const {
          c,
          m,
          y,
          k
        } = this; // Get the rgb values

        const r = 255 * (1 - Math.min(1, c * (1 - k) + k));
        const g = 255 * (1 - Math.min(1, m * (1 - k) + k));
        const b = 255 * (1 - Math.min(1, y * (1 - k) + k)); // Form the color and return it

        const color = new Color(r, g, b);
        return color;
      } else {
        return this;
      }
    }
    toArray() {
      const {
        _a,
        _b,
        _c,
        _d,
        space
      } = this;
      return [_a, _b, _c, _d, space];
    }
    toHex() {
      const [r, g, b] = this._clamped().map(componentHex);
      return `#${r}${g}${b}`;
    }
    toRgb() {
      const [rV, gV, bV] = this._clamped();
      const string = `rgb(${rV},${gV},${bV})`;
      return string;
    }
    toString() {
      return this.toHex();
    }
    xyz() {
      // Normalise the red, green and blue values
      const {
        _a: r255,
        _b: g255,
        _c: b255
      } = this.rgb();
      const [r, g, b] = [r255, g255, b255].map(v => v / 255); // Convert to the lab rgb space

      const rL = r > 0.04045 ? Math.pow((r + 0.055) / 1.055, 2.4) : r / 12.92;
      const gL = g > 0.04045 ? Math.pow((g + 0.055) / 1.055, 2.4) : g / 12.92;
      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

      const xU = (rL * 0.4124 + gL * 0.3576 + bL * 0.1805) / 0.95047;
      const yU = (rL * 0.2126 + gL * 0.7152 + bL * 0.0722) / 1.00000;
      const zU = (rL * 0.0193 + gL * 0.1192 + bL * 0.9505) / 1.08883; // Get the proper xyz values by applying the bounding

      const x = xU > 0.008856 ? Math.pow(xU, 1 / 3) : 7.787 * xU + 16 / 116;
      const y = yU > 0.008856 ? Math.pow(yU, 1 / 3) : 7.787 * yU + 16 / 116;
      const z = zU > 0.008856 ? Math.pow(zU, 1 / 3) : 7.787 * zU + 16 / 116; // Make and return the color

      const color = new Color(x, y, z, 'xyz');
      return color;
    }
    /*
    Input and Output methods
    */

    _clamped() {
      const {
        _a,
        _b,
        _c
      } = this.rgb();
      const {
        max,
        min,
        round
      } = Math;
      const format = v => max(0, min(round(v), 255));
      return [_a, _b, _c].map(format);
    }
    /*
    Constructing colors
    */
  }

  class Point {
    // Initialize
    constructor() {
      this.init(...arguments);
    } // Clone point

    clone() {
      return new Point(this);
    }
    init(x, y) {
      const base = {
        x: 0,
        y: 0
      }; // ensure source as object

      const source = Array.isArray(x) ? {
        x: x[0],
        y: x[1]
      } : typeof x === 'object' ? {
        x: x.x,
        y: x.y
      } : {
        x: x,
        y: y
      }; // merge source

      this.x = source.x == null ? base.x : source.x;
      this.y = source.y == null ? base.y : source.y;
      return this;
    }
    toArray() {
      return [this.x, this.y];
    }
    transform(m) {
      return this.clone().transformO(m);
    } // Transform point with matrix

    transformO(m) {
      if (!Matrix.isMatrixLike(m)) {
        m = new Matrix(m);
      }
      const {
        x,
        y
      } = this; // Perform the matrix multiplication

      this.x = m.a * x + m.c * y + m.e;
      this.y = m.b * x + m.d * y + m.f;
      return this;
    }
  }
  function point(x, y) {
    return new Point(x, y).transform(this.screenCTM().inverse());
  }
  function closeEnough(a, b, threshold) {
    return Math.abs(b - a) < (threshold || 1e-6);
  }
  class Matrix {
    constructor() {
      this.init(...arguments);
    }
    static formatTransforms(o) {
      // Get all of the parameters required to form the matrix
      const flipBoth = o.flip === 'both' || o.flip === true;
      const flipX = o.flip && (flipBoth || o.flip === 'x') ? -1 : 1;
      const flipY = o.flip && (flipBoth || o.flip === 'y') ? -1 : 1;
      const skewX = o.skew && o.skew.length ? o.skew[0] : isFinite(o.skew) ? o.skew : isFinite(o.skewX) ? o.skewX : 0;
      const skewY = o.skew && o.skew.length ? o.skew[1] : isFinite(o.skew) ? o.skew : isFinite(o.skewY) ? o.skewY : 0;
      const scaleX = o.scale && o.scale.length ? o.scale[0] * flipX : isFinite(o.scale) ? o.scale * flipX : isFinite(o.scaleX) ? o.scaleX * flipX : flipX;
      const scaleY = o.scale && o.scale.length ? o.scale[1] * flipY : isFinite(o.scale) ? o.scale * flipY : isFinite(o.scaleY) ? o.scaleY * flipY : flipY;
      const shear = o.shear || 0;
      const theta = o.rotate || o.theta || 0;
      const origin = new Point(o.origin || o.around || o.ox || o.originX, o.oy || o.originY);
      const ox = origin.x;
      const oy = origin.y; // We need Point to be invalid if nothing was passed because we cannot default to 0 here. Thats why NaN

      const position = new Point(o.position || o.px || o.positionX || NaN, o.py || o.positionY || NaN);
      const px = position.x;
      const py = position.y;
      const translate = new Point(o.translate || o.tx || o.translateX, o.ty || o.translateY);
      const tx = translate.x;
      const ty = translate.y;
      const relative = new Point(o.relative || o.rx || o.relativeX, o.ry || o.relativeY);
      const rx = relative.x;
      const ry = relative.y; // Populate all of the values

      return {
        scaleX,
        scaleY,
        skewX,
        skewY,
        shear,
        theta,
        rx,
        ry,
        tx,
        ty,
        ox,
        oy,
        px,
        py
      };
    }
    static fromArray(a) {
      return {
        a: a[0],
        b: a[1],
        c: a[2],
        d: a[3],
        e: a[4],
        f: a[5]
      };
    }
    static isMatrixLike(o) {
      return o.a != null || o.b != null || o.c != null || o.d != null || o.e != null || o.f != null;
    } // left matrix, right matrix, target matrix which is overwritten

    static matrixMultiply(l, r, o) {
      // Work out the product directly
      const a = l.a * r.a + l.c * r.b;
      const b = l.b * r.a + l.d * r.b;
      const c = l.a * r.c + l.c * r.d;
      const d = l.b * r.c + l.d * r.d;
      const e = l.e + l.a * r.e + l.c * r.f;
      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

      o.a = a;
      o.b = b;
      o.c = c;
      o.d = d;
      o.e = e;
      o.f = f;
      return o;
    }
    around(cx, cy, matrix) {
      return this.clone().aroundO(cx, cy, matrix);
    } // Transform around a center point

    aroundO(cx, cy, matrix) {
      const dx = cx || 0;
      const dy = cy || 0;
      return this.translateO(-dx, -dy).lmultiplyO(matrix).translateO(dx, dy);
    } // Clones this matrix

    clone() {
      return new Matrix(this);
    } // Decomposes this matrix into its affine parameters

    decompose() {
      let cx = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 0;
      let cy = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;
      // Get the parameters from the matrix
      const a = this.a;
      const b = this.b;
      const c = this.c;
      const d = this.d;
      const e = this.e;
      const f = this.f; // Figure out if the winding direction is clockwise or counterclockwise

      const determinant = a * d - b * c;
      const ccw = determinant > 0 ? 1 : -1; // Since we only shear in x, we can use the x basis to get the x scale
      // and the rotation of the resulting matrix

      const sx = ccw * Math.sqrt(a * a + b * b);
      const thetaRad = Math.atan2(ccw * b, ccw * a);
      const theta = 180 / Math.PI * thetaRad;
      const ct = Math.cos(thetaRad);
      const st = Math.sin(thetaRad); // We can then solve the y basis vector simultaneously to get the other
      // two affine parameters directly from these parameters

      const lam = (a * c + b * d) / determinant;
      const sy = c * sx / (lam * a - b) || d * sx / (lam * b + a); // Use the translations

      const tx = e - cx + cx * ct * sx + cy * (lam * ct * sx - st * sy);
      const ty = f - cy + cx * st * sx + cy * (lam * st * sx + ct * sy); // Construct the decomposition and return it

      return {
        // Return the affine parameters
        scaleX: sx,
        scaleY: sy,
        shear: lam,
        rotate: theta,
        translateX: tx,
        translateY: ty,
        originX: cx,
        originY: cy,
        // Return the matrix parameters
        a: this.a,
        b: this.b,
        c: this.c,
        d: this.d,
        e: this.e,
        f: this.f
      };
    } // Check if two matrices are equal

    equals(other) {
      if (other === this) return true;
      const comp = new Matrix(other);
      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);
    } // Flip matrix on x or y, at a given offset

    flip(axis, around) {
      return this.clone().flipO(axis, around);
    }
    flipO(axis, around) {
      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
    } // Initialize

    init(source) {
      const base = Matrix.fromArray([1, 0, 0, 1, 0, 0]); // ensure source as object

      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

      this.a = source.a != null ? source.a : base.a;
      this.b = source.b != null ? source.b : base.b;
      this.c = source.c != null ? source.c : base.c;
      this.d = source.d != null ? source.d : base.d;
      this.e = source.e != null ? source.e : base.e;
      this.f = source.f != null ? source.f : base.f;
      return this;
    }
    inverse() {
      return this.clone().inverseO();
    } // Inverses matrix

    inverseO() {
      // Get the current parameters out of the matrix
      const a = this.a;
      const b = this.b;
      const c = this.c;
      const d = this.d;
      const e = this.e;
      const f = this.f; // Invert the 2x2 matrix in the top left

      const det = a * d - b * c;
      if (!det) throw new Error('Cannot invert ' + this); // Calculate the top 2x2 matrix

      const na = d / det;
      const nb = -b / det;
      const nc = -c / det;
      const nd = a / det; // Apply the inverted matrix to the top right

      const ne = -(na * e + nc * f);
      const nf = -(nb * e + nd * f); // Construct the inverted matrix

      this.a = na;
      this.b = nb;
      this.c = nc;
      this.d = nd;
      this.e = ne;
      this.f = nf;
      return this;
    }
    lmultiply(matrix) {
      return this.clone().lmultiplyO(matrix);
    }
    lmultiplyO(matrix) {
      const r = this;
      const l = matrix instanceof Matrix ? matrix : new Matrix(matrix);
      return Matrix.matrixMultiply(l, r, this);
    } // Left multiplies by the given matrix

    multiply(matrix) {
      return this.clone().multiplyO(matrix);
    }
    multiplyO(matrix) {
      // Get the matrices
      const l = this;
      const r = matrix instanceof Matrix ? matrix : new Matrix(matrix);
      return Matrix.matrixMultiply(l, r, this);
    } // Rotate matrix

    rotate(r, cx, cy) {
      return this.clone().rotateO(r, cx, cy);
    }
    rotateO(r) {
      let cx = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;
      let cy = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 0;
      // Convert degrees to radians
      r = radians(r);
      const cos = Math.cos(r);
      const sin = Math.sin(r);
      const {
        a,
        b,
        c,
        d,
        e,
        f
      } = this;
      this.a = a * cos - b * sin;
      this.b = b * cos + a * sin;
      this.c = c * cos - d * sin;
      this.d = d * cos + c * sin;
      this.e = e * cos - f * sin + cy * sin - cx * cos + cx;
      this.f = f * cos + e * sin - cx * sin - cy * cos + cy;
      return this;
    } // Scale matrix

    scale(x, y, cx, cy) {
      return this.clone().scaleO(...arguments);
    }
    scaleO(x) {
      let y = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : x;
      let cx = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 0;
      let cy = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : 0;
      // Support uniform scaling
      if (arguments.length === 3) {
        cy = cx;
        cx = y;
        y = x;
      }
      const {
        a,
        b,
        c,
        d,
        e,
        f
      } = this;
      this.a = a * x;
      this.b = b * y;
      this.c = c * x;
      this.d = d * y;
      this.e = e * x - cx * x + cx;
      this.f = f * y - cy * y + cy;
      return this;
    } // Shear matrix

    shear(a, cx, cy) {
      return this.clone().shearO(a, cx, cy);
    }
    shearO(lx) {
      let cy = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 0;
      const {
        a,
        b,
        c,
        d,
        e,
        f
      } = this;
      this.a = a + b * lx;
      this.c = c + d * lx;
      this.e = e + f * lx - cy * lx;
      return this;
    } // Skew Matrix

    skew(x, y, cx, cy) {
      return this.clone().skewO(...arguments);
    }
    skewO(x) {
      let y = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : x;
      let cx = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 0;
      let cy = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : 0;
      // support uniformal skew
      if (arguments.length === 3) {
        cy = cx;
        cx = y;
        y = x;
      } // Convert degrees to radians

      x = radians(x);
      y = radians(y);
      const lx = Math.tan(x);
      const ly = Math.tan(y);
      const {
        a,
        b,
        c,
        d,
        e,
        f
      } = this;
      this.a = a + b * lx;
      this.b = b + a * ly;
      this.c = c + d * lx;
      this.d = d + c * ly;
      this.e = e + f * lx - cy * lx;
      this.f = f + e * ly - cx * ly;
      return this;
    } // SkewX

    skewX(x, cx, cy) {
      return this.skew(x, 0, cx, cy);
    } // SkewY

    skewY(y, cx, cy) {
      return this.skew(0, y, cx, cy);
    }
    toArray() {
      return [this.a, this.b, this.c, this.d, this.e, this.f];
    } // Convert matrix to string

    toString() {
      return 'matrix(' + this.a + ',' + this.b + ',' + this.c + ',' + this.d + ',' + this.e + ',' + this.f + ')';
    } // Transform a matrix into another matrix by manipulating the space

    transform(o) {
      // Check if o is a matrix and then left multiply it directly
      if (Matrix.isMatrixLike(o)) {
        const matrix = new Matrix(o);
        return matrix.multiplyO(this);
      } // Get the proposed transformations and the current transformations

      const t = Matrix.formatTransforms(o);
      const current = this;
      const {
        x: ox,
        y: oy
      } = new Point(t.ox, t.oy).transform(current); // Construct the resulting matrix

      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

      if (isFinite(t.px) || isFinite(t.py)) {
        const origin = new Point(ox, oy).transform(transformer); // TODO: Replace t.px with isFinite(t.px)
        // Doesnt work because t.px is also 0 if it wasnt passed

        const dx = isFinite(t.px) ? t.px - origin.x : 0;
        const dy = isFinite(t.py) ? t.py - origin.y : 0;
        transformer.translateO(dx, dy);
      } // Translate now after positioning

      transformer.translateO(t.tx, t.ty);
      return transformer;
    } // Translate matrix

    translate(x, y) {
      return this.clone().translateO(x, y);
    }
    translateO(x, y) {
      this.e += x || 0;
      this.f += y || 0;
      return this;
    }
    valueOf() {
      return {
        a: this.a,
        b: this.b,
        c: this.c,
        d: this.d,
        e: this.e,
        f: this.f
      };
    }
  }
  function ctm() {
    return new Matrix(this.node.getCTM());
  }
  function screenCTM() {
    /* https://bugzilla.mozilla.org/show_bug.cgi?id=1344537
       This is needed because FF does not return the transformation matrix
       for the inner coordinate system when getScreenCTM() is called on nested svgs.
       However all other Browsers do that */
    if (typeof this.isRoot === 'function' && !this.isRoot()) {
      const rect = this.rect(1, 1);
      const m = rect.node.getScreenCTM();
      rect.remove();
      return new Matrix(m);
    }
    return new Matrix(this.node.getScreenCTM());
  }
  register(Matrix, 'Matrix');
  function parser() {
    // Reuse cached element if possible
    if (!parser.nodes) {
      const svg = makeInstance().size(2, 0);
      svg.node.style.cssText = ['opacity: 0', 'position: absolute', 'left: -100%', 'top: -100%', 'overflow: hidden'].join(';');
      svg.attr('focusable', 'false');
      svg.attr('aria-hidden', 'true');
      const path = svg.path().node;
      parser.nodes = {
        svg,
        path
      };
    }
    if (!parser.nodes.svg.node.parentNode) {
      const b = globals.document.body || globals.document.documentElement;
      parser.nodes.svg.addTo(b);
    }
    return parser.nodes;
  }
  function isNulledBox(box) {
    return !box.width && !box.height && !box.x && !box.y;
  }
  function domContains(node) {
    return node === globals.document || (globals.document.documentElement.contains || function (node) {
      // This is IE - it does not support contains() for top-level SVGs
      while (node.parentNode) {
        node = node.parentNode;
      }
      return node === globals.document;
    }).call(globals.document.documentElement, node);
  }
  class Box {
    constructor() {
      this.init(...arguments);
    }
    addOffset() {
      // offset by window scroll position, because getBoundingClientRect changes when window is scrolled
      this.x += globals.window.pageXOffset;
      this.y += globals.window.pageYOffset;
      return new Box(this);
    }
    init(source) {
      const base = [0, 0, 0, 0];
      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;
      this.x = source[0] || 0;
      this.y = source[1] || 0;
      this.width = this.w = source[2] || 0;
      this.height = this.h = source[3] || 0; // Add more bounding box properties

      this.x2 = this.x + this.w;
      this.y2 = this.y + this.h;
      this.cx = this.x + this.w / 2;
      this.cy = this.y + this.h / 2;
      return this;
    }
    isNulled() {
      return isNulledBox(this);
    } // Merge rect box with another, return a new instance

    merge(box) {
      const x = Math.min(this.x, box.x);
      const y = Math.min(this.y, box.y);
      const width = Math.max(this.x + this.width, box.x + box.width) - x;
      const height = Math.max(this.y + this.height, box.y + box.height) - y;
      return new Box(x, y, width, height);
    }
    toArray() {
      return [this.x, this.y, this.width, this.height];
    }
    toString() {
      return this.x + ' ' + this.y + ' ' + this.width + ' ' + this.height;
    }
    transform(m) {
      if (!(m instanceof Matrix)) {
        m = new Matrix(m);
      }
      let xMin = Infinity;
      let xMax = -Infinity;
      let yMin = Infinity;
      let yMax = -Infinity;
      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)];
      pts.forEach(function (p) {
        p = p.transform(m);
        xMin = Math.min(xMin, p.x);
        xMax = Math.max(xMax, p.x);
        yMin = Math.min(yMin, p.y);
        yMax = Math.max(yMax, p.y);
      });
      return new Box(xMin, yMin, xMax - xMin, yMax - yMin);
    }
  }
  function getBox(el, getBBoxFn, retry) {
    let box;
    try {
      // Try to get the box with the provided function
      box = getBBoxFn(el.node); // If the box is worthless and not even in the dom, retry
      // by throwing an error here...

      if (isNulledBox(box) && !domContains(el.node)) {
        throw new Error('Element not in the dom');
      }
    } catch (e) {
      // ... and calling the retry handler here
      box = retry(el);
    }
    return box;
  }
  function bbox() {
    // Function to get bbox is getBBox()
    const getBBox = node => node.getBBox(); // Take all measures so that a stupid browser renders the element
    // so we can get the bbox from it when we try again

    const retry = el => {
      try {
        const clone = el.clone().addTo(parser().svg).show();
        const box = clone.node.getBBox();
        clone.remove();
        return box;
      } catch (e) {
        // We give up...
        throw new Error(`Getting bbox of element "${el.node.nodeName}" is not possible: ${e.toString()}`);
      }
    };
    const box = getBox(this, getBBox, retry);
    const bbox = new Box(box);
    return bbox;
  }
  function rbox(el) {
    const getRBox = node => node.getBoundingClientRect();
    const retry = el => {
      // There is no point in trying tricks here because if we insert the element into the dom ourselves
      // it obviously will be at the wrong position
      throw new Error(`Getting rbox of element "${el.node.nodeName}" is not possible`);
    };
    const box = getBox(this, getRBox, retry);
    const rbox = new Box(box); // If an element was passed, we want the bbox in the coordinate system of that element

    if (el) {
      return rbox.transform(el.screenCTM().inverseO());
    } // Else we want it in absolute screen coordinates
    // Therefore we need to add the scrollOffset

    return rbox.addOffset();
  } // Checks whether the given point is inside the bounding box

  function inside(x, y) {
    const box = this.bbox();
    return x > box.x && y > box.y && x < box.x + box.width && y < box.y + box.height;
  }
  registerMethods({
    viewbox: {
      viewbox(x, y, width, height) {
        // act as getter
        if (x == null) return new Box(this.attr('viewBox')); // act as setter

        return this.attr('viewBox', new Box(x, y, width, height));
      },
      zoom(level, point) {
        // Its best to rely on the attributes here and here is why:
        // clientXYZ: Doesn't work on non-root svgs because they dont have a CSSBox (silly!)
        // getBoundingClientRect: Doesn't work because Chrome just ignores width and height of nested svgs completely
        //                        that means, their clientRect is always as big as the content.
        //                        Furthermore this size is incorrect if the element is further transformed by its parents
        // computedStyle: Only returns meaningful values if css was used with px. We dont go this route here!
        // getBBox: returns the bounding box of its content - that doesnt help!
        let {
          width,
          height
        } = this.attr(['width', 'height']); // Width and height is a string when a number with a unit is present which we can't use
        // So we try clientXYZ

        if (!width && !height || typeof width === 'string' || typeof height === 'string') {
          width = this.node.clientWidth;
          height = this.node.clientHeight;
        } // Giving up...

        if (!width || !height) {
          throw new Error('Impossible to get absolute width and height. Please provide an absolute width and height attribute on the zooming element');
        }
        const v = this.viewbox();
        const zoomX = width / v.width;
        const zoomY = height / v.height;
        const zoom = Math.min(zoomX, zoomY);
        if (level == null) {
          return zoom;
        }
        let zoomAmount = zoom / level; // Set the zoomAmount to the highest value which is safe to process and recover from
        // The * 100 is a bit of wiggle room for the matrix transformation

        if (zoomAmount === Infinity) zoomAmount = Number.MAX_SAFE_INTEGER / 100;
        point = point || new Point(width / 2 / zoomX + v.x, height / 2 / zoomY + v.y);
        const box = new Box(v).transform(new Matrix({
          scale: zoomAmount,
          origin: point
        }));
        return this.viewbox(box);
      }
    }
  });
  register(Box, 'Box');
  class List extends Array {
    constructor() {
      let arr = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : [];
      for (var _len2 = arguments.length, args = new Array(_len2 > 1 ? _len2 - 1 : 0), _key2 = 1; _key2 < _len2; _key2++) {
        args[_key2 - 1] = arguments[_key2];
      }
      super(arr, ...args);
      if (typeof arr === 'number') return this;
      this.length = 0;
      this.push(...arr);
    }
  }
  extend([List], {
    each(fnOrMethodName) {
      for (var _len3 = arguments.length, args = new Array(_len3 > 1 ? _len3 - 1 : 0), _key3 = 1; _key3 < _len3; _key3++) {
        args[_key3 - 1] = arguments[_key3];
      }
      if (typeof fnOrMethodName === 'function') {
        return this.map((el, i, arr) => {
          return fnOrMethodName.call(el, el, i, arr);
        });
      } else {
        return this.map(el => {
          return el[fnOrMethodName](...args);
        });
      }
    },
    toArray() {
      return Array.prototype.concat.apply([], this);
    }
  });
  const reserved = ['toArray', 'constructor', 'each'];
  List.extend = function (methods) {
    methods = methods.reduce((obj, name) => {
      // Don't overwrite own methods
      if (reserved.includes(name)) return obj; // Don't add private methods

      if (name[0] === '_') return obj; // Relay every call to each()

      obj[name] = function () {
        for (var _len4 = arguments.length, attrs = new Array(_len4), _key4 = 0; _key4 < _len4; _key4++) {
          attrs[_key4] = arguments[_key4];
        }
        return this.each(name, ...attrs);
      };
      return obj;
    }, {});
    extend([List], methods);
  };
  function baseFind(query, parent) {
    return new List(map((parent || globals.document).querySelectorAll(query), function (node) {
      return adopt(node);
    }));
  } // Scoped find method

  function find(query) {
    return baseFind(query, this.node);
  }
  function findOne(query) {
    return adopt(this.node.querySelector(query));
  }
  let listenerId = 0;
  const windowEvents = {};
  function getEvents(instance) {
    let n = instance.getEventHolder(); // We dont want to save events in global space

    if (n === globals.window) n = windowEvents;
    if (!n.events) n.events = {};
    return n.events;
  }
  function getEventTarget(instance) {
    return instance.getEventTarget();
  }
  function clearEvents(instance) {
    let n = instance.getEventHolder();
    if (n === globals.window) n = windowEvents;
    if (n.events) n.events = {};
  } // Add event binder in the SVG namespace

  function on(node, events, listener, binding, options) {
    const l = listener.bind(binding || node);
    const instance = makeInstance(node);
    const bag = getEvents(instance);
    const n = getEventTarget(instance); // events can be an array of events or a string of events

    events = Array.isArray(events) ? events : events.split(delimiter); // add id to listener

    if (!listener._svgjsListenerId) {
      listener._svgjsListenerId = ++listenerId;
    }
    events.forEach(function (event) {
      const ev = event.split('.')[0];
      const ns = event.split('.')[1] || '*'; // ensure valid object

      bag[ev] = bag[ev] || {};
      bag[ev][ns] = bag[ev][ns] || {}; // reference listener

      bag[ev][ns][listener._svgjsListenerId] = l; // add listener

      n.addEventListener(ev, l, options || false);
    });
  } // Add event unbinder in the SVG namespace

  function off(node, events, listener, options) {
    const instance = makeInstance(node);
    const bag = getEvents(instance);
    const n = getEventTarget(instance); // listener can be a function or a number

    if (typeof listener === 'function') {
      listener = listener._svgjsListenerId;
      if (!listener) return;
    } // events can be an array of events or a string or undefined

    events = Array.isArray(events) ? events : (events || '').split(delimiter);
    events.forEach(function (event) {
      const ev = event && event.split('.')[0];
      const ns = event && event.split('.')[1];
      let namespace, l;
      if (listener) {
        // remove listener reference
        if (bag[ev] && bag[ev][ns || '*']) {
          // removeListener
          n.removeEventListener(ev, bag[ev][ns || '*'][listener], options || false);
          delete bag[ev][ns || '*'][listener];
        }
      } else if (ev && ns) {
        // remove all listeners for a namespaced event
        if (bag[ev] && bag[ev][ns]) {
          for (l in bag[ev][ns]) {
            off(n, [ev, ns].join('.'), l);
          }
          delete bag[ev][ns];
        }
      } else if (ns) {
        // remove all listeners for a specific namespace
        for (event in bag) {
          for (namespace in bag[event]) {
            if (ns === namespace) {
              off(n, [event, ns].join('.'));
            }
          }
        }
      } else if (ev) {
        // remove all listeners for the event
        if (bag[ev]) {
          for (namespace in bag[ev]) {
            off(n, [ev, namespace].join('.'));
          }
          delete bag[ev];
        }
      } else {
        // remove all listeners on a given node
        for (event in bag) {
          off(n, event);
        }
        clearEvents(instance);
      }
    });
  }
  function dispatch(node, event, data, options) {
    const n = getEventTarget(node); // Dispatch event

    if (event instanceof globals.window.Event) {
      n.dispatchEvent(event);
    } else {
      event = new globals.window.CustomEvent(event, {
        detail: data,
        cancelable: true,
        ...options
      });
      n.dispatchEvent(event);
    }
    return event;
  }
  class EventTarget extends Base {
    addEventListener() {}
    dispatch(event, data, options) {
      return dispatch(this, event, data, options);
    }
    dispatchEvent(event) {
      const bag = this.getEventHolder().events;
      if (!bag) return true;
      const events = bag[event.type];
      for (const i in events) {
        for (const j in events[i]) {
          events[i][j](event);
        }
      }
      return !event.defaultPrevented;
    } // Fire given event

    fire(event, data, options) {
      this.dispatch(event, data, options);
      return this;
    }
    getEventHolder() {
      return this;
    }
    getEventTarget() {
      return this;
    } // Unbind event from listener

    off(event, listener) {
      off(this, event, listener);
      return this;
    } // Bind given event to listener

    on(event, listener, binding, options) {
      on(this, event, listener, binding, options);
      return this;
    }
    removeEventListener() {}
  }
  register(EventTarget, 'EventTarget');
  function noop() {} // Default animation values

  const timeline = {
    duration: 400,
    ease: '>',
    delay: 0
  }; // Default attribute values

  const attrs = {
    // fill and stroke
    'fill-opacity': 1,
    'stroke-opacity': 1,
    'stroke-width': 0,
    'stroke-linejoin': 'miter',
    'stroke-linecap': 'butt',
    fill: '#000000',
    stroke: '#000000',
    opacity: 1,
    // position
    x: 0,
    y: 0,
    cx: 0,
    cy: 0,
    // size
    width: 0,
    height: 0,
    // radius
    r: 0,
    rx: 0,
    ry: 0,
    // gradient
    offset: 0,
    'stop-opacity': 1,
    'stop-color': '#000000',
    // text
    'text-anchor': 'start'
  };
  class SVGArray extends Array {
    constructor() {
      super(...arguments);
      this.init(...arguments);
    }
    clone() {
      return new this.constructor(this);
    }
    init(arr) {
      // This catches the case, that native map tries to create an array with new Array(1)
      if (typeof arr === 'number') return this;
      this.length = 0;
      this.push(...this.parse(arr));
      return this;
    } // Parse whitespace separated string

    parse() {
      let array = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : [];
      // If already is an array, no need to parse it
      if (array instanceof Array) return array;
      return array.trim().split(delimiter).map(parseFloat);
    }
    toArray() {
      return Array.prototype.concat.apply([], this);
    }
    toSet() {
      return new Set(this);
    }
    toString() {
      return this.join(' ');
    } // Flattens the array if needed

    valueOf() {
      const ret = [];
      ret.push(...this);
      return ret;
    }
  }
  class SVGNumber {
    // Initialize
    constructor() {
      this.init(...arguments);
    }
    convert(unit) {
      return new SVGNumber(this.value, unit);
    } // Divide number

    divide(number) {
      number = new SVGNumber(number);
      return new SVGNumber(this / number, this.unit || number.unit);
    }
    init(value, unit) {
      unit = Array.isArray(value) ? value[1] : unit;
      value = Array.isArray(value) ? value[0] : value; // initialize defaults

      this.value = 0;
      this.unit = unit || ''; // parse value

      if (typeof value === 'number') {
        // ensure a valid numeric value
        this.value = isNaN(value) ? 0 : !isFinite(value) ? value < 0 ? -3.4e+38 : +3.4e+38 : value;
      } else if (typeof value === 'string') {
        unit = value.match(numberAndUnit);
        if (unit) {
          // make value numeric
          this.value = parseFloat(unit[1]); // normalize

          if (unit[5] === '%') {
            this.value /= 100;
          } else if (unit[5] === 's') {
            this.value *= 1000;
          } // store unit

          this.unit = unit[5];
        }
      } else {
        if (value instanceof SVGNumber) {
          this.value = value.valueOf();
          this.unit = value.unit;
        }
      }
      return this;
    } // Subtract number

    minus(number) {
      number = new SVGNumber(number);
      return new SVGNumber(this - number, this.unit || number.unit);
    } // Add number

    plus(number) {
      number = new SVGNumber(number);
      return new SVGNumber(this + number, this.unit || number.unit);
    } // Multiply number

    times(number) {
      number = new SVGNumber(number);
      return new SVGNumber(this * number, this.unit || number.unit);
    }
    toArray() {
      return [this.value, this.unit];
    }
    toJSON() {
      return this.toString();
    }
    toString() {
      return (this.unit === '%' ? ~~(this.value * 1e8) / 1e6 : this.unit === 's' ? this.value / 1e3 : this.value) + this.unit;
    }
    valueOf() {
      return this.value;
    }
  }
  const hooks = [];
  function registerAttrHook(fn) {
    hooks.push(fn);
  } // Set svg element attribute

  function attr(attr, val, ns) {
    // act as full getter
    if (attr == null) {
      // get an object of attributes
      attr = {};
      val = this.node.attributes;
      for (const node of val) {
        attr[node.nodeName] = isNumber.test(node.nodeValue) ? parseFloat(node.nodeValue) : node.nodeValue;
      }
      return attr;
    } else if (attr instanceof Array) {
      // loop through array and get all values
      return attr.reduce((last, curr) => {
        last[curr] = this.attr(curr);
        return last;
      }, {});
    } else if (typeof attr === 'object' && attr.constructor === Object) {
      // apply every attribute individually if an object is passed
      for (val in attr) this.attr(val, attr[val]);
    } else if (val === null) {
      // remove value
      this.node.removeAttribute(attr);
    } else if (val == null) {
      // act as a getter if the first and only argument is not an object
      val = this.node.getAttribute(attr);
      return val == null ? attrs[attr] : isNumber.test(val) ? parseFloat(val) : val;
    } else {
      // Loop through hooks and execute them to convert value
      val = hooks.reduce((_val, hook) => {
        return hook(attr, _val, this);
      }, val); // ensure correct numeric values (also accepts NaN and Infinity)

      if (typeof val === 'number') {
        val = new SVGNumber(val);
      } else if (Color.isColor(val)) {
        // ensure full hex color
        val = new Color(val);
      } else if (val.constructor === Array) {
        // Check for plain arrays and parse array values
        val = new SVGArray(val);
      } // if the passed attribute is leading...

      if (attr === 'leading') {
        // ... call the leading method instead
        if (this.leading) {
          this.leading(val);
        }
      } else {
        // set given attribute on node
        typeof ns === 'string' ? this.node.setAttributeNS(ns, attr, val.toString()) : this.node.setAttribute(attr, val.toString());
      } // rebuild if required

      if (this.rebuild && (attr === 'font-size' || attr === 'x')) {
        this.rebuild();
      }
    }
    return this;
  }
  class Dom extends EventTarget {
    constructor(node, attrs) {
      super();
      this.node = node;
      this.type = node.nodeName;
      if (attrs && node !== attrs) {
        this.attr(attrs);
      }
    } // Add given element at a position

    add(element, i) {
      element = makeInstance(element); // If non-root svg nodes are added we have to remove their namespaces

      if (element.removeNamespace && this.node instanceof globals.window.SVGElement) {
        element.removeNamespace();
      }
      if (i == null) {
        this.node.appendChild(element.node);
      } else if (element.node !== this.node.childNodes[i]) {
        this.node.insertBefore(element.node, this.node.childNodes[i]);
      }
      return this;
    } // Add element to given container and return self

    addTo(parent, i) {
      return makeInstance(parent).put(this, i);
    } // Returns all child elements

    children() {
      return new List(map(this.node.children, function (node) {
        return adopt(node);
      }));
    } // Remove all elements in this container

    clear() {
      // remove children
      while (this.node.hasChildNodes()) {
        this.node.removeChild(this.node.lastChild);
      }
      return this;
    } // Clone element

    clone() {
      let deep = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : true;
      // write dom data to the dom so the clone can pickup the data
      this.writeDataToDom(); // clone element and assign new id

      return new this.constructor(assignNewId(this.node.cloneNode(deep)));
    } // Iterates over all children and invokes a given block

    each(block, deep) {
      const children = this.children();
      let i, il;
      for (i = 0, il = children.length; i < il; i++) {
        block.apply(children[i], [i, children]);
        if (deep) {
          children[i].each(block, deep);
        }
      }
      return this;
    }
    element(nodeName, attrs) {
      return this.put(new Dom(create(nodeName), attrs));
    } // Get first child

    first() {
      return adopt(this.node.firstChild);
    } // Get a element at the given index

    get(i) {
      return adopt(this.node.childNodes[i]);
    }
    getEventHolder() {
      return this.node;
    }
    getEventTarget() {
      return this.node;
    } // Checks if the given element is a child

    has(element) {
      return this.index(element) >= 0;
    }
    html(htmlOrFn, outerHTML) {
      return this.xml(htmlOrFn, outerHTML, html);
    } // Get / set id

    id(id) {
      // generate new id if no id set
      if (typeof id === 'undefined' && !this.node.id) {
        this.node.id = eid(this.type);
      } // don't set directly with this.node.id to make `null` work correctly

      return this.attr('id', id);
    } // Gets index of given element

    index(element) {
      return [].slice.call(this.node.childNodes).indexOf(element.node);
    } // Get the last child

    last() {
      return adopt(this.node.lastChild);
    } // matches the element vs a css selector

    matches(selector) {
      const el = this.node;
      const matcher = el.matches || el.matchesSelector || el.msMatchesSelector || el.mozMatchesSelector || el.webkitMatchesSelector || el.oMatchesSelector || null;
      return matcher && matcher.call(el, selector);
    } // Returns the parent element instance

    parent(type) {
      let parent = this; // check for parent

      if (!parent.node.parentNode) return null; // get parent element

      parent = adopt(parent.node.parentNode);
      if (!type) return parent; // loop trough ancestors if type is given

      do {
        if (typeof type === 'string' ? parent.matches(type) : parent instanceof type) return parent;
      } while (parent = adopt(parent.node.parentNode));
      return parent;
    } // Basically does the same as `add()` but returns the added element instead

    put(element, i) {
      element = makeInstance(element);
      this.add(element, i);
      return element;
    } // Add element to given container and return container

    putIn(parent, i) {
      return makeInstance(parent).add(this, i);
    } // Remove element

    remove() {
      if (this.parent()) {
        this.parent().removeElement(this);
      }
      return this;
    } // Remove a given child

    removeElement(element) {
      this.node.removeChild(element.node);
      return this;
    } // Replace this with element

    replace(element) {
      element = makeInstance(element);
      if (this.node.parentNode) {
        this.node.parentNode.replaceChild(element.node, this.node);
      }
      return element;
    }
    round() {
      let precision = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 2;
      let map = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : null;
      const factor = 10 ** precision;
      const attrs = this.attr(map);
      for (const i in attrs) {
        if (typeof attrs[i] === 'number') {
          attrs[i] = Math.round(attrs[i] * factor) / factor;
        }
      }
      this.attr(attrs);
      return this;
    } // Import / Export raw svg

    svg(svgOrFn, outerSVG) {
      return this.xml(svgOrFn, outerSVG, svg);
    } // Return id on string conversion

    toString() {
      return this.id();
    }
    words(text) {
      // This is faster than removing all children and adding a new one
      this.node.textContent = text;
      return this;
    }
    wrap(node) {
      const parent = this.parent();
      if (!parent) {
        return this.addTo(node);
      }
      const position = parent.index(this);
      return parent.put(node, position).put(this);
    } // write svgjs data to the dom

    writeDataToDom() {
      // dump variables recursively
      this.each(function () {
        this.writeDataToDom();
      });
      return this;
    } // Import / Export raw svg

    xml(xmlOrFn, outerXML, ns) {
      if (typeof xmlOrFn === 'boolean') {
        ns = outerXML;
        outerXML = xmlOrFn;
        xmlOrFn = null;
      } // act as getter if no svg string is given

      if (xmlOrFn == null || typeof xmlOrFn === 'function') {
        // The default for exports is, that the outerNode is included
        outerXML = outerXML == null ? true : outerXML; // write svgjs data to the dom

        this.writeDataToDom();
        let current = this; // An export modifier was passed

        if (xmlOrFn != null) {
          current = adopt(current.node.cloneNode(true)); // If the user wants outerHTML we need to process this node, too

          if (outerXML) {
            const result = xmlOrFn(current);
            current = result || current; // The user does not want this node? Well, then he gets nothing

            if (result === false) return '';
          } // Deep loop through all children and apply modifier

          current.each(function () {
            const result = xmlOrFn(this);
            const _this = result || this; // If modifier returns false, discard node

            if (result === false) {
              this.remove(); // If modifier returns new node, use it
            } else if (result && this !== _this) {
              this.replace(_this);
            }
          }, true);
        } // Return outer or inner content

        return outerXML ? current.node.outerHTML : current.node.innerHTML;
      } // Act as setter if we got a string
      // The default for import is, that the current node is not replaced

      outerXML = outerXML == null ? false : outerXML; // Create temporary holder

      const well = create('wrapper', ns);
      const fragment = globals.document.createDocumentFragment(); // Dump raw svg

      well.innerHTML = xmlOrFn; // Transplant nodes into the fragment

      for (let len = well.children.length; len--;) {
        fragment.appendChild(well.firstElementChild);
      }
      const parent = this.parent(); // Add the whole fragment at once

      return outerXML ? this.replace(fragment) && parent : this.add(fragment);
    }
  }
  extend(Dom, {
    attr,
    find,
    findOne
  });
  register(Dom, 'Dom');
  class Element extends Dom {
    constructor(node, attrs) {
      super(node, attrs); // initialize data object

      this.dom = {}; // create circular reference

      this.node.instance = this;
      if (node.hasAttribute('svgjs:data')) {
        // pull svgjs data from the dom (getAttributeNS doesn't work in html5)
        this.setData(JSON.parse(node.getAttribute('svgjs:data')) || {});
      }
    } // Move element by its center

    center(x, y) {
      return this.cx(x).cy(y);
    } // Move by center over x-axis

    cx(x) {
      return x == null ? this.x() + this.width() / 2 : this.x(x - this.width() / 2);
    } // Move by center over y-axis

    cy(y) {
      return y == null ? this.y() + this.height() / 2 : this.y(y - this.height() / 2);
    } // Get defs

    defs() {
      const root = this.root();
      return root && root.defs();
    } // Relative move over x and y axes

    dmove(x, y) {
      return this.dx(x).dy(y);
    } // Relative move over x axis

    dx() {
      let x = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 0;
      return this.x(new SVGNumber(x).plus(this.x()));
    } // Relative move over y axis

    dy() {
      let y = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 0;
      return this.y(new SVGNumber(y).plus(this.y()));
    }
    getEventHolder() {
      return this;
    } // Set height of element

    height(height) {
      return this.attr('height', height);
    } // Move element to given x and y values

    move(x, y) {
      return this.x(x).y(y);
    } // return array of all ancestors of given type up to the root svg

    parents() {
      let until = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : this.root();
      until = makeInstance(until);
      const parents = new List();
      let parent = this;
      while ((parent = parent.parent()) && parent.node !== globals.document && parent.nodeName !== '#document-fragment') {
        parents.push(parent);
        if (parent.node === until.node) {
          break;
        }
      }
      return parents;
    } // Get referenced element form attribute value

    reference(attr) {
      attr = this.attr(attr);
      if (!attr) return null;
      const m = (attr + '').match(reference);
      return m ? makeInstance(m[1]) : null;
    } // Get parent document

    root() {
      const p = this.parent(getClass(root));
      return p && p.root();
    } // set given data to the elements data property

    setData(o) {
      this.dom = o;
      return this;
    } // Set element size to given width and height

    size(width, height) {
      const p = proportionalSize(this, width, height);
      return this.width(new SVGNumber(p.width)).height(new SVGNumber(p.height));
    } // Set width of element

    width(width) {
      return this.attr('width', width);
    } // write svgjs data to the dom

    writeDataToDom() {
      // remove previously set data
      this.node.removeAttribute('svgjs:data');
      if (Object.keys(this.dom).length) {
        this.node.setAttribute('svgjs:data', JSON.stringify(this.dom)); // see #428
      }

      return super.writeDataToDom();
    } // Move over x-axis

    x(x) {
      return this.attr('x', x);
    } // Move over y-axis

    y(y) {
      return this.attr('y', y);
    }
  }
  extend(Element, {
    bbox,
    rbox,
    inside,
    point,
    ctm,
    screenCTM
  });
  register(Element, 'Element');
  const sugar = {
    stroke: ['color', 'width', 'opacity', 'linecap', 'linejoin', 'miterlimit', 'dasharray', 'dashoffset'],
    fill: ['color', 'opacity', 'rule'],
    prefix: function (t, a) {
      return a === 'color' ? t : t + '-' + a;
    }
  } // Add sugar for fill and stroke
  ;

  ['fill', 'stroke'].forEach(function (m) {
    const extension = {};
    let i;
    extension[m] = function (o) {
      if (typeof o === 'undefined') {
        return this.attr(m);
      }
      if (typeof o === 'string' || o instanceof Color || Color.isRgb(o) || o instanceof Element) {
        this.attr(m, o);
      } else {
        // set all attributes from sugar.fill and sugar.stroke list
        for (i = sugar[m].length - 1; i >= 0; i--) {
          if (o[sugar[m][i]] != null) {
            this.attr(sugar.prefix(m, sugar[m][i]), o[sugar[m][i]]);
          }
        }
      }
      return this;
    };
    registerMethods(['Element', 'Runner'], extension);
  });
  registerMethods(['Element', 'Runner'], {
    // Let the user set the matrix directly
    matrix: function (mat, b, c, d, e, f) {
      // Act as a getter
      if (mat == null) {
        return new Matrix(this);
      } // Act as a setter, the user can pass a matrix or a set of numbers

      return this.attr('transform', new Matrix(mat, b, c, d, e, f));
    },
    // Map rotation to transform
    rotate: function (angle, cx, cy) {
      return this.transform({
        rotate: angle,
        ox: cx,
        oy: cy
      }, true);
    },
    // Map skew to transform
    skew: function (x, y, cx, cy) {
      return arguments.length === 1 || arguments.length === 3 ? this.transform({
        skew: x,
        ox: y,
        oy: cx
      }, true) : this.transform({
        skew: [x, y],
        ox: cx,
        oy: cy
      }, true);
    },
    shear: function (lam, cx, cy) {
      return this.transform({
        shear: lam,
        ox: cx,
        oy: cy
      }, true);
    },
    // Map scale to transform
    scale: function (x, y, cx, cy) {
      return arguments.length === 1 || arguments.length === 3 ? this.transform({
        scale: x,
        ox: y,
        oy: cx
      }, true) : this.transform({
        scale: [x, y],
        ox: cx,
        oy: cy
      }, true);
    },
    // Map translate to transform
    translate: function (x, y) {
      return this.transform({
        translate: [x, y]
      }, true);
    },
    // Map relative translations to transform
    relative: function (x, y) {
      return this.transform({
        relative: [x, y]
      }, true);
    },
    // Map flip to transform
    flip: function () {
      let direction = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 'both';
      let origin = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 'center';
      if ('xybothtrue'.indexOf(direction) === -1) {
        origin = direction;
        direction = 'both';
      }
      return this.transform({
        flip: direction,
        origin: origin
      }, true);
    },
    // Opacity
    opacity: function (value) {
      return this.attr('opacity', value);
    }
  });
  registerMethods('radius', {
    // Add x and y radius
    radius: function (x) {
      let y = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : x;
      const type = (this._element || this).type;
      return type === 'radialGradient' ? this.attr('r', new SVGNumber(x)) : this.rx(x).ry(y);
    }
  });
  registerMethods('Path', {
    // Get path length
    length: function () {
      return this.node.getTotalLength();
    },
    // Get point at length
    pointAt: function (length) {
      return new Point(this.node.getPointAtLength(length));
    }
  });
  registerMethods(['Element', 'Runner'], {
    // Set font
    font: function (a, v) {
      if (typeof a === 'object') {
        for (v in a) this.font(v, a[v]);
        return this;
      }
      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);
    }
  }); // Add events to elements

  const methods = ['click', 'dblclick', 'mousedown', 'mouseup', 'mouseover', 'mouseout', 'mousemove', 'mouseenter', 'mouseleave', 'touchstart', 'touchmove', 'touchleave', 'touchend', 'touchcancel'].reduce(function (last, event) {
    // add event to Element
    const fn = function (f) {
      if (f === null) {
        this.off(event);
      } else {
        this.on(event, f);
      }
      return this;
    };
    last[event] = fn;
    return last;
  }, {});
  registerMethods('Element', methods);
  function untransform() {
    return this.attr('transform', null);
  } // merge the whole transformation chain into one matrix and returns it

  function matrixify() {
    const matrix = (this.attr('transform') || '').
    // split transformations
    split(transforms).slice(0, -1).map(function (str) {
      // generate key => value pairs
      const kv = str.trim().split('(');
      return [kv[0], kv[1].split(delimiter).map(function (str) {
        return parseFloat(str);
      })];
    }).reverse() // merge every transformation into one matrix
    .reduce(function (matrix, transform) {
      if (transform[0] === 'matrix') {
        return matrix.lmultiply(Matrix.fromArray(transform[1]));
      }
      return matrix[transform[0]].apply(matrix, transform[1]);
    }, new Matrix());
    return matrix;
  } // add an element to another parent without changing the visual representation on the screen

  function toParent(parent, i) {
    if (this === parent) return this;
    const ctm = this.screenCTM();
    const pCtm = parent.screenCTM().inverse();
    this.addTo(parent, i).untransform().transform(pCtm.multiply(ctm));
    return this;
  } // same as above with parent equals root-svg

  function toRoot(i) {
    return this.toParent(this.root(), i);
  } // Add transformations

  function transform(o, relative) {
    // Act as a getter if no object was passed
    if (o == null || typeof o === 'string') {
      const decomposed = new Matrix(this).decompose();
      return o == null ? decomposed : decomposed[o];
    }
    if (!Matrix.isMatrixLike(o)) {
      // Set the origin according to the defined transform
      o = {
        ...o,
        origin: getOrigin(o, this)
      };
    } // The user can pass a boolean, an Element or an Matrix or nothing

    const cleanRelative = relative === true ? this : relative || false;
    const result = new Matrix(cleanRelative).transform(o);
    return this.attr('transform', result);
  }
  registerMethods('Element', {
    untransform,
    matrixify,
    toParent,
    toRoot,
    transform
  });
  class Container extends Element {
    flatten() {
      this.each(function () {
        if (this instanceof Container) {
          return this.flatten().ungroup();
        }
      });
      return this;
    }
    ungroup() {
      let parent = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : this.parent();
      let index = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : parent.index(this);
      // when parent != this, we want append all elements to the end
      index = index === -1 ? parent.children().length : index;
      this.each(function (i, children) {
        // reverse each
        return children[children.length - i - 1].toParent(parent, index);
      });
      return this.remove();
    }
  }
  register(Container, 'Container');
  class Defs extends Container {
    constructor(node) {
      let attrs = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : node;
      super(nodeOrNew('defs', node), attrs);
    }
    flatten() {
      return this;
    }
    ungroup() {
      return this;
    }
  }
  register(Defs, 'Defs');
  class Shape extends Element {}
  register(Shape, 'Shape');
  function rx(rx) {
    return this.attr('rx', rx);
  } // Radius y value

  function ry(ry) {
    return this.attr('ry', ry);
  } // Move over x-axis

  function x$3(x) {
    return x == null ? this.cx() - this.rx() : this.cx(x + this.rx());
  } // Move over y-axis

  function y$3(y) {
    return y == null ? this.cy() - this.ry() : this.cy(y + this.ry());
  } // Move by center over x-axis

  function cx$1(x) {
    return this.attr('cx', x);
  } // Move by center over y-axis

  function cy$1(y) {
    return this.attr('cy', y);
  } // Set width of element

  function width$2(width) {
    return width == null ? this.rx() * 2 : this.rx(new SVGNumber(width).divide(2));
  } // Set height of element

  function height$2(height) {
    return height == null ? this.ry() * 2 : this.ry(new SVGNumber(height).divide(2));
  }
  var circled = {
    __proto__: null,
    rx: rx,
    ry: ry,
    x: x$3,
    y: y$3,
    cx: cx$1,
    cy: cy$1,
    width: width$2,
    height: height$2
  };
  class Ellipse extends Shape {
    constructor(node) {
      let attrs = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : node;
      super(nodeOrNew('ellipse', node), attrs);
    }
    size(width, height) {
      const p = proportionalSize(this, width, height);
      return this.rx(new SVGNumber(p.width).divide(2)).ry(new SVGNumber(p.height).divide(2));
    }
  }
  extend(Ellipse, circled);
  registerMethods('Container', {
    // Create an ellipse
    ellipse: wrapWithAttrCheck(function () {
      let width = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 0;
      let height = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : width;
      return this.put(new Ellipse()).size(width, height).move(0, 0);
    })
  });
  register(Ellipse, 'Ellipse');
  class Fragment extends Dom {
    constructor() {
      let node = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : globals.document.createDocumentFragment();
      super(node);
    } // Import / Export raw xml

    xml(xmlOrFn, outerXML, ns) {
      if (typeof xmlOrFn === 'boolean') {
        ns = outerXML;
        outerXML = xmlOrFn;
        xmlOrFn = null;
      } // because this is a fragment we have to put all elements into a wrapper first
      // before we can get the innerXML from it

      if (xmlOrFn == null || typeof xmlOrFn === 'function') {
        const wrapper = new Dom(create('wrapper', ns));
        wrapper.add(this.node.cloneNode(true));
        return wrapper.xml(false, ns);
      } // Act as setter if we got a string

      return super.xml(xmlOrFn, false, ns);
    }
  }
  register(Fragment, 'Fragment');
  function from(x, y) {
    return (this._element || this).type === 'radialGradient' ? this.attr({
      fx: new SVGNumber(x),
      fy: new SVGNumber(y)
    }) : this.attr({
      x1: new SVGNumber(x),
      y1: new SVGNumber(y)
    });
  }
  function to(x, y) {
    return (this._element || this).type === 'radialGradient' ? this.attr({
      cx: new SVGNumber(x),
      cy: new SVGNumber(y)
    }) : this.attr({
      x2: new SVGNumber(x),
      y2: new SVGNumber(y)
    });
  }
  var gradiented = {
    __proto__: null,
    from: from,
    to: to
  };
  class Gradient extends Container {
    constructor(type, attrs) {
      super(nodeOrNew(type + 'Gradient', typeof type === 'string' ? null : type), attrs);
    } // custom attr to handle transform

    attr(a, b, c) {
      if (a === 'transform') a = 'gradientTransform';
      return super.attr(a, b, c);
    }
    bbox() {
      return new Box();
    }
    targets() {
      return baseFind('svg [fill*="' + this.id() + '"]');
    } // Alias string conversion to fill

    toString() {
      return this.url();
    } // Update gradient

    update(block) {
      // remove all stops
      this.clear(); // invoke passed block

      if (typeof block === 'function') {
        block.call(this, this);
      }
      return this;
    } // Return the fill id

    url() {
      return 'url("#' + this.id() + '")';
    }
  }
  extend(Gradient, gradiented);
  registerMethods({
    Container: {
      // Create gradient element in defs
      gradient() {
        return this.defs().gradient(...arguments);
      }
    },
    // define gradient
    Defs: {
      gradient: wrapWithAttrCheck(function (type, block) {
        return this.put(new Gradient(type)).update(block);
      })
    }
  });
  register(Gradient, 'Gradient');
  class Pattern extends Container {
    // Initialize node
    constructor(node) {
      let attrs = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : node;
      super(nodeOrNew('pattern', node), attrs);
    } // custom attr to handle transform

    attr(a, b, c) {
      if (a === 'transform') a = 'patternTransform';
      return super.attr(a, b, c);
    }
    bbox() {
      return new Box();
    }
    targets() {
      return baseFind('svg [fill*="' + this.id() + '"]');
    } // Alias string conversion to fill

    toString() {
      return this.url();
    } // Update pattern by rebuilding

    update(block) {
      // remove content
      this.clear(); // invoke passed block

      if (typeof block === 'function') {
        block.call(this, this);
      }
      return this;
    } // Return the fill id

    url() {
      return 'url("#' + this.id() + '")';
    }
  }
  registerMethods({
    Container: {
      // Create pattern element in defs
      pattern() {
        return this.defs().pattern(...arguments);
      }
    },
    Defs: {
      pattern: wrapWithAttrCheck(function (width, height, block) {
        return this.put(new Pattern()).update(block).attr({
          x: 0,
          y: 0,
          width: width,
          height: height,
          patternUnits: 'userSpaceOnUse'
        });
      })
    }
  });
  register(Pattern, 'Pattern');
  class Image extends Shape {
    constructor(node) {
      let attrs = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : node;
      super(nodeOrNew('image', node), attrs);
    } // (re)load image

    load(url, callback) {
      if (!url) return this;
      const img = new globals.window.Image();
      on(img, 'load', function (e) {
        const p = this.parent(Pattern); // ensure image size

        if (this.width() === 0 && this.height() === 0) {
          this.size(img.width, img.height);
        }
        if (p instanceof Pattern) {
          // ensure pattern size if not set
          if (p.width() === 0 && p.height() === 0) {
            p.size(this.width(), this.height());
          }
        }
        if (typeof callback === 'function') {
          callback.call(this, e);
        }
      }, this);
      on(img, 'load error', function () {
        // dont forget to unbind memory leaking events
        off(img);
      });
      return this.attr('href', img.src = url, xlink);
    }
  }
  registerAttrHook(function (attr, val, _this) {
    // convert image fill and stroke to patterns
    if (attr === 'fill' || attr === 'stroke') {
      if (isImage.test(val)) {
        val = _this.root().defs().image(val);
      }
    }
    if (val instanceof Image) {
      val = _this.root().defs().pattern(0, 0, pattern => {
        pattern.add(val);
      });
    }
    return val;
  });
  registerMethods({
    Container: {
      // create image element, load image and set its size
      image: wrapWithAttrCheck(function (source, callback) {
        return this.put(new Image()).size(0, 0).load(source, callback);
      })
    }
  });
  register(Image, 'Image');
  class PointArray extends SVGArray {
    // Get bounding box of points
    bbox() {
      let maxX = -Infinity;
      let maxY = -Infinity;
      let minX = Infinity;
      let minY = Infinity;
      this.forEach(function (el) {
        maxX = Math.max(el[0], maxX);
        maxY = Math.max(el[1], maxY);
        minX = Math.min(el[0], minX);
        minY = Math.min(el[1], minY);
      });
      return new Box(minX, minY, maxX - minX, maxY - minY);
    } // Move point string

    move(x, y) {
      const box = this.bbox(); // get relative offset

      x -= box.x;
      y -= box.y; // move every point

      if (!isNaN(x) && !isNaN(y)) {
        for (let i = this.length - 1; i >= 0; i--) {
          this[i] = [this[i][0] + x, this[i][1] + y];
        }
      }
      return this;
    } // Parse point string and flat array

    parse() {
      let array = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : [0, 0];
      const points = []; // if it is an array, we flatten it and therefore clone it to 1 depths

      if (array instanceof Array) {
        array = Array.prototype.concat.apply([], array);
      } else {
        // Else, it is considered as a string
        // parse points
        array = array.trim().split(delimiter).map(parseFloat);
      } // validate points - https://svgwg.org/svg2-draft/shapes.html#DataTypePoints
      // Odd number of coordinates is an error. In such cases, drop the last odd coordinate.

      if (array.length % 2 !== 0) array.pop(); // wrap points in two-tuples

      for (let i = 0, len = array.length; i < len; i = i + 2) {
        points.push([array[i], array[i + 1]]);
      }
      return points;
    } // Resize poly string

    size(width, height) {
      let i;
      const box = this.bbox(); // recalculate position of all points according to new size

      for (i = this.length - 1; i >= 0; i--) {
        if (box.width) this[i][0] = (this[i][0] - box.x) * width / box.width + box.x;
        if (box.height) this[i][1] = (this[i][1] - box.y) * height / box.height + box.y;
      }
      return this;
    } // Convert array to line object

    toLine() {
      return {
        x1: this[0][0],
        y1: this[0][1],
        x2: this[1][0],
        y2: this[1][1]
      };
    } // Convert array to string

    toString() {
      const array = []; // convert to a poly point string

      for (let i = 0, il = this.length; i < il; i++) {
        array.push(this[i].join(','));
      }
      return array.join(' ');
    }
    transform(m) {
      return this.clone().transformO(m);
    } // transform points with matrix (similar to Point.transform)

    transformO(m) {
      if (!Matrix.isMatrixLike(m)) {
        m = new Matrix(m);
      }
      for (let i = this.length; i--;) {
        // Perform the matrix multiplication
        const [x, y] = this[i];
        this[i][0] = m.a * x + m.c * y + m.e;
        this[i][1] = m.b * x + m.d * y + m.f;
      }
      return this;
    }
  }
  const MorphArray = PointArray; // Move by left top corner over x-axis

  function x$2(x) {
    return x == null ? this.bbox().x : this.move(x, this.bbox().y);
  } // Move by left top corner over y-axis

  function y$2(y) {
    return y == null ? this.bbox().y : this.move(this.bbox().x, y);
  } // Set width of element

  function width$1(width) {
    const b = this.bbox();
    return width == null ? b.width : this.size(width, b.height);
  } // Set height of element

  function height$1(height) {
    const b = this.bbox();
    return height == null ? b.height : this.size(b.width, height);
  }
  var pointed = {
    __proto__: null,
    MorphArray: MorphArray,
    x: x$2,
    y: y$2,
    width: width$1,
    height: height$1
  };
  class Line extends Shape {
    // Initialize node
    constructor(node) {
      let attrs = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : node;
      super(nodeOrNew('line', node), attrs);
    } // Get array

    array() {
      return new PointArray([[this.attr('x1'), this.attr('y1')], [this.attr('x2'), this.attr('y2')]]);
    } // Move by left top corner

    move(x, y) {
      return this.attr(this.array().move(x, y).toLine());
    } // Overwrite native plot() method

    plot(x1, y1, x2, y2) {
      if (x1 == null) {
        return this.array();
      } else if (typeof y1 !== 'undefined') {
        x1 = {
          x1,
          y1,
          x2,
          y2
        };
      } else {
        x1 = new PointArray(x1).toLine();
      }
      return this.attr(x1);
    } // Set element size to given width and height

    size(width, height) {
      const p = proportionalSize(this, width, height);
      return this.attr(this.array().size(p.width, p.height).toLine());
    }
  }
  extend(Line, pointed);
  registerMethods({
    Container: {
      // Create a line element
      line: wrapWithAttrCheck(function () {
        for (var _len5 = arguments.length, args = new Array(_len5), _key5 = 0; _key5 < _len5; _key5++) {
          args[_key5] = arguments[_key5];
        }
        // make sure plot is called as a setter
        // x1 is not necessarily a number, it can also be an array, a string and a PointArray
        return Line.prototype.plot.apply(this.put(new Line()), args[0] != null ? args : [0, 0, 0, 0]);
      })
    }
  });
  register(Line, 'Line');
  class Marker extends Container {
    // Initialize node
    constructor(node) {
      let attrs = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : node;
      super(nodeOrNew('marker', node), attrs);
    } // Set height of element

    height(height) {
      return this.attr('markerHeight', height);
    }
    orient(orient) {
      return this.attr('orient', orient);
    } // Set marker refX and refY

    ref(x, y) {
      return this.attr('refX', x).attr('refY', y);
    } // Return the fill id

    toString() {
      return 'url(#' + this.id() + ')';
    } // Update marker

    update(block) {
      // remove all content
      this.clear(); // invoke passed block

      if (typeof block === 'function') {
        block.call(this, this);
      }
      return this;
    } // Set width of element

    width(width) {
      return this.attr('markerWidth', width);
    }
  }
  registerMethods({
    Container: {
      marker() {
        // Create marker element in defs
        return this.defs().marker(...arguments);
      }
    },
    Defs: {
      // Create marker
      marker: wrapWithAttrCheck(function (width, height, block) {
        // Set default viewbox to match the width and height, set ref to cx and cy and set orient to auto
        return this.put(new Marker()).size(width, height).ref(width / 2, height / 2).viewbox(0, 0, width, height).attr('orient', 'auto').update(block);
      })
    },
    marker: {
      // Create and attach markers
      marker(marker, width, height, block) {
        let attr = ['marker']; // Build attribute name

        if (marker !== 'all') attr.push(marker);
        attr = attr.join('-'); // Set marker attribute

        marker = arguments[1] instanceof Marker ? arguments[1] : this.defs().marker(width, height, block);
        return this.attr(attr, marker);
      }
    }
  });
  register(Marker, 'Marker');

  /***
  Base Class
  ==========
  The base stepper class that will be
  ***/

  function makeSetterGetter(k, f) {
    return function (v) {
      if (v == null) return this[k];
      this[k] = v;
      if (f) f.call(this);
      return this;
    };
  }
  const easing = {
    '-': function (pos) {
      return pos;
    },
    '<>': function (pos) {
      return -Math.cos(pos * Math.PI) / 2 + 0.5;
    },
    '>': function (pos) {
      return Math.sin(pos * Math.PI / 2);
    },
    '<': function (pos) {
      return -Math.cos(pos * Math.PI / 2) + 1;
    },
    bezier: function (x1, y1, x2, y2) {
      // see https://www.w3.org/TR/css-easing-1/#cubic-bezier-algo
      return function (t) {
        if (t < 0) {
          if (x1 > 0) {
            return y1 / x1 * t;
          } else if (x2 > 0) {
            return y2 / x2 * t;
          } else {
            return 0;
          }
        } else if (t > 1) {
          if (x2 < 1) {
            return (1 - y2) / (1 - x2) * t + (y2 - x2) / (1 - x2);
          } else if (x1 < 1) {
            return (1 - y1) / (1 - x1) * t + (y1 - x1) / (1 - x1);
          } else {
            return 1;
          }
        } else {
          return 3 * t * (1 - t) ** 2 * y1 + 3 * t ** 2 * (1 - t) * y2 + t ** 3;
        }
      };
    },
    // see https://www.w3.org/TR/css-easing-1/#step-timing-function-algo
    steps: function (steps) {
      let stepPosition = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 'end';
      // deal with "jump-" prefix
      stepPosition = stepPosition.split('-').reverse()[0];
      let jumps = steps;
      if (stepPosition === 'none') {
        --jumps;
      } else if (stepPosition === 'both') {
        ++jumps;
      } // The beforeFlag is essentially useless

      return function (t) {
        let beforeFlag = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
        // Step is called currentStep in referenced url
        let step = Math.floor(t * steps);
        const jumping = t * step % 1 === 0;
        if (stepPosition === 'start' || stepPosition === 'both') {
          ++step;
        }
        if (beforeFlag && jumping) {
          --step;
        }
        if (t >= 0 && step < 0) {
          step = 0;
        }
        if (t <= 1 && step > jumps) {
          step = jumps;
        }
        return step / jumps;
      };
    }
  };
  class Stepper {
    done() {
      return false;
    }
  }
  /***
  Easing Functions
  ================
  ***/

  class Ease extends Stepper {
    constructor() {
      let fn = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : timeline.ease;
      super();
      this.ease = easing[fn] || fn;
    }
    step(from, to, pos) {
      if (typeof from !== 'number') {
        return pos < 1 ? from : to;
      }
      return from + (to - from) * this.ease(pos);
    }
  }
  /***
  Controller Types
  ================
  ***/

  class Controller extends Stepper {
    constructor(fn) {
      super();
      this.stepper = fn;
    }
    done(c) {
      return c.done;
    }
    step(current, target, dt, c) {
      return this.stepper(current, target, dt, c);
    }
  }
  function recalculate() {
    // Apply the default parameters
    const duration = (this._duration || 500) / 1000;
    const overshoot = this._overshoot || 0; // Calculate the PID natural response

    const eps = 1e-10;
    const pi = Math.PI;
    const os = Math.log(overshoot / 100 + eps);
    const zeta = -os / Math.sqrt(pi * pi + os * os);
    const wn = 3.9 / (zeta * duration); // Calculate the Spring values

    this.d = 2 * zeta * wn;
    this.k = wn * wn;
  }
  class Spring extends Controller {
    constructor() {
      let duration = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 500;
      let overshoot = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;
      super();
      this.duration(duration).overshoot(overshoot);
    }
    step(current, target, dt, c) {
      if (typeof current === 'string') return current;
      c.done = dt === Infinity;
      if (dt === Infinity) return target;
      if (dt === 0) return current;
      if (dt > 100) dt = 16;
      dt /= 1000; // Get the previous velocity

      const velocity = c.velocity || 0; // Apply the control to get the new position and store it

      const acceleration = -this.d * velocity - this.k * (current - target);
      const newPosition = current + velocity * dt + acceleration * dt * dt / 2; // Store the velocity

      c.velocity = velocity + acceleration * dt; // Figure out if we have converged, and if so, pass the value

      c.done = Math.abs(target - newPosition) + Math.abs(velocity) < 0.002;
      return c.done ? target : newPosition;
    }
  }
  extend(Spring, {
    duration: makeSetterGetter('_duration', recalculate),
    overshoot: makeSetterGetter('_overshoot', recalculate)
  });
  class PID extends Controller {
    constructor() {
      let p = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 0.1;
      let i = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0.01;
      let d = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 0;
      let windup = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : 1000;
      super();
      this.p(p).i(i).d(d).windup(windup);
    }
    step(current, target, dt, c) {
      if (typeof current === 'string') return current;
      c.done = dt === Infinity;
      if (dt === Infinity) return target;
      if (dt === 0) return current;
      const p = target - current;
      let i = (c.integral || 0) + p * dt;
      const d = (p - (c.error || 0)) / dt;
      const windup = this._windup; // antiwindup

      if (windup !== false) {
        i = Math.max(-windup, Math.min(i, windup));
      }
      c.error = p;
      c.integral = i;
      c.done = Math.abs(p) < 0.001;
      return c.done ? target : current + (this.P * p + this.I * i + this.D * d);
    }
  }
  extend(PID, {
    windup: makeSetterGetter('_windup'),
    p: makeSetterGetter('P'),
    i: makeSetterGetter('I'),
    d: makeSetterGetter('D')
  });
  const segmentParameters = {
    M: 2,
    L: 2,
    H: 1,
    V: 1,
    C: 6,
    S: 4,
    Q: 4,
    T: 2,
    A: 7,
    Z: 0
  };
  const pathHandlers = {
    M: function (c, p, p0) {
      p.x = p0.x = c[0];
      p.y = p0.y = c[1];
      return ['M', p.x, p.y];
    },
    L: function (c, p) {
      p.x = c[0];
      p.y = c[1];
      return ['L', c[0], c[1]];
    },
    H: function (c, p) {
      p.x = c[0];
      return ['H', c[0]];
    },
    V: function (c, p) {
      p.y = c[0];
      return ['V', c[0]];
    },
    C: function (c, p) {
      p.x = c[4];
      p.y = c[5];
      return ['C', c[0], c[1], c[2], c[3], c[4], c[5]];
    },
    S: function (c, p) {
      p.x = c[2];
      p.y = c[3];
      return ['S', c[0], c[1], c[2], c[3]];
    },
    Q: function (c, p) {
      p.x = c[2];
      p.y = c[3];
      return ['Q', c[0], c[1], c[2], c[3]];
    },
    T: function (c, p) {
      p.x = c[0];
      p.y = c[1];
      return ['T', c[0], c[1]];
    },
    Z: function (c, p, p0) {
      p.x = p0.x;
      p.y = p0.y;
      return ['Z'];
    },
    A: function (c, p) {
      p.x = c[5];
      p.y = c[6];
      return ['A', c[0], c[1], c[2], c[3], c[4], c[5], c[6]];
    }
  };
  const mlhvqtcsaz = 'mlhvqtcsaz'.split('');
  for (let i = 0, il = mlhvqtcsaz.length; i < il; ++i) {
    pathHandlers[mlhvqtcsaz[i]] = function (i) {
      return function (c, p, p0) {
        if (i === 'H') c[0] = c[0] + p.x;else if (i === 'V') c[0] = c[0] + p.y;else if (i === 'A') {
          c[5] = c[5] + p.x;
          c[6] = c[6] + p.y;
        } else {
          for (let j = 0, jl = c.length; j < jl; ++j) {
            c[j] = c[j] + (j % 2 ? p.y : p.x);
          }
        }
        return pathHandlers[i](c, p, p0);
      };
    }(mlhvqtcsaz[i].toUpperCase());
  }
  function makeAbsolut(parser) {
    const command = parser.segment[0];
    return pathHandlers[command](parser.segment.slice(1), parser.p, parser.p0);
  }
  function segmentComplete(parser) {
    return parser.segment.length && parser.segment.length - 1 === segmentParameters[parser.segment[0].toUpperCase()];
  }
  function startNewSegment(parser, token) {
    parser.inNumber && finalizeNumber(parser, false);
    const pathLetter = isPathLetter.test(token);
    if (pathLetter) {
      parser.segment = [token];
    } else {
      const lastCommand = parser.lastCommand;
      const small = lastCommand.toLowerCase();
      const isSmall = lastCommand === small;
      parser.segment = [small === 'm' ? isSmall ? 'l' : 'L' : lastCommand];
    }
    parser.inSegment = true;
    parser.lastCommand = parser.segment[0];
    return pathLetter;
  }
  function finalizeNumber(parser, inNumber) {
    if (!parser.inNumber) throw new Error('Parser Error');
    parser.number && parser.segment.push(parseFloat(parser.number));
    parser.inNumber = inNumber;
    parser.number = '';
    parser.pointSeen = false;
    parser.hasExponent = false;
    if (segmentComplete(parser)) {
      finalizeSegment(parser);
    }
  }
  function finalizeSegment(parser) {
    parser.inSegment = false;
    if (parser.absolute) {
      parser.segment = makeAbsolut(parser);
    }
    parser.segments.push(parser.segment);
  }
  function isArcFlag(parser) {
    if (!parser.segment.length) return false;
    const isArc = parser.segment[0].toUpperCase() === 'A';
    const length = parser.segment.length;
    return isArc && (length === 4 || length === 5);
  }
  function isExponential(parser) {
    return parser.lastToken.toUpperCase() === 'E';
  }
  function pathParser(d) {
    let toAbsolute = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
    let index = 0;
    let token = '';
    const parser = {
      segment: [],
      inNumber: false,
      number: '',
      lastToken: '',
      inSegment: false,
      segments: [],
      pointSeen: false,
      hasExponent: false,
      absolute: toAbsolute,
      p0: new Point(),
      p: new Point()
    };
    while (parser.lastToken = token, token = d.charAt(index++)) {
      if (!parser.inSegment) {
        if (startNewSegment(parser, token)) {
          continue;
        }
      }
      if (token === '.') {
        if (parser.pointSeen || parser.hasExponent) {
          finalizeNumber(parser, false);
          --index;
          continue;
        }
        parser.inNumber = true;
        parser.pointSeen = true;
        parser.number += token;
        continue;
      }
      if (!isNaN(parseInt(token))) {
        if (parser.number === '0' || isArcFlag(parser)) {
          parser.inNumber = true;
          parser.number = token;
          finalizeNumber(parser, true);
          continue;
        }
        parser.inNumber = true;
        parser.number += token;
        continue;
      }
      if (token === ' ' || token === ',') {
        if (parser.inNumber) {
          finalizeNumber(parser, false);
        }
        continue;
      }
      if (token === '-') {
        if (parser.inNumber && !isExponential(parser)) {
          finalizeNumber(parser, false);
          --index;
          continue;
        }
        parser.number += token;
        parser.inNumber = true;
        continue;
      }
      if (token.toUpperCase() === 'E') {
        parser.number += token;
        parser.hasExponent = true;
        continue;
      }
      if (isPathLetter.test(token)) {
        if (parser.inNumber) {
          finalizeNumber(parser, false);
        } else if (!segmentComplete(parser)) {
          throw new Error('parser Error');
        } else {
          finalizeSegment(parser);
        }
        --index;
      }
    }
    if (parser.inNumber) {
      finalizeNumber(parser, false);
    }
    if (parser.inSegment && segmentComplete(parser)) {
      finalizeSegment(parser);
    }
    return parser.segments;
  }
  function arrayToString(a) {
    let s = '';
    for (let i = 0, il = a.length; i < il; i++) {
      s += a[i][0];
      if (a[i][1] != null) {
        s += a[i][1];
        if (a[i][2] != null) {
          s += ' ';
          s += a[i][2];
          if (a[i][3] != null) {
            s += ' ';
            s += a[i][3];
            s += ' ';
            s += a[i][4];
            if (a[i][5] != null) {
              s += ' ';
              s += a[i][5];
              s += ' ';
              s += a[i][6];
              if (a[i][7] != null) {
                s += ' ';
                s += a[i][7];
              }
            }
          }
        }
      }
    }
    return s + ' ';
  }
  class PathArray extends SVGArray {
    // Get bounding box of path
    bbox() {
      parser().path.setAttribute('d', this.toString());
      return new Box(parser.nodes.path.getBBox());
    } // Move path string

    move(x, y) {
      // get bounding box of current situation
      const box = this.bbox(); // get relative offset

      x -= box.x;
      y -= box.y;
      if (!isNaN(x) && !isNaN(y)) {
        // move every point
        for (let l, i = this.length - 1; i >= 0; i--) {
          l = this[i][0];
          if (l === 'M' || l === 'L' || l === 'T') {
            this[i][1] += x;
            this[i][2] += y;
          } else if (l === 'H') {
            this[i][1] += x;
          } else if (l === 'V') {
            this[i][1] += y;
          } else if (l === 'C' || l === 'S' || l === 'Q') {
            this[i][1] += x;
            this[i][2] += y;
            this[i][3] += x;
            this[i][4] += y;
            if (l === 'C') {
              this[i][5] += x;
              this[i][6] += y;
            }
          } else if (l === 'A') {
            this[i][6] += x;
            this[i][7] += y;
          }
        }
      }
      return this;
    } // Absolutize and parse path to array

    parse() {
      let d = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 'M0 0';
      if (Array.isArray(d)) {
        d = Array.prototype.concat.apply([], d).toString();
      }
      return pathParser(d);
    } // Resize path string

    size(width, height) {
      // get bounding box of current situation
      const box = this.bbox();
      let i, l; // If the box width or height is 0 then we ignore
      // transformations on the respective axis

      box.width = box.width === 0 ? 1 : box.width;
      box.height = box.height === 0 ? 1 : box.height; // recalculate position of all points according to new size

      for (i = this.length - 1; i >= 0; i--) {
        l = this[i][0];
        if (l === 'M' || l === 'L' || l === 'T') {
          this[i][1] = (this[i][1] - box.x) * width / box.width + box.x;
          this[i][2] = (this[i][2] - box.y) * height / box.height + box.y;
        } else if (l === 'H') {
          this[i][1] = (this[i][1] - box.x) * width / box.width + box.x;
        } else if (l === 'V') {
          this[i][1] = (this[i][1] - box.y) * height / box.height + box.y;
        } else if (l === 'C' || l === 'S' || l === 'Q') {
          this[i][1] = (this[i][1] - box.x) * width / box.width + box.x;
          this[i][2] = (this[i][2] - box.y) * height / box.height + box.y;
          this[i][3] = (this[i][3] - box.x) * width / box.width + box.x;
          this[i][4] = (this[i][4] - box.y) * height / box.height + box.y;
          if (l === 'C') {
            this[i][5] = (this[i][5] - box.x) * width / box.width + box.x;
            this[i][6] = (this[i][6] - box.y) * height / box.height + box.y;
          }
        } else if (l === 'A') {
          // resize radii
          this[i][1] = this[i][1] * width / box.width;
          this[i][2] = this[i][2] * height / box.height; // move position values

          this[i][6] = (this[i][6] - box.x) * width / box.width + box.x;
          this[i][7] = (this[i][7] - box.y) * height / box.height + box.y;
        }
      }
      return this;
    } // Convert array to string

    toString() {
      return arrayToString(this);
    }
  }
  const getClassForType = value => {
    const type = typeof value;
    if (type === 'number') {
      return SVGNumber;
    } else if (type === 'string') {
      if (Color.isColor(value)) {
        return Color;
      } else if (delimiter.test(value)) {
        return isPathLetter.test(value) ? PathArray : SVGArray;
      } else if (numberAndUnit.test(value)) {
        return SVGNumber;
      } else {
        return NonMorphable;
      }
    } else if (morphableTypes.indexOf(value.constructor) > -1) {
      return value.constructor;
    } else if (Array.isArray(value)) {
      return SVGArray;
    } else if (type === 'object') {
      return ObjectBag;
    } else {
      return NonMorphable;
    }
  };
  class Morphable {
    constructor(stepper) {
      this._stepper = stepper || new Ease('-');
      this._from = null;
      this._to = null;
      this._type = null;
      this._context = null;
      this._morphObj = null;
    }
    at(pos) {
      const _this = this;
      return this._morphObj.fromArray(this._from.map(function (i, index) {
        return _this._stepper.step(i, _this._to[index], pos, _this._context[index], _this._context);
      }));
    }
    done() {
      const complete = this._context.map(this._stepper.done).reduce(function (last, curr) {
        return last && curr;
      }, true);
      return complete;
    }
    from(val) {
      if (val == null) {
        return this._from;
      }
      this._from = this._set(val);
      return this;
    }
    stepper(stepper) {
      if (stepper == null) return this._stepper;
      this._stepper = stepper;
      return this;
    }
    to(val) {
      if (val == null) {
        return this._to;
      }
      this._to = this._set(val);
      return this;
    }
    type(type) {
      // getter
      if (type == null) {
        return this._type;
      } // setter

      this._type = type;
      return this;
    }
    _set(value) {
      if (!this._type) {
        this.type(getClassForType(value));
      }
      let result = new this._type(value);
      if (this._type === Color) {
        result = this._to ? result[this._to[4]]() : this._from ? result[this._from[4]]() : result;
      }
      if (this._type === ObjectBag) {
        result = this._to ? result.align(this._to) : this._from ? result.align(this._from) : result;
      }
      result = result.toArray();
      this._morphObj = this._morphObj || new this._type();
      this._context = this._context || Array.apply(null, Array(result.length)).map(Object).map(function (o) {
        o.done = true;
        return o;
      });
      return result;
    }
  }
  class NonMorphable {
    constructor() {
      this.init(...arguments);
    }
    init(val) {
      val = Array.isArray(val) ? val[0] : val;
      this.value = val;
      return this;
    }
    toArray() {
      return [this.value];
    }
    valueOf() {
      return this.value;
    }
  }
  class TransformBag {
    constructor() {
      this.init(...arguments);
    }
    init(obj) {
      if (Array.isArray(obj)) {
        obj = {
          scaleX: obj[0],
          scaleY: obj[1],
          shear: obj[2],
          rotate: obj[3],
          translateX: obj[4],
          translateY: obj[5],
          originX: obj[6],
          originY: obj[7]
        };
      }
      Object.assign(this, TransformBag.defaults, obj);
      return this;
    }
    toArray() {
      const v = this;
      return [v.scaleX, v.scaleY, v.shear, v.rotate, v.translateX, v.translateY, v.originX, v.originY];
    }
  }
  TransformBag.defaults = {
    scaleX: 1,
    scaleY: 1,
    shear: 0,
    rotate: 0,
    translateX: 0,
    translateY: 0,
    originX: 0,
    originY: 0
  };
  const sortByKey = (a, b) => {
    return a[0] < b[0] ? -1 : a[0] > b[0] ? 1 : 0;
  };
  class ObjectBag {
    constructor() {
      this.init(...arguments);
    }
    align(other) {
      for (let i = 0, il = this.values.length; i < il; ++i) {
        if (this.values[i] === Color) {
          const space = other[i + 6];
          const color = new Color(this.values.splice(i + 2, 5))[space]().toArray();
          this.values.splice(i + 2, 0, ...color);
        }
      }
      return this;
    }
    init(objOrArr) {
      this.values = [];
      if (Array.isArray(objOrArr)) {
        this.values = objOrArr.slice();
        return;
      }
      objOrArr = objOrArr || {};
      const entries = [];
      for (const i in objOrArr) {
        const Type = getClassForType(objOrArr[i]);
        const val = new Type(objOrArr[i]).toArray();
        entries.push([i, Type, val.length, ...val]);
      }
      entries.sort(sortByKey);
      this.values = entries.reduce((last, curr) => last.concat(curr), []);
      return this;
    }
    toArray() {
      return this.values;
    }
    valueOf() {
      const obj = {};
      const arr = this.values; // for (var i = 0, len = arr.length; i < len; i += 2) {

      while (arr.length) {
        const key = arr.shift();
        const Type = arr.shift();
        const num = arr.shift();
        const values = arr.splice(0, num);
        obj[key] = new Type(values).valueOf();
      }
      return obj;
    }
  }
  const morphableTypes = [NonMorphable, TransformBag, ObjectBag];
  function registerMorphableType() {
    let type = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : [];
    morphableTypes.push(...[].concat(type));
  }
  function makeMorphable() {
    extend(morphableTypes, {
      to(val) {
        return new Morphable().type(this.constructor).from(this.valueOf()).to(val);
      },
      fromArray(arr) {
        this.init(arr);
        return this;
      }
    });
  }
  class Path extends Shape {
    // Initialize node
    constructor(node) {
      let attrs = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : node;
      super(nodeOrNew('path', node), attrs);
    } // Get array

    array() {
      return this._array || (this._array = new PathArray(this.attr('d')));
    } // Clear array cache

    clear() {
      delete this._array;
      return this;
    } // Set height of element

    height(height) {
      return height == null ? this.bbox().height : this.size(this.bbox().width, height);
    } // Move by left top corner

    move(x, y) {
      return this.attr('d', this.array().move(x, y));
    } // Plot new path

    plot(d) {
      return d == null ? this.array() : this.clear().attr('d', typeof d === 'string' ? d : this._array = new PathArray(d));
    } // Set element size to given width and height

    size(width, height) {
      const p = proportionalSize(this, width, height);
      return this.attr('d', this.array().size(p.width, p.height));
    } // Set width of element

    width(width) {
      return width == null ? this.bbox().width : this.size(width, this.bbox().height);
    } // Move by left top corner over x-axis

    x(x) {
      return x == null ? this.bbox().x : this.move(x, this.bbox().y);
    } // Move by left top corner over y-axis

    y(y) {
      return y == null ? this.bbox().y : this.move(this.bbox().x, y);
    }
  } // Define morphable array

  Path.prototype.MorphArray = PathArray; // Add parent method

  registerMethods({
    Container: {
      // Create a wrapped path element
      path: wrapWithAttrCheck(function (d) {
        // make sure plot is called as a setter
        return this.put(new Path()).plot(d || new PathArray());
      })
    }
  });
  register(Path, 'Path');
  function array$4() {
    return this._array || (this._array = new PointArray(this.attr('points')));
  } // Clear array cache

  function clear() {
    delete this._array;
    return this;
  } // Move by left top corner

  function move$2(x, y) {
    return this.attr('points', this.array().move(x, y));
  } // Plot new path

  function plot(p) {
    return p == null ? this.array() : this.clear().attr('points', typeof p === 'string' ? p : this._array = new PointArray(p));
  } // Set element size to given width and height

  function size$1(width, height) {
    const p = proportionalSize(this, width, height);
    return this.attr('points', this.array().size(p.width, p.height));
  }
  var poly = {
    __proto__: null,
    array: array$4,
    clear: clear,
    move: move$2,
    plot: plot,
    size: size$1
  };
  class Polygon extends Shape {
    // Initialize node
    constructor(node) {
      let attrs = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : node;
      super(nodeOrNew('polygon', node), attrs);
    }
  }
  registerMethods({
    Container: {
      // Create a wrapped polygon element
      polygon: wrapWithAttrCheck(function (p) {
        // make sure plot is called as a setter
        return this.put(new Polygon()).plot(p || new PointArray());
      })
    }
  });
  extend(Polygon, pointed);
  extend(Polygon, poly);
  register(Polygon, 'Polygon');
  class Polyline extends Shape {
    // Initialize node
    constructor(node) {
      let attrs = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : node;
      super(nodeOrNew('polyline', node), attrs);
    }
  }
  registerMethods({
    Container: {
      // Create a wrapped polygon element
      polyline: wrapWithAttrCheck(function (p) {
        // make sure plot is called as a setter
        return this.put(new Polyline()).plot(p || new PointArray());
      })
    }
  });
  extend(Polyline, pointed);
  extend(Polyline, poly);
  register(Polyline, 'Polyline');
  class Rect extends Shape {
    // Initialize node
    constructor(node) {
      let attrs = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : node;
      super(nodeOrNew('rect', node), attrs);
    }
  }
  extend(Rect, {
    rx,
    ry
  });
  registerMethods({
    Container: {
      // Create a rect element
      rect: wrapWithAttrCheck(function (width, height) {
        return this.put(new Rect()).size(width, height);
      })
    }
  });
  register(Rect, 'Rect');
  class Queue {
    constructor() {
      this._first = null;
      this._last = null;
    } // Shows us the first item in the list

    first() {
      return this._first && this._first.value;
    } // Shows us the last item in the list

    last() {
      return this._last && this._last.value;
    }
    push(value) {
      // An item stores an id and the provided value
      const item = typeof value.next !== 'undefined' ? value : {
        value: value,
        next: null,
        prev: null
      }; // Deal with the queue being empty or populated

      if (this._last) {
        item.prev = this._last;
        this._last.next = item;
        this._last = item;
      } else {
        this._last = item;
        this._first = item;
      } // Return the current item

      return item;
    } // Removes the item that was returned from the push

    remove(item) {
      // Relink the previous item
      if (item.prev) item.prev.next = item.next;
      if (item.next) item.next.prev = item.prev;
      if (item === this._last) this._last = item.prev;
      if (item === this._first) this._first = item.next; // Invalidate item

      item.prev = null;
      item.next = null;
    }
    shift() {
      // Check if we have a value
      const remove = this._first;
      if (!remove) return null; // If we do, remove it and relink things

      this._first = remove.next;
      if (this._first) this._first.prev = null;
      this._last = this._first ? this._last : null;
      return remove.value;
    }
  }
  const Animator = {
    nextDraw: null,
    frames: new Queue(),
    timeouts: new Queue(),
    immediates: new Queue(),
    timer: () => globals.window.performance || globals.window.Date,
    transforms: [],
    frame(fn) {
      // Store the node
      const node = Animator.frames.push({
        run: fn
      }); // Request an animation frame if we don't have one

      if (Animator.nextDraw === null) {
        Animator.nextDraw = globals.window.requestAnimationFrame(Animator._draw);
      } // Return the node so we can remove it easily

      return node;
    },
    timeout(fn, delay) {
      delay = delay || 0; // Work out when the event should fire

      const time = Animator.timer().now() + delay; // Add the timeout to the end of the queue

      const node = Animator.timeouts.push({
        run: fn,
        time: time
      }); // Request another animation frame if we need one

      if (Animator.nextDraw === null) {
        Animator.nextDraw = globals.window.requestAnimationFrame(Animator._draw);
      }
      return node;
    },
    immediate(fn) {
      // Add the immediate fn to the end of the queue
      const node = Animator.immediates.push(fn); // Request another animation frame if we need one

      if (Animator.nextDraw === null) {
        Animator.nextDraw = globals.window.requestAnimationFrame(Animator._draw);
      }
      return node;
    },
    cancelFrame(node) {
      node != null && Animator.frames.remove(node);
    },
    clearTimeout(node) {
      node != null && Animator.timeouts.remove(node);
    },
    cancelImmediate(node) {
      node != null && Animator.immediates.remove(node);
    },
    _draw(now) {
      // Run all the timeouts we can run, if they are not ready yet, add them
      // to the end of the queue immediately! (bad timeouts!!! [sarcasm])
      let nextTimeout = null;
      const lastTimeout = Animator.timeouts.last();
      while (nextTimeout = Animator.timeouts.shift()) {
        // Run the timeout if its time, or push it to the end
        if (now >= nextTimeout.time) {
          nextTimeout.run();
        } else {
          Animator.timeouts.push(nextTimeout);
        } // If we hit the last item, we should stop shifting out more items

        if (nextTimeout === lastTimeout) break;
      } // Run all of the animation frames

      let nextFrame = null;
      const lastFrame = Animator.frames.last();
      while (nextFrame !== lastFrame && (nextFrame = Animator.frames.shift())) {
        nextFrame.run(now);
      }
      let nextImmediate = null;
      while (nextImmediate = Animator.immediates.shift()) {
        nextImmediate();
      } // If we have remaining timeouts or frames, draw until we don't anymore

      Animator.nextDraw = Animator.timeouts.first() || Animator.frames.first() ? globals.window.requestAnimationFrame(Animator._draw) : null;
    }
  };
  const makeSchedule = function (runnerInfo) {
    const start = runnerInfo.start;
    const duration = runnerInfo.runner.duration();
    const end = start + duration;
    return {
      start: start,
      duration: duration,
      end: end,
      runner: runnerInfo.runner
    };
  };
  const defaultSource = function () {
    const w = globals.window;
    return (w.performance || w.Date).now();
  };
  class Timeline extends EventTarget {
    // Construct a new timeline on the given element
    constructor() {
      let timeSource = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : defaultSource;
      super();
      this._timeSource = timeSource; // Store the timing variables

      this._startTime = 0;
      this._speed = 1.0; // Determines how long a runner is hold in memory. Can be a dt or true/false

      this._persist = 0; // Keep track of the running animations and their starting parameters

      this._nextFrame = null;
      this._paused = true;
      this._runners = [];
      this._runnerIds = [];
      this._lastRunnerId = -1;
      this._time = 0;
      this._lastSourceTime = 0;
      this._lastStepTime = 0; // Make sure that step is always called in class context

      this._step = this._stepFn.bind(this, false);
      this._stepImmediate = this._stepFn.bind(this, true);
    }
    active() {
      return !!this._nextFrame;
    }
    finish() {
      // Go to end and pause
      this.time(this.getEndTimeOfTimeline() + 1);
      return this.pause();
    } // Calculates the end of the timeline

    getEndTime() {
      const lastRunnerInfo = this.getLastRunnerInfo();
      const lastDuration = lastRunnerInfo ? lastRunnerInfo.runner.duration() : 0;
      const lastStartTime = lastRunnerInfo ? lastRunnerInfo.start : this._time;
      return lastStartTime + lastDuration;
    }
    getEndTimeOfTimeline() {
      const endTimes = this._runners.map(i => i.start + i.runner.duration());
      return Math.max(0, ...endTimes);
    }
    getLastRunnerInfo() {
      return this.getRunnerInfoById(this._lastRunnerId);
    }
    getRunnerInfoById(id) {
      return this._runners[this._runnerIds.indexOf(id)] || null;
    }
    pause() {
      this._paused = true;
      return this._continue();
    }
    persist(dtOrForever) {
      if (dtOrForever == null) return this._persist;
      this._persist = dtOrForever;
      return this;
    }
    play() {
      // Now make sure we are not paused and continue the animation
      this._paused = false;
      return this.updateTime()._continue();
    }
    reverse(yes) {
      const currentSpeed = this.speed();
      if (yes == null) return this.speed(-currentSpeed);
      const positive = Math.abs(currentSpeed);
      return this.speed(yes ? -positive : positive);
    } // schedules a runner on the timeline

    schedule(runner, delay, when) {
      if (runner == null) {
        return this._runners.map(makeSchedule);
      } // The start time for the next animation can either be given explicitly,
      // derived from the current timeline time or it can be relative to the
      // last start time to chain animations directly

      let absoluteStartTime = 0;
      const endTime = this.getEndTime();
      delay = delay || 0; // Work out when to start the animation

      if (when == null || when === 'last' || when === 'after') {
        // Take the last time and increment
        absoluteStartTime = endTime;
      } else if (when === 'absolute' || when === 'start') {
        absoluteStartTime = delay;
        delay = 0;
      } else if (when === 'now') {
        absoluteStartTime = this._time;
      } else if (when === 'relative') {
        const runnerInfo = this.getRunnerInfoById(runner.id);
        if (runnerInfo) {
          absoluteStartTime = runnerInfo.start + delay;
          delay = 0;
        }
      } else if (when === 'with-last') {
        const lastRunnerInfo = this.getLastRunnerInfo();
        const lastStartTime = lastRunnerInfo ? lastRunnerInfo.start : this._time;
        absoluteStartTime = lastStartTime;
      } else {
        throw new Error('Invalid value for the "when" parameter');
      } // Manage runner

      runner.unschedule();
      runner.timeline(this);
      const persist = runner.persist();
      const runnerInfo = {
        persist: persist === null ? this._persist : persist,
        start: absoluteStartTime + delay,
        runner
      };
      this._lastRunnerId = runner.id;
      this._runners.push(runnerInfo);
      this._runners.sort((a, b) => a.start - b.start);
      this._runnerIds = this._runners.map(info => info.runner.id);
      this.updateTime()._continue();
      return this;
    }
    seek(dt) {
      return this.time(this._time + dt);
    }
    source(fn) {
      if (fn == null) return this._timeSource;
      this._timeSource = fn;
      return this;
    }
    speed(speed) {
      if (speed == null) return this._speed;
      this._speed = speed;
      return this;
    }
    stop() {
      // Go to start and pause
      this.time(0);
      return this.pause();
    }
    time(time) {
      if (time == null) return this._time;
      this._time = time;
      return this._continue(true);
    } // Remove the runner from this timeline

    unschedule(runner) {
      const index = this._runnerIds.indexOf(runner.id);
      if (index < 0) return this;
      this._runners.splice(index, 1);
      this._runnerIds.splice(index, 1);
      runner.timeline(null);
      return this;
    } // Makes sure, that after pausing the time doesn't jump

    updateTime() {
      if (!this.active()) {
        this._lastSourceTime = this._timeSource();
      }
      return this;
    } // Checks if we are running and continues the animation

    _continue() {
      let immediateStep = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false;
      Animator.cancelFrame(this._nextFrame);
      this._nextFrame = null;
      if (immediateStep) return this._stepImmediate();
      if (this._paused) return this;
      this._nextFrame = Animator.frame(this._step);
      return this;
    }
    _stepFn() {
      let immediateStep = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false;
      // Get the time delta from the last time and update the time
      const time = this._timeSource();
      let dtSource = time - this._lastSourceTime;
      if (immediateStep) dtSource = 0;
      const dtTime = this._speed * dtSource + (this._time - this._lastStepTime);
      this._lastSourceTime = time; // Only update the time if we use the timeSource.
      // Otherwise use the current time

      if (!immediateStep) {
        // Update the time
        this._time += dtTime;
        this._time = this._time < 0 ? 0 : this._time;
      }
      this._lastStepTime = this._time;
      this.fire('time', this._time); // This is for the case that the timeline was seeked so that the time
      // is now before the startTime of the runner. Thats why we need to set
      // the runner to position 0
      // FIXME:
      // However, reseting in insertion order leads to bugs. Considering the case,
      // where 2 runners change the same attribute but in different times,
      // reseting both of them will lead to the case where the later defined
      // runner always wins the reset even if the other runner started earlier
      // and therefore should win the attribute battle
      // this can be solved by reseting them backwards

      for (let k = this._runners.length; k--;) {
        // Get and run the current runner and ignore it if its inactive
        const runnerInfo = this._runners[k];
        const runner = runnerInfo.runner; // Make sure that we give the actual difference
        // between runner start time and now

        const dtToStart = this._time - runnerInfo.start; // Dont run runner if not started yet
        // and try to reset it

        if (dtToStart <= 0) {
          runner.reset();
        }
      } // Run all of the runners directly

      let runnersLeft = false;
      for (let i = 0, len = this._runners.length; i < len; i++) {
        // Get and run the current runner and ignore it if its inactive
        const runnerInfo = this._runners[i];
        const runner = runnerInfo.runner;
        let dt = dtTime; // Make sure that we give the actual difference
        // between runner start time and now

        const dtToStart = this._time - runnerInfo.start; // Dont run runner if not started yet

        if (dtToStart <= 0) {
          runnersLeft = true;
          continue;
        } else if (dtToStart < dt) {
          // Adjust dt to make sure that animation is on point
          dt = dtToStart;
        }
        if (!runner.active()) continue; // If this runner is still going, signal that we need another animation
        // frame, otherwise, remove the completed runner

        const finished = runner.step(dt).done;
        if (!finished) {
          runnersLeft = true; // continue
        } else if (runnerInfo.persist !== true) {
          // runner is finished. And runner might get removed
          const endTime = runner.duration() - runner.time() + this._time;
          if (endTime + runnerInfo.persist < this._time) {
            // Delete runner and correct index
            runner.unschedule();
            --i;
            --len;
          }
        }
      } // Basically: we continue when there are runners right from us in time
      // when -->, and when runners are left from us when <--

      if (runnersLeft && !(this._speed < 0 && this._time === 0) || this._runnerIds.length && this._speed < 0 && this._time > 0) {
        this._continue();
      } else {
        this.pause();
        this.fire('finished');
      }
      return this;
    }
  }
  registerMethods({
    Element: {
      timeline: function (timeline) {
        if (timeline == null) {
          this._timeline = this._timeline || new Timeline();
          return this._timeline;
        } else {
          this._timeline = timeline;
          return this;
        }
      }
    }
  });
  class Runner extends EventTarget {
    constructor(options) {
      super(); // Store a unique id on the runner, so that we can identify it later

      this.id = Runner.id++; // Ensure a default value

      options = options == null ? timeline.duration : options; // Ensure that we get a controller

      options = typeof options === 'function' ? new Controller(options) : options; // Declare all of the variables

      this._element = null;
      this._timeline = null;
      this.done = false;
      this._queue = []; // Work out the stepper and the duration

      this._duration = typeof options === 'number' && options;
      this._isDeclarative = options instanceof Controller;
      this._stepper = this._isDeclarative ? options : new Ease(); // We copy the current values from the timeline because they can change

      this._history = {}; // Store the state of the runner

      this.enabled = true;
      this._time = 0;
      this._lastTime = 0; // At creation, the runner is in reseted state

      this._reseted = true; // Save transforms applied to this runner

      this.transforms = new Matrix();
      this.transformId = 1; // Looping variables

      this._haveReversed = false;
      this._reverse = false;
      this._loopsDone = 0;
      this._swing = false;
      this._wait = 0;
      this._times = 1;
      this._frameId = null; // Stores how long a runner is stored after beeing done

      this._persist = this._isDeclarative ? true : null;
    }
    static sanitise(duration, delay, when) {
      // Initialise the default parameters
      let times = 1;
      let swing = false;
      let wait = 0;
      duration = duration || timeline.duration;
      delay = delay || timeline.delay;
      when = when || 'last'; // If we have an object, unpack the values

      if (typeof duration === 'object' && !(duration instanceof Stepper)) {
        delay = duration.delay || delay;
        when = duration.when || when;
        swing = duration.swing || swing;
        times = duration.times || times;
        wait = duration.wait || wait;
        duration = duration.duration || timeline.duration;
      }
      return {
        duration: duration,
        delay: delay,
        swing: swing,
        times: times,
        wait: wait,
        when: when
      };
    }
    active(enabled) {
      if (enabled == null) return this.enabled;
      this.enabled = enabled;
      return this;
    }
    /*
    Private Methods
    ===============
    Methods that shouldn't be used externally
    */

    addTransform(transform, index) {
      this.transforms.lmultiplyO(transform);
      return this;
    }
    after(fn) {
      return this.on('finished', fn);
    }
    animate(duration, delay, when) {
      const o = Runner.sanitise(duration, delay, when);
      const runner = new Runner(o.duration);
      if (this._timeline) runner.timeline(this._timeline);
      if (this._element) runner.element(this._element);
      return runner.loop(o).schedule(o.delay, o.when);
    }
    clearTransform() {
      this.transforms = new Matrix();
      return this;
    } // TODO: Keep track of all transformations so that deletion is faster

    clearTransformsFromQueue() {
      if (!this.done || !this._timeline || !this._timeline._runnerIds.includes(this.id)) {
        this._queue = this._queue.filter(item => {
          return !item.isTransform;
        });
      }
    }
    delay(delay) {
      return this.animate(0, delay);
    }
    duration() {
      return this._times * (this._wait + this._duration) - this._wait;
    }
    during(fn) {
      return this.queue(null, fn);
    }
    ease(fn) {
      this._stepper = new Ease(fn);
      return this;
    }
    /*
    Runner Definitions
    ==================
    These methods help us define the runtime behaviour of the Runner or they
    help us make new runners from the current runner
    */

    element(element) {
      if (element == null) return this._element;
      this._element = element;
      element._prepareRunner();
      return this;
    }
    finish() {
      return this.step(Infinity);
    }
    loop(times, swing, wait) {
      // Deal with the user passing in an object
      if (typeof times === 'object') {
        swing = times.swing;
        wait = times.wait;
        times = times.times;
      } // Sanitise the values and store them

      this._times = times || Infinity;
      this._swing = swing || false;
      this._wait = wait || 0; // Allow true to be passed

      if (this._times === true) {
        this._times = Infinity;
      }
      return this;
    }
    loops(p) {
      const loopDuration = this._duration + this._wait;
      if (p == null) {
        const loopsDone = Math.floor(this._time / loopDuration);
        const relativeTime = this._time - loopsDone * loopDuration;
        const position = relativeTime / this._duration;
        return Math.min(loopsDone + position, this._times);
      }
      const whole = Math.floor(p);
      const partial = p % 1;
      const time = loopDuration * whole + this._duration * partial;
      return this.time(time);
    }
    persist(dtOrForever) {
      if (dtOrForever == null) return this._persist;
      this._persist = dtOrForever;
      return this;
    }
    position(p) {
      // Get all of the variables we need
      const x = this._time;
      const d = this._duration;
      const w = this._wait;
      const t = this._times;
      const s = this._swing;
      const r = this._reverse;
      let position;
      if (p == null) {
        /*
        This function converts a time to a position in the range [0, 1]
        The full explanation can be found in this desmos demonstration
          https://www.desmos.com/calculator/u4fbavgche
        The logic is slightly simplified here because we can use booleans
        */
        // Figure out the value without thinking about the start or end time
        const f = function (x) {
          const swinging = s * Math.floor(x % (2 * (w + d)) / (w + d));
          const backwards = swinging && !r || !swinging && r;
          const uncliped = Math.pow(-1, backwards) * (x % (w + d)) / d + backwards;
          const clipped = Math.max(Math.min(uncliped, 1), 0);
          return clipped;
        }; // Figure out the value by incorporating the start time

        const endTime = t * (w + d) - w;
        position = x <= 0 ? Math.round(f(1e-5)) : x < endTime ? f(x) : Math.round(f(endTime - 1e-5));
        return position;
      } // Work out the loops done and add the position to the loops done

      const loopsDone = Math.floor(this.loops());
      const swingForward = s && loopsDone % 2 === 0;
      const forwards = swingForward && !r || r && swingForward;
      position = loopsDone + (forwards ? p : 1 - p);
      return this.loops(position);
    }
    progress(p) {
      if (p == null) {
        return Math.min(1, this._time / this.duration());
      }
      return this.time(p * this.duration());
    }
    /*
    Basic Functionality
    ===================
    These methods allow us to attach basic functions to the runner directly
    */

    queue(initFn, runFn, retargetFn, isTransform) {
      this._queue.push({
        initialiser: initFn || noop,
        runner: runFn || noop,
        retarget: retargetFn,
        isTransform: isTransform,
        initialised: false,
        finished: false
      });
      const timeline = this.timeline();
      timeline && this.timeline()._continue();
      return this;
    }
    reset() {
      if (this._reseted) return this;
      this.time(0);
      this._reseted = true;
      return this;
    }
    reverse(reverse) {
      this._reverse = reverse == null ? !this._reverse : reverse;
      return this;
    }
    schedule(timeline, delay, when) {
      // The user doesn't need to pass a timeline if we already have one
      if (!(timeline instanceof Timeline)) {
        when = delay;
        delay = timeline;
        timeline = this.timeline();
      } // If there is no timeline, yell at the user...

      if (!timeline) {
        throw Error('Runner cannot be scheduled without timeline');
      } // Schedule the runner on the timeline provided

      timeline.schedule(this, delay, when);
      return this;
    }
    step(dt) {
      // If we are inactive, this stepper just gets skipped
      if (!this.enabled) return this; // Update the time and get the new position

      dt = dt == null ? 16 : dt;
      this._time += dt;
      const position = this.position(); // Figure out if we need to run the stepper in this frame

      const running = this._lastPosition !== position && this._time >= 0;
      this._lastPosition = position; // Figure out if we just started

      const duration = this.duration();
      const justStarted = this._lastTime <= 0 && this._time > 0;
      const justFinished = this._lastTime < duration && this._time >= duration;
      this._lastTime = this._time;
      if (justStarted) {
        this.fire('start', this);
      } // Work out if the runner is finished set the done flag here so animations
      // know, that they are running in the last step (this is good for
      // transformations which can be merged)

      const declarative = this._isDeclarative;
      this.done = !declarative && !justFinished && this._time >= duration; // Runner is running. So its not in reseted state anymore

      this._reseted = false;
      let converged = false; // Call initialise and the run function

      if (running || declarative) {
        this._initialise(running); // clear the transforms on this runner so they dont get added again and again

        this.transforms = new Matrix();
        converged = this._run(declarative ? dt : position);
        this.fire('step', this);
      } // correct the done flag here
      // declaritive animations itself know when they converged

      this.done = this.done || converged && declarative;
      if (justFinished) {
        this.fire('finished', this);
      }
      return this;
    }
    /*
    Runner animation methods
    ========================
    Control how the animation plays
    */

    time(time) {
      if (time == null) {
        return this._time;
      }
      const dt = time - this._time;
      this.step(dt);
      return this;
    }
    timeline(timeline) {
      // check explicitly for undefined so we can set the timeline to null
      if (typeof timeline === 'undefined') return this._timeline;
      this._timeline = timeline;
      return this;
    }
    unschedule() {
      const timeline = this.timeline();
      timeline && timeline.unschedule(this);
      return this;
    } // Run each initialise function in the runner if required

    _initialise(running) {
      // If we aren't running, we shouldn't initialise when not declarative
      if (!running && !this._isDeclarative) return; // Loop through all of the initialisers

      for (let i = 0, len = this._queue.length; i < len; ++i) {
        // Get the current initialiser
        const current = this._queue[i]; // Determine whether we need to initialise

        const needsIt = this._isDeclarative || !current.initialised && running;
        running = !current.finished; // Call the initialiser if we need to

        if (needsIt && running) {
          current.initialiser.call(this);
          current.initialised = true;
        }
      }
    } // Save a morpher to the morpher list so that we can retarget it later

    _rememberMorpher(method, morpher) {
      this._history[method] = {
        morpher: morpher,
        caller: this._queue[this._queue.length - 1]
      }; // We have to resume the timeline in case a controller
      // is already done without being ever run
      // This can happen when e.g. this is done:
      //    anim = el.animate(new SVG.Spring)
      // and later
      //    anim.move(...)

      if (this._isDeclarative) {
        const timeline = this.timeline();
        timeline && timeline.play();
      }
    } // Try to set the target for a morpher if the morpher exists, otherwise
    // Run each run function for the position or dt given

    _run(positionOrDt) {
      // Run all of the _queue directly
      let allfinished = true;
      for (let i = 0, len = this._queue.length; i < len; ++i) {
        // Get the current function to run
        const current = this._queue[i]; // Run the function if its not finished, we keep track of the finished
        // flag for the sake of declarative _queue

        const converged = current.runner.call(this, positionOrDt);
        current.finished = current.finished || converged === true;
        allfinished = allfinished && current.finished;
      } // We report when all of the constructors are finished

      return allfinished;
    } // do nothing and return false

    _tryRetarget(method, target, extra) {
      if (this._history[method]) {
        // if the last method wasnt even initialised, throw it away
        if (!this._history[method].caller.initialised) {
          const index = this._queue.indexOf(this._history[method].caller);
          this._queue.splice(index, 1);
          return false;
        } // for the case of transformations, we use the special retarget function
        // which has access to the outer scope

        if (this._history[method].caller.retarget) {
          this._history[method].caller.retarget.call(this, target, extra); // for everything else a simple morpher change is sufficient
        } else {
          this._history[method].morpher.to(target);
        }
        this._history[method].caller.finished = false;
        const timeline = this.timeline();
        timeline && timeline.play();
        return true;
      }
      return false;
    }
  }
  Runner.id = 0;
  class FakeRunner {
    constructor() {
      let transforms = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : new Matrix();
      let id = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : -1;
      let done = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : true;
      this.transforms = transforms;
      this.id = id;
      this.done = done;
    }
    clearTransformsFromQueue() {}
  }
  extend([Runner, FakeRunner], {
    mergeWith(runner) {
      return new FakeRunner(runner.transforms.lmultiply(this.transforms), runner.id);
    }
  }); // FakeRunner.emptyRunner = new FakeRunner()

  const lmultiply = (last, curr) => last.lmultiplyO(curr);
  const getRunnerTransform = runner => runner.transforms;
  function mergeTransforms() {
    // Find the matrix to apply to the element and apply it
    const runners = this._transformationRunners.runners;
    const netTransform = runners.map(getRunnerTransform).reduce(lmultiply, new Matrix());
    this.transform(netTransform);
    this._transformationRunners.merge();
    if (this._transformationRunners.length() === 1) {
      this._frameId = null;
    }
  }
  class RunnerArray {
    constructor() {
      this.runners = [];
      this.ids = [];
    }
    add(runner) {
      if (this.runners.includes(runner)) return;
      const id = runner.id + 1;
      this.runners.push(runner);
      this.ids.push(id);
      return this;
    }
    clearBefore(id) {
      const deleteCnt = this.ids.indexOf(id + 1) || 1;
      this.ids.splice(0, deleteCnt, 0);
      this.runners.splice(0, deleteCnt, new FakeRunner()).forEach(r => r.clearTransformsFromQueue());
      return this;
    }
    edit(id, newRunner) {
      const index = this.ids.indexOf(id + 1);
      this.ids.splice(index, 1, id + 1);
      this.runners.splice(index, 1, newRunner);
      return this;
    }
    getByID(id) {
      return this.runners[this.ids.indexOf(id + 1)];
    }
    length() {
      return this.ids.length;
    }
    merge() {
      let lastRunner = null;
      for (let i = 0; i < this.runners.length; ++i) {
        const runner = this.runners[i];
        const condition = lastRunner && runner.done && lastRunner.done // don't merge runner when persisted on timeline
        && (!runner._timeline || !runner._timeline._runnerIds.includes(runner.id)) && (!lastRunner._timeline || !lastRunner._timeline._runnerIds.includes(lastRunner.id));
        if (condition) {
          // the +1 happens in the function
          this.remove(runner.id);
          const newRunner = runner.mergeWith(lastRunner);
          this.edit(lastRunner.id, newRunner);
          lastRunner = newRunner;
          --i;
        } else {
          lastRunner = runner;
        }
      }
      return this;
    }
    remove(id) {
      const index = this.ids.indexOf(id + 1);
      this.ids.splice(index, 1);
      this.runners.splice(index, 1);
      return this;
    }
  }
  registerMethods({
    Element: {
      animate(duration, delay, when) {
        const o = Runner.sanitise(duration, delay, when);
        const timeline = this.timeline();
        return new Runner(o.duration).loop(o).element(this).timeline(timeline.play()).schedule(o.delay, o.when);
      },
      delay(by, when) {
        return this.animate(0, by, when);
      },
      // this function searches for all runners on the element and deletes the ones
      // which run before the current one. This is because absolute transformations
      // overwfrite anything anyway so there is no need to waste time computing
      // other runners
      _clearTransformRunnersBefore(currentRunner) {
        this._transformationRunners.clearBefore(currentRunner.id);
      },
      _currentTransform(current) {
        return this._transformationRunners.runners // we need the equal sign here to make sure, that also transformations
        // on the same runner which execute before the current transformation are
        // taken into account
        .filter(runner => runner.id <= current.id).map(getRunnerTransform).reduce(lmultiply, new Matrix());
      },
      _addRunner(runner) {
        this._transformationRunners.add(runner); // Make sure that the runner merge is executed at the very end of
        // all Animator functions. Thats why we use immediate here to execute
        // the merge right after all frames are run

        Animator.cancelImmediate(this._frameId);
        this._frameId = Animator.immediate(mergeTransforms.bind(this));
      },
      _prepareRunner() {
        if (this._frameId == null) {
          this._transformationRunners = new RunnerArray().add(new FakeRunner(new Matrix(this)));
        }
      }
    }
  }); // Will output the elements from array A that are not in the array B

  const difference = (a, b) => a.filter(x => !b.includes(x));
  extend(Runner, {
    attr(a, v) {
      return this.styleAttr('attr', a, v);
    },
    // Add animatable styles
    css(s, v) {
      return this.styleAttr('css', s, v);
    },
    styleAttr(type, nameOrAttrs, val) {
      if (typeof nameOrAttrs === 'string') {
        return this.styleAttr(type, {
          [nameOrAttrs]: val
        });
      }
      let attrs = nameOrAttrs;
      if (this._tryRetarget(type, attrs)) return this;
      let morpher = new Morphable(this._stepper).to(attrs);
      let keys = Object.keys(attrs);
      this.queue(function () {
        morpher = morpher.from(this.element()[type](keys));
      }, function (pos) {
        this.element()[type](morpher.at(pos).valueOf());
        return morpher.done();
      }, function (newToAttrs) {
        // Check if any new keys were added
        const newKeys = Object.keys(newToAttrs);
        const differences = difference(newKeys, keys); // If their are new keys, initialize them and add them to morpher

        if (differences.length) {
          // Get the values
          const addedFromAttrs = this.element()[type](differences); // Get the already initialized values

          const oldFromAttrs = new ObjectBag(morpher.from()).valueOf(); // Merge old and new

          Object.assign(oldFromAttrs, addedFromAttrs);
          morpher.from(oldFromAttrs);
        } // Get the object from the morpher

        const oldToAttrs = new ObjectBag(morpher.to()).valueOf(); // Merge in new attributes

        Object.assign(oldToAttrs, newToAttrs); // Change morpher target

        morpher.to(oldToAttrs); // Make sure that we save the work we did so we don't need it to do again

        keys = newKeys;
        attrs = newToAttrs;
      });
      this._rememberMorpher(type, morpher);
      return this;
    },
    zoom(level, point) {
      if (this._tryRetarget('zoom', level, point)) return this;
      let morpher = new Morphable(this._stepper).to(new SVGNumber(level));
      this.queue(function () {
        morpher = morpher.from(this.element().zoom());
      }, function (pos) {
        this.element().zoom(morpher.at(pos), point);
        return morpher.done();
      }, function (newLevel, newPoint) {
        point = newPoint;
        morpher.to(newLevel);
      });
      this._rememberMorpher('zoom', morpher);
      return this;
    },
    /**
     ** absolute transformations
     **/
    //
    // M v -----|-----(D M v = F v)------|----->  T v
    //
    // 1. define the final state (T) and decompose it (once)
    //    t = [tx, ty, the, lam, sy, sx]
    // 2. on every frame: pull the current state of all previous transforms
    //    (M - m can change)
    //   and then write this as m = [tx0, ty0, the0, lam0, sy0, sx0]
    // 3. Find the interpolated matrix F(pos) = m + pos * (t - m)
    //   - Note F(0) = M
    //   - Note F(1) = T
    // 4. Now you get the delta matrix as a result: D = F * inv(M)
    transform(transforms, relative, affine) {
      // If we have a declarative function, we should retarget it if possible
      relative = transforms.relative || relative;
      if (this._isDeclarative && !relative && this._tryRetarget('transform', transforms)) {
        return this;
      } // Parse the parameters

      const isMatrix = Matrix.isMatrixLike(transforms);
      affine = transforms.affine != null ? transforms.affine : affine != null ? affine : !isMatrix; // Create a morepher and set its type

      const morpher = new Morphable(this._stepper).type(affine ? TransformBag : Matrix);
      let origin;
      let element;
      let current;
      let currentAngle;
      let startTransform;
      function setup() {
        // make sure element and origin is defined
        element = element || this.element();
        origin = origin || getOrigin(transforms, element);
        startTransform = new Matrix(relative ? undefined : element); // add the runner to the element so it can merge transformations

        element._addRunner(this); // Deactivate all transforms that have run so far if we are absolute

        if (!relative) {
          element._clearTransformRunnersBefore(this);
        }
      }
      function run(pos) {
        // clear all other transforms before this in case something is saved
        // on this runner. We are absolute. We dont need these!
        if (!relative) this.clearTransform();
        const {
          x,
          y
        } = new Point(origin).transform(element._currentTransform(this));
        let target = new Matrix({
          ...transforms,
          origin: [x, y]
        });
        let start = this._isDeclarative && current ? current : startTransform;
        if (affine) {
          target = target.decompose(x, y);
          start = start.decompose(x, y); // Get the current and target angle as it was set

          const rTarget = target.rotate;
          const rCurrent = start.rotate; // Figure out the shortest path to rotate directly

          const possibilities = [rTarget - 360, rTarget, rTarget + 360];
          const distances = possibilities.map(a => Math.abs(a - rCurrent));
          const shortest = Math.min(...distances);
          const index = distances.indexOf(shortest);
          target.rotate = possibilities[index];
        }
        if (relative) {
          // we have to be careful here not to overwrite the rotation
          // with the rotate method of Matrix
          if (!isMatrix) {
            target.rotate = transforms.rotate || 0;
          }
          if (this._isDeclarative && currentAngle) {
            start.rotate = currentAngle;
          }
        }
        morpher.from(start);
        morpher.to(target);
        const affineParameters = morpher.at(pos);
        currentAngle = affineParameters.rotate;
        current = new Matrix(affineParameters);
        this.addTransform(current);
        element._addRunner(this);
        return morpher.done();
      }
      function retarget(newTransforms) {
        // only get a new origin if it changed since the last call
        if ((newTransforms.origin || 'center').toString() !== (transforms.origin || 'center').toString()) {
          origin = getOrigin(newTransforms, element);
        } // overwrite the old transformations with the new ones

        transforms = {
          ...newTransforms,
          origin
        };
      }
      this.queue(setup, run, retarget, true);
      this._isDeclarative && this._rememberMorpher('transform', morpher);
      return this;
    },
    // Animatable x-axis
    x(x, relative) {
      return this._queueNumber('x', x);
    },
    // Animatable y-axis
    y(y) {
      return this._queueNumber('y', y);
    },
    dx() {
      let x = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 0;
      return this._queueNumberDelta('x', x);
    },
    dy() {
      let y = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 0;
      return this._queueNumberDelta('y', y);
    },
    dmove(x, y) {
      return this.dx(x).dy(y);
    },
    _queueNumberDelta(method, to) {
      to = new SVGNumber(to); // Try to change the target if we have this method already registerd

      if (this._tryRetarget(method, to)) return this; // Make a morpher and queue the animation

      const morpher = new Morphable(this._stepper).to(to);
      let from = null;
      this.queue(function () {
        from = this.element()[method]();
        morpher.from(from);
        morpher.to(from + to);
      }, function (pos) {
        this.element()[method](morpher.at(pos));
        return morpher.done();
      }, function (newTo) {
        morpher.to(from + new SVGNumber(newTo));
      }); // Register the morpher so that if it is changed again, we can retarget it

      this._rememberMorpher(method, morpher);
      return this;
    },
    _queueObject(method, to) {
      // Try to change the target if we have this method already registerd
      if (this._tryRetarget(method, to)) return this; // Make a morpher and queue the animation

      const morpher = new Morphable(this._stepper).to(to);
      this.queue(function () {
        morpher.from(this.element()[method]());
      }, function (pos) {
        this.element()[method](morpher.at(pos));
        return morpher.done();
      }); // Register the morpher so that if it is changed again, we can retarget it

      this._rememberMorpher(method, morpher);
      return this;
    },
    _queueNumber(method, value) {
      return this._queueObject(method, new SVGNumber(value));
    },
    // Animatable center x-axis
    cx(x) {
      return this._queueNumber('cx', x);
    },
    // Animatable center y-axis
    cy(y) {
      return this._queueNumber('cy', y);
    },
    // Add animatable move
    move(x, y) {
      return this.x(x).y(y);
    },
    // Add animatable center
    center(x, y) {
      return this.cx(x).cy(y);
    },
    // Add animatable size
    size(width, height) {
      // animate bbox based size for all other elements
      let box;
      if (!width || !height) {
        box = this._element.bbox();
      }
      if (!width) {
        width = box.width / box.height * height;
      }
      if (!height) {
        height = box.height / box.width * width;
      }
      return this.width(width).height(height);
    },
    // Add animatable width
    width(width) {
      return this._queueNumber('width', width);
    },
    // Add animatable height
    height(height) {
      return this._queueNumber('height', height);
    },
    // Add animatable plot
    plot(a, b, c, d) {
      // Lines can be plotted with 4 arguments
      if (arguments.length === 4) {
        return this.plot([a, b, c, d]);
      }
      if (this._tryRetarget('plot', a)) return this;
      const morpher = new Morphable(this._stepper).type(this._element.MorphArray).to(a);
      this.queue(function () {
        morpher.from(this._element.array());
      }, function (pos) {
        this._element.plot(morpher.at(pos));
        return morpher.done();
      });
      this._rememberMorpher('plot', morpher);
      return this;
    },
    // Add leading method
    leading(value) {
      return this._queueNumber('leading', value);
    },
    // Add animatable viewbox
    viewbox(x, y, width, height) {
      return this._queueObject('viewbox', new Box(x, y, width, height));
    },
    update(o) {
      if (typeof o !== 'object') {
        return this.update({
          offset: arguments[0],
          color: arguments[1],
          opacity: arguments[2]
        });
      }
      if (o.opacity != null) this.attr('stop-opacity', o.opacity);
      if (o.color != null) this.attr('stop-color', o.color);
      if (o.offset != null) this.attr('offset', o.offset);
      return this;
    }
  });
  extend(Runner, {
    rx,
    ry,
    from,
    to
  });
  register(Runner, 'Runner');
  class Svg extends Container {
    constructor(node) {
      let attrs = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : node;
      super(nodeOrNew('svg', node), attrs);
      this.namespace();
    } // Creates and returns defs element

    defs() {
      if (!this.isRoot()) return this.root().defs();
      return adopt(this.node.querySelector('defs')) || this.put(new Defs());
    }
    isRoot() {
      return !this.node.parentNode || !(this.node.parentNode instanceof globals.window.SVGElement) && this.node.parentNode.nodeName !== '#document-fragment';
    } // Add namespaces

    namespace() {
      if (!this.isRoot()) return this.root().namespace();
      return this.attr({
        xmlns: svg,
        version: '1.1'
      }).attr('xmlns:xlink', xlink, xmlns).attr('xmlns:svgjs', svgjs, xmlns);
    }
    removeNamespace() {
      return this.attr({
        xmlns: null,
        version: null
      }).attr('xmlns:xlink', null, xmlns).attr('xmlns:svgjs', null, xmlns);
    } // Check if this is a root svg
    // If not, call root() from this element

    root() {
      if (this.isRoot()) return this;
      return super.root();
    }
  }
  registerMethods({
    Container: {
      // Create nested svg document
      nested: wrapWithAttrCheck(function () {
        return this.put(new Svg());
      })
    }
  });
  register(Svg, 'Svg', true);
  class Symbol$1 extends Container {
    // Initialize node
    constructor(node) {
      let attrs = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : node;
      super(nodeOrNew('symbol', node), attrs);
    }
  }
  registerMethods({
    Container: {
      symbol: wrapWithAttrCheck(function () {
        return this.put(new Symbol$1());
      })
    }
  });
  register(Symbol$1, 'Symbol');
  function plain(text) {
    // clear if build mode is disabled
    if (this._build === false) {
      this.clear();
    } // create text node

    this.node.appendChild(globals.document.createTextNode(text));
    return this;
  } // Get length of text element

  function length() {
    return this.node.getComputedTextLength();
  } // Move over x-axis
  // Text is moved by its bounding box
  // text-anchor does NOT matter

  function x$1(x) {
    let box = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : this.bbox();
    if (x == null) {
      return box.x;
    }
    return this.attr('x', this.attr('x') + x - box.x);
  } // Move over y-axis

  function y$1(y) {
    let box = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : this.bbox();
    if (y == null) {
      return box.y;
    }
    return this.attr('y', this.attr('y') + y - box.y);
  }
  function move$1(x, y) {
    let box = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : this.bbox();
    return this.x(x, box).y(y, box);
  } // Move center over x-axis

  function cx(x) {
    let box = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : this.bbox();
    if (x == null) {
      return box.cx;
    }
    return this.attr('x', this.attr('x') + x - box.cx);
  } // Move center over y-axis

  function cy(y) {
    let box = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : this.bbox();
    if (y == null) {
      return box.cy;
    }
    return this.attr('y', this.attr('y') + y - box.cy);
  }
  function center(x, y) {
    let box = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : this.bbox();
    return this.cx(x, box).cy(y, box);
  }
  function ax(x) {
    return this.attr('x', x);
  }
  function ay(y) {
    return this.attr('y', y);
  }
  function amove(x, y) {
    return this.ax(x).ay(y);
  } // Enable / disable build mode

  function build(build) {
    this._build = !!build;
    return this;
  }
  var textable = {
    __proto__: null,
    plain: plain,
    length: length,
    x: x$1,
    y: y$1,
    move: move$1,
    cx: cx,
    cy: cy,
    center: center,
    ax: ax,
    ay: ay,
    amove: amove,
    build: build
  };
  class Text extends Shape {
    // Initialize node
    constructor(node) {
      let attrs = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : node;
      super(nodeOrNew('text', node), attrs);
      this.dom.leading = new SVGNumber(1.3); // store leading value for rebuilding

      this._rebuild = true; // enable automatic updating of dy values

      this._build = false; // disable build mode for adding multiple lines
    } // Set / get leading

    leading(value) {
      // act as getter
      if (value == null) {
        return this.dom.leading;
      } // act as setter

      this.dom.leading = new SVGNumber(value);
      return this.rebuild();
    } // Rebuild appearance type

    rebuild(rebuild) {
      // store new rebuild flag if given
      if (typeof rebuild === 'boolean') {
        this._rebuild = rebuild;
      } // define position of all lines

      if (this._rebuild) {
        const self = this;
        let blankLineOffset = 0;
        const leading = this.dom.leading;
        this.each(function (i) {
          const fontSize = globals.window.getComputedStyle(this.node).getPropertyValue('font-size');
          const dy = leading * new SVGNumber(fontSize);
          if (this.dom.newLined) {
            this.attr('x', self.attr('x'));
            if (this.text() === '\n') {
              blankLineOffset += dy;
            } else {
              this.attr('dy', i ? dy + blankLineOffset : 0);
              blankLineOffset = 0;
            }
          }
        });
        this.fire('rebuild');
      }
      return this;
    } // overwrite method from parent to set data properly

    setData(o) {
      this.dom = o;
      this.dom.leading = new SVGNumber(o.leading || 1.3);
      return this;
    } // Set the text content

    text(text) {
      // act as getter
      if (text === undefined) {
        const children = this.node.childNodes;
        let firstLine = 0;
        text = '';
        for (let i = 0, len = children.length; i < len; ++i) {
          // skip textPaths - they are no lines
          if (children[i].nodeName === 'textPath') {
            if (i === 0) firstLine = 1;
            continue;
          } // add newline if its not the first child and newLined is set to true

          if (i !== firstLine && children[i].nodeType !== 3 && adopt(children[i]).dom.newLined === true) {
            text += '\n';
          } // add content of this node

          text += children[i].textContent;
        }
        return text;
      } // remove existing content

      this.clear().build(true);
      if (typeof text === 'function') {
        // call block
        text.call(this, this);
      } else {
        // store text and make sure text is not blank
        text = (text + '').split('\n'); // build new lines

        for (let j = 0, jl = text.length; j < jl; j++) {
          this.newLine(text[j]);
        }
      } // disable build mode and rebuild lines

      return this.build(false).rebuild();
    }
  }
  extend(Text, textable);
  registerMethods({
    Container: {
      // Create text element
      text: wrapWithAttrCheck(function () {
        let text = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : '';
        return this.put(new Text()).text(text);
      }),
      // Create plain text element
      plain: wrapWithAttrCheck(function () {
        let text = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : '';
        return this.put(new Text()).plain(text);
      })
    }
  });
  register(Text, 'Text');
  class Tspan extends Shape {
    // Initialize node
    constructor(node) {
      let attrs = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : node;
      super(nodeOrNew('tspan', node), attrs);
      this._build = false; // disable build mode for adding multiple lines
    } // Shortcut dx

    dx(dx) {
      return this.attr('dx', dx);
    } // Shortcut dy

    dy(dy) {
      return this.attr('dy', dy);
    } // Create new line

    newLine() {
      // mark new line
      this.dom.newLined = true; // fetch parent

      const text = this.parent(); // early return in case we are not in a text element

      if (!(text instanceof Text)) {
        return this;
      }
      const i = text.index(this);
      const fontSize = globals.window.getComputedStyle(this.node).getPropertyValue('font-size');
      const dy = text.dom.leading * new SVGNumber(fontSize); // apply new position

      return this.dy(i ? dy : 0).attr('x', text.x());
    } // Set text content

    text(text) {
      if (text == null) return this.node.textContent + (this.dom.newLined ? '\n' : '');
      if (typeof text === 'function') {
        this.clear().build(true);
        text.call(this, this);
        this.build(false);
      } else {
        this.plain(text);
      }
      return this;
    }
  }
  extend(Tspan, textable);
  registerMethods({
    Tspan: {
      tspan: wrapWithAttrCheck(function () {
        let text = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : '';
        const tspan = new Tspan(); // clear if build mode is disabled

        if (!this._build) {
          this.clear();
        } // add new tspan

        return this.put(tspan).text(text);
      })
    },
    Text: {
      newLine: function () {
        let text = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : '';
        return this.tspan(text).newLine();
      }
    }
  });
  register(Tspan, 'Tspan');
  class Circle extends Shape {
    constructor(node) {
      let attrs = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : node;
      super(nodeOrNew('circle', node), attrs);
    }
    radius(r) {
      return this.attr('r', r);
    } // Radius x value

    rx(rx) {
      return this.attr('r', rx);
    } // Alias radius x value

    ry(ry) {
      return this.rx(ry);
    }
    size(size) {
      return this.radius(new SVGNumber(size).divide(2));
    }
  }
  extend(Circle, {
    x: x$3,
    y: y$3,
    cx: cx$1,
    cy: cy$1,
    width: width$2,
    height: height$2
  });
  registerMethods({
    Container: {
      // Create circle element
      circle: wrapWithAttrCheck(function () {
        let size = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 0;
        return this.put(new Circle()).size(size).move(0, 0);
      })
    }
  });
  register(Circle, 'Circle');
  class ClipPath extends Container {
    constructor(node) {
      let attrs = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : node;
      super(nodeOrNew('clipPath', node), attrs);
    } // Unclip all clipped elements and remove itself

    remove() {
      // unclip all targets
      this.targets().forEach(function (el) {
        el.unclip();
      }); // remove clipPath from parent

      return super.remove();
    }
    targets() {
      return baseFind('svg [clip-path*="' + this.id() + '"]');
    }
  }
  registerMethods({
    Container: {
      // Create clipping element
      clip: wrapWithAttrCheck(function () {
        return this.defs().put(new ClipPath());
      })
    },
    Element: {
      // Distribute clipPath to svg element
      clipper() {
        return this.reference('clip-path');
      },
      clipWith(element) {
        // use given clip or create a new one
        const clipper = element instanceof ClipPath ? element : this.parent().clip().add(element); // apply mask

        return this.attr('clip-path', 'url("#' + clipper.id() + '")');
      },
      // Unclip element
      unclip() {
        return this.attr('clip-path', null);
      }
    }
  });
  register(ClipPath, 'ClipPath');
  class ForeignObject extends Element {
    constructor(node) {
      let attrs = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : node;
      super(nodeOrNew('foreignObject', node), attrs);
    }
  }
  registerMethods({
    Container: {
      foreignObject: wrapWithAttrCheck(function (width, height) {
        return this.put(new ForeignObject()).size(width, height);
      })
    }
  });
  register(ForeignObject, 'ForeignObject');
  function dmove(dx, dy) {
    this.children().forEach((child, i) => {
      let bbox; // We have to wrap this for elements that dont have a bbox
      // e.g. title and other descriptive elements

      try {
        // Get the childs bbox
        bbox = child.bbox();
      } catch (e) {
        return;
      } // Get childs matrix

      const m = new Matrix(child); // Translate childs matrix by amount and
      // transform it back into parents space

      const matrix = m.translate(dx, dy).transform(m.inverse()); // Calculate new x and y from old box

      const p = new Point(bbox.x, bbox.y).transform(matrix); // Move element

      child.move(p.x, p.y);
    });
    return this;
  }
  function dx(dx) {
    return this.dmove(dx, 0);
  }
  function dy(dy) {
    return this.dmove(0, dy);
  }
  function height(height) {
    let box = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : this.bbox();
    if (height == null) return box.height;
    return this.size(box.width, height, box);
  }
  function move() {
    let x = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 0;
    let y = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0;
    let box = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : this.bbox();
    const dx = x - box.x;
    const dy = y - box.y;
    return this.dmove(dx, dy);
  }
  function size(width, height) {
    let box = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : this.bbox();
    const p = proportionalSize(this, width, height, box);
    const scaleX = p.width / box.width;
    const scaleY = p.height / box.height;
    this.children().forEach((child, i) => {
      const o = new Point(box).transform(new Matrix(child).inverse());
      child.scale(scaleX, scaleY, o.x, o.y);
    });
    return this;
  }
  function width(width) {
    let box = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : this.bbox();
    if (width == null) return box.width;
    return this.size(width, box.height, box);
  }
  function x(x) {
    let box = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : this.bbox();
    if (x == null) return box.x;
    return this.move(x, box.y, box);
  }
  function y(y) {
    let box = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : this.bbox();
    if (y == null) return box.y;
    return this.move(box.x, y, box);
  }
  var containerGeometry = {
    __proto__: null,
    dmove: dmove,
    dx: dx,
    dy: dy,
    height: height,
    move: move,
    size: size,
    width: width,
    x: x,
    y: y
  };
  class G extends Container {
    constructor(node) {
      let attrs = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : node;
      super(nodeOrNew('g', node), attrs);
    }
  }
  extend(G, containerGeometry);
  registerMethods({
    Container: {
      // Create a group element
      group: wrapWithAttrCheck(function () {
        return this.put(new G());
      })
    }
  });
  register(G, 'G');
  class A extends Container {
    constructor(node) {
      let attrs = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : node;
      super(nodeOrNew('a', node), attrs);
    } // Link target attribute

    target(target) {
      return this.attr('target', target);
    } // Link url

    to(url) {
      return this.attr('href', url, xlink);
    }
  }
  extend(A, containerGeometry);
  registerMethods({
    Container: {
      // Create a hyperlink element
      link: wrapWithAttrCheck(function (url) {
        return this.put(new A()).to(url);
      })
    },
    Element: {
      unlink() {
        const link = this.linker();
        if (!link) return this;
        const parent = link.parent();
        if (!parent) {
          return this.remove();
        }
        const index = parent.index(link);
        parent.add(this, index);
        link.remove();
        return this;
      },
      linkTo(url) {
        // reuse old link if possible
        let link = this.linker();
        if (!link) {
          link = new A();
          this.wrap(link);
        }
        if (typeof url === 'function') {
          url.call(link, link);
        } else {
          link.to(url);
        }
        return this;
      },
      linker() {
        const link = this.parent();
        if (link && link.node.nodeName.toLowerCase() === 'a') {
          return link;
        }
        return null;
      }
    }
  });
  register(A, 'A');
  class Mask extends Container {
    // Initialize node
    constructor(node) {
      let attrs = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : node;
      super(nodeOrNew('mask', node), attrs);
    } // Unmask all masked elements and remove itself

    remove() {
      // unmask all targets
      this.targets().forEach(function (el) {
        el.unmask();
      }); // remove mask from parent

      return super.remove();
    }
    targets() {
      return baseFind('svg [mask*="' + this.id() + '"]');
    }
  }
  registerMethods({
    Container: {
      mask: wrapWithAttrCheck(function () {
        return this.defs().put(new Mask());
      })
    },
    Element: {
      // Distribute mask to svg element
      masker() {
        return this.reference('mask');
      },
      maskWith(element) {
        // use given mask or create a new one
        const masker = element instanceof Mask ? element : this.parent().mask().add(element); // apply mask

        return this.attr('mask', 'url("#' + masker.id() + '")');
      },
      // Unmask element
      unmask() {
        return this.attr('mask', null);
      }
    }
  });
  register(Mask, 'Mask');
  class Stop extends Element {
    constructor(node) {
      let attrs = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : node;
      super(nodeOrNew('stop', node), attrs);
    } // add color stops

    update(o) {
      if (typeof o === 'number' || o instanceof SVGNumber) {
        o = {
          offset: arguments[0],
          color: arguments[1],
          opacity: arguments[2]
        };
      } // set attributes

      if (o.opacity != null) this.attr('stop-opacity', o.opacity);
      if (o.color != null) this.attr('stop-color', o.color);
      if (o.offset != null) this.attr('offset', new SVGNumber(o.offset));
      return this;
    }
  }
  registerMethods({
    Gradient: {
      // Add a color stop
      stop: function (offset, color, opacity) {
        return this.put(new Stop()).update(offset, color, opacity);
      }
    }
  });
  register(Stop, 'Stop');
  function cssRule(selector, rule) {
    if (!selector) return '';
    if (!rule) return selector;
    let ret = selector + '{';
    for (const i in rule) {
      ret += unCamelCase(i) + ':' + rule[i] + ';';
    }
    ret += '}';
    return ret;
  }
  class Style extends Element {
    constructor(node) {
      let attrs = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : node;
      super(nodeOrNew('style', node), attrs);
    }
    addText() {
      let w = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : '';
      this.node.textContent += w;
      return this;
    }
    font(name, src) {
      let params = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
      return this.rule('@font-face', {
        fontFamily: name,
        src: src,
        ...params
      });
    }
    rule(selector, obj) {
      return this.addText(cssRule(selector, obj));
    }
  }
  registerMethods('Dom', {
    style(selector, obj) {
      return this.put(new Style()).rule(selector, obj);
    },
    fontface(name, src, params) {
      return this.put(new Style()).font(name, src, params);
    }
  });
  register(Style, 'Style');
  class TextPath extends Text {
    // Initialize node
    constructor(node) {
      let attrs = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : node;
      super(nodeOrNew('textPath', node), attrs);
    } // return the array of the path track element

    array() {
      const track = this.track();
      return track ? track.array() : null;
    } // Plot path if any

    plot(d) {
      const track = this.track();
      let pathArray = null;
      if (track) {
        pathArray = track.plot(d);
      }
      return d == null ? pathArray : this;
    } // Get the path element

    track() {
      return this.reference('href');
    }
  }
  registerMethods({
    Container: {
      textPath: wrapWithAttrCheck(function (text, path) {
        // Convert text to instance if needed
        if (!(text instanceof Text)) {
          text = this.text(text);
        }
        return text.path(path);
      })
    },
    Text: {
      // Create path for text to run on
      path: wrapWithAttrCheck(function (track) {
        let importNodes = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
        const textPath = new TextPath(); // if track is a path, reuse it

        if (!(track instanceof Path)) {
          // create path element
          track = this.defs().path(track);
        } // link textPath to path and add content

        textPath.attr('href', '#' + track, xlink); // Transplant all nodes from text to textPath

        let node;
        if (importNodes) {
          while (node = this.node.firstChild) {
            textPath.node.appendChild(node);
          }
        } // add textPath element as child node and return textPath

        return this.put(textPath);
      }),
      // Get the textPath children
      textPath() {
        return this.findOne('textPath');
      }
    },
    Path: {
      // creates a textPath from this path
      text: wrapWithAttrCheck(function (text) {
        // Convert text to instance if needed
        if (!(text instanceof Text)) {
          text = new Text().addTo(this.parent()).text(text);
        } // Create textPath from text and path and return

        return text.path(this);
      }),
      targets() {
        return baseFind('svg textPath').filter(node => {
          return (node.attr('href') || '').includes(this.id());
        }); // Does not work in IE11. Use when IE support is dropped
        // return baseFind('svg textPath[*|href*="' + this.id() + '"]')
      }
    }
  });

  TextPath.prototype.MorphArray = PathArray;
  register(TextPath, 'TextPath');
  class Use extends Shape {
    constructor(node) {
      let attrs = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : node;
      super(nodeOrNew('use', node), attrs);
    } // Use element as a reference

    use(element, file) {
      // Set lined element
      return this.attr('href', (file || '') + '#' + element, xlink);
    }
  }
  registerMethods({
    Container: {
      // Create a use element
      use: wrapWithAttrCheck(function (element, file) {
        return this.put(new Use()).use(element, file);
      })
    }
  });
  register(Use, 'Use');

  /* Optional Modules */
  const SVG = makeInstance;
  extend([Svg, Symbol$1, Image, Pattern, Marker], getMethodsFor('viewbox'));
  extend([Line, Polyline, Polygon, Path], getMethodsFor('marker'));
  extend(Text, getMethodsFor('Text'));
  extend(Path, getMethodsFor('Path'));
  extend(Defs, getMethodsFor('Defs'));
  extend([Text, Tspan], getMethodsFor('Tspan'));
  extend([Rect, Ellipse, Gradient, Runner], getMethodsFor('radius'));
  extend(EventTarget, getMethodsFor('EventTarget'));
  extend(Dom, getMethodsFor('Dom'));
  extend(Element, getMethodsFor('Element'));
  extend(Shape, getMethodsFor('Shape'));
  extend([Container, Fragment], getMethodsFor('Container'));
  extend(Gradient, getMethodsFor('Gradient'));
  extend(Runner, getMethodsFor('Runner'));
  List.extend(getMethodNames());
  registerMorphableType([SVGNumber, Color, Box, Matrix, SVGArray, PointArray, PathArray]);
  makeMorphable();

  function getPaper() {
    // eslint-disable-next-line new-cap
    return SVG();
  }

  function appendResiduesPosition(data) {
    let options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
    const residues = data.residues;
    const {
      leftRightBorders = 20,
      spaceBetweenResidues = 20,
      width = 600
    } = options;
    let xPos = leftRightBorders;
    let xOld = xPos;
    let line = 0;
    // we create a temporary paper in order to get the width of the text blocs

    const paper = getPaper();
    for (let i = 0; i < residues.all.length; i++) {
      let residue = residues.all[i];
      let textWidth = getTextWidth(paper, residue.label, options);
      xPos += textWidth;
      if (xPos > width - leftRightBorders) {
        xOld = leftRightBorders;
        xPos = leftRightBorders + textWidth;
        line++;
      }
      setPaper(residue, xOld, xPos, line);
      xPos += spaceBetweenResidues;
      xOld = xPos;
    }
    residues.nbLines = line;
  }
  function setPaper(residue, xFrom, xTo, line) {
    residue.paper = {
      xFrom,
      xTo,
      line,
      usedSlots: [],
      topPosition: 0,
      bottomPosition: 0
    };
  }
  function getTextWidth(paper, label) {
    let options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
    const {
      labelFontFamily = 'Verdana',
      labelSize = 12
    } = options;
    let text = paper.text(label);
    text.font({
      family: labelFontFamily,
      size: labelSize,
      weight: 'bold',
      fill: '#888'
    });
    let textWidth = text.length() || text.bbox().width;
    text.remove();
    return textWidth;
  }

  function appendResults(data, analysisResult) {
    let options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
    const numberResidues = data.residues.residues.length;
    const {
      merge = {},
      filter = {}
    } = options;
    let results = JSON.parse(JSON.stringify(analysisResult));
    results = results.filter(result => !result.type.match(/^-B[0-9]$/));
    // we calculate all the lines based on the results
    for (let result of results) {
      let parts = result.type.split(/:|(?=[a-z])/); // we may have ':' but not mandatory
      if (parts.length === 2) {
        result.internal = true;
        if (parts[1].match(/^[abcd][1-9]/)) {
          [parts[0], parts[1]] = [parts[1], parts[0]];
        }
        result.to = getNumber(parts[0]) - 1;
        result.from = numberResidues - getNumber(parts[1]);
      } else {
        if (parts[0].match(/^[abcd][1-9]/)) {
          result.fromBegin = true;
          result.position = getNumber(parts[0]) - 1;
        }
        if (parts[0].match(/^[wxyz][1-9]/)) {
          result.fromEnd = true;
          result.position = numberResidues - 1 - getNumber(parts[0]);
        }
      }
      if (result.fromEnd) result.color = 'red';
      if (result.fromBegin) result.color = 'blue';
      if (result.internal) {
        switch (result.type.substring(0, 1)) {
          case 'a':
            result.color = 'green';
            break;
          case 'b':
            result.color = 'orange';
            break;
          case 'c':
            result.color = 'cyan';
            break;
          default:
            result.color = 'green';
        }
      }
    }
    if (merge.charge) {
      const unique = {};
      for (let result of results) {
        if (!unique[result.type]) {
          unique[result.type] = [];
        }
        unique[result.type].push(result);
      }
      results = [];
      for (let key in unique) {
        let current = unique[key][0];
        current.similarity = unique[key].reduce((previous, item) => previous + item.similarity, 0);
        current.similarity = current.similarity / unique[key].length;
        results.push(current);
        current.charge = '';
      }
    }
    for (let result of results) {
      if (result.similarity > 0.95) {
        result.textColor = 'black';
      } else if (result.similarity > 0.9) {
        result.textColor = '#333';
      } else if (result.similariy > 0.8) {
        result.textColor = '#666';
      } else {
        result.textColor = '#999';
      }
    }
    results = filterResults(results, filter);

    // sort by residue length
    results.sort((a, b) => a.length - b.length);
    data.results = results;
  }
  function getNumber(text) {
    return Number(text.replace(/^.([0-9]+).*$/, '$1'));
  }
  function filterResults(results, filter) {
    if (!filter) return;
    let {
      minRelativeQuantity = 0,
      minSimilarity = 0,
      minQuantity = 0,
      showInternals = true
    } = filter;
    if (minRelativeQuantity) {
      minQuantity = Math.max(...results.map(entry => entry.quantity)) * minRelativeQuantity;
    }
    if (minSimilarity) {
      results = results.filter(result => !result.similarity || result.similarity >= minSimilarity);
    }
    if (minQuantity) {
      results = results.filter(result => !result.quantity || result.quantity >= minQuantity);
    }
    if (!showInternals) {
      results = results.filter(result => !result.internal);
    }
    return results;
  }

  function appendRows(data) {
    let allResidues = data.residues.all.sort((a, b) => a.line - b.line);
    data.rows = [];
    for (let residue of allResidues) {
      let line = residue.paper.line;
      if (!data.rows[line]) {
        data.rows[line] = {
          residues: []
        };
      }
      data.rows[line].residues.push(residue);
    }
    for (let row of data.rows) {
      row.info = {};
    }
  }

  /* eslint-disable no-loop-func */

  /**
   * For each row we calculate internals, label over and label under
   * @param {*} data
   */
  function appendRowsInformation(data) {
    for (let row of data.rows) {
      let filtered = row.residues.filter(entry => entry.fromBegin !== undefined);
      if (filtered.length > 0) {
        row.info.firstResidue = filtered[0].fromBegin;
        row.info.lastResidue = filtered[filtered.length - 1].fromBegin;
      }
      row.internals = [];
    }
    for (let result of data.results) {
      if (result.internal) {
        let fromResidue = data.residues.residues[result.from];
        let from = fromResidue.fromBegin;
        let toResidue = data.residues.residues[result.to];
        let to = toResidue.fromBegin;
        for (let row of data.rows) {
          if (from <= row.info.lastResidue && to >= row.info.firstResidue) {
            result = JSON.parse(JSON.stringify(result));
            result.fromResidue = fromResidue;
            if (from < row.info.firstResidue) {
              result.firstIndex = true;
            } else {
              row.residues.forEach((residue, index) => {
                if (residue.fromBegin === from) {
                  result.firstIndex = index;
                }
              });
            }
            result.toResidue = toResidue;
            if (to > row.info.lastResidue) {
              result.lastIndex = true;
            } else {
              row.residues.forEach((residue, index) => {
                if (residue.fromBegin === to) {
                  result.lastIndex = index;
                }
              });
            }
            row.internals.push(result);
          }
        }
      } else if (result.position !== undefined && data.residues.residues[result.position]) {
        let residue = data.residues.residues[result.position];
        if (result.fromEnd) {
          residue.info.nbOver++;
          residue.results.end.push(result);
        }
        if (result.fromBegin) {
          residue.info.nbUnder++;
          residue.results.begin.push(result);
        }
      }
    }
    for (let row of data.rows) {
      let maxNbOver = 0;
      let maxNbUnder = 0;
      for (let residue of row.residues) {
        if (residue.info.nbOver > maxNbOver) maxNbOver = residue.info.nbOver;
        if (residue.info.nbUnder > maxNbUnder) maxNbUnder = residue.info.nbUnder;
      }
      row.info.nbOver = maxNbOver;
      row.info.nbUnder = maxNbUnder;
    }
  }

  function addCSS(paper) {
    let cssCode = `
    .highlight {
        stroke: yellow;
        stroke-width: 5px;
        fill: red;
    }
    .highlightText {
        fill: red;
    }
    `;
    let style = paper.element('style');
    style.words(cssCode);
  }

  function addScript(paper) {
    let scriptCode = ` // <![CDATA[
        function mouseOver(evt) {
           
            let targetRange=evt.target.id.replace(/^line/,'');
            let from=targetRange.replace(/-.*/,'')*1;
            let to=targetRange.replace(/.*-/,'')*1;
            let children=evt.target.parentNode.children;
            for (let child of children) {
                if (child.id === evt.target.id) {
                    child.setAttribute('class','highlight');
                }
                if (child.nodeName === 'text' && child.id.startsWith("residue")) {
                    let residueNumber=child.id.replace(/residue-/,'')*1;
                    if (residueNumber>=from && residueNumber<=to) {
                        child.setAttribute('class','highlightText');
                    }
                }
            }
        }
        function mouseOut(evt) {
            let children=evt.target.parentNode.children;
            for (let child of children) {
                if (child.id === evt.target.id) {
                    child.setAttribute('class','');
                }
                if (child.nodeName === 'text' && child.id.startsWith("residue")) {
                    child.setAttribute('class','');
                }
            }
        }
     // ]]>
    `;
    let script = paper.element('script');
    script.attr({
      type: 'application/ecmascript'
    });
    script.words(scriptCode);
  }

  function drawLabel(paper, result, x, y, options) {
    let label = result.type;
    let similarity = String(Math.round(result.similarity * 100));
    let charge = result.charge > 0 ? `+${result.charge}` : result.charge;
    let text = paper.plain(label);
    text.font({
      fill: result.textColor,
      family: options.labelFontFamily,
      weight: 'bold',
      size: options.labelSize,
      anchor: 'end'
    });
    text.attr({
      x,
      y
    });
    let textWidth = 0;
    text = paper.plain(charge);
    text.font({
      fill: result.textColor,
      family: options.labelFontFamily,
      size: options.labelSize / 2
    });
    text.attr({
      x: x + textWidth,
      y: y - options.labelSize / 2
    });
    text = paper.plain(similarity);
    text.font({
      fill: result.textColor,
      family: options.labelFontFamily,
      size: options.labelSize / 2
    });
    text.attr({
      x: x + textWidth,
      y
    });
  }

  function drawInternals(paper, row, options) {
    let fromX = 0;
    let toX = 0;
    for (const internal of row.internals) {
      options.verticalPosition += options.spaceBetweenInternalLines;
      if (internal.firstIndex === true) {
        fromX = 0;
      } else {
        fromX = internal.fromResidue.paper.xFrom - options.spaceBetweenResidues / 2;
      }
      if (internal.lastIndex === true) {
        toX = options.width - 1;
      } else {
        toX = internal.toResidue.paper.xTo + options.spaceBetweenResidues / 2;
      }
      let y = options.verticalPosition;
      let drawLine = paper.line(fromX, y, toX, y);
      drawLine.attr({
        onmouseover: 'mouseOver(evt)',
        onmouseout: 'mouseOut(evt)',
        id: `line${internal.fromResidue.fromBegin}-${internal.toResidue.fromBegin}`
      });
      drawLine.stroke({
        color: internal.color,
        width: options.strokeWidth
      });
      let center = (fromX + toX + internal.type.length * options.labelSize * 2 / 3) / 2;
      drawLabel(paper, internal, center, y - 2, options);
    }
    options.verticalPosition += options.spaceBetweenInternalLines * 2;
  }

  function drawReplacements(paper, data, options) {
    let replacements = data.residues.replacements;
    replacements = Object.keys(replacements).map(key => {
      return {
        key,
        ...replacements[key]
      };
    });
    for (let replacement of replacements) {
      options.verticalPosition += options.spaceBetweenInternalLines;
      let text = paper.plain(`${replacement.label} = ${replacement.key}`);
      text.font({
        fill: 'darkviolet',
        family: options.labelFontFamily,
        weight: 'bold',
        size: 10
      });
      text.attr({
        x: options.leftRightBorders,
        y: options.verticalPosition
      });
    }
    options.verticalPosition += options.spaceBetweenInternalLines;
  }

  function drawTerminals(paper, row, options) {
    for (let residue of row.residues) {
      if (!residue.results) continue;
      for (let result of residue.results.begin) {
        let line = paper.line(residue.paper.xTo + options.spaceBetweenResidues / 2, residue.paper.y, residue.paper.xTo + options.spaceBetweenResidues / 2, residue.paper.y - 8);
        line.stroke({
          color: result.color,
          width: options.strokeWidth,
          linecap: 'round'
        });
        line = paper.line(residue.paper.xTo + options.spaceBetweenResidues / 2, residue.paper.y, residue.paper.xTo + options.spaceBetweenResidues / 2 - 5, residue.paper.y + 5);
        line.stroke({
          color: result.color,
          width: options.strokeWidth,
          linecap: 'round'
        });
        drawLabel(paper, result, residue.paper.xTo + options.spaceBetweenResidues / 2, residue.paper.y + options.labelSize + 6 + residue.paper.bottomPosition * options.labelSize, options);
        residue.paper.bottomPosition++;
      }
      for (let result of residue.results.end) {
        let line = paper.line(residue.paper.xTo + options.spaceBetweenResidues / 2 + options.strokeWidth, residue.paper.y, residue.paper.xTo + options.spaceBetweenResidues / 2 + options.strokeWidth, residue.paper.y - 8);
        line.stroke({
          color: result.color,
          width: options.strokeWidth,
          linecap: 'round'
        });
        line = paper.line(residue.paper.xTo + options.spaceBetweenResidues / 2 + options.strokeWidth, residue.paper.y - 8, residue.paper.xTo + options.spaceBetweenResidues / 2 + 5 + options.strokeWidth, residue.paper.y - 13);
        line.stroke({
          color: result.color,
          width: options.strokeWidth,
          linecap: 'round'
        });
        drawLabel(paper, result, residue.paper.xTo + options.spaceBetweenResidues, residue.paper.y - 17 - residue.paper.topPosition * options.labelSize, options);
        residue.paper.topPosition++;
      }
    }
  }

  function drawSequence(paper, row, options) {
    // need to plan some space for the OVER
    options.verticalPosition += row.info.nbOver * (options.labelSize + 1);
    for (const residue of row.residues) {
      residue.paper.y = options.verticalPosition;
      let text = paper.plain(residue.label);
      let textColor = residue.replaced ? 'darkviolet' : residue.kind === 'residue' ? '#555' : '#CCC';
      text.font({
        family: options.labelFontFamily,
        size: 12,
        weight: 'bold',
        fill: textColor
      });
      text.attr({
        x: residue.paper.xFrom,
        y: residue.paper.y
      });
      text.attr({
        id: `residue-${residue.fromBegin}`
      });
    }
    drawTerminals(paper, row, options);

    // need to plan some space for the UNDER
    options.verticalPosition += row.info.nbUnder * (options.labelSize + 1);
    options.verticalPosition += options.spaceBetweenInternalLines * 2;
  }

  /**
   *
   * @param {string} sequence
   * @param {array} analysisResults
   * @param {object} [options={}]
   * @param {number} [options.leftRightBorders=50]
   * @param {number} [options.width=600]
   * @param {number} [options.spaceBetweenResidues=30]
   * @param {number} [options.spaceBetweenInternalLines=12]
   * @param {number} [options.strokeWidth=2]
   * @param {string} [options.labelFontFamily='Verdana']
   * @param {number} [options.labelSize=8]
   * @param {number} [options.parsing] Sequence parsing options
   * @param {object} [options.merge={}]
   * @param {boolean} [options.merge.charge] Merge results if only differs by charge
   * @param {object} [options.filter={}] define some filters
   * @param {number} [options.filter.minSimilarity=0]  minimal similarity
   * @param {number} [options.filter.minQuantity=0]  minimal quantity
   * @param {number} [options.filter.minRelativeQuantity=0]  minimal relative quantity. This value should be between 0 and 1 and supersede minQuantity.
   * @param {boolean} [options.filter.showInternals=true] show the internal fragments
   *
   */
  function sequenceSVG(sequence, analysisResults) {
    let options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
    const {
      width = 600,
      leftRightBorders = 50,
      spaceBetweenResidues = 30,
      spaceBetweenInternalLines = 12,
      strokeWidth = 2,
      labelFontFamily = 'Verdana',
      labelSize = 8,
      parsing,
      merge,
      filter
    } = options;
    const drawOptions = {
      spaceBetweenResidues,
      leftRightBorders,
      spaceBetweenInternalLines,
      strokeWidth,
      labelSize,
      labelFontFamily,
      verticalPosition: spaceBetweenInternalLines,
      width
    };
    let data = {};
    appendResidues(data, sequence, parsing);
    appendResults(data, analysisResults, {
      merge,
      filter
    });
    appendResiduesPosition(data, {
      leftRightBorders,
      spaceBetweenResidues,
      labelFontFamily,
      labelSize,
      width
    });
    appendRows(data);
    appendInternals(data);
    appendRowsInformation(data);

    // We start to create the SVG and create the paper
    const paper = getPaper();
    addCSS(paper);
    addScript(paper);
    for (let row of data.rows) {
      drawInternals(paper, row, drawOptions);
      drawSequence(paper, row, drawOptions);
    }
    drawReplacements(paper, data, drawOptions);
    paper.size(width, drawOptions.verticalPosition);
    let svg = paper.svg();
    paper.clear();
    return svg;
  }

  var ReportPkg = /*#__PURE__*/Object.freeze({
    __proto__: null,
    sequenceSVG: sequenceSVG
  });

  const toString$1 = Object.prototype.toString;
  /**
   * Checks if an object is an instance of an Array (array or typed array).
   *
   * @param {any} value - Object to check.
   * @returns {boolean} True if the object is an array.
   */
  function isAnyArray$1(value) {
    return toString$1.call(value).endsWith('Array]');
  }

  var commonjsGlobal = typeof globalThis !== 'undefined' ? globalThis : typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {};

  function getDefaultExportFromCjs (x) {
  	return x && x.__esModule && Object.prototype.hasOwnProperty.call(x, 'default') ? x['default'] : x;
  }

  function commonjsRequire (path) {
  	throw new Error('Could not dynamically require "' + path + '". Please configure the dynamicRequireTargets or/and ignoreDynamicRequires option of @rollup/plugin-commonjs appropriately for this require call to work.');
  }

  var medianQuickselect_min = {exports: {}};

  (function (module) {
    (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);
        };
      module.exports ? module.exports = a : window.median = a;
    })();
  })(medianQuickselect_min);
  var quickSelectMedian = medianQuickselect_min.exports;

  /**
   * Calculates the median of an array
   *
   * @param input - Array containing values
   * @returns - median
   */
  function xMedian(input) {
    if (!isAnyArray$1(input)) {
      throw new TypeError('input must be an array');
    }
    if (input.length === 0) {
      throw new TypeError('input must not be empty');
    }
    // we need to slice because the order of elements is changed in the quickselect
    // https://github.com/mad-gooze/median-quickselect
    return quickSelectMedian(input.slice());
  }

  /**
   * This function
   * @param output - undefined or a new array
   * @param length - length of the output array
   * @returns
   */
  function getOutputArray(output, length) {
    if (output !== undefined) {
      if (!isAnyArray$1(output)) {
        throw new TypeError('output option must be an array if specified');
      }
      if (output.length !== length) {
        throw new TypeError('the output array does not have the correct length');
      }
      return output;
    } else {
      return new Float64Array(length);
    }
  }

  /**
   * Checks if input is of type array
   *
   * @param input - input
   */
  function xCheck$1(input) {
    if (!isAnyArray$1(input)) {
      throw new TypeError('input must be an array');
    }
    if (input.length === 0) {
      throw new TypeError('input must not be empty');
    }
  }

  /**
   * Returns the closest index of a `target`
   *
   * @param array - array of numbers
   * @param target - target
   * @returns - closest index
   */
  function xFindClosestIndex$2(array, target) {
    let options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
    const {
      sorted = true
    } = options;
    if (sorted) {
      let low = 0;
      let high = array.length - 1;
      let middle = 0;
      while (high - low > 1) {
        middle = low + (high - low >> 1);
        if (array[middle] < target) {
          low = middle;
        } else if (array[middle] > target) {
          high = middle;
        } else {
          return middle;
        }
      }
      if (low < array.length - 1) {
        if (Math.abs(target - array[low]) < Math.abs(array[low + 1] - target)) {
          return low;
        } else {
          return low + 1;
        }
      } else {
        return low;
      }
    } else {
      let index = 0;
      let diff = Number.POSITIVE_INFINITY;
      for (let i = 0; i < array.length; i++) {
        const currentDiff = Math.abs(array[i] - target);
        if (currentDiff < diff) {
          diff = currentDiff;
          index = i;
        }
      }
      return index;
    }
  }

  /**
   * Returns an object with {fromIndex, toIndex} for a specific from / to
   *
   * @param x - array of numbers
   * @param options - Options
   */
  function xGetFromToIndex$1(x) {
    let options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
    let {
      fromIndex,
      toIndex,
      from,
      to
    } = options;
    if (fromIndex === undefined) {
      if (from !== undefined) {
        fromIndex = xFindClosestIndex$2(x, from);
      } else {
        fromIndex = 0;
      }
    }
    if (toIndex === undefined) {
      if (to !== undefined) {
        toIndex = xFindClosestIndex$2(x, to);
      } else {
        toIndex = x.length - 1;
      }
    }
    if (fromIndex < 0) fromIndex = 0;
    if (toIndex < 0) toIndex = 0;
    if (fromIndex >= x.length) fromIndex = x.length - 1;
    if (toIndex >= x.length) toIndex = x.length - 1;
    if (fromIndex > toIndex) [fromIndex, toIndex] = [toIndex, fromIndex];
    return {
      fromIndex,
      toIndex
    };
  }

  /**
   * Computes the maximal value of an array of values
   *
   * @param array - array of numbers
   * @param options - options
   */
  function xMaxValue$1(array) {
    let options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
    xCheck$1(array);
    const {
      fromIndex,
      toIndex
    } = xGetFromToIndex$1(array, options);
    let maxValue = array[fromIndex];
    for (let i = fromIndex + 1; i <= toIndex; i++) {
      if (array[i] > maxValue) {
        maxValue = array[i];
      }
    }
    return maxValue;
  }

  /**
   * Computes the minimal value of an array of values
   *
   * @param array - array of numbers
   * @param options - options
   */
  function xMinValue(array) {
    let options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
    xCheck$1(array);
    const {
      fromIndex,
      toIndex
    } = xGetFromToIndex$1(array, options);
    let minValue = array[fromIndex];
    for (let i = fromIndex + 1; i <= toIndex; i++) {
      if (array[i] < minValue) {
        minValue = array[i];
      }
    }
    return minValue;
  }

  /**
   * Check if the values are separated always by the same difference
   *
   * @param array - Monotone growing array of number
   */
  function xIsEquallySpaced(array) {
    let options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
    if (array.length < 3) return true;
    const {
      tolerance = 0.05
    } = options;
    let maxDx = 0;
    let minDx = Number.MAX_SAFE_INTEGER;
    for (let i = 0; i < array.length - 1; ++i) {
      let absoluteDifference = array[i + 1] - array[i];
      if (absoluteDifference < minDx) {
        minDx = absoluteDifference;
      }
      if (absoluteDifference > maxDx) {
        maxDx = absoluteDifference;
      }
    }
    return (maxDx - minDx) / maxDx < tolerance;
  }

  /**
   * Returns true if x is monotone
   *
   * @param array - array of numbers
   */
  function xIsMonotone(array) {
    if (array.length <= 2) {
      return true;
    }
    if (array[0] === array[1]) {
      // maybe a constant series
      for (let i = 1; i < array.length - 1; i++) {
        if (array[i] !== array[i + 1]) return false;
      }
      return true;
    }
    if (array[0] < array[array.length - 1]) {
      for (let i = 0; i < array.length - 1; i++) {
        if (array[i] >= array[i + 1]) return false;
      }
    } else {
      for (let i = 0; i < array.length - 1; i++) {
        if (array[i] <= array[i + 1]) return false;
      }
    }
    return true;
  }

  /**
   * Returns true if x is monotone
   *
   * @param array - array of numbers
   */
  function xIsMonotoneIncreasing(array) {
    if (array.length < 2) {
      return true;
    }
    for (let i = 0; i < array.length - 1; i++) {
      if (array[i] >= array[i + 1]) return false;
    }
    return true;
  }

  /**
   * This function calculates the median absolute deviation (MAD)
   * https://en.wikipedia.org/wiki/Median_absolute_deviation
   * @param array
   */
  function xMedianAbsoluteDeviation(array) {
    const median = xMedian(array);
    const averageDeviations = new Float64Array(array.length);
    for (let i = 0; i < array.length; i++) {
      averageDeviations[i] = Math.abs(array[i] - median);
    }
    return {
      median,
      mad: xMedian(averageDeviations)
    };
  }

  /**
   * Determine noise level using MAD https://en.wikipedia.org/wiki/Median_absolute_deviation
   * Constant to convert mad to sd calculated using https://www.wolframalpha.com/input?i=sqrt%282%29+inverse+erf%280.5%29
   * This assumes a gaussian distribution of the noise
   * @param array
   * @returns noise level corresponding to one standard deviation
   */
  function xNoiseStandardDeviation(array) {
    const {
      mad,
      median
    } = xMedianAbsoluteDeviation(array);
    return {
      sd: mad / 0.6744897501960817,
      mad,
      median
    };
  }

  /**
   * Calculate the sum of the values
   *
   * @param array - Object that contains property x (an ordered increasing array) and y (an array).
   * @param options - Options.
   * @returns XSum value on the specified range.
   */
  function xSum(array) {
    let options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
    xCheck$1(array);
    const {
      fromIndex,
      toIndex
    } = xGetFromToIndex$1(array, options);
    let sumValue = array[fromIndex];
    for (let i = fromIndex + 1; i <= toIndex; i++) {
      sumValue += array[i];
    }
    return sumValue;
  }

  /**
   * Divides the data with either the sum, the absolute sum or the maximum of the data
   * @param array - Array containing values
   * @param options - options
   * @returns - normalized data
   */
  function xNormed(input) {
    let options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
    const {
      algorithm = 'absolute',
      value = 1
    } = options;
    xCheck$1(input);
    const output = getOutputArray(options.output, input.length);
    if (input.length === 0) {
      throw new Error('input must not be empty');
    }
    switch (algorithm.toLowerCase()) {
      case 'absolute':
        {
          let absoluteSumValue = absoluteSum(input) / value;
          if (absoluteSumValue === 0) {
            throw new Error('xNormed: trying to divide by 0');
          }
          for (let i = 0; i < input.length; i++) {
            output[i] = input[i] / absoluteSumValue;
          }
          return output;
        }
      case 'max':
        {
          let currentMaxValue = xMaxValue$1(input);
          if (currentMaxValue === 0) {
            throw new Error('xNormed: trying to divide by 0');
          }
          const factor = value / currentMaxValue;
          for (let i = 0; i < input.length; i++) {
            output[i] = input[i] * factor;
          }
          return output;
        }
      case 'sum':
        {
          let sumFactor = xSum(input) / value;
          if (sumFactor === 0) {
            throw new Error('xNormed: trying to divide by 0');
          }
          for (let i = 0; i < input.length; i++) {
            output[i] = input[i] / sumFactor;
          }
          return output;
        }
      default:
        throw new Error(`norm: unknown algorithm: ${algorithm}`);
    }
  }
  function absoluteSum(input) {
    let sumValue = 0;
    for (let i = 0; i < input.length; i++) {
      sumValue += Math.abs(input[i]);
    }
    return sumValue;
  }

  /**
   * Throw an error in no an object of x,y arrays
   *
   * @param data - array of points {x,y,z}
   */
  function xyCheck(data) {
    let options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
    const {
      minLength
    } = options;
    if (typeof data !== 'object' || !isAnyArray$1(data.x) || !isAnyArray$1(data.y)) {
      throw new Error('Data must be an object of x and y arrays');
    }
    if (data.x.length !== data.y.length) {
      throw new Error('The x and y arrays must have the same length');
    }
    if (minLength && data.x.length < minLength) {
      throw new Error(`data.x must have a length of at least ${minLength}`);
    }
  }

  /**
   * Filters x,y values to allow strictly growing values in x axis.
   *
   * @param data - Object that contains property x (an ordered increasing array) and y (an array).
   */
  function xyEnsureGrowingX(data) {
    xyCheck(data);
    if (xIsMonotoneIncreasing(data.x)) return data;
    const x = Array.from(data.x);
    const y = Array.from(data.y);
    let prevX = Number.NEGATIVE_INFINITY;
    let currentIndex = 0;
    for (let index = 0; index < x.length; index++) {
      if (prevX < x[index]) {
        if (currentIndex < index) {
          x[currentIndex] = x[index];
          y[currentIndex] = y[index];
        }
        currentIndex++;
        prevX = x[index];
      }
    }
    x.length = currentIndex;
    y.length = currentIndex;
    return {
      x,
      y
    };
  }

  /**
   * Join x / y values when difference in X is closer than delta.
   * When joining, y values are summed and x values are weighted average
   *
   * @param data - Object that contains property x (an ordered increasing array) and y (an array)
   * @param options - Options
   * @returns - An object with the xyIntegration function
   */
  function xyJoinX(data) {
    let options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
    xyCheck(data);
    const {
      delta = 1
    } = options;
    const deltaIsFunction = typeof delta === 'function';
    const x = Array.from(data.x);
    const y = Array.from(data.y);
    if (x.length < 2) {
      return {
        x,
        y
      };
    }
    let position = 0;
    for (let i = 1; i < x.length; i++) {
      let difference = x[i] - x[i - 1];
      let currentDelta = deltaIsFunction ? delta((x[i] + x[i - 1]) / 2) : delta;
      if (difference <= currentDelta) {
        // we join
        if (y[position] !== 0 || y[i] !== 0) {
          x[position] = (x[position] * y[position] + x[i] * y[i]) / (y[position] + y[i]);
          y[position] += y[i];
        }
      } else {
        position++;
        x[position] = x[i];
        y[position] = y[i];
      }
    }
    x.length = position + 1;
    y.length = position + 1;
    return {
      x,
      y
    };
  }

  /**
   * This function performs a quick sort of the x array while transforming the y array to preserve the coordinates.
   *
   * @param data - Object that contains property x (Array) and y (Array)
   */
  function xySortX(data) {
    const {
      x,
      y
    } = data;
    if (xIsMonotone(x) && x.length > 1) {
      if (x[0] < x[1]) {
        return {
          x: Float64Array.from(x),
          y: Float64Array.from(y)
        };
      } else {
        return {
          x: Float64Array.from(x).reverse(),
          y: Float64Array.from(y).reverse()
        };
      }
    }
    let xyObject = x.map((val, index) => ({
      x: val,
      y: y[index]
    })).sort((a, b) => a.x - b.x);
    let response = {
      x: new Float64Array(x.length),
      y: new Float64Array(y.length)
    };
    for (let i = 0; i < x.length; i++) {
      response.x[i] = xyObject[i].x;
      response.y[i] = xyObject[i].y;
    }
    return response;
  }

  /**
   * Throw an error in no an object of x,y arrays
   *
   * @param points - list of points
   */
  function xyObjectCheck() {
    let points = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : [];
    if (!Array.isArray(points)) {
      throw new Error('ArrayPoints must be an array of {x,y} object');
    }
    if (points.length > 0 && (points[0].x === undefined || points[0].y === undefined)) {
      throw new Error('ArrayPoints must be an array of {x,y} object');
    }
  }

  /**
   * Finds the max x value and return a {x,y,index} point
   *
   * @param points - Object that contains property x (an ordered increasing array) and y (an array)
   */
  function xyObjectMaxXPoint() {
    let points = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : [];
    xyObjectCheck(points);
    if (points.length === 0) return {
      x: 0,
      y: 0
    };
    let current = {
      x: points[0].x,
      y: points[0].y,
      index: 0
    };
    for (let i = 1; i < points.length; i++) {
      if (points[i].x > current.x) {
        current = {
          x: points[i].x,
          y: points[i].y,
          index: i
        };
      }
    }
    return current;
  }

  /**
   * Finds the min x value and return a {x,y,index} point
   *
   * @param points - Object that contains property x (an ordered increasing array) and y (an array)
   */
  function xyObjectMinXPoint() {
    let points = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : [];
    xyObjectCheck(points);
    if (points.length === 0) return {
      x: 0,
      y: 0
    };
    let current = {
      x: points[0].x,
      y: points[0].y,
      index: 0
    };
    for (let i = 1; i < points.length; i++) {
      if (points[i].x < current.x) {
        current = {
          x: points[i].x,
          y: points[i].y,
          index: i
        };
      }
    }
    return current;
  }

  /**
   * Calculate the sum of Y values
   *
   * @param points - Object that contains property x and y (an array)
   */
  function xyObjectSumY() {
    let points = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : [];
    xyObjectCheck(points);
    let sum = 0;
    for (let point of points) {
      sum += point.y;
    }
    return sum;
  }

  /**
   * Filter the array by taking the higher peaks and only
   * keep one per slot.
   * There are 2 different slots, the smallest one will have the
   * extra annotation `close` to true
   * @param {array} peaks - array of all the peaks
   * @param {object} [options={}]
   * @param {number} [options.from] - min X value of the window to consider
   * @param {number} [options.to] - max X value of the window to consider
   * @param {number} [options.searchMonoisotopicRatio=0] - search previous peaks with at least ratio height
   * @param {number} [options.limit=20] - max number of peaks
   * @param {number} [options.threshold=0.01] - minimal intensity compare to base peak
   * @param {number} [options.numberSlots=10] - define the number of slots and indirectly the slot width
   * @param {number} [options.numberCloseSlots=50]
   * @returns {array} - copy of peaks with 'close' annotation
   */

  function getBestPeaks(peaks) {
    let options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
    const {
      searchMonoisotopicRatio = 0,
      from = xyObjectMinXPoint(peaks).x,
      to = xyObjectMaxXPoint(peaks).x,
      limit = 20,
      threshold = 0.01,
      numberCloseSlots = 50,
      numberSlots = 10
    } = options;
    let slot = (to - from) / numberSlots;
    let closeSlot = (to - from) / numberCloseSlots;
    let selected = peaks.filter(peak => peak.x >= from && peak.x <= to).map(peak => {
      return {
        peak,
        monoisotopic: false
      };
    });
    if (searchMonoisotopicRatio) {
      selected = selected.sort((a, b) => b.peak.x - a.peak.x);
      for (let i = 0; i < selected.length; i++) {
        let item = selected[i];
        for (let j = i + 1; j < selected.length; j++) {
          let nextItem = selected[j];
          if (item.peak.x - nextItem.peak.x < 0.09) continue;
          if (item.peak.x - nextItem.peak.x > 1.1) break;
          if (nextItem.peak.y > item.peak.y * searchMonoisotopicRatio) {
            item.monoisotopic = false;
            nextItem.monoisotopic = true;
            break;
          }
        }
      }
    }
    selected = selected.sort((a, b) => {
      if (a.monoisotopic && !b.monoisotopic) return -1;
      if (b.monoisotopic && !a.monoisotopic) return 1;
      return b.peak.y - a.peak.y;
    });
    let toReturn = [];
    if (selected.length === 0) return [];
    let minY = selected[0].peak.y * threshold;
    peakLoop: for (let item of selected) {
      if (item.peak.y < minY) {
        if (item.monoisotopic) {
          continue;
        } else {
          break;
        }
      }
      let close = false;
      for (let existing of toReturn) {
        if (Math.abs(existing.x - item.peak.x) < closeSlot) {
          continue peakLoop;
        }
        if (Math.abs(existing.x - item.peak.x) < slot) {
          close = true;
        }
      }
      let newPeak = JSON.parse(JSON.stringify(item.peak));
      newPeak.close = close;
      toReturn.push(newPeak);
      if (toReturn.length === limit) break;
    }
    return toReturn.sort((a, b) => a.x - b.x);
  }

  /**
   * Filter the array of peaks
   * @param {array} peaks - array of all the peaks
   * @param {object} [options={}]
   * @param {number} [options.from] - min X value of the window to consider
   * @param {number} [options.to] - max X value of the window to consider
   * @param {number} [options.threshold=0.01] - minimal intensity compare to base peak
   * @param {number} [options.limit=undefined] - maximal number of peaks (based on intensity)
   * @param {number} [options.sumValue] // if sumValue is defined, maxValue is ignored
   * @returns {array} - copy of peaks with 'close' annotation
   */

  function getPeaks(peaks) {
    let options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
    const {
      from = xyObjectMinXPoint(peaks).x,
      to = xyObjectMaxXPoint(peaks).x,
      threshold = 0.01,
      limit,
      sumValue
    } = options;
    let maxY = Number.MIN_SAFE_INTEGER;
    for (let peak of peaks) {
      if (peak.y > maxY) maxY = peak.y;
    }
    let minY = maxY * threshold;
    peaks = peaks.filter(peak => peak.x >= from && peak.x <= to && peak.y >= minY);
    if (limit && peaks.length > limit) {
      peaks.sort((a, b) => b.y - a.y);
      peaks = peaks.slice(0, limit);
    }
    if (sumValue) {
      peaks = JSON.parse(JSON.stringify(peaks));
      const currentSum = xyObjectSumY(peaks);
      const ratio = sumValue / currentSum;
      peaks.forEach(peak => peak.y *= ratio);
    }
    return peaks.sort((a, b) => a.x - b.x);
  }

  /**
   * Filter the array of peaks
   * @param {array} peaks - array of all the peaks
   * @param {string} mf - Molecular formula of the parent molecule
   * @param {object} [options={}]
   * @param {number} [options.from] - min X value of the window to consider
   * @param {number} [options.to] - max X value of the window to consider
   * @param {number} [options.threshold=0.01] - minimal intensity compare to base peak
   * @param {number} [options.limit=undefined] - maximal number of peaks (based on intensity)
   * @param {string} [options.ionizations]
   * @param {number} [options.precision]
   * @returns {array} - copy of peaks with 'close' annotation
   */

  async function getFragmentPeaks(peaks, mf) {
    let options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
    const emdb = new EMDB();
    const {
      ionizations = '',
      precision
    } = options;
    const mfInfo = new MF(mf).getInfo();
    const ranges = Object.keys(mfInfo.atoms).map(key => `${key}0-${mfInfo.atoms[key]}`).join(' ');
    peaks = getPeaks(peaks, options);
    for (let peak of peaks) {
      peak.mfs = (await emdb.fromMonoisotopicMass(peak.x, {
        precision,
        ranges,
        ionizations
      })).mfs;
    }
    peaks = peaks.filter(peak => peak.mfs.length > 0);
    return peaks;
  }

  /**
   * Remove an integer number of time the specifiedd monoisotopic mass
   * Mass remainder analysis (MARA): https://doi.org/10.1021/acs.analchem.7b04730
   * @param {object} spectrum
   * @param {number} mass
   * @param {object} [options={}
   * @param {number} [options.delta=0.001]
   */
  function getMassRemainder(spectrum, mass) {
    let options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
    const {
      delta = 0.001
    } = options;
    const x = spectrum.x.slice();
    const y = spectrum.y;
    for (let i = 0; i < x.length; i++) {
      const factor = Math.floor(x[i] / mass);
      x[i] = x[i] - factor * mass;
    }
    // we sort and join
    return xyJoinX(xySortX({
      x,
      y
    }), {
      delta
    });
  }

  function calculateOverlapFromDiff$1(diffs) {
    if (diffs[1].length === 0) return 0;
    let sumPos = 0;
    for (let i = 0; i < diffs[1].length; i++) {
      sumPos += Math.abs(diffs[1][i]);
    }
    return 1 - sumPos;
  }

  /**
   * This code requires the use of an array like  [[x1,y1],[x2,y2], ...]
   * If it is not the right format, we will just convert it
   * Otherwise we return the correct format
   * @param {Peaks} peaks
   * @returns [number[], number[]]
   */
  function checkPeaks$1(peaks) {
    // if it is already a 2D array of points, we just return them
    if (Array.isArray(peaks) && Array.isArray(peaks[0]) && peaks.length === 2) {
      return peaks;
    }
    if (Array.isArray(peaks.x) && Array.isArray(peaks.y)) {
      return [peaks.x, peaks.y];
    }
    const x = new Array(peaks.length);
    const y = new Array(peaks.length);
    for (let i = 0; i < peaks.length; i++) {
      x[i] = peaks[i][0];
      y[i] = peaks[i][1];
    }
    return [x, y];
  }

  function extract$1(array, from, to) {
    const newArray = [[], []];
    let j = 0;
    const length = array[0] ? array[0].length : 0;
    for (let i = 0; i < length; i++) {
      if ((!from || array[0][i] >= from) && (!to || array[0][i] <= to)) {
        newArray[0][j] = array[0][i];
        newArray[1][j] = array[1][i];
        j++;
      }
    }
    return newArray;
  }

  // returns an new array based on array1 where there is a peak of array2 at a distance under width/2
  function getCommonArray$1(array1, array2, width) {
    const newArray = [[], []];
    let pos2 = 0;
    width /= 2;
    let j = 0;
    const array1Length = array1[0] ? array1[0].length : 0;
    const array2Length = array2[0] ? array2[0].length : 0;
    for (let i = 0; i < array1Length; i++) {
      while (pos2 < array2Length && array1[0][i] > array2[0][pos2] + width) {
        pos2++;
      }
      if (pos2 < array2Length && array1[0][i] > array2[0][pos2] - width) {
        newArray[0][j] = array1[0][i];
        newArray[1][j] = array1[1][i];
        j++;
      }
    }
    return newArray;
  }

  var mlStat$1 = {};

  var array$3 = {};

  (function (exports) {

    function compareNumbers(a, b) {
      return a - b;
    }

    /**
     * Computes the sum of the given values
     * @param {Array} values
     * @returns {number}
     */
    exports.sum = function sum(values) {
      var sum = 0;
      for (var i = 0; i < values.length; i++) {
        sum += values[i];
      }
      return sum;
    };

    /**
     * Computes the maximum of the given values
     * @param {Array} values
     * @returns {number}
     */
    exports.max = function max(values) {
      var max = values[0];
      var l = values.length;
      for (var i = 1; i < l; i++) {
        if (values[i] > max) max = values[i];
      }
      return max;
    };

    /**
     * Computes the minimum of the given values
     * @param {Array} values
     * @returns {number}
     */
    exports.min = function min(values) {
      var min = values[0];
      var l = values.length;
      for (var i = 1; i < l; i++) {
        if (values[i] < min) min = values[i];
      }
      return min;
    };

    /**
     * Computes the min and max of the given values
     * @param {Array} values
     * @returns {{min: number, max: number}}
     */
    exports.minMax = function minMax(values) {
      var min = values[0];
      var max = values[0];
      var l = values.length;
      for (var i = 1; i < l; i++) {
        if (values[i] < min) min = values[i];
        if (values[i] > max) max = values[i];
      }
      return {
        min: min,
        max: max
      };
    };

    /**
     * Computes the arithmetic mean of the given values
     * @param {Array} values
     * @returns {number}
     */
    exports.arithmeticMean = function arithmeticMean(values) {
      var sum = 0;
      var l = values.length;
      for (var i = 0; i < l; i++) {
        sum += values[i];
      }
      return sum / l;
    };

    /**
     * {@link arithmeticMean}
     */
    exports.mean = exports.arithmeticMean;

    /**
     * Computes the geometric mean of the given values
     * @param {Array} values
     * @returns {number}
     */
    exports.geometricMean = function geometricMean(values) {
      var mul = 1;
      var l = values.length;
      for (var i = 0; i < l; i++) {
        mul *= values[i];
      }
      return Math.pow(mul, 1 / l);
    };

    /**
     * Computes the mean of the log of the given values
     * If the return value is exponentiated, it gives the same result as the
     * geometric mean.
     * @param {Array} values
     * @returns {number}
     */
    exports.logMean = function logMean(values) {
      var lnsum = 0;
      var l = values.length;
      for (var i = 0; i < l; i++) {
        lnsum += Math.log(values[i]);
      }
      return lnsum / l;
    };

    /**
     * Computes the weighted grand mean for a list of means and sample sizes
     * @param {Array} means - Mean values for each set of samples
     * @param {Array} samples - Number of original values for each set of samples
     * @returns {number}
     */
    exports.grandMean = function grandMean(means, samples) {
      var sum = 0;
      var n = 0;
      var l = means.length;
      for (var i = 0; i < l; i++) {
        sum += samples[i] * means[i];
        n += samples[i];
      }
      return sum / n;
    };

    /**
     * Computes the truncated mean of the given values using a given percentage
     * @param {Array} values
     * @param {number} percent - The percentage of values to keep (range: [0,1])
     * @param {boolean} [alreadySorted=false]
     * @returns {number}
     */
    exports.truncatedMean = function truncatedMean(values, percent, alreadySorted) {
      if (alreadySorted === undefined) alreadySorted = false;
      if (!alreadySorted) {
        values = [].concat(values).sort(compareNumbers);
      }
      var l = values.length;
      var k = Math.floor(l * percent);
      var sum = 0;
      for (var i = k; i < l - k; i++) {
        sum += values[i];
      }
      return sum / (l - 2 * k);
    };

    /**
     * Computes the harmonic mean of the given values
     * @param {Array} values
     * @returns {number}
     */
    exports.harmonicMean = function harmonicMean(values) {
      var sum = 0;
      var l = values.length;
      for (var i = 0; i < l; i++) {
        if (values[i] === 0) {
          throw new RangeError('value at index ' + i + 'is zero');
        }
        sum += 1 / values[i];
      }
      return l / sum;
    };

    /**
     * Computes the contraharmonic mean of the given values
     * @param {Array} values
     * @returns {number}
     */
    exports.contraHarmonicMean = function contraHarmonicMean(values) {
      var r1 = 0;
      var r2 = 0;
      var l = values.length;
      for (var i = 0; i < l; i++) {
        r1 += values[i] * values[i];
        r2 += values[i];
      }
      if (r2 < 0) {
        throw new RangeError('sum of values is negative');
      }
      return r1 / r2;
    };

    /**
     * Computes the median of the given values
     * @param {Array} values
     * @param {boolean} [alreadySorted=false]
     * @returns {number}
     */
    exports.median = function median(values, alreadySorted) {
      if (alreadySorted === undefined) alreadySorted = false;
      if (!alreadySorted) {
        values = [].concat(values).sort(compareNumbers);
      }
      var l = values.length;
      var half = Math.floor(l / 2);
      if (l % 2 === 0) {
        return (values[half - 1] + values[half]) * 0.5;
      } else {
        return values[half];
      }
    };

    /**
     * Computes the variance of the given values
     * @param {Array} values
     * @param {boolean} [unbiased=true] - if true, divide by (n-1); if false, divide by n.
     * @returns {number}
     */
    exports.variance = function variance(values, unbiased) {
      if (unbiased === undefined) unbiased = true;
      var theMean = exports.mean(values);
      var theVariance = 0;
      var l = values.length;
      for (var i = 0; i < l; i++) {
        var x = values[i] - theMean;
        theVariance += x * x;
      }
      if (unbiased) {
        return theVariance / (l - 1);
      } else {
        return theVariance / l;
      }
    };

    /**
     * Computes the standard deviation of the given values
     * @param {Array} values
     * @param {boolean} [unbiased=true] - if true, divide by (n-1); if false, divide by n.
     * @returns {number}
     */
    exports.standardDeviation = function standardDeviation(values, unbiased) {
      return Math.sqrt(exports.variance(values, unbiased));
    };
    exports.standardError = function standardError(values) {
      return exports.standardDeviation(values) / Math.sqrt(values.length);
    };

    /**
     * IEEE Transactions on biomedical engineering, vol. 52, no. 1, january 2005, p. 76-
     * Calculate the standard deviation via the Median of the absolute deviation
     *  The formula for the standard deviation only holds for Gaussian random variables.
     * @returns {{mean: number, stdev: number}}
     */
    exports.robustMeanAndStdev = function robustMeanAndStdev(y) {
      var mean = 0,
        stdev = 0;
      var length = y.length,
        i = 0;
      for (i = 0; i < length; i++) {
        mean += y[i];
      }
      mean /= length;
      var averageDeviations = new Array(length);
      for (i = 0; i < length; i++) averageDeviations[i] = Math.abs(y[i] - mean);
      averageDeviations.sort(compareNumbers);
      if (length % 2 === 1) {
        stdev = averageDeviations[(length - 1) / 2] / 0.6745;
      } else {
        stdev = 0.5 * (averageDeviations[length / 2] + averageDeviations[length / 2 - 1]) / 0.6745;
      }
      return {
        mean: mean,
        stdev: stdev
      };
    };
    exports.quartiles = function quartiles(values, alreadySorted) {
      if (typeof alreadySorted === 'undefined') alreadySorted = false;
      if (!alreadySorted) {
        values = [].concat(values).sort(compareNumbers);
      }
      var quart = values.length / 4;
      var q1 = values[Math.ceil(quart) - 1];
      var q2 = exports.median(values, true);
      var q3 = values[Math.ceil(quart * 3) - 1];
      return {
        q1: q1,
        q2: q2,
        q3: q3
      };
    };
    exports.pooledStandardDeviation = function pooledStandardDeviation(samples, unbiased) {
      return Math.sqrt(exports.pooledVariance(samples, unbiased));
    };
    exports.pooledVariance = function pooledVariance(samples, unbiased) {
      if (typeof unbiased === 'undefined') unbiased = true;
      var sum = 0;
      var length = 0,
        l = samples.length;
      for (var i = 0; i < l; i++) {
        var values = samples[i];
        var vari = exports.variance(values);
        sum += (values.length - 1) * vari;
        if (unbiased) length += values.length - 1;else length += values.length;
      }
      return sum / length;
    };
    exports.mode = function mode(values) {
      var l = values.length,
        itemCount = new Array(l),
        i;
      for (i = 0; i < l; i++) {
        itemCount[i] = 0;
      }
      var itemArray = new Array(l);
      var count = 0;
      for (i = 0; i < l; i++) {
        var index = itemArray.indexOf(values[i]);
        if (index >= 0) itemCount[index]++;else {
          itemArray[count] = values[i];
          itemCount[count] = 1;
          count++;
        }
      }
      var maxValue = 0,
        maxIndex = 0;
      for (i = 0; i < count; i++) {
        if (itemCount[i] > maxValue) {
          maxValue = itemCount[i];
          maxIndex = i;
        }
      }
      return itemArray[maxIndex];
    };
    exports.covariance = function covariance(vector1, vector2, unbiased) {
      if (typeof unbiased === 'undefined') unbiased = true;
      var mean1 = exports.mean(vector1);
      var mean2 = exports.mean(vector2);
      if (vector1.length !== vector2.length) throw 'Vectors do not have the same dimensions';
      var cov = 0,
        l = vector1.length;
      for (var i = 0; i < l; i++) {
        var x = vector1[i] - mean1;
        var y = vector2[i] - mean2;
        cov += x * y;
      }
      if (unbiased) return cov / (l - 1);else return cov / l;
    };
    exports.skewness = function skewness(values, unbiased) {
      if (typeof unbiased === 'undefined') unbiased = true;
      var theMean = exports.mean(values);
      var s2 = 0,
        s3 = 0,
        l = values.length;
      for (var i = 0; i < l; i++) {
        var dev = values[i] - theMean;
        s2 += dev * dev;
        s3 += dev * dev * dev;
      }
      var m2 = s2 / l;
      var m3 = s3 / l;
      var g = m3 / Math.pow(m2, 3 / 2.0);
      if (unbiased) {
        var a = Math.sqrt(l * (l - 1));
        var b = l - 2;
        return a / b * g;
      } else {
        return g;
      }
    };
    exports.kurtosis = function kurtosis(values, unbiased) {
      if (typeof unbiased === 'undefined') unbiased = true;
      var theMean = exports.mean(values);
      var n = values.length,
        s2 = 0,
        s4 = 0;
      for (var i = 0; i < n; i++) {
        var dev = values[i] - theMean;
        s2 += dev * dev;
        s4 += dev * dev * dev * dev;
      }
      var m2 = s2 / n;
      var m4 = s4 / n;
      if (unbiased) {
        var v = s2 / (n - 1);
        var a = n * (n + 1) / ((n - 1) * (n - 2) * (n - 3));
        var b = s4 / (v * v);
        var c = (n - 1) * (n - 1) / ((n - 2) * (n - 3));
        return a * b - 3 * c;
      } else {
        return m4 / (m2 * m2) - 3;
      }
    };
    exports.entropy = function entropy(values, eps) {
      if (typeof eps === 'undefined') eps = 0;
      var sum = 0,
        l = values.length;
      for (var i = 0; i < l; i++) sum += values[i] * Math.log(values[i] + eps);
      return -sum;
    };
    exports.weightedMean = function weightedMean(values, weights) {
      var sum = 0,
        l = values.length;
      for (var i = 0; i < l; i++) sum += values[i] * weights[i];
      return sum;
    };
    exports.weightedStandardDeviation = function weightedStandardDeviation(values, weights) {
      return Math.sqrt(exports.weightedVariance(values, weights));
    };
    exports.weightedVariance = function weightedVariance(values, weights) {
      var theMean = exports.weightedMean(values, weights);
      var vari = 0,
        l = values.length;
      var a = 0,
        b = 0;
      for (var i = 0; i < l; i++) {
        var z = values[i] - theMean;
        var w = weights[i];
        vari += w * (z * z);
        b += w;
        a += w * w;
      }
      return vari * (b / (b * b - a));
    };
    exports.center = function center(values, inPlace) {
      if (typeof inPlace === 'undefined') inPlace = false;
      var result = values;
      if (!inPlace) result = [].concat(values);
      var theMean = exports.mean(result),
        l = result.length;
      for (var i = 0; i < l; i++) result[i] -= theMean;
    };
    exports.standardize = function standardize(values, standardDev, inPlace) {
      if (typeof standardDev === 'undefined') standardDev = exports.standardDeviation(values);
      if (typeof inPlace === 'undefined') inPlace = false;
      var l = values.length;
      var result = inPlace ? values : new Array(l);
      for (var i = 0; i < l; i++) result[i] = values[i] / standardDev;
      return result;
    };
    exports.cumulativeSum = function cumulativeSum(array) {
      var l = array.length;
      var result = new Array(l);
      result[0] = array[0];
      for (var i = 1; i < l; i++) result[i] = result[i - 1] + array[i];
      return result;
    };
  })(array$3);

  var matrix$1 = {};

  (function (exports) {

    var arrayStat = array$3;
    function compareNumbers(a, b) {
      return a - b;
    }
    exports.max = function max(matrix) {
      var max = -Infinity;
      for (var i = 0; i < matrix.length; i++) {
        for (var j = 0; j < matrix[i].length; j++) {
          if (matrix[i][j] > max) max = matrix[i][j];
        }
      }
      return max;
    };
    exports.min = function min(matrix) {
      var min = Infinity;
      for (var i = 0; i < matrix.length; i++) {
        for (var j = 0; j < matrix[i].length; j++) {
          if (matrix[i][j] < min) min = matrix[i][j];
        }
      }
      return min;
    };
    exports.minMax = function minMax(matrix) {
      var min = Infinity;
      var max = -Infinity;
      for (var i = 0; i < matrix.length; i++) {
        for (var j = 0; j < matrix[i].length; j++) {
          if (matrix[i][j] < min) min = matrix[i][j];
          if (matrix[i][j] > max) max = matrix[i][j];
        }
      }
      return {
        min: min,
        max: max
      };
    };
    exports.entropy = function entropy(matrix, eps) {
      if (typeof eps === 'undefined') {
        eps = 0;
      }
      var sum = 0,
        l1 = matrix.length,
        l2 = matrix[0].length;
      for (var i = 0; i < l1; i++) {
        for (var j = 0; j < l2; j++) {
          sum += matrix[i][j] * Math.log(matrix[i][j] + eps);
        }
      }
      return -sum;
    };
    exports.mean = function mean(matrix, dimension) {
      if (typeof dimension === 'undefined') {
        dimension = 0;
      }
      var rows = matrix.length,
        cols = matrix[0].length,
        theMean,
        N,
        i,
        j;
      if (dimension === -1) {
        theMean = [0];
        N = rows * cols;
        for (i = 0; i < rows; i++) {
          for (j = 0; j < cols; j++) {
            theMean[0] += matrix[i][j];
          }
        }
        theMean[0] /= N;
      } else if (dimension === 0) {
        theMean = new Array(cols);
        N = rows;
        for (j = 0; j < cols; j++) {
          theMean[j] = 0;
          for (i = 0; i < rows; i++) {
            theMean[j] += matrix[i][j];
          }
          theMean[j] /= N;
        }
      } else if (dimension === 1) {
        theMean = new Array(rows);
        N = cols;
        for (j = 0; j < rows; j++) {
          theMean[j] = 0;
          for (i = 0; i < cols; i++) {
            theMean[j] += matrix[j][i];
          }
          theMean[j] /= N;
        }
      } else {
        throw new Error('Invalid dimension');
      }
      return theMean;
    };
    exports.sum = function sum(matrix, dimension) {
      if (typeof dimension === 'undefined') {
        dimension = 0;
      }
      var rows = matrix.length,
        cols = matrix[0].length,
        theSum,
        i,
        j;
      if (dimension === -1) {
        theSum = [0];
        for (i = 0; i < rows; i++) {
          for (j = 0; j < cols; j++) {
            theSum[0] += matrix[i][j];
          }
        }
      } else if (dimension === 0) {
        theSum = new Array(cols);
        for (j = 0; j < cols; j++) {
          theSum[j] = 0;
          for (i = 0; i < rows; i++) {
            theSum[j] += matrix[i][j];
          }
        }
      } else if (dimension === 1) {
        theSum = new Array(rows);
        for (j = 0; j < rows; j++) {
          theSum[j] = 0;
          for (i = 0; i < cols; i++) {
            theSum[j] += matrix[j][i];
          }
        }
      } else {
        throw new Error('Invalid dimension');
      }
      return theSum;
    };
    exports.product = function product(matrix, dimension) {
      if (typeof dimension === 'undefined') {
        dimension = 0;
      }
      var rows = matrix.length,
        cols = matrix[0].length,
        theProduct,
        i,
        j;
      if (dimension === -1) {
        theProduct = [1];
        for (i = 0; i < rows; i++) {
          for (j = 0; j < cols; j++) {
            theProduct[0] *= matrix[i][j];
          }
        }
      } else if (dimension === 0) {
        theProduct = new Array(cols);
        for (j = 0; j < cols; j++) {
          theProduct[j] = 1;
          for (i = 0; i < rows; i++) {
            theProduct[j] *= matrix[i][j];
          }
        }
      } else if (dimension === 1) {
        theProduct = new Array(rows);
        for (j = 0; j < rows; j++) {
          theProduct[j] = 1;
          for (i = 0; i < cols; i++) {
            theProduct[j] *= matrix[j][i];
          }
        }
      } else {
        throw new Error('Invalid dimension');
      }
      return theProduct;
    };
    exports.standardDeviation = function standardDeviation(matrix, means, unbiased) {
      var vari = exports.variance(matrix, means, unbiased),
        l = vari.length;
      for (var i = 0; i < l; i++) {
        vari[i] = Math.sqrt(vari[i]);
      }
      return vari;
    };
    exports.variance = function variance(matrix, means, unbiased) {
      if (typeof unbiased === 'undefined') {
        unbiased = true;
      }
      means = means || exports.mean(matrix);
      var rows = matrix.length;
      if (rows === 0) return [];
      var cols = matrix[0].length;
      var vari = new Array(cols);
      for (var j = 0; j < cols; j++) {
        var sum1 = 0,
          sum2 = 0,
          x = 0;
        for (var i = 0; i < rows; i++) {
          x = matrix[i][j] - means[j];
          sum1 += x;
          sum2 += x * x;
        }
        if (unbiased) {
          vari[j] = (sum2 - sum1 * sum1 / rows) / (rows - 1);
        } else {
          vari[j] = (sum2 - sum1 * sum1 / rows) / rows;
        }
      }
      return vari;
    };
    exports.median = function median(matrix) {
      var rows = matrix.length,
        cols = matrix[0].length;
      var medians = new Array(cols);
      for (var i = 0; i < cols; i++) {
        var data = new Array(rows);
        for (var j = 0; j < rows; j++) {
          data[j] = matrix[j][i];
        }
        data.sort(compareNumbers);
        var N = data.length;
        if (N % 2 === 0) {
          medians[i] = (data[N / 2] + data[N / 2 - 1]) * 0.5;
        } else {
          medians[i] = data[Math.floor(N / 2)];
        }
      }
      return medians;
    };
    exports.mode = function mode(matrix) {
      var rows = matrix.length,
        cols = matrix[0].length,
        modes = new Array(cols),
        i,
        j;
      for (i = 0; i < cols; i++) {
        var itemCount = new Array(rows);
        for (var k = 0; k < rows; k++) {
          itemCount[k] = 0;
        }
        var itemArray = new Array(rows);
        var count = 0;
        for (j = 0; j < rows; j++) {
          var index = itemArray.indexOf(matrix[j][i]);
          if (index >= 0) {
            itemCount[index]++;
          } else {
            itemArray[count] = matrix[j][i];
            itemCount[count] = 1;
            count++;
          }
        }
        var maxValue = 0,
          maxIndex = 0;
        for (j = 0; j < count; j++) {
          if (itemCount[j] > maxValue) {
            maxValue = itemCount[j];
            maxIndex = j;
          }
        }
        modes[i] = itemArray[maxIndex];
      }
      return modes;
    };
    exports.skewness = function skewness(matrix, unbiased) {
      if (typeof unbiased === 'undefined') unbiased = true;
      var means = exports.mean(matrix);
      var n = matrix.length,
        l = means.length;
      var skew = new Array(l);
      for (var j = 0; j < l; j++) {
        var s2 = 0,
          s3 = 0;
        for (var i = 0; i < n; i++) {
          var dev = matrix[i][j] - means[j];
          s2 += dev * dev;
          s3 += dev * dev * dev;
        }
        var m2 = s2 / n;
        var m3 = s3 / n;
        var g = m3 / Math.pow(m2, 3 / 2);
        if (unbiased) {
          var a = Math.sqrt(n * (n - 1));
          var b = n - 2;
          skew[j] = a / b * g;
        } else {
          skew[j] = g;
        }
      }
      return skew;
    };
    exports.kurtosis = function kurtosis(matrix, unbiased) {
      if (typeof unbiased === 'undefined') unbiased = true;
      var means = exports.mean(matrix);
      var n = matrix.length,
        m = matrix[0].length;
      var kurt = new Array(m);
      for (var j = 0; j < m; j++) {
        var s2 = 0,
          s4 = 0;
        for (var i = 0; i < n; i++) {
          var dev = matrix[i][j] - means[j];
          s2 += dev * dev;
          s4 += dev * dev * dev * dev;
        }
        var m2 = s2 / n;
        var m4 = s4 / n;
        if (unbiased) {
          var v = s2 / (n - 1);
          var a = n * (n + 1) / ((n - 1) * (n - 2) * (n - 3));
          var b = s4 / (v * v);
          var c = (n - 1) * (n - 1) / ((n - 2) * (n - 3));
          kurt[j] = a * b - 3 * c;
        } else {
          kurt[j] = m4 / (m2 * m2) - 3;
        }
      }
      return kurt;
    };
    exports.standardError = function standardError(matrix) {
      var samples = matrix.length;
      var standardDeviations = exports.standardDeviation(matrix);
      var l = standardDeviations.length;
      var standardErrors = new Array(l);
      var sqrtN = Math.sqrt(samples);
      for (var i = 0; i < l; i++) {
        standardErrors[i] = standardDeviations[i] / sqrtN;
      }
      return standardErrors;
    };
    exports.covariance = function covariance(matrix, dimension) {
      return exports.scatter(matrix, undefined, dimension);
    };
    exports.scatter = function scatter(matrix, divisor, dimension) {
      if (typeof dimension === 'undefined') {
        dimension = 0;
      }
      if (typeof divisor === 'undefined') {
        if (dimension === 0) {
          divisor = matrix.length - 1;
        } else if (dimension === 1) {
          divisor = matrix[0].length - 1;
        }
      }
      var means = exports.mean(matrix, dimension);
      var rows = matrix.length;
      if (rows === 0) {
        return [[]];
      }
      var cols = matrix[0].length,
        cov,
        i,
        j,
        s,
        k;
      if (dimension === 0) {
        cov = new Array(cols);
        for (i = 0; i < cols; i++) {
          cov[i] = new Array(cols);
        }
        for (i = 0; i < cols; i++) {
          for (j = i; j < cols; j++) {
            s = 0;
            for (k = 0; k < rows; k++) {
              s += (matrix[k][j] - means[j]) * (matrix[k][i] - means[i]);
            }
            s /= divisor;
            cov[i][j] = s;
            cov[j][i] = s;
          }
        }
      } else if (dimension === 1) {
        cov = new Array(rows);
        for (i = 0; i < rows; i++) {
          cov[i] = new Array(rows);
        }
        for (i = 0; i < rows; i++) {
          for (j = i; j < rows; j++) {
            s = 0;
            for (k = 0; k < cols; k++) {
              s += (matrix[j][k] - means[j]) * (matrix[i][k] - means[i]);
            }
            s /= divisor;
            cov[i][j] = s;
            cov[j][i] = s;
          }
        }
      } else {
        throw new Error('Invalid dimension');
      }
      return cov;
    };
    exports.correlation = function correlation(matrix) {
      var means = exports.mean(matrix),
        standardDeviations = exports.standardDeviation(matrix, true, means),
        scores = exports.zScores(matrix, means, standardDeviations),
        rows = matrix.length,
        cols = matrix[0].length,
        i,
        j;
      var cor = new Array(cols);
      for (i = 0; i < cols; i++) {
        cor[i] = new Array(cols);
      }
      for (i = 0; i < cols; i++) {
        for (j = i; j < cols; j++) {
          var c = 0;
          for (var k = 0, l = scores.length; k < l; k++) {
            c += scores[k][j] * scores[k][i];
          }
          c /= rows - 1;
          cor[i][j] = c;
          cor[j][i] = c;
        }
      }
      return cor;
    };
    exports.zScores = function zScores(matrix, means, standardDeviations) {
      means = means || exports.mean(matrix);
      if (typeof standardDeviations === 'undefined') standardDeviations = exports.standardDeviation(matrix, true, means);
      return exports.standardize(exports.center(matrix, means, false), standardDeviations, true);
    };
    exports.center = function center(matrix, means, inPlace) {
      means = means || exports.mean(matrix);
      var result = matrix,
        l = matrix.length,
        i,
        j,
        jj;
      if (!inPlace) {
        result = new Array(l);
        for (i = 0; i < l; i++) {
          result[i] = new Array(matrix[i].length);
        }
      }
      for (i = 0; i < l; i++) {
        var row = result[i];
        for (j = 0, jj = row.length; j < jj; j++) {
          row[j] = matrix[i][j] - means[j];
        }
      }
      return result;
    };
    exports.standardize = function standardize(matrix, standardDeviations, inPlace) {
      if (typeof standardDeviations === 'undefined') standardDeviations = exports.standardDeviation(matrix);
      var result = matrix,
        l = matrix.length,
        i,
        j,
        jj;
      if (!inPlace) {
        result = new Array(l);
        for (i = 0; i < l; i++) {
          result[i] = new Array(matrix[i].length);
        }
      }
      for (i = 0; i < l; i++) {
        var resultRow = result[i];
        var sourceRow = matrix[i];
        for (j = 0, jj = resultRow.length; j < jj; j++) {
          if (standardDeviations[j] !== 0 && !isNaN(standardDeviations[j])) {
            resultRow[j] = sourceRow[j] / standardDeviations[j];
          }
        }
      }
      return result;
    };
    exports.weightedVariance = function weightedVariance(matrix, weights) {
      var means = exports.mean(matrix);
      var rows = matrix.length;
      if (rows === 0) return [];
      var cols = matrix[0].length;
      var vari = new Array(cols);
      for (var j = 0; j < cols; j++) {
        var sum = 0;
        var a = 0,
          b = 0;
        for (var i = 0; i < rows; i++) {
          var z = matrix[i][j] - means[j];
          var w = weights[i];
          sum += w * (z * z);
          b += w;
          a += w * w;
        }
        vari[j] = sum * (b / (b * b - a));
      }
      return vari;
    };
    exports.weightedMean = function weightedMean(matrix, weights, dimension) {
      if (typeof dimension === 'undefined') {
        dimension = 0;
      }
      var rows = matrix.length;
      if (rows === 0) return [];
      var cols = matrix[0].length,
        means,
        i,
        ii,
        j,
        w,
        row;
      if (dimension === 0) {
        means = new Array(cols);
        for (i = 0; i < cols; i++) {
          means[i] = 0;
        }
        for (i = 0; i < rows; i++) {
          row = matrix[i];
          w = weights[i];
          for (j = 0; j < cols; j++) {
            means[j] += row[j] * w;
          }
        }
      } else if (dimension === 1) {
        means = new Array(rows);
        for (i = 0; i < rows; i++) {
          means[i] = 0;
        }
        for (j = 0; j < rows; j++) {
          row = matrix[j];
          w = weights[j];
          for (i = 0; i < cols; i++) {
            means[j] += row[i] * w;
          }
        }
      } else {
        throw new Error('Invalid dimension');
      }
      var weightSum = arrayStat.sum(weights);
      if (weightSum !== 0) {
        for (i = 0, ii = means.length; i < ii; i++) {
          means[i] /= weightSum;
        }
      }
      return means;
    };
    exports.weightedCovariance = function weightedCovariance(matrix, weights, means, dimension) {
      dimension = dimension || 0;
      means = means || exports.weightedMean(matrix, weights, dimension);
      var s1 = 0,
        s2 = 0;
      for (var i = 0, ii = weights.length; i < ii; i++) {
        s1 += weights[i];
        s2 += weights[i] * weights[i];
      }
      var factor = s1 / (s1 * s1 - s2);
      return exports.weightedScatter(matrix, weights, means, factor, dimension);
    };
    exports.weightedScatter = function weightedScatter(matrix, weights, means, factor, dimension) {
      dimension = dimension || 0;
      means = means || exports.weightedMean(matrix, weights, dimension);
      if (typeof factor === 'undefined') {
        factor = 1;
      }
      var rows = matrix.length;
      if (rows === 0) {
        return [[]];
      }
      var cols = matrix[0].length,
        cov,
        i,
        j,
        k,
        s;
      if (dimension === 0) {
        cov = new Array(cols);
        for (i = 0; i < cols; i++) {
          cov[i] = new Array(cols);
        }
        for (i = 0; i < cols; i++) {
          for (j = i; j < cols; j++) {
            s = 0;
            for (k = 0; k < rows; k++) {
              s += weights[k] * (matrix[k][j] - means[j]) * (matrix[k][i] - means[i]);
            }
            cov[i][j] = s * factor;
            cov[j][i] = s * factor;
          }
        }
      } else if (dimension === 1) {
        cov = new Array(rows);
        for (i = 0; i < rows; i++) {
          cov[i] = new Array(rows);
        }
        for (i = 0; i < rows; i++) {
          for (j = i; j < rows; j++) {
            s = 0;
            for (k = 0; k < cols; k++) {
              s += weights[k] * (matrix[j][k] - means[j]) * (matrix[i][k] - means[i]);
            }
            cov[i][j] = s * factor;
            cov[j][i] = s * factor;
          }
        }
      } else {
        throw new Error('Invalid dimension');
      }
      return cov;
    };
  })(matrix$1);

  var array$2 = mlStat$1.array = array$3;
  mlStat$1.matrix = matrix$1;

  function normalize$1(array) {
    const min = array$2.min(array[1]);
    const max = array$2.max(array[1]);
    const sum = array$2.sum(array[1]);
    const length = array[1] ? array[1].length : 0;
    if (sum !== 0) {
      for (let i = 0; i < length; i++) {
        array[1][i] /= sum;
      }
    }
    return {
      sum,
      min,
      max
    };
  }

  // this method will systematically take care of both array
  function commonExtractAndNormalize$1(array1, array2, width, from, to, common) {
    if (!Array.isArray(array1) || !Array.isArray(array2)) {
      return {
        info: undefined,
        data: undefined
      };
    }
    const extract1 = extract$1(array1, from, to);
    const extract2 = extract$1(array2, from, to);
    let common1, common2, info1, info2;
    if (common & COMMON_SECOND$1) {
      common1 = getCommonArray$1(extract1, extract2, width);
      info1 = normalize$1(common1);
    } else {
      common1 = extract1;
      info1 = normalize$1(common1);
    }
    if (common & COMMON_FIRST$1) {
      common2 = getCommonArray$1(extract2, extract1, width);
      info2 = normalize$1(common2);
    } else {
      common2 = extract2;
      info2 = normalize$1(common2);
    }
    return {
      info1,
      info2,
      data1: common1,
      data2: common2
    };
  }

  function extractAndNormalize$1(array, from, to) {
    if (!Array.isArray(array)) {
      return {
        info: undefined,
        data: undefined
      };
    }
    const newArray = extract$1(array, from, to);
    const info = normalize$1(newArray);
    return {
      info,
      data: newArray
    };
  }

  // Adapted from: http://stackoverflow.com/questions/563198/how-do-you-detect-where-two-line-segments-intersect/1968345#1968345
  function getIntersection$1(segment1, segment2) {
    const p0X = segment1[0][0];
    const p0Y = segment1[0][1];
    const p1X = segment1[1][0];
    const p1Y = segment1[1][1];
    const p2X = segment2[0][0];
    const p2Y = segment2[0][1];
    const p3X = segment2[1][0];
    const p3Y = segment2[1][1];
    const s1X = p1X - p0X;
    const s1Y = p1Y - p0Y;
    const s2X = p3X - p2X;
    const s2Y = p3Y - p2Y;
    const s = (-s1Y * (p0X - p2X) + s1X * (p0Y - p2Y)) / (-s2X * s1Y + s1X * s2Y);
    const t = (s2X * (p0Y - p2Y) - s2Y * (p0X - p2X)) / (-s2X * s1Y + s1X * s2Y);
    if (s >= 0 && s <= 1 && t >= 0 && t <= 1) {
      return {
        x: p0X + t * s1X,
        y: p0Y + t * s1Y
      };
    }
    return null; // No collision
  }

  const COMMON_NO$1 = 0;
  const COMMON_FIRST$1 = 1;
  const COMMON_SECOND$1 = 2;
  const COMMON_BOTH$1 = 3;
  /**
   * A number, or a string containing a number.
   * @typedef {([number[],number[]]|[number,number][]|{x:number[],y:number[]})} Peaks
   */
  /**
   * Create a comparator class
   * {object} [options={}]
   * {string} [options.common=''] should we take only common peaks 'first', 'second', 'both', ''
   * {number} [options.widthBottom=2] bottom trapezoid width for similarity evaluation
   * {number} [options.widthTop=1] top trapezoid width for similarity evaluation
   * {number} [options.from] from region used for similarity calculation
   * {number} [options.to] to region used for similarity calculation
   */
  class Comparator$1 {
    constructor() {
      let options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
      this.array1 = [];
      this.array2 = [];
      this.setOptions(options);
    }
    /*
       2 formats are allowed:
       [[x1,x2,...],[y1,y2,...]] or [[x1,y1],[x2,y2], ...]
      */
    setOptions() {
      let options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
      if (typeof options.common === 'string') {
        if (options.common.toLowerCase() === 'first') {
          this.common = COMMON_FIRST$1;
        } else if (options.common.toLowerCase() === 'second') {
          this.common = COMMON_SECOND$1;
        } else if (options.common.toLowerCase() === 'both') {
          this.common = COMMON_BOTH$1;
        } else {
          this.common = COMMON_NO$1;
        }
      } else if (options.common === true) {
        this.common = COMMON_BOTH$1;
      } else {
        this.common = COMMON_NO$1;
      }
      this.trapezoid = options.trapezoid;
      this.commonFactor = options.commonFactor || this.commonFactor || 4;
      const {
        widthBottom = this.widthBottom || 2,
        widthTop = this.widthTop || 1,
        from = this.from,
        to = this.to
      } = options;
      this.setTrapezoid(widthBottom, widthTop);
      this.setFromTo(from, to);
    }
    /**
     *
     * @param {Peaks} peaks
     */
    setPeaks1(peaks) {
      this.array1 = checkPeaks$1(peaks);
      if (this.common) {
        const extracts = commonExtractAndNormalize$1(this.array1, this.array2, this.widthBottom, this.from, this.to, this.common);
        this.array1Extract = extracts.data1;
        this.array1ExtractInfo = extracts.info1;
        this.array2Extract = extracts.data2;
        this.array2ExtractInfo = extracts.info2;
      } else {
        const extract = extractAndNormalize$1(this.array1, this.from, this.to);
        this.array1Extract = extract.data;
        this.array1ExtractInfo = extract.info;
      }
    }
    /**
     *
     * @param {Peaks} peaks
     */
    setPeaks2(peaks) {
      this.array2 = checkPeaks$1(peaks);
      if (this.common) {
        const extracts = commonExtractAndNormalize$1(this.array1, this.array2, this.widthBottom, this.from, this.to, this.common);
        this.array1Extract = extracts.data1;
        this.array1ExtractInfo = extracts.info1;
        this.array2Extract = extracts.data2;
        this.array2ExtractInfo = extracts.info2;
      } else {
        const extract = extractAndNormalize$1(this.array2, this.from, this.to);
        this.array2Extract = extract.data;
        this.array2ExtractInfo = extract.info;
      }
    }
    getExtract1() {
      return this.array1Extract;
    }
    getExtract2() {
      return this.array2Extract;
    }
    getExtractInfo1() {
      return this.array1ExtractInfo;
    }
    getExtractInfo2() {
      return this.array2ExtractInfo;
    }
    /**
     * Set the new bottom and top width of the trapezoid
     * @param {number} newWidthBottom
     * @param {number} newWidthTop
     */
    setTrapezoid(newWidthBottom, newWidthTop) {
      this.widthTop = newWidthTop;
      this.widthBottom = newWidthBottom;
      this.widthSlope = (this.widthBottom - this.widthTop) / 2;
      if (this.widthBottom < this.widthTop) {
        throw new Error('widthBottom has to be larger than widthTop');
      }
    }
    /**
     * Set the from / to for comparison
     * @param {number} newFrom - set the new from value
     * @param {number} newTo - set the new to value
     * @returns
     */
    setFromTo(newFrom, newTo) {
      if (newFrom === this.from && newTo === this.to) return;
      this.from = newFrom;
      this.to = newTo;
      if (this.common) {
        const extracts = commonExtractAndNormalize$1(this.array1, this.array2, this.widthBottom, this.from, this.to, this.common, this.commonFactor);
        this.array1Extract = extracts.data1;
        this.array1ExtractInfo = extracts.info1;
        this.array2Extract = extracts.data2;
        this.array2ExtractInfo = extracts.info2;
      } else {
        let extract1 = extractAndNormalize$1(this.array1, this.from, this.to);
        this.array1Extract = extract1.data;
        this.array1ExtractInfo = extract1.info;
        let extract2 = extractAndNormalize$1(this.array2, this.from, this.to);
        this.array2Extract = extract2.data;
        this.array2ExtractInfo = extract2.info;
      }
    }
    /**
     *
     * @param {number} x1
     * @param {number} y1
     * @param {number} x2
     * @param {number} y2
     * @returns
     */
    getOverlap(x1, y1, x2, y2) {
      if (y1 === 0 || y2 === 0) return 0;
      // TAKE CARE !!! We multiply the diff by 2 !!!
      const diff = Math.abs(x1 - x2) * 2;
      if (diff > this.widthBottom) return 0;
      if (diff <= this.widthTop) {
        return Math.min(y1, y2);
      }
      const maxValue = Math.max(y1, y2) * (this.widthBottom - diff) / (this.widthBottom - this.widthTop);
      return Math.min(y1, y2, maxValue);
    }
    /**
     * This is the old trapezoid similarity
     * @param {number} x1
     * @param {number} y1
     * @param {number} x2
     * @param {number} y2
     * @param {number} widthTop
     * @param {number} widthBottom
     * @returns
     */
    getOverlapTrapezoid(x1, y1, x2, y2, widthTop, widthBottom) {
      // eslint-disable-next-line no-console
      console.error('getOverlapTrapezoid should not be used anymore');
      const factor = 2 / (widthTop + widthBottom); // correction for surface=1
      if (y1 === 0 || y2 === 0) return 0;
      if (x1 === x2) {
        // they have the same position
        return Math.min(y1, y2);
      }
      const diff = Math.abs(x1 - x2);
      if (diff >= widthBottom) return 0;
      if (y1 === y2) {
        // do they have the same height ???
        // we need to find the common length
        if (diff <= widthTop) {
          return ((widthTop + widthBottom) / 2 - diff) * y1 * factor;
        } else if (diff <= widthBottom) {
          return (widthBottom - diff) * y1 / 2 * (diff - widthTop) / (widthBottom - widthTop) * factor;
        }
        return 0;
      } else {
        // the height are different and not the same position ...
        // we need to consider only one segment to find its intersection
        const small = Math.min(y1, y2);
        const big = Math.max(y1, y2);
        const targets = [[[0, 0], [this.widthSlope, small]], [[this.widthSlope, small], [this.widthSlope + widthTop, small]], [[widthTop + this.widthSlope, small], [widthBottom, 0]]];
        let segment;
        if (x1 > x2 && y1 > y2 || x1 < x2 && y1 < y2) {
          segment = [[diff, 0], [diff + this.widthSlope, big]];
        } else {
          segment = [[diff + this.widthSlope, big], [diff, 0]];
        }
        for (let i = 0; i < 3; i++) {
          const intersection = getIntersection$1(targets[i], segment);
          if (intersection) {
            switch (i) {
              case 0:
                return small - diff * intersection.y / 2 * factor;
              case 1:
                // to simplify ...
                //     console.log("           ",widthSlope,small,big,intersection.x)
                return (this.widthSlope * small / (2 * big) * small + (widthTop + this.widthSlope - intersection.x) * small + this.widthSlope * small / 2) * factor;
              case 2:
                return (widthBottom - diff) * intersection.y / 2 * factor;
              default:
                throw new Error(`unexpected intersection value: ${i}`);
            }
          }
        }
      }
      return NaN;
    }
    /**
     * This method calculates the total diff. The sum of positive value will yield to overlap
     * @returns
     */
    calculateDiff() {
      // we need to take 2 pointers
      // and travel progressively between them ...
      const newFirst = [this.array1Extract[0].slice(), this.array1Extract[1].slice()];
      const newSecond = [this.array2Extract[0].slice(), this.array2Extract[1].slice()];
      const array1Length = this.array1Extract[0] ? this.array1Extract[0].length : 0;
      const array2Length = this.array2Extract[0] ? this.array2Extract[0].length : 0;
      let pos1 = 0;
      let pos2 = 0;
      let previous2 = 0;
      while (pos1 < array1Length) {
        const diff = newFirst[0][pos1] - this.array2Extract[0][pos2];
        if (Math.abs(diff) < this.widthBottom) {
          // there is some overlap
          let overlap;
          if (this.trapezoid) {
            // old trapezoid overlap similarity
            overlap = this.getOverlapTrapezoid(newFirst[0][pos1], newFirst[1][pos1], newSecond[0][pos2], newSecond[1][pos2], this.widthTop, this.widthBottom);
          } else {
            overlap = this.getOverlap(newFirst[0][pos1], newFirst[1][pos1], newSecond[0][pos2], newSecond[1][pos2], this.widthTop, this.widthBottom);
          }
          newFirst[1][pos1] -= overlap;
          newSecond[1][pos2] -= overlap;
          if (pos2 < array2Length - 1) {
            pos2++;
          } else {
            pos1++;
            pos2 = previous2;
          }
        } else if (diff > 0 && pos2 < array2Length - 1) {
          pos2++;
          previous2 = pos2;
        } else {
          pos1++;
          pos2 = previous2;
        }
      }
      return newSecond;
    }
    /**
     * Set the new peaks and return info
     * @param {Peaks} newPeaks1
     * @param {Peaks} newPeaks2
     * @returns
     */
    getSimilarity(newPeaks1, newPeaks2) {
      if (newPeaks1) this.setPeaks1(newPeaks1);
      if (newPeaks2) this.setPeaks2(newPeaks2);
      const diff = this.calculateDiff();
      return {
        diff,
        extract1: this.getExtract1(),
        extract2: this.getExtract2(),
        extractInfo1: this.getExtractInfo1(),
        extractInfo2: this.getExtractInfo2(),
        similarity: calculateOverlapFromDiff$1(diff),
        widthBottom: this.widthBottom,
        widthTop: this.widthTop
      };
    }
    /**
     * This works mainly when you have a array1 that is fixed
     * newPeaks2 have to be normalized ! (sum to 1)
     * @param {Peaks} newPeaks2
     * @param {number} from
     * @param {number} to
     * @returns
     */
    fastSimilarity(newPeaks2, from, to) {
      this.array1Extract = extract$1(this.array1, from, to);
      this.array2Extract = newPeaks2;
      if (this.common & COMMON_SECOND$1) {
        this.array1Extract = getCommonArray$1(this.array1Extract, this.array2Extract, this.widthBottom);
      }
      normalize$1(this.array1Extract);
      const diff = this.calculateDiff();
      return calculateOverlapFromDiff$1(diff);
    }
  }

  /**
   * @param {object}   [options={}]
   * @param {object}   [options.minCharge=1]
   * @param {object}   [options.maxCharge=10]
   * @param {object}   [options.similarity={}]
   * @param {object}   [options.similarity.widthBottom]
   * @param {object}   [options.similarity.widthTop]
   * @param {object}   [options.similarity.widthFunction] - function called with mass that should return an object width containing top and bottom
   * @param {object}   [options.similarity.zone={}]
   * @param {object}   [options.similarity.zone.low=-0.5] - window shift based on observed monoisotopic mass
   * @param {object}   [options.similarity.zone.high=2.5] - to value for the comparison window
   * @param {object}   [options.similarity.common]
   */

  const NEUTRON_MASS$1 = 1;
  function getPeakChargeBySimilarity(spectrum, targetMass) {
    let options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
    let {
      similarity = {},
      minCharge = 1,
      maxCharge = 10
    } = options;
    let {
      zone = {},
      widthFunction
    } = similarity;
    let {
      low = -0.5,
      high = 2.5
    } = zone;
    if (!spectrum || !spectrum.data.x.length > 0) {
      throw Error('You need to add an experimental spectrum first using setMassSpectrum');
    }
    let width = {
      bottom: similarity.widthBottom,
      top: similarity.widthTop
    };
    similarity = JSON.parse(JSON.stringify(similarity));
    similarity.common = 'second';
    let experimentalData = spectrum.data;
    let similarityProcessor = new Comparator$1(similarity);
    similarityProcessor.setPeaks1([experimentalData.x, experimentalData.y]);
    if (widthFunction && typeof widthFunction === 'string') {
      // eslint-disable-next-line no-new-func
      widthFunction = new Function('mass', widthFunction);
      let checkTopBottom = widthFunction(123);
      if (!checkTopBottom.bottom || !checkTopBottom.top) {
        throw Error('widthFunction should return an object with bottom and top properties');
      }
    }
    let fromCharge = minCharge * maxCharge > 0 ? Math.round(Math.min(Math.abs(minCharge), Math.abs(maxCharge))) : 1;
    let toCharge = Math.round(Math.max(Math.abs(minCharge), Math.abs(maxCharge)));
    let fromIsotope = Math.ceil(low);
    let toIsotope = Math.floor(high);
    let isotopeHeight = 1 / (toIsotope - fromIsotope + 1);
    let results = [];
    for (let charge = fromCharge; charge < toCharge + 1; charge++) {
      let isotopePositions = {
        x: [],
        y: []
      };
      for (let isotopePosition = fromIsotope; isotopePosition < toIsotope + 1; isotopePosition++) {
        isotopePositions.x.push(targetMass + isotopePosition * NEUTRON_MASS$1 / charge);
        isotopePositions.y.push(isotopeHeight);
      }
      let from = targetMass + low / Math.abs(charge);
      let to = targetMass + high / Math.abs(charge);
      similarityProcessor.setFromTo(from, to);
      if (widthFunction) {
        width = widthFunction(targetMass);
        similarityProcessor.setTrapezoid(width.bottom, width.top);
      }
      similarityProcessor.setPeaks2([isotopePositions.x, isotopePositions.y]);
      let result = similarityProcessor.getSimilarity();
      results.push({
        charge,
        similarity: result.similarity
      });
    }
    return results.sort((a, b) => b.similarity - a.similarity)[0].charge;
  }

  /**
   * When a spectrum is continous ?
   * - has more than 100 points
   * - deltaX change can not be more than a factor 2
   * - deltaX may not be larger than 0.1
   * - if y is zero it does not count
   * @param {object} spectrum
   * @param {object} [options={}]
   * @param {number} [options.minLength=100]
   * @param {number} [options.relativeHeightThreshold=0.001] // Under this value the
   * @param {number} [options.maxDeltaRatio=3]
   */

  function isContinuous(spectrum) {
    let options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
    const {
      minLength = 100,
      maxDeltaRatio = 3,
      relativeHeightThreshold = 0.001
    } = options;
    const minHeight = xMaxValue$1(spectrum.data.y) * relativeHeightThreshold;
    const minRadio = 1 / maxDeltaRatio;
    const maxRatio = 1 * maxDeltaRatio;
    if (spectrum.continuous === undefined) {
      let xs = spectrum.data.x;
      let ys = spectrum.data.y;
      if (xs.length < minLength) {
        spectrum.continuous = false;
      } else {
        let previousDelta = xs[1] - xs[0];
        spectrum.continuous = true;
        let success = 0;
        let failed = 0;
        for (let i = 0; i < xs.length - 1; i++) {
          if (ys[i] < minHeight || ys[i + 1] < minHeight) {
            previousDelta = 0;
            continue;
          }
          let delta = xs[i + 1] - xs[i];
          if (previousDelta) {
            let ratio = delta / previousDelta;
            if ((Math.abs(delta) > 0.1 || ratio < minRadio || ratio > maxRatio) && ys[i] !== 0 && ys[i + 1] !== 0) {
              failed++;
              break;
            } else {
              success++;
            }
          }
          previousDelta = delta;
        }
        if (success / failed < 10) {
          spectrum.continuous = false;
        }
      }
    }
    return spectrum.continuous;
  }

  var IDX = 256,
    HEX = [],
    BUFFER;
  while (IDX--) HEX[IDX] = (IDX + 256).toString(16).substring(1);
  function v4() {
    var i = 0,
      num,
      out = '';
    if (!BUFFER || IDX + 16 > 256) {
      BUFFER = Array(i = 256);
      while (i--) BUFFER[i] = 256 * Math.random() | 0;
      i = IDX = 0;
    }
    for (; i < 16; i++) {
      num = BUFFER[IDX + i];
      if (i == 6) out += HEX[num & 15 | 64];else if (i == 8) out += HEX[num & 63 | 128];else out += HEX[num];
      if (i & 1 && i > 1 && i < 11) out += '-';
    }
    IDX++;
    return out;
  }

  /**
   * Apply Savitzky Golay algorithm
   * @param [ys] Array of y values
   * @param [xs] Array of X or deltaX
   * @return  Array containing the new ys (same length)
   */
  function sgg(ys, xs) {
    let options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
    let {
      windowSize = 9,
      derivative = 0,
      polynomial = 3
    } = options;
    if (windowSize % 2 === 0 || windowSize < 5 || !Number.isInteger(windowSize)) {
      throw new RangeError('Invalid window size (should be odd and at least 5 integer number)');
    }
    if (!isAnyArray$1(ys)) {
      throw new TypeError('Y values must be an array');
    }
    if (typeof xs === 'undefined') {
      throw new TypeError('X must be defined');
    }
    if (windowSize > ys.length) {
      throw new RangeError(`Window size is higher than the data length ${windowSize}>${ys.length}`);
    }
    if (derivative < 0 || !Number.isInteger(derivative)) {
      throw new RangeError('Derivative should be a positive integer');
    }
    if (polynomial < 1 || !Number.isInteger(polynomial)) {
      throw new RangeError('Polynomial should be a positive integer');
    }
    if (polynomial >= 6) {
      // eslint-disable-next-line no-console
      console.warn('You should not use polynomial grade higher than 5 if you are' + ' not sure that your data arises from such a model. Possible polynomial oscillation problems');
    }
    let half = Math.floor(windowSize / 2);
    let np = ys.length;
    let ans = new Float64Array(np);
    let weights = fullWeights(windowSize, polynomial, derivative);
    let hs = 0;
    let constantH = true;
    if (isAnyArray$1(xs)) {
      constantH = false;
    } else {
      hs = Math.pow(xs, derivative);
    }
    //For the borders
    for (let i = 0; i < half; i++) {
      let wg1 = weights[half - i - 1];
      let wg2 = weights[half + i + 1];
      let d1 = 0;
      let d2 = 0;
      for (let l = 0; l < windowSize; l++) {
        d1 += wg1[l] * ys[l];
        d2 += wg2[l] * ys[np - windowSize + l];
      }
      if (constantH) {
        ans[half - i - 1] = d1 / hs;
        ans[np - half + i] = d2 / hs;
      } else {
        hs = getHs(xs, half - i - 1, half, derivative);
        ans[half - i - 1] = d1 / hs;
        hs = getHs(xs, np - half + i, half, derivative);
        ans[np - half + i] = d2 / hs;
      }
    }
    //For the internal points
    let wg = weights[half];
    for (let i = windowSize; i <= np; i++) {
      let d = 0;
      for (let l = 0; l < windowSize; l++) d += wg[l] * ys[l + i - windowSize];
      if (!constantH) {
        hs = getHs(xs, i - half - 1, half, derivative);
      }
      ans[i - half - 1] = d / hs;
    }
    return ans;
  }
  function getHs(h, center, half, derivative) {
    let hs = 0;
    let count = 0;
    for (let i = center - half; i < center + half; i++) {
      if (i >= 0 && i < h.length - 1) {
        hs += h[i + 1] - h[i];
        count++;
      }
    }
    return Math.pow(hs / count, derivative);
  }
  function gramPoly(i, m, k, s) {
    let Grampoly = 0;
    if (k > 0) {
      Grampoly = (4 * k - 2) / (k * (2 * m - k + 1)) * (i * gramPoly(i, m, k - 1, s) + s * gramPoly(i, m, k - 1, s - 1)) - (k - 1) * (2 * m + k) / (k * (2 * m - k + 1)) * gramPoly(i, m, k - 2, s);
    } else {
      if (k === 0 && s === 0) {
        Grampoly = 1;
      } else {
        Grampoly = 0;
      }
    }
    return Grampoly;
  }
  function genFact(a, b) {
    let gf = 1;
    if (a >= b) {
      for (let j = a - b + 1; j <= a; j++) {
        gf *= j;
      }
    }
    return gf;
  }
  function weight(i, t, m, n, s) {
    let sum = 0;
    for (let k = 0; k <= n; k++) {
      sum += (2 * k + 1) * (genFact(2 * m, k) / genFact(2 * m + k + 1, k + 1)) * gramPoly(i, m, k, 0) * gramPoly(t, m, k, s);
    }
    return sum;
  }
  /**
   * @private
   * @param m  Number of points
   * @param n  Polynomial grade
   * @param s  Derivative
   */
  function fullWeights(m, n, s) {
    let weights = new Array(m);
    let np = Math.floor(m / 2);
    for (let t = -np; t <= np; t++) {
      weights[t + np] = new Float64Array(m);
      for (let j = -np; j <= np; j++) {
        weights[t + np][j + np] = weight(j, t, np, n, s);
      }
    }
    return weights;
  }

  /**
   * 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.
   * This process is done in place and is very fast.
   * @param data
   * @param peaks
   */
  function optimizeTop(data, peaks) {
    const {
      x,
      y
    } = data;
    for (const peak of peaks) {
      let currentIndex = peak.index;
      // The detected peak could be moved 1 or 2 units to left or right.
      if (y[currentIndex - 1] >= y[currentIndex - 2] && y[currentIndex - 1] >= y[currentIndex]) {
        currentIndex--;
      } else {
        if (y[currentIndex + 1] >= y[currentIndex] && y[currentIndex + 1] >= y[currentIndex + 2]) {
          currentIndex++;
        } else {
          if (y[currentIndex - 2] >= y[currentIndex - 3] && y[currentIndex - 2] >= y[currentIndex - 1]) {
            currentIndex -= 2;
          } else {
            if (y[currentIndex + 2] >= y[currentIndex + 1] && y[currentIndex + 2] >= y[currentIndex + 3]) {
              currentIndex += 2;
            }
          }
        }
      }
      // interpolation to a sin() function
      if (y[currentIndex - 1] > 0 && y[currentIndex + 1] > 0 && y[currentIndex] >= y[currentIndex - 1] && y[currentIndex] >= y[currentIndex + 1] && (y[currentIndex] !== y[currentIndex - 1] || y[currentIndex] !== y[currentIndex + 1])) {
        let alpha = 20 * Math.log10(y[currentIndex - 1]);
        let beta = 20 * Math.log10(y[currentIndex]);
        let gamma = 20 * Math.log10(y[currentIndex + 1]);
        let p = 0.5 * (alpha - gamma) / (alpha - 2 * beta + gamma);
        peak.x = x[currentIndex] + (x[currentIndex] - x[currentIndex - 1]) * p;
        peak.y = y[currentIndex] - 0.25 * (y[currentIndex - 1] - y[currentIndex + 1]) * p;
      }
    }
  }

  /**
   * Global spectra deconvolution
   * @param  data - Object data with x and y arrays. Values in x has to be growing
   * @param {number} [options.broadRatio = 0.00] - If `broadRatio` is higher than 0, then all the peaks which second derivative
   * smaller than `broadRatio * maxAbsSecondDerivative` will be marked with the soft mask equal to true.

   */
  function gsd(data) {
    let options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
    let {
      sgOptions = {
        windowSize: 9,
        polynomial: 3
      },
      noiseLevel,
      smoothY = false,
      maxCriteria = true,
      minMaxRatio = 0.00025,
      realTopDetection = false
    } = options;
    let {
      x,
      y
    } = data;
    if (!xIsMonotoneIncreasing(x)) {
      throw new Error('GSD only accepts monotone increasing x values');
    }
    //rescale;
    y = y.slice();
    // If the max difference between delta x is less than 5%, then,
    // we can assume it to be equally spaced variable
    let equallySpaced = xIsEquallySpaced(x);
    if (noiseLevel === undefined) {
      if (equallySpaced) {
        const noiseInfo = xNoiseStandardDeviation(y);
        if (maxCriteria) {
          noiseLevel = noiseInfo.median + 1.5 * noiseInfo.sd;
        } else {
          noiseLevel = -noiseInfo.median + 1.5 * noiseInfo.sd;
        }
      } else {
        noiseLevel = 0;
      }
    } else {
      if (maxCriteria === false) {
        noiseLevel *= -1;
      }
    }
    if (maxCriteria === false) {
      for (let i = 0; i < y.length; i++) {
        y[i] *= -1;
      }
    }
    if (noiseLevel !== undefined) {
      for (let i = 0; i < y.length; i++) {
        if (y[i] < noiseLevel) {
          y[i] = noiseLevel;
        }
      }
    }
    let yData = y;
    let dY, ddY;
    const {
      windowSize,
      polynomial
    } = sgOptions;
    if (equallySpaced) {
      if (smoothY) {
        yData = sgg(y, x[1] - x[0], {
          windowSize,
          polynomial,
          derivative: 0
        });
      }
      dY = sgg(y, x[1] - x[0], {
        windowSize,
        polynomial,
        derivative: 1
      });
      ddY = sgg(y, x[1] - x[0], {
        windowSize,
        polynomial,
        derivative: 2
      });
    } else {
      if (smoothY) {
        yData = sgg(y, x, {
          windowSize,
          polynomial,
          derivative: 0
        });
      }
      dY = sgg(y, x, {
        windowSize,
        polynomial,
        derivative: 1
      });
      ddY = sgg(y, x, {
        windowSize,
        polynomial,
        derivative: 2
      });
    }
    const minY = xMinValue(yData);
    const maxY = xMaxValue$1(yData);
    if (minY > maxY || minY === maxY) return [];
    const yThreshold = minY + (maxY - minY) * minMaxRatio;
    const dX = x[1] - x[0];
    let lastMax = null;
    let lastMin = null;
    let minddY = [];
    let intervalL = [];
    let intervalR = [];
    // By the intermediate value theorem We cannot find 2 consecutive maximum or minimum
    for (let i = 1; i < yData.length - 1; ++i) {
      if (dY[i] < dY[i - 1] && dY[i] <= dY[i + 1] || dY[i] <= dY[i - 1] && dY[i] < dY[i + 1]) {
        lastMin = {
          x: x[i],
          index: i
        };
        if (dX > 0 && lastMax !== null) {
          intervalL.push(lastMax);
          intervalR.push(lastMin);
        }
      }
      // Maximum in first derivative
      if (dY[i] >= dY[i - 1] && dY[i] > dY[i + 1] || dY[i] > dY[i - 1] && dY[i] >= dY[i + 1]) {
        lastMax = {
          x: x[i],
          index: i
        };
        if (dX < 0 && lastMin !== null) {
          intervalL.push(lastMax);
          intervalR.push(lastMin);
        }
      }
      // Minimum in second derivative
      if (ddY[i] < ddY[i - 1] && ddY[i] < ddY[i + 1]) {
        minddY.push(i);
      }
    }
    let lastK = -1;
    const peaks = [];
    for (const minddYIndex of minddY) {
      let deltaX = x[minddYIndex];
      let possible = -1;
      let k = lastK + 1;
      let minDistance = Number.POSITIVE_INFINITY;
      let currentDistance = 0;
      while (possible === -1 && k < intervalL.length) {
        currentDistance = Math.abs(deltaX - (intervalL[k].x + intervalR[k].x) / 2);
        if (currentDistance < (intervalR[k].x - intervalL[k].x) / 2) {
          possible = k;
          lastK = k;
        }
        ++k;
        // Not getting closer?
        if (currentDistance >= minDistance) {
          break;
        }
        minDistance = currentDistance;
      }
      if (possible !== -1) {
        if (yData[minddYIndex] > yThreshold) {
          let width = Math.abs(intervalR[possible].x - intervalL[possible].x);
          peaks.push({
            id: v4(),
            x: deltaX,
            y: yData[minddYIndex],
            width,
            index: minddYIndex,
            ddY: ddY[minddYIndex],
            inflectionPoints: {
              from: intervalL[possible],
              to: intervalR[possible]
            }
          });
        }
      }
    }
    if (realTopDetection) {
      optimizeTop({
        x,
        y: yData
      }, peaks);
    }
    peaks.forEach(peak => {
      if (!maxCriteria) {
        peak.y *= -1;
        peak.ddY = peak.ddY * -1;
      }
    });
    peaks.sort((a, b) => {
      return a.x - b.x;
    });
    return peaks;
  }

  /**
   * @param {object}   [options={}]
   * @param {object}   [options.min=1]
   * @param {object}   [options.max=10]
   * @param {object}   [options.low=-1]
   * @param {object}   [options.high=1]
   * @param {object}   [options.precision=100]
   */

  const NEUTRON_MASS = 1;
  function appendPeaksCharge(peaks) {
    let options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
    let {
      precision = 100,
      low = -1,
      high = 1,
      min: minCharge = 1,
      max: maxCharge = 10
    } = options;
    let fromCharge = minCharge * maxCharge > 0 ? Math.round(Math.min(Math.abs(minCharge), Math.abs(maxCharge))) : 1;
    let toCharge = Math.round(Math.max(Math.abs(minCharge), Math.abs(maxCharge)));
    let fromIsotope = Math.ceil(low);
    let toIsotope = Math.floor(high);
    let numberIsotopes = toIsotope - fromIsotope + 1;
    let isotopeIntensity = 1 / numberIsotopes;
    let fromIndex = 0;
    let localFromIndex = 0;
    let localToIndex = 0;
    for (let peakIndex = 0; peakIndex < peaks.length; peakIndex++) {
      let peak = peaks[peakIndex];
      let targetMass = peak.x;
      localFromIndex = fromIndex;
      let bestCharge = fromCharge;
      let bestChargeMatch = 0;
      for (let charge = fromCharge; charge < toCharge + 1; charge++) {
        let theoreticalPositions = {
          x: [],
          y: new Array(numberIsotopes).fill(isotopeIntensity)
        };
        let massRange = precision * 1e-6 * targetMass;
        for (let isotopePosition = fromIsotope; isotopePosition < toIsotope + 1; isotopePosition++) {
          theoreticalPositions.x.push(targetMass + isotopePosition * NEUTRON_MASS / charge);
        }
        let fromMass = targetMass + low / Math.abs(charge) - massRange;
        let toMass = targetMass + high / Math.abs(charge) + massRange;
        if (charge === 1) {
          // we may move the fromIndex
          while (peaks[fromIndex].x < fromMass) {
            fromIndex++;
          }
        }

        /*
         * Find the from / to index for the specific peak and specific charge
         */
        while (peaks[localFromIndex].x < fromMass) {
          localFromIndex++;
        }
        localToIndex = localFromIndex;
        let localHeightSum = 0;
        while (localToIndex < peaks.length && peaks[localToIndex].x < toMass) {
          localHeightSum += peaks[localToIndex].y;
          localToIndex++;
        }
        localToIndex--;

        //  console.log({ localFromIndex, localToIndex });
        /*
          Calculate the overlap for a specific peak and specific charge
        */
        let currentTheoreticalPosition = 0;
        let theoreticalMaxValue = 1 / numberIsotopes;
        let totalMatch = 0;
        for (let index = localFromIndex; index <= localToIndex; index++) {
          let minMass = theoreticalPositions.x[currentTheoreticalPosition] - massRange / charge;
          let maxMass = theoreticalPositions.x[currentTheoreticalPosition] + massRange / charge;
          while (maxMass < peaks[index].x) {
            currentTheoreticalPosition++;
            theoreticalMaxValue = 1 / numberIsotopes;
            minMass = theoreticalPositions.x[currentTheoreticalPosition] - massRange / charge;
            maxMass = theoreticalPositions.x[currentTheoreticalPosition] + massRange / charge;
          }
          while (index < peaks.length && peaks[index].x < minMass) {
            index++;
          }

          //    console.log({ index, minMass, maxMass, massRange, localHeightSum });
          if (index < peaks.length && peaks[index].x <= maxMass) {
            while (index < peaks.length && peaks[index].x <= maxMass) {
              if (peaks[index].x >= minMass && peaks[index].x <= maxMass) {
                let value = peaks[index].y / localHeightSum;
                //      console.log({ theoreticalMaxValue, value });
                value = Math.min(theoreticalMaxValue, value);
                theoreticalMaxValue -= value;
                totalMatch += value;
              }
              index++;
            }
            index--;
          }
          if (totalMatch > bestChargeMatch) {
            bestCharge = charge;
            bestChargeMatch = totalMatch;
          }
        }
      }
      peak.charge = bestCharge;
    }
    return peaks;
  }

  /**
   * Filter the array of peaks
   * @param {Spectrum} spectrum - array of all the peaks
   * @param {object} [options={}]
   * @param {object} [options.charge={}]
   * @param {number} [options.charge.min=1]
   * @param {number} [options.charge.max=10]
   * @param {number} [options.charge.low=-1]
   * @param {number} [options.charge.high=1]
   * @param {number} [options.charge.precision=30]
   * @returns {array} - copy of peaks with 'close' annotation
   */

  function peakPicking(spectrum) {
    let options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
    const {
      charge: chargeOptions = {}
    } = options;
    if (!spectrum.peaks || spectrum.peaks.length === 0) {
      spectrum.peaks = [];
      const keys = Object.keys(spectrum.data).filter(key => key !== 'x' && key !== 'y');
      if (spectrum.isContinuous()) {
        // some experimental data are really problematic and we need to add this line
        const data = xyEnsureGrowingX(spectrum.data);
        const gsdPeaks = gsd(data, {
          minMaxRatio: 0.00025,
          // Threshold to determine if a given peak should be considered as a noise
          realTopDetection: true,
          smoothY: false,
          sgOptions: {
            windowSize: 7,
            polynomial: 3
          }
        });
        for (let gsdPeak of gsdPeaks) {
          const peak = {
            x: gsdPeak.x,
            y: gsdPeak.y,
            width: gsdPeak.width
          };
          for (let key of keys) {
            peak[key] = spectrum.data[key][gsdPeak.index];
          }
          spectrum.peaks.push(peak);
        }
      } else {
        spectrum.peaks = [];
        let data = spectrum.data;
        for (let i = 0; i < data.x.length; i++) {
          const peak = {
            x: data.x[i],
            y: data.y[i],
            width: 0
          };
          for (let key of keys) {
            peak[key] = spectrum.data[key][i];
          }
          spectrum.peaks.push(peak);
        }
      }
      // required and linked to https://github.com/mljs/global-spectral-deconvolution/issues/17
      spectrum.peaks = spectrum.peaks.filter(peak => !isNaN(peak.x) && !isNaN(peak.y));
      appendPeaksCharge(spectrum.peaks, chargeOptions);
    }
    return spectrum.peaks;
  }

  function maybeToPrecision(value, digits) {
    if (value < 0) {
      value = 0 - value;
      if (typeof digits === 'number') {
        return `- ${value.toPrecision(digits)}`;
      } else {
        return `- ${value.toString()}`;
      }
    } else {
      if (typeof digits === 'number') {
        return value.toPrecision(digits);
      } else {
        return value.toString();
      }
    }
  }

  function checkArraySize(x, y) {
    if (!isAnyArray$1(x) || !isAnyArray$1(y)) {
      throw new TypeError('x and y must be arrays');
    }
    if (x.length !== y.length) {
      throw new RangeError('x and y arrays must have the same length');
    }
  }

  class BaseRegression {
    constructor() {
      if (new.target === BaseRegression) {
        throw new Error('BaseRegression must be subclassed');
      }
    }
    predict(x) {
      if (typeof x === 'number') {
        return this._predict(x);
      } else if (isAnyArray$1(x)) {
        const y = [];
        for (let i = 0; i < x.length; i++) {
          y.push(this._predict(x[i]));
        }
        return y;
      } else {
        throw new TypeError('x must be a number or array');
      }
    }
    _predict() {
      throw new Error('_predict must be implemented');
    }
    train() {
      // Do nothing for this package
    }
    toString() {
      return '';
    }
    toLaTeX() {
      return '';
    }

    /**
     * Return the correlation coefficient of determination (r) and chi-square.
     * @param {Array<number>} x
     * @param {Array<number>} y
     * @return {object}
     */
    score(x, y) {
      if (!isAnyArray$1(x) || !isAnyArray$1(y) || x.length !== y.length) {
        throw new Error('x and y must be arrays of the same length');
      }
      const n = x.length;
      const y2 = new Array(n);
      for (let i = 0; i < n; i++) {
        y2[i] = this._predict(x[i]);
      }
      let xSum = 0;
      let ySum = 0;
      let chi2 = 0;
      let rmsd = 0;
      let xSquared = 0;
      let ySquared = 0;
      let xY = 0;
      for (let i = 0; i < n; i++) {
        xSum += y2[i];
        ySum += y[i];
        xSquared += y2[i] * y2[i];
        ySquared += y[i] * y[i];
        xY += y2[i] * y[i];
        if (y[i] !== 0) {
          chi2 += (y[i] - y2[i]) * (y[i] - y2[i]) / y[i];
        }
        rmsd += (y[i] - y2[i]) * (y[i] - y2[i]);
      }
      const r = (n * xY - xSum * ySum) / Math.sqrt((n * xSquared - xSum * xSum) * (n * ySquared - ySum * ySum));
      return {
        r: r,
        r2: r * r,
        chi2: chi2,
        rmsd: Math.sqrt(rmsd / n)
      };
    }
  }

  class SimpleLinearRegression extends BaseRegression {
    constructor(x, y) {
      super();
      if (x === true) {
        this.slope = y.slope;
        this.intercept = y.intercept;
        this.coefficients = [y.intercept, y.slope];
      } else {
        checkArraySize(x, y);
        regress$1(this, x, y);
      }
    }
    toJSON() {
      return {
        name: 'simpleLinearRegression',
        slope: this.slope,
        intercept: this.intercept
      };
    }
    _predict(x) {
      return this.slope * x + this.intercept;
    }
    computeX(y) {
      return (y - this.intercept) / this.slope;
    }
    toString(precision) {
      let result = 'f(x) = ';
      if (this.slope !== 0) {
        const xFactor = maybeToPrecision(this.slope, precision);
        result += `${xFactor === '1' ? '' : `${xFactor} * `}x`;
        if (this.intercept !== 0) {
          const absIntercept = Math.abs(this.intercept);
          const operator = absIntercept === this.intercept ? '+' : '-';
          result += ` ${operator} ${maybeToPrecision(absIntercept, precision)}`;
        }
      } else {
        result += maybeToPrecision(this.intercept, precision);
      }
      return result;
    }
    toLaTeX(precision) {
      return this.toString(precision);
    }
    static load(json) {
      if (json.name !== 'simpleLinearRegression') {
        throw new TypeError('not a SLR model');
      }
      return new SimpleLinearRegression(true, json);
    }
  }
  function regress$1(slr, x, y) {
    const n = x.length;
    let xSum = 0;
    let ySum = 0;
    let xSquared = 0;
    let xY = 0;
    for (let i = 0; i < n; i++) {
      xSum += x[i];
      ySum += y[i];
      xSquared += x[i] * x[i];
      xY += x[i] * y[i];
    }
    const numerator = n * xY - xSum * ySum;
    slr.slope = numerator / (n * xSquared - xSum * xSum);
    slr.intercept = 1 / n * ySum - slr.slope * (1 / n) * xSum;
    slr.coefficients = [slr.intercept, slr.slope];
  }

  class PowerRegression extends BaseRegression {
    constructor(x, y) {
      super();
      if (x === true) {
        // reloading model
        this.A = y.A;
        this.B = y.B;
      } else {
        checkArraySize(x, y);
        regress(this, x, y);
      }
    }
    _predict(newInputs) {
      return this.A * Math.pow(newInputs, this.B);
    }
    toJSON() {
      return {
        name: 'powerRegression',
        A: this.A,
        B: this.B
      };
    }
    toString(precision) {
      return `f(x) = ${maybeToPrecision(this.A, precision)} * x^${maybeToPrecision(this.B, precision)}`;
    }
    toLaTeX(precision) {
      let latex = '';
      if (this.B >= 0) {
        latex = `f(x) = ${maybeToPrecision(this.A, precision)}x^{${maybeToPrecision(this.B, precision)}}`;
      } else {
        latex = `f(x) = \\frac{${maybeToPrecision(this.A, precision)}}{x^{${maybeToPrecision(-this.B, precision)}}}`;
      }
      latex = latex.replace(/e([+-]?[0-9]+)/g, 'e^{$1}');
      return latex;
    }
    static load(json) {
      if (json.name !== 'powerRegression') {
        throw new TypeError('not a power regression model');
      }
      return new PowerRegression(true, json);
    }
  }
  function regress(pr, x, y) {
    const n = x.length;
    const xl = new Array(n);
    const yl = new Array(n);
    for (let i = 0; i < n; i++) {
      xl[i] = Math.log(x[i]);
      yl[i] = Math.log(y[i]);
    }
    const linear = new SimpleLinearRegression(xl, yl);
    pr.A = Math.exp(linear.intercept);
    pr.B = linear.slope;
  }

  function peaksWidth(peaks) {
    let xs = peaks.map(peak => peak.x);
    let widths = peaks.map(peak => peak.width);
    if (xs.length < 2) {
      throw new Error(`peaksWidth: not enough peaks (less than 2) for automatic width calculation: ${xs.length}`);
    }
    let regression = new PowerRegression(xs, widths, {
      computeQuality: true,
      computeCoefficient: true
    });
    if (isNaN(regression.A) || isNaN(regression.B)) {
      throw new Error('peaksWidth: can not calculate regression');
    }
    let from = xMinValue(xs);
    let to = xMaxValue$1(xs);
    let regressionChart = {
      x: [],
      y: []
    };
    for (let x = from; x <= to; x += (to - from) / 1000) {
      regressionChart.x.push(x);
      regressionChart.y.push(regression.predict(x));
    }
    return {
      widths: {
        x: xs,
        y: widths
      },
      fit: regressionChart,
      score: regression.score(xs, widths),
      predictFct: regression.predict.bind(regression),
      tex: regression.toLaTeX(3),
      A: regression.A,
      B: regression.B,
      predictFctString: `${regression.A} * mass ** ${regression.B}`
    };
  }

  /**
   * Class dealing with mass spectra and peak picking
   */
  class Spectrum {
    constructor() {
      let data = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {
        x: [],
        y: []
      };
      if (typeof data !== 'object' || !isAnyArray$1(data.x) || !isAnyArray$1(data.y)) {
        throw new TypeError('Spectrum data must be an object with x:[], y:[]');
      }
      this.data = {}; // we make a copy so that we can add new properties
      for (let key in data) {
        this.data[key] = data[key];
      }
      Object.defineProperty(this.data, 'xOriginal', {
        enumerable: false,
        writable: true
      });
      this.cache = {};
      this.peaks = [];
    }
    maxY() {
      return xMaxValue$1(this.data.y);
    }
    sumY() {
      if (!this.cache.sumY) {
        this.cache.sumY = this.data.y.reduce((previous, current) => previous + current, 0);
      }
      return this.cache.sumY;
    }
    scaleY() {
      let intensity = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 1;
      let basePeak = this.maxY() / intensity;
      this.data.y = this.data.y.map(y => y / basePeak);
      return this;
    }
    rescaleX(callback) {
      this.ensureOriginalX();
      for (let i = 0; i < this.data.x.length; i++) {
        this.data.x[i] = callback(this.data.xOriginal[i]);
      }
      return this;
    }
    ensureOriginalX() {
      if (!this.data.xOriginal) {
        this.data.xOriginal = this.data.x.slice(0);
      }
    }
    normedY() {
      let total = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 1;
      this.data.y = xNormed(this.data.y);
      if (total !== 1) {
        this.data.y = this.data.y.map(y => y * total);
      }
      return this;
    }
    peakPicking() {
      peakPicking(this);
      return this.peaks;
    }
    peaksWidth() {
      peakPicking(this);
      return peaksWidth(this.peaks);
    }
    getBestPeaks(options) {
      peakPicking(this);
      return getBestPeaks(this.peaks, options);
    }
    getPeakChargeBySimilarity(targetMass, options) {
      return getPeakChargeBySimilarity(this, targetMass, options);
    }
    getPeaks(options) {
      peakPicking(this);
      return getPeaks(this.peaks, options);
    }

    /**
     * Returns also peaks possible for a specific molecular formula
     * @example
     *  const spectrum = new Spectrum({x:[], y:[]})
     *  await spectrum.getFragmentPeaks();
     * @param {string} mf
     * @param {object} options
     * @returns
     */
    getFragmentPeaksFct(mf, options) {
      peakPicking(this);
      return getFragmentPeaks(this.peaks, mf, options);
    }
    isContinuous() {
      return isContinuous(this);
    }

    /**
     * Remove an integer number of time the specifiedd monoisotopic mass
     * Mass remainder analysis (MARA): https://doi.org/10.1021/acs.analchem.7b04730
     */
    getMassRemainderFct(mass) {
      let options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
      return getMassRemainder(this.data, mass, options);
    }
  }

  /**
   * @param {object}   [entry={}]
   * @param {object}   [options={}]
   * @param {number}   [options.min=-Infinity] - Minimal unsaturation
   * @param {number}   [options.max=+Infinity] - Maximal unsaturation
   * @param {number}   [options.onlyInteger=false] - Integer unsaturation
   * @param {number}   [options.onlyNonInteger=false] - Non integer unsaturation
   * @return {boolean}
   */

  function unsaturationMatcher(entry) {
    let options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
    const {
      min = Number.MIN_SAFE_INTEGER,
      max = Number.MAX_SAFE_INTEGER,
      onlyInteger,
      onlyNonInteger
    } = options;
    if (entry.unsaturation !== undefined) {
      if (entry.unsaturation < min || entry.unsaturation > max) return false;
      if (onlyInteger && !Number.isInteger(entry.unsaturation)) return false;
      if (onlyNonInteger && Number.isInteger(entry.unsaturation)) return false;
    }
    return true;
  }

  /**
   * Returns true if the entry containing MF information match
   * @param {object}   [entry={}] - object containing mw, ...
   * @param {object}   [options={}]
   * @param {number}   [options.minMW=0] - Minimal molecular weight
   * @param {number}   [options.maxMW=+Infinity] - Maximal molecular weight
   * @param {number}   [options.minEM=0] - Minimal monoisotopic mass
   * @param {number}   [options.maxEM=+Infinity] - Maximal monoisotopic mass
   * @param {number}   [options.minCharge=-Infinity] - Minimal charge
   * @param {number}   [options.maxCharge=+Infinity] - Maximal charge
   * @param {object}   [options.unsaturation={}]
   * @param {number}   [options.unsaturation.min=-Infinity] - Minimal unsaturation
   * @param {number}   [options.unsaturation.max=+Infinity] - Maximal unsaturation
   * @param {number}   [options.unsaturation.onlyInteger=false] - Integer unsaturation
   * @param {number}   [options.unsaturation.onlyNonInteger=false] - Non integer unsaturation
   * @param {object}   [options.atoms] - object of atom:{min, max}
   * @return {boolean}
   */

  function generalMatcher(entry) {
    let options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
    const {
      minMW = 0,
      maxMW = +Infinity,
      minEM = 0,
      maxEM = +Infinity,
      minCharge = Number.MIN_SAFE_INTEGER,
      maxCharge = Number.MAX_SAFE_INTEGER,
      unsaturation = {},
      atoms
    } = options;
    if (entry.mw !== undefined) {
      if (entry.mw < minMW || entry.mw > maxMW) return false;
    }
    if (entry.em !== undefined) {
      if (entry.em < minEM || entry.em > maxEM) return false;
    }
    if (entry.charge !== undefined) {
      if (entry.charge < minCharge || entry.charge > maxCharge) return false;
    }
    if (unsaturation !== undefined && entry.unsaturation !== undefined) {
      if (!unsaturationMatcher(entry, unsaturation)) return false;
    }
    if (entry.atoms !== undefined && atoms) {
      // all the atoms of the entry must fit in the range
      for (let atom in entry.atoms) {
        if (!atoms[atom]) return false;
        if (entry.atoms[atom] < atoms[atom].min) return false;
        if (entry.atoms[atom] > atoms[atom].max) return false;
      }
    }
    return true;
  }

  /**
   * Returns the closest index of a `target`
   *
   * @param array - array of numbers
   * @param target - target
   * @returns - closest index
   */
  function xFindClosestIndex$1(array, target) {
    let options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
    const {
      sorted = true
    } = options;
    if (sorted) {
      let low = 0;
      let high = array.length - 1;
      let middle = 0;
      while (high - low > 1) {
        middle = low + (high - low >> 1);
        if (array[middle] < target) {
          low = middle;
        } else if (array[middle] > target) {
          high = middle;
        } else {
          return middle;
        }
      }
      if (low < array.length - 1) {
        if (Math.abs(target - array[low]) < Math.abs(array[low + 1] - target)) {
          return low;
        } else {
          return low + 1;
        }
      } else {
        return low;
      }
    } else {
      let index = 0;
      let diff = Number.POSITIVE_INFINITY;
      for (let i = 0; i < array.length; i++) {
        const currentDiff = Math.abs(array[i] - target);
        if (currentDiff < diff) {
          diff = currentDiff;
          index = i;
        }
      }
      return index;
    }
  }

  /**
   * @param {object}         [entry={}]
   * @param {object}         [options={}]
   * @param {object}         [options.ionization={ mf: '', em: 0, charge: 0 }] - ionization method
   * @param {boolean}        [options.forceIonization=false] - If true ignore existing ionizations
   * @param {number}         [options.precision=1000] - The precision on the experimental mass
   * @param {number}         [options.targetMass] - Target mass, allows to calculate error and filter results
   * @param {Array<number>}  [options.targetMasses] - Target masses: SORTED array of numbers
   * @param {Array<number>}  [options.targetIntensities] - Target intensities: SORTED array of numbers
   * @param {number}         [options.minEM=0] - Minimal monoisotopic mass
   * @param {number}         [options.maxEM=+Infinity] - Maximal monoisotopic mass
   * @param {number}         [options.minMSEM=0] - Minimal monoisotopic mass observed by mass
   * @param {number}         [options.maxMSEM=+Infinity] - Maximal monoisotopic mass observed by mass
   * @param {number}         [options.minCharge=-Infinity] - Minimal charge
   * @param {number}         [options.maxCharge=+Infinity] - Maximal charge
   * @param {boolean}        [options.allowNegativeAtoms=false] - Allow to have negative number of atoms
   * @param {object}         [options.unsaturation={}]
   * @param {number}         [options.unsaturation.min=-Infinity] - Minimal unsaturation
   * @param {number}         [options.unsaturation.max=+Infinity] - Maximal unsaturation
   * @param {number}         [options.unsaturation.onlyInteger=false] - Integer unsaturation
   * @param {number}         [options.unsaturation.onlyNonInteger=false] - Non integer unsaturation
   * @param {object}         [options.atoms] - object of atom:{min, max}
   * @param {Function}       [options.callback] - a function that contains information about the current MF
   * @return {boolean}
   */

  /**
   * We always recalculate msem
   */

  function msemMatcher(entry) {
    let options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
    const {
      ionization = {
        mf: '',
        em: 0,
        charge: 0,
        atoms: {}
      },
      forceIonization = false,
      precision = 1000,
      minCharge = Number.MIN_SAFE_INTEGER,
      maxCharge = Number.MAX_SAFE_INTEGER,
      unsaturation = {},
      targetMass,
      // if present we will calculate the errors
      targetMasses,
      // if present we will calculate the smallest error
      targetIntensities,
      // if present it will be added in the report
      minEM = -Infinity,
      maxEM = +Infinity,
      minMSEM = -Infinity,
      maxMSEM = +Infinity,
      allowNegativeAtoms = false,
      atoms,
      callback
    } = options;
    let msInfo = getMsInfo(entry, {
      ionization,
      forceIonization,
      targetMass
    });
    let ms = msInfo.ms;
    if (entry.em !== undefined) {
      if (entry.em < minEM || entry.em > maxEM) return false;
    }
    if (ms.em !== undefined) {
      if (ms.em < minMSEM || ms.em > maxMSEM) return false;
    }
    if (targetMass && Math.abs(ms.ppm) > precision) return false;
    if (entry.charge !== undefined) {
      if (ms.charge < minCharge || ms.charge > maxCharge) return false;
    }
    if (unsaturation !== undefined && entry.unsaturation !== undefined) {
      if (!unsaturationMatcher(entry, unsaturation)) {
        return false;
      }
    }
    if (entry.atoms !== undefined && atoms) {
      // all the atoms of the entry must fit in the range
      for (let atom in entry.atoms) {
        if (!atoms[atom]) return false;
        if (entry.atoms[atom] < atoms[atom].min) return false;
        if (entry.atoms[atom] > atoms[atom].max) return false;
      }
    }
    if (entry.atoms !== undefined && !allowNegativeAtoms) {
      const ionizationAtoms = msInfo.ionization && msInfo.ionization.atoms || {};
      const atomKeys = new Set(Object.keys(ionizationAtoms).concat(Object.keys(entry.atoms)));
      for (let atom of atomKeys) {
        if ((entry.atoms[atom] || 0) + (ionizationAtoms[atom] || 0) < 0) {
          return false;
        }
      }
    }
    if (targetMasses && targetMasses.length > 0) {
      let index = xFindClosestIndex$1(targetMasses, ms.em);
      let closestMass = targetMasses[index];
      msInfo = getMsInfo(entry, {
        ionization,
        forceIonization,
        targetMass: closestMass
      });
      msInfo.ms.target = {
        mass: closestMass
      };
      if (targetIntensities) {
        msInfo.ms.target.intensity = targetIntensities[index];
      }
      // need to find the closest targetMasses
      if (Math.abs(msInfo.ms.ppm) > precision) return false;
    }
    if (callback) {
      if (!callback(entry)) return false;
    }
    return msInfo;
  }

  /**
   * returns all the possible neutral mass for a defined experimental (targetMass) mass
   */

  class TargetMassCache {
    constructor(targetMass, possibilities) {
      let options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
      const {
        allowNeutral = false,
        // msem because em in this case !
        minCharge = Number.MIN_SAFE_INTEGER,
        maxCharge = Number.MAX_SAFE_INTEGER,
        charge = 0,
        precision = 100
      } = options;
      if (!possibilities || possibilities.length === 0) return;
      let firstPossibility = possibilities[0];
      let currentMinCharge = Math.max(minCharge, firstPossibility.minCharge + charge);
      let currentMaxCharge = Math.min(maxCharge, firstPossibility.maxCharge + charge);
      this.minCharge = currentMinCharge;
      this.maxCharge = currentMaxCharge;
      let size = this.maxCharge - this.minCharge + 1;
      this.data = [];
      let minMass = 0;
      let maxMass = 0;
      let range = targetMass * precision / 1e6;
      for (let i = 0; i < size; i++) {
        let currentCharge = i + this.minCharge;
        if (currentCharge === 0) {
          if (allowNeutral) {
            minMass = targetMass - range;
            maxMass = targetMass + range;
          } else {
            minMass = Number.MAX_SAFE_INTEGER;
            maxMass = Number.MIN_SAFE_INTEGER;
          }
        } else {
          minMass = (targetMass - range) * Math.abs(currentCharge) + ELECTRON_MASS * currentCharge;
          maxMass = (targetMass + range) * Math.abs(currentCharge) + ELECTRON_MASS * currentCharge;
        }
        this.data.push({
          charge: currentCharge,
          minMass,
          maxMass
        });
      }
    }
    getMinMass(charge) {
      return this.data[charge - this.minCharge] ? this.data[charge - this.minCharge].minMass : Number.MAX_SAFE_INTEGER;
    }
    getMaxMass(charge) {
      return this.data[charge - this.minCharge] ? this.data[charge - this.minCharge].maxMass : Number.MIN_SAFE_INTEGER;
    }
  }

  /**
   * @param {number}        targetMass - Monoisotopic mass
   * @param {object}        [options={}]
   * @param {number}        [options.maxIterations=10000000] - Maximum number of iterations
   * @param {boolean}       [options.allowNeutral=true]
   * @param {boolean}       [options.uniqueMFs=true]
   * @param {number}        [options.limit=1000] - Maximum number of results
   * @param {string}        [options.ionizations=''] - string containing a comma separated list of modifications
   * @param {string}        [options.ranges='C0-100 H0-100 O0-100 N0-100'] - range of mfs to search
   * @param {number}        [options.precision=100] - Allowed mass range based on precision
   * @param {object}        [options.filter={}]
   * @param {number}        [options.filter.minCharge=-Infinity] - Minimal charge
   * @param {number}        [options.filter.maxCharge=+Infinity] - Maximal charge
   * @param {object}        [options.filter.unsaturation={}]
   * @param {number}        [options.filter.unsaturation.min=-Infinity] - Minimal unsaturation
   * @param {number}        [options.filter.unsaturation.max=+Infinity] - Maximal unsaturation
   * @param {number}        [options.filter.unsaturation.onlyInteger=false] - Integer unsaturation
   * @param {number}        [options.filter.unsaturation.onlyNonInteger=false] - Non integer unsaturation
   * @param {object}        [options.filter.atoms] - object of atom:{min, max}
   * @param {function}      [options.filter.callback] - a function to filter the MF
   * @returns {Promise}
   */

  async function findMFs(targetMass) {
    let options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
    const {
      filter = {},
      maxIterations = 1e8,
      limit = 1000,
      allowNeutral = true,
      // if there is no msem we use em !
      uniqueMFs = false,
      // if there is no msem we use em !
      ranges = [{
        mf: 'C',
        min: 0,
        max: 100
      }, {
        mf: 'H',
        min: 0,
        max: 100
      }, {
        mf: 'O',
        min: 0,
        max: 100
      }, {
        mf: 'N',
        min: 0,
        max: 100
      }]
    } = options;
    let targetMassCache;
    const {
      minCharge = Number.MIN_SAFE_INTEGER,
      maxCharge = Number.MAX_SAFE_INTEGER,
      unsaturation = {}
    } = filter;
    let filterUnsaturation = !!unsaturation;
    // we calculate not the real unsaturation but the one before dividing by 2 + 1
    let fakeMinUnsaturation = unsaturation.min === undefined ? Number.MIN_SAFE_INTEGER : (unsaturation.min - 1) * 2;
    let fakeMaxUnsaturation = unsaturation.max === undefined ? Number.MAX_SAFE_INTEGER : (unsaturation.max - 1) * 2;
    let filterCharge = minCharge !== Number.MIN_SAFE_INTEGER || maxCharge !== Number.MAX_SAFE_INTEGER;
    let advancedFilter;
    if (filter.atoms || filter.callback) {
      advancedFilter = {
        atoms: filter.atoms,
        callback: filter.callback
      };
    }
    let result = {
      mfs: [],
      info: {
        numberMFEvaluated: 0,
        numberResults: 0
      }
    };
    let orderMapping = []; // used to sort the atoms

    // we need to make the processing for all the ionizations
    let ionizations = preprocessIonizations(options.ionizations);
    for (let ionization of ionizations) {
      let currentIonization = {
        currentMonoisotopicMass: ionization.em || 0,
        currentCharge: ionization.charge,
        currentUnsaturation: 0 // we don't take into account the unsaturation of the ionization agent
      };
      // if (DEBUG) console.log('new ionization', ionization.mf, ionization.em, ionization.charge);
      // ionization em and charge will be used to set the first atom value
      let possibilities = preprocessRanges(ranges);
      orderMapping = getOrderMapping$1(possibilities);
      if (possibilities.length === 0) return {
        mfs: []
      };
      targetMassCache = new TargetMassCache(targetMass, possibilities, {
        ...options,
        ...{
          charge: ionization.charge
        }
      });
      let theEnd = false;
      let maxPosition = possibilities.length;
      let lastPosition = possibilities.length - 1;
      let currentPosition = 0;
      let currentAtom;
      let previousAtom;
      let lastPossibility = possibilities[lastPosition];
      initializePossibilities(possibilities, currentIonization, targetMassCache);

      //  if (DEBUG) console.log('possibilities', possibilities.map((a) => `${a.mf + a.originalMinCount}-${a.originalMaxCount}`));

      let isValid = false; // designed so that the first time it is not a valid solution
      while (!theEnd) {
        if (result.info.numberMFEvaluated++ > maxIterations) {
          throw Error(`Iteration number is over the current maximum of: ${maxIterations}`);
        }
        if (filterUnsaturation) {
          let unsaturationValue = lastPossibility.currentUnsaturation;
          let isOdd = Math.abs(unsaturationValue % 2);
          if (unsaturation.onlyInteger && isOdd === 1 || unsaturation.onlyNonInteger && isOdd === 0 || fakeMinUnsaturation > unsaturationValue || fakeMaxUnsaturation < unsaturationValue) {
            isValid = false;
          }
        }
        if (filterCharge && (lastPossibility.currentCharge < minCharge || lastPossibility.currentCharge > maxCharge)) {
          isValid = false;
        }
        if (isValid) {
          let minMass = targetMassCache.getMinMass(lastPossibility.currentCharge);
          let maxMass = targetMassCache.getMaxMass(lastPossibility.currentCharge);
          if (lastPossibility.currentMonoisotopicMass < minMass || lastPossibility.currentMonoisotopicMass > maxMass) {
            isValid = false;
          }
        }
        if (isValid) {
          result.info.numberResults++;
          let newResult = getResult$1(possibilities, targetMass, allowNeutral, ionization, orderMapping);
          if (advancedFilter) {
            isValid = msemMatcher(newResult, advancedFilter) !== false;
          }
          if (isValid) {
            result.mfs.push(newResult);
            if (result.mfs.length > 2 * limit) {
              if (uniqueMFs) ensureUniqueMF(result);
              result.mfs.sort((a, b) => Math.abs(a.ms.ppm) - Math.abs(b.ms.ppm));
              result.mfs.length = limit;
            }
          }
        }
        isValid = true;
        // we need to setup all the arrays if possible
        while (currentPosition < maxPosition && currentPosition >= 0) {
          currentAtom = possibilities[currentPosition];
          previousAtom = currentPosition === 0 ? currentIonization : possibilities[currentPosition - 1];
          if (currentAtom.currentCount < currentAtom.currentMaxCount) {
            currentAtom.currentCount++;
            updateCurrentAtom(currentAtom, previousAtom);
            if (currentPosition < lastPosition) {
              currentPosition++;
              setCurrentMinMax(possibilities[currentPosition], possibilities[currentPosition - 1], targetMassCache);
            } else {
              break;
            }
          } else {
            currentPosition--;
          }
        }
        if (currentPosition < 0) {
          theEnd = true;
        }
      }
    }
    if (uniqueMFs) ensureUniqueMF(result);
    result.mfs.sort((a, b) => Math.abs(a.ms.ppm) - Math.abs(b.ms.ppm));
    if (result.mfs.length > limit) {
      result.mfs.length = limit;
    }
    result.mfs.forEach(mf => delete mf.currentCounts);
    return result;
  }

  /**
   * Ensure that we have only once the same MF
   * In order to improve the speed we just consider the em
   * @param {object} result
   */
  function ensureUniqueMF(result) {
    result.mfs.sort((a, b) => a.em - b.em);
    let previousEM = 0;
    let bestCounts = [];
    const mfs = [];
    next: for (let current of result.mfs) {
      if (current.em - previousEM > 1e-8) {
        previousEM = current.em;
        bestCounts = current.currentCounts;
        mfs.push(current);
      } else {
        for (let i = 0; i < current.currentCounts.length; i++) {
          // better priority ???
          if (current.currentCounts[i] > bestCounts[i]) {
            mfs.pop();
            mfs.push(current);
            bestCounts = current.currentCounts;
            continue;
          } else if (current.currentCounts[i] < bestCounts[i]) {
            continue next;
          }
        }
      }
    }
    result.mfs = mfs;
  }
  function updateCurrentAtom(currentAtom, previousAtom) {
    currentAtom.currentMonoisotopicMass = previousAtom.currentMonoisotopicMass + currentAtom.em * currentAtom.currentCount;
    currentAtom.currentCharge = previousAtom.currentCharge + currentAtom.charge * currentAtom.currentCount;
    currentAtom.currentUnsaturation = previousAtom.currentUnsaturation + currentAtom.unsaturation * currentAtom.currentCount;
  }
  function getResult$1(possibilities, targetMass, allowNeutralMolecules, ionization, orderMapping) {
    let lastPossibility = possibilities[possibilities.length - 1];
    let result = {
      em: lastPossibility.currentMonoisotopicMass - ionization.em,
      unsaturation: lastPossibility.currentUnsaturation,
      mf: '',
      charge: lastPossibility.currentCharge - ionization.charge,
      ionization,
      atoms: {},
      groups: {},
      currentCounts: possibilities.map(possibility => possibility.currentCount)
    };

    // we check that the first time we meet the ionization group it does not end
    // in the final result

    for (let i = 0; i < possibilities.length; i++) {
      let possibility = possibilities[orderMapping[i]];
      if (possibility.currentCount !== 0) {
        if (possibility.isGroup) {
          if (possibility.currentCount === 1) {
            result.mf += `${possibility.mf}`;
          } else if (possibility.mf.match(/^\([^()]*\)$/)) {
            result.mf += `${possibility.mf}${possibility.currentCount}`;
          } else {
            result.mf += `(${possibility.mf})${possibility.currentCount}`;
          }
          if (result.groups[possibility.mf]) {
            result.groups[possibility.mf] += possibility.currentCount;
          } else {
            result.groups[possibility.mf] = possibility.currentCount;
          }
        } else {
          result.mf += possibility.mf;
          if (possibility.currentCount !== 1) {
            result.mf += possibility.currentCount;
          }
        }
        for (let atom in possibility.atoms) {
          if (result.atoms[atom]) {
            result.atoms[atom] += possibility.atoms[atom] * possibility.currentCount;
          } else {
            result.atoms[atom] = possibility.atoms[atom] * possibility.currentCount;
          }
        }
      }
    }
    result.unsaturation = (result.unsaturation + Math.abs(result.charge)) / 2 + 1;
    result.ms = getMsInfo(result, {
      targetMass,
      allowNeutralMolecules
    }).ms;
    return result;
  }
  function setCurrentMinMax(currentAtom, previousAtom, targetMassCache) {
    // the current min max can only be optimize if the charge will not change anymore
    if (currentAtom.innerCharge === true || currentAtom.charge !== 0) {
      currentAtom.currentMinCount = currentAtom.originalMinCount;
      currentAtom.currentMaxCount = currentAtom.originalMaxCount;
      currentAtom.currentCount = currentAtom.currentMinCount - 1;
    } else {
      // no more change of charge, we can optimize
      let currentMass = previousAtom !== undefined ? previousAtom.currentMonoisotopicMass : 0;
      let currentCharge = previousAtom !== undefined ? previousAtom.currentCharge : 0;
      currentAtom.currentMinCount = Math.max(Math.floor((targetMassCache.getMinMass(currentCharge) - currentMass - currentAtom.maxInnerMass) / currentAtom.em), currentAtom.originalMinCount);
      currentAtom.currentMaxCount = Math.min(Math.floor((targetMassCache.getMaxMass(currentCharge) - currentMass - currentAtom.minInnerMass) / currentAtom.em), currentAtom.originalMaxCount);
      currentAtom.currentCount = currentAtom.currentMinCount - 1;
    }
  }
  function initializePossibilities(possibilities, currentIonization, targetMassCache) {
    for (let i = 0; i < possibilities.length; i++) {
      if (i === 0) {
        updateCurrentAtom(possibilities[i], currentIonization);
        setCurrentMinMax(possibilities[i], currentIonization, targetMassCache);
      } else {
        updateCurrentAtom(possibilities[i], possibilities[i - 1]);
      }
    }
  }
  function getOrderMapping$1(possibilities) {
    let mapping = possibilities.map((p, i) => ({
      atom: p.mf,
      index: i
    }));
    mapping.sort((a, b) => {
      return atomSorter(a.atom, b.atom);
    });
    return mapping.map(a => a.index);
  }

  /**
   *
   * @param {object}   experimentalSpectrum
   * @param {object}   database
   * @param {object}   [options={}]
   * @param {function} [options.onStep] - Callback to do after each step
   * @param {string}   [options.ionizations=''] - string containing a comma separated list of modifications
   * @param {number}   [options.precision=100] - Allowed mass range based on precision
   */
  async function appendFragmentsInfo(experimentalSpectrum, database) {
    let options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
    const {
      ionizations,
      onStep,
      precision
    } = options;
    if (!experimentalSpectrum) {
      throw new Error('Experimental spectrum is not defined');
    }
    if (!database) {
      throw new Error('Database is not defined');
    }
    const peaks = experimentalSpectrum.getPeaks({
      sumValue: 1
    });
    for (let entry of database) {
      const ranges = Object.keys(entry.atoms).map(atom => `${atom}0-${entry.atoms[atom]}`).join(' ');
      entry.fragments = {
        nbFound: 0,
        intensityFound: 0,
        assignments: []
      };
      for (let i = 0; i < peaks.length; i++) {
        if (onStep) await onStep(i);
        const peak = peaks[i];
        const possibleMFs = await findMFs(peak.x, {
          ionizations,
          precision,
          ranges
        });
        if (possibleMFs.mfs.length > 0) {
          entry.fragments.nbFound++;
          entry.fragments.intensityFound += peak.y;
          entry.fragments.assignments.push({
            peak,
            bestMF: possibleMFs.mfs[0]
          });
        }
      }
    }
  }

  /**
   * Modify object a to join it with b and make the sum of each of the keys
   * @param {*} a
   * @param {*} source1
   *
   * @return {object}
   */

  function sum(target) {
    for (var i = 1; i < arguments.length; i++) {
      let toSum = arguments[i];
      for (var key of Object.keys(toSum)) {
        if (target[key]) {
          target[key] += toSum[key];
        } else {
          target[key] = toSum[key];
        }
      }
    }
    return target;
  }
  var lib = sum;
  var sum$1 = lib;

  /**
   * Generate all the possible combinations of molecular formula and calculate
   * for each of them the monoisotopic mass and observed moniisotopic mass (m/z)
   * In the molecular formula there may be a comment after the '$' symbol
   *
   * @param keys
   * @param {object}        [options={}]
   * @param {number}        [options.limit=10000000] - Maximum number of results
   * @param {boolean}       [options.estimate=false] - estimate the number of MF without filters
   * @param {boolean}       [options.canonizeMF=true] - Canonize molecular formula
   * @param {boolean}       [options.uniqueMFs=true] - Force canonization and make MF unique
   * @param {string}        [options.ionizations=''] - Comma separated list of ionizations (to charge the molecule)
   * @param {function}      [options.onStep] - Callback to do after each step
   * @param {object}        [options.filter={}] - Minimal monoisotopic mass
   * @param {number}        [options.filter.minMass=0] - Minimal monoisotopic mass
   * @param {number}        [options.filter.maxMass=+Infinity] - Maximal monoisotopic mass
   * @param {number}        [options.filter.minEM=0] - Minimal neutral monoisotopic mass
   * @param {number}        [options.filter.maxEM=+Infinity] - Maximal neutral monoisotopic mass
   * @param {number}        [options.filter.precision=1000] - The precision on the experimental mass
   * @param {number}        [options.filter.targetMass] - Target mass, allows to calculate error and filter results
   * @param {Array<number>} [options.filter.targetMasses] - Target masses: SORTED array of numbers
   * @param {number}        [options.filter.precision=1000] - Precision
   * @param {number}        [options.filter.minCharge=-Infinity] - Minimal charge
   * @param {number}        [options.filter.maxCharge=+Infinity] - Maximal charge
   * @param {boolean}       [options.filter.allowNegativeAtoms=false] - Allow to have negative number of atoms
   * @param {object}        [options.filter.unsaturation={}]
   * @param {number}        [options.filter.unsaturation.min=-Infinity] - Minimal unsaturation
   * @param {number}        [options.filter.unsaturation.max=+Infinity] - Maximal unsaturation
   * @param {boolean}       [options.filter.unsaturation.onlyInteger=false] - Integer unsaturation
   * @param {boolean}       [options.filter.unsaturation.onlyNonInteger=false] - Non integer unsaturation
   * @param {object}        [options.filter.atoms] - object of atom:{min, max}
   * @param {function}      [options.filter.callback] - a function to filter the MF
   * @param {string}        [options.filterFct]
   * @param {object}        [options.links]
   * @param {boolean}       [options.links.filter] We filter all the MF that do not match the '*X'
   * @returns {Promise}
   */

  async function generateMFs(keys) {
    let options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
    options = {
      ...options
    };
    let {
      limit = 100000,
      uniqueMFs = true,
      estimate = false,
      onStep
    } = options;
    options.filterFctVariables = {};
    for (let i = 0; i < keys.length; i++) {
      const key = keys[i];
      if (typeof key === 'object' && key.name) {
        options.filterFctVariables[key.name] = i;
        keys[i] = key.value;
      }
    }
    if (options.filterFct) {
      // we create a real javascript function
      let variables = Object.keys(options.filterFctVariables);
      variables.push('mm', 'mz', 'charge', 'unsaturation', 'atoms');
      // eslint-disable-next-line no-new-func
      options.filterFct = new Function(...variables, `return ${options.filterFct}`);
    }
    if (uniqueMFs === true) options.canonizeMF = true;
    if (options.canonizeMF === undefined) options.canonizeMF = true;
    options.ionizations = preprocessIonizations(options.ionizations);
    if (!Array.isArray(keys)) {
      throw new Error('You need to specify an array of strings or arrays');
    }

    // we allow String delimited by ". or ;" instead of an array
    for (let i = 0; i < keys.length; i++) {
      if (!Array.isArray(keys[i])) {
        keys[i] = keys[i].split(/[.,]/);
      }
    }

    // we allow ranges in a string ...
    // problem with ranges is that we need to know to what the range applies
    for (let i = 0; i < keys.length; i++) {
      let parts = keys[i];
      let newParts = [];
      for (let j = 0; j < parts.length; j++) {
        let part = parts[j];
        let comment = part.replace(/^([^$]*\$|.*)/, '');
        part = part.replace(/\$.*/, '').replace(/\s/g, '');
        if (part.match(/[0-9]-[0-9-]/)) {
          // deal with negative numbers
          // there are ranges ... we are in trouble !
          newParts = newParts.concat(processRange(part, comment, {
            limit
          }));
        } else {
          newParts.push(parts[j]); // the part with the comments !
        }
      }

      keys[i] = newParts;
    }
    if (estimate) {
      let total = keys.reduce((previous, current) => previous * current.length, 1);
      return total * options.ionizations.length;
    }
    let results = [];
    let sizes = [];
    let currents = [];
    for (let i = 0; i < keys.length; i++) {
      sizes.push(keys[i].length - 1);
      currents.push(0);
    }
    let position = 0;
    let evolution = 0;
    while (position < currents.length) {
      if (currents[position] < sizes[position]) {
        if (onStep) await onStep(evolution);
        evolution++;
        appendResult(results, currents, keys, options);
        currents[position]++;
        for (let i = 0; i < position; i++) {
          currents[i] = 0;
        }
        position = 0;
      } else {
        position++;
      }
      if (evolution > limit) {
        throw new Error(`You have reached the limit of ${limit}. You could still change this value using the limit option but it is likely to crash.`);
      }
    }
    appendResult(results, currents, keys, options);
    if (uniqueMFs) {
      let uniqueMFsObject = {};
      results.forEach(result => {
        uniqueMFsObject[result.mf + result.ionization.mf] = result;
      });
      results = Object.keys(uniqueMFsObject).map(k => uniqueMFsObject[k]);
    }
    results.sort((a, b) => a.em - b.em);
    return results;
  }
  let ems = {};

  // internal method used as a cache
  function getMonoisotopicMass(mfString) {
    if (!ems[mfString]) {
      // we need to calculate based on the mf but not very often ...
      let mf = new MF(mfString);
      let info = mf.getInfo();
      ems[mfString] = {
        em: info.monoisotopicMass,
        charge: info.charge,
        mw: info.mass,
        unsaturation: (info.unsaturation - 1) * 2,
        atoms: info.atoms
      };
    }
    return ems[mfString];
  }
  function getEMFromParts(parts, currents, ionization) {
    let charge = 0;
    let em = 0;
    let mw = 0;
    let unsaturation = 0;
    let validUnsaturation = true;
    let atoms = {};
    for (let i = 0; i < parts.length; i++) {
      let part = parts[i][currents[i]];
      if (part) {
        let info = getMonoisotopicMass(part);
        charge += info.charge;
        em += info.em;
        mw += info.mw;
        sum$1(atoms, info.atoms);
        if (info.unsaturation && validUnsaturation) {
          unsaturation += info.unsaturation;
        }
      }
    }
    return {
      charge,
      em,
      mw,
      ionization,
      unsaturation: unsaturation / 2 + 1 ,
      atoms
    };
  }
  function appendResult(results, currents, keys) {
    let options = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : {};
    const {
      canonizeMF,
      filter,
      ionizations,
      links = {}
    } = options;
    // this script is designed to combine molecular formula
    // that may contain comments after a "$" sign
    // therefore we should put all the comments at the ned

    if (links.filter) {
      let stars = [];
      for (let i = 0; i < keys.length; i++) {
        let anchors = keys[i][currents[i]].match(/#[0-9]+/g);
        if (anchors) stars.push(...anchors);
      }
      if (stars.length % 2 === 1) return;
      stars = stars.sort();
      for (let i = 0; i < stars.length; i += 2) {
        if (stars[i] !== stars[i + 1]) return;
      }
    }
    for (let ionization of ionizations) {
      let result = getEMFromParts(keys, currents, ionization);
      if (options.filterFct) {
        let variables = [];
        for (let key in options.filterFctVariables) {
          variables.push(currents[options.filterFctVariables[key]]);
        }
        variables.push(result.em, (result.em + ionization.em - ionization.charge * ELECTRON_MASS) / Math.abs(ionization.charge), result.charge + result.ionization.charge, result.unsaturation, result.atoms);
        if (!options.filterFct.apply(null, variables)) continue;
      }
      result.parts = [];
      result.mf = '';
      let comments = [];
      for (let i = 0; i < keys.length; i++) {
        let key = keys[i][currents[i]];
        if (key) {
          if (key.indexOf('$') > -1) {
            comments.push(key.replace(/^[^$]*\$/, ''));
            key = key.replace(/\$.*/, '');
          }
          result.parts[i] = key;
          result.mf += key;
        }
      }
      if (comments.length > 0) {
        result.comment = comments.join(' ');
      }
      let match = msemMatcher(result, filter);
      if (!match) continue;
      result.ms = match.ms;
      result.ionization = match.ionization;
      if (canonizeMF) {
        result.mf = new MF(result.mf).toMF();
      }
      results.push(result);
    }
  }

  /**
   * Generates a database 'generated' from an array of molecular formula
   * @param {array} mfsArray - Array of string or Array of array containing the parts to combine
   * @param {object} [options={}]
   * @param {boolean} [options.estimate=false] - estimate the number of MF without filters
   * @param {string} [options.databaseName='generated']
   * @param {function} [options.onStep] - Callback to do after each step
   * @param {number} [options.limit=10000000] - Maximum number of results
   * @param {boolean} [options.canonizeMF=true] - Canonize molecular formula
   * @param {boolean} [options.uniqueMFs=true] - Force canonization and make MF unique
   * @param {string} [options.ionizations=''] - Comma separated list of ionizations (to charge the molecule)
   * @param {object} [options.filter={}]
   * @param {number} [options.filter.minMass=0] - Minimal monoisotopic mass
   * @param {number} [options.filter.maxMass=+Infinity] - Maximal monoisotopic mass
   * @param {number} [options.filter.minEM=0] - Minimal neutral monoisotopic mass
   * @param {number} [options.filter.maxEM=+Infinity] - Maximal neutral monoisotopic mass
   * @param {number} [options.filter.minMSEM=0] - Minimal observed monoisotopic mass
   * @param {number} [options.filter.maxMSEM=+Infinity] - Maximal observed monoisotopic mass
   * @param {number} [options.filter.minCharge=-Infinity] - Minimal charge
   * @param {number} [options.filter.maxCharge=+Infinity] - Maximal charge
   *
   * @param {object} [options.filter.unsaturation={}]
   * @param {number} [options.filter.unsaturation.min=-Infinity] - Minimal unsaturation
   * @param {number} [options.filter.unsaturation.max=+Infinity] - Maximal unsaturation
   * @param {boolean} [options.filter.unsaturation.onlyInteger=false] - Integer unsaturation
   * @param {boolean} [options.filter.unsaturation.onlyNonInteger=false] - Non integer unsaturation
   * @param {object} [options.filter.atoms] - object of atom:{min, max}
   * @param {function} [options.filter.callback] - a function to filter the MF
   * @param {string}  [options.filterFct] - A string representing a function
   *
   * @example
   *
   * const {EMDB} = require('emdb');
   * let emdb = new EMDB();
   * let array = ['C1-10', 'H1-10'];
   * emdb.fromArray(array); // create a database 'generated' combining all possibilies
   * console.log(emdb.get('generated').length); // 100
   *
   * @example
   * const {EMDB} = require('emdb');
   * let emdb = new EMDB();
   * let array = ['C1-10 H1-10'];
   * emdb.fromArray(array); // create a database 'generated' combining all possibilies
   * console.log(emdb.get('generated').length); // 100
   *
   * @example
   * const {EMDB} = require('emdb');
   * let emdb = new EMDB();
   * // in case of an array of array, one of the group is allwed
   * let array = [['C1-10','H1-10'],'Cl0-1 Br0-1'];
   * emdb.fromArray(array); // create a database 'generated' combining all possibilies
   * console.log(emdb.get('generated').length); // 80
   *
   * @example
   * <script src="https://www.lactame.com/lib/mass-tools/HEAD/mass-tools.js" />
   * <script>
   *   let emdb = new MassTools.EMDB();
   *   let array = ['C1-10', 'H1-10'];
   *   emdb.fromArray(array); // create a database 'generated' combining all possibilities
   *   console.log(emdb.get('generated').length); // 100
   * </script>
   *
   * // from the browser
   */

  async function fromArray(mfsArray) {
    let options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
    return generateMFs(mfsArray, options);
  }

  /**
   * Generates a database 'monoisotopic' from a monoisotopic mass and various options
   * @param {number|string|array}    masses - Monoisotopic mass
   * @param {object}    [options={}]
   * @param {number}    [options.maxIterations=10000000] - Maximum number of iterations
   * @param {function}  [options.onStep] - Callback to do after each step
   * @param {boolean}   [options.allowNeutral=true]
   * @param {boolean}   [options.uniqueMFs=true]
   * @param {number}    [options.limit=1000] - Maximum number of results
   * @param {string}    [options.ionizations=''] - string containing a comma separated list of modifications
   * @param {string}    [options.ranges='C0-100 H0-100 O0-100 N0-100'] - range of mfs to search
   * @param {number}    [options.precision=100] - Allowed mass range based on precision
   * @param {object}    [options.filter={}]
   * @param {number}    [options.filter.minCharge=-Infinity] - Minimal charge
   * @param {number}    [options.filter.maxCharge=+Infinity] - Maximal charge
   * @param {object}    [options.filter.unsaturation={}]
   * @param {number}    [options.filter.unsaturation.min=-Infinity] - Minimal unsaturation
   * @param {number}    [options.filter.unsaturation.max=+Infinity] - Maximal unsaturation
   * @param {number}    [options.filter.unsaturation.onlyInteger=false] - Integer unsaturation
   * @param {number}    [options.filter.unsaturation.onlyNonInteger=false] - Non integer unsaturation
   * @param {object}    [options.filter.atoms] - object of atom:{min, max}
   * @param {function}  [options.filter.callback] - a function to filter the MF
   * @returns {Promise}
   */

  async function fromMonoisotopicMass(masses) {
    let options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
    if (typeof masses === 'string') {
      masses = masses.split(/[ ,;\r\n\t]/).map(Number);
    }
    if (typeof masses === 'number') {
      masses = [masses];
    }
    let results = [];
    for (let mass of masses) {
      results.push(await findMFs(mass, options));
    }
    return {
      mfs: results.map(entry => entry.mfs).flat(),
      info: {
        numberMFEvaluated: results.reduce((sum, current) => sum += current.info.numberMFEvaluated, 0),
        numberResults: results.reduce((sum, current) => sum += current.info.numberResults, 0)
      }
    };
  }

  /**
   * Add a database starting from a peptidic sequence
   *
   * @param {string} [sequencesString] Sequence as a string of 1 letter or 3 letters code. Could also be a correct molecular formula respecting uppercase, lowercase
   * @param {object} [options={}]
   * @param {boolean} [options.estimate=false] - estimate the number of MF without filters
   * @param {function} [options.onStep] - Callback to do after each step
   * @param {number} [options.limit=100000]
   * @param {string} [options.ionizations='']
   * @param {object} [options.info={}]
   * @param {string} [options.info.kind] - rna, ds-dna or dna. Default if contains U: rna, otherwise ds-dna
   * @param {string} [options.info.fivePrime=monophosphate] - alcohol, monophosphate, diphosphate, triphosphate
   * @param {string} [options.info.circular=false]
   * @param {array}   [options.mfsArray=[]]
   * @param {object}  [options.fragmentation={}] Object defining options for fragmentation
   * @param {boolean} [options.fragmentation.a=false] If true allow fragments of type 'a'
   * @param {boolean} [options.fragmentation.ab=false] If true allow fragments of type 'a' minus base
   * @param {boolean} [options.fragmentation.b=false] If true allow fragments of type 'b'
   * @param {boolean} [options.fragmentation.c=false] If true allow fragments of type 'c'
   * @param {boolean} [options.fragmentation.d=false] If true allow fragments of type 'd'
   * @param {boolean} [options.fragmentation.dh2o=false] If true allow fragments of type 'd' with water loss
   * @param {boolean} [options.fragmentation.w=false] If true allow fragments of type 'w'
   * @param {boolean} [options.fragmentation.x=false] If true allow fragments of type 'x'
   * @param {boolean} [options.fragmentation.y=false] If true allow fragments of type 'y'
   * @param {boolean} [options.fragmentation.z=false] If true allow fragments of type 'z'
   * @param {boolean} [options.baseLoss=false] If true allow base loss at all the positions
   *
   * @param {object} [options.filter={}] Object defining options for molecular formula filter
   * @param {number} [options.filter.minMass=0] - Minimal monoisotopic mass
   * @param {number} [options.filter.maxMass=+Infinity] - Maximal monoisotopic mass
   * @param {number} [options.filter.minEM=0] - Minimal neutral monoisotopic mass
   * @param {number} [options.filter.maxEM=+Infinity] - Maximal neutral monoisotopic mass
   * @param {number} [options.filter.minMSEM=0] - Minimal observed monoisotopic mass
   * @param {number} [options.filter.maxMSEM=+Infinity] - Maximal observed monoisotopic mass
   * @param {number} [options.filter.minCharge=-Infinity] - Minimal charge
   * @param {number} [options.filter.maxCharge=+Infinity] - Maximal charge
   * @param {object} [options.filter.unsaturation={}]
   * @param {number} [options.filter.unsaturation.min=-Infinity] - Minimal unsaturation
   * @param {number} [options.filter.unsaturation.max=+Infinity] - Maximal unsaturation
   * @param {boolean} [options.filter.unsaturation.onlyInteger=false] - Integer unsaturation
   * @param {boolean} [options.filter.unsaturation.onlyNonInteger=false] - Non integer unsaturation
   * @returns {Promise}
   */

  async function fromNucleicSequence(sequencesString) {
    let options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
    const {
      mfsArray = [],
      fragmentation = {},
      filter = {},
      ionizations = '',
      info = {},
      estimate = false,
      limit = 100000,
      onStep
    } = options;
    let sequences = sequenceToMF$1(sequencesString, info).split('.');
    let fragmentsArray = sequences.slice();

    // calculate fragmentation
    for (let i = 0; i < sequences.length; i++) {
      let sequence = sequences[i];
      let fragments = generateFragments(sequence, fragmentation);
      if (i === 1) {
        // complementary sequence
        fragments = fragments.map(fragment => fragment.replace(/\$/g, '$cmp-'));
      }
      fragmentsArray = fragmentsArray.concat(fragments);
      if (fragmentation.baseLoss) {
        fragmentsArray = fragmentsArray.concat(baseLoss(sequence));
      }
    }
    mfsArray.push(fragmentsArray);
    let combined = await generateMFs(mfsArray, {
      ionizations,
      filter,
      uniqueMFs: false,
      estimate,
      onStep,
      limit
    });
    if (Array.isArray(combined)) {
      // not an estimation
      combined.forEach(result => {
        result.sequence = groupsToSequence(result.parts.filter(part => part).join(' '));
      });
    }
    return combined;
  }

  function fragmentPeptide(sequence) {
    let options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
    const {
      digestion = {},
      protonation,
      fragmentation,
      protonationPH
    } = options;
    sequence = sequenceToMF(sequence);
    let fragmentsArray = [sequence];
    // do we also have some digest fragments ?
    if (digestion.enzyme) {
      let digests = digestPeptide(sequence, digestion);
      if (options.protonation) {
        digests = chargePeptide(digests, {
          pH: options.protonationPH
        });
      }
      fragmentsArray = fragmentsArray.concat(digests);
    }

    // allow neutral loss
    if (options.allowNeutralLoss) {
      sequence = allowNeutralLoss(sequence);
    }

    // apply protonation
    if (protonation) {
      sequence = chargePeptide(sequence, {
        pH: protonationPH
      });
    }

    // calculate fragmentation
    let fragments = generatePeptideFragments(sequence, fragmentation);
    fragmentsArray = fragmentsArray.concat(fragments);
    return fragmentsArray;
  }

  /**
   * Add a database starting from a peptidic sequence
   *
   * @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
   * @param {object}         [options={}]
   * @param {boolean}        [options.estimate=false] - estimate the number of MF without filters
   * @param {string}         [options.ionizations='']
   * @param {function}       [options.onStep] - Callback to do after each step
   * @param {array}          [options.mfsArray=[]]
   * @param {boolean}        [options.protonation=false]
   * @param {number}         [options.protonationPH=7]
   * @param {boolean}        [options.allowNeutralLoss=false]
   * @param {number}         [options.limit=100000]
   *
   * @param {object}         [options.digestion={}] Object defining options for digestion
   * @param {number}         [options.digestion.minMissed=0] Minimal number of allowed missed cleavage
   * @param {number}         [options.digestion.maxMissed=0] Maximal number of allowed missed cleavage
   * @param {number}         [options.digestion.minResidue=0] Minimal number of residues
   * @param {number}         [options.digestion.maxResidue=+Infinity] Maximal number of residues
   * @param {string}         [options.digestion.enzyme] Mandatory field containing the name of the enzyme among: chymotrypsin, trypsin, glucph4, glucph8, thermolysin, cyanogenbromide
   *
   * @param {object}         [options.fragmentation={}] Object defining options for fragmentation
   * @param {boolean}        [options.fragmentation.a=false] If true allow fragments of type 'a'
   * @param {boolean}        [options.fragmentation.b=false] If true allow fragments of type 'b'
   * @param {boolean}        [options.fragmentation.c=false] If true allow fragments of type 'c'
   * @param {boolean}        [options.fragmentation.x=false] If true allow fragments of type 'x'
   * @param {boolean}        [options.fragmentation.y=false] If true allow fragments of type 'y'
   * @param {boolean}        [options.fragmentation.z=false] If true allow fragments of type 'z'
   * @param {boolean}        [options.fragmentation.ya=false] If true allow fragments of type 'ya'
   * @param {boolean}        [options.fragmentation.yb=false] If true allow fragments of type 'yb'
   * @param {boolean}        [options.fragmentation.yc=false] If true allow fragments of type 'yc'
   * @param {boolean}        [options.fragmentation.zc=false] If true allow fragments of type 'zc'
   * @param {number}         [options.fragmentation.minInternal=0] Minimal internal fragment length
   * @param {number}         [options.fragmentation.maxInternal=+Infinity] Maximal internal fragment length
   *
   * @param {object}         [options.filter={}] Object defining options for molecular formula filter
   * @param {number}         [options.filter.precision=1000] - The precision on the experimental mass
   * @param {number}         [options.filter.targetMass] - Target mass, allows to calculate error and filter results
   * @param {Array<number>}  [options.filter.targetMasses] - Target masses: SORTED array of numbers
   * @param {Array<number>}  [options.filter.targetIntensities] - Target intensities: SORTED array of numbers
   * @param {number}         [options.filter.minEM=0] - Minimal neutral monoisotopic mass
   * @param {number}         [options.filter.maxEM=+Infinity] - Maximal neutral monoisotopic mass
   * @param {number}         [options.filter.minMSEM=0] - Minimal observed monoisotopic mass
   * @param {number}         [options.filter.maxMSEM=+Infinity] - Maximal observed monoisotopic mass
   * @param {number}         [options.filter.minCharge=-Infinity] - Minimal charge
   * @param {number}         [options.filter.maxCharge=+Infinity] - Maximal charge
   * @param {object}         [options.filter.unsaturation={}]
   * @param {number}         [options.filter.unsaturation.min=-Infinity] - Minimal unsaturation
   * @param {number}         [options.filter.unsaturation.max=+Infinity] - Maximal unsaturation
   * @param {boolean}         [options.filter.unsaturation.onlyInteger=false] - Integer unsaturation
   * @param {boolean}         [options.filter.unsaturation.onlyNonInteger=false] - Non integer unsaturation
   * @param {function}       [options.filter.callback] - a function to filter the MF
   * @returns {Promise}
   */

  async function fromPeptidicSequence(sequences) {
    let options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
    const {
      digestion = {},
      mfsArray: originalMFsArray = [],
      allowNeutralLoss = false,
      protonation = false,
      protonationPH = 7,
      fragmentation = {},
      filter = {},
      ionizations = '',
      limit = 100000,
      estimate = false,
      onStep,
      links = {}
    } = options;
    const hasLinked = sequences.includes('#');
    const mfsArrayLinked = JSON.parse(JSON.stringify(originalMFsArray));
    const mfsArrayUnlinked = JSON.parse(JSON.stringify(originalMFsArray));
    const unlinked = [];
    mfsArrayUnlinked.push(unlinked);
    for (const sequence of sequences.split(/[,:]/)) {
      let fragmentsArray = fragmentPeptide(sequence, {
        digestion,
        protonation,
        fragmentation,
        protonationPH,
        allowNeutralLoss
      });
      mfsArrayLinked.push(fragmentsArray.filter(fragment => fragment.includes('#')));
      unlinked.push(...fragmentsArray.filter(fragment => !fragment.includes('#')));
    }
    let combined = await generateMFs(mfsArrayUnlinked, {
      ionizations,
      filter,
      estimate,
      limit,
      onStep,
      links
    });
    if (hasLinked) {
      combined.push(...(await generateMFs(mfsArrayLinked, {
        ionizations,
        filter,
        estimate,
        limit,
        onStep,
        links
      })));
    }
    if (!estimate) {
      combined.forEach(result => {
        result.sequence = groupsToSequence(result.parts.filter(part => part).join(' '));
      });
    }
    return combined;
  }

  /**
   * Generates a database 'generated' from an array of molecular formula
   * @param {string} rangesString - a string representing the range to search
   * @param {object} [options={}]
   * @param {boolean} [options.estimate=false] - estimate the number of MF without filters
   * @param {function} [options.onStep] - Callback to do after each step
   * @param {string} [options.databaseName='generated']
   * @param {number} [options.limit=100000] - Maximum number of results
   * @param {boolean} [options.canonizeMF=true] - Canonize molecular formula
   * @param {boolean} [options.uniqueMFs=true] - Force canonization and make MF unique
   * @param {string} [options.ionizations=''] - Comma separated list of ionizations (to charge the molecule)
   * @param {object} [options.filter={}]
   * @param {number} [options.filter.minMass=0] - Minimal monoisotopic mass
   * @param {number} [options.filter.maxMass=+Infinity] - Maximal monoisotopic mass
   * @param {number} [options.filter.minEM=0] - Minimal neutral monoisotopic mass
   * @param {number} [options.filter.maxEM=+Infinity] - Maximal neutral monoisotopic mass
   * @param {number} [options.filter.minMSEM=0] - Minimal observed monoisotopic mass
   * @param {number} [options.filter.maxMSEM=+Infinity] - Maximal observed monoisotopic mass
   * @param {number} [options.filter.minCharge=-Infinity] - Minimal charge
   * @param {number} [options.filter.maxCharge=+Infinity] - Maximal charge
   *
   * @param {object} [options.filter.unsaturation={}]
   * @param {number} [options.filter.unsaturation.min=-Infinity] - Minimal unsaturation
   * @param {number} [options.filter.unsaturation.max=+Infinity] - Maximal unsaturation
   * @param {boolean} [options.filter.unsaturation.onlyInteger=false] - Integer unsaturation
   * @param {boolean} [options.filter.unsaturation.onlyNonInteger=false] - Non integer unsaturation
   * @param {function} [options.filter.callback] - a function to filter the MF
   * @param {object} [options.filter.atoms] - object of atom:{min, max}
   *
   * @returns {Promise} - list of possible molecular formula
   *
   * @example
   * const {EMDB} = require('emdb');
   * let emdb = new EMDB();
   * // semi-columns separated for combination, comma for 'or'
   * emdb.fromRange('C1-10, H1-10; Cl0-1 Br0-1'); // create a database 'generated' combining all possibilies
   * console.log(emdb.get('generated').length); // 80
   */

  async function fromRange(rangesString) {
    let options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
    let ranges = rangesString.split(/ *[;\r\n] */);
    for (let i = 0; i < ranges.length; i++) {
      let range = ranges[i];
      if (range.includes(',')) {
        ranges[i] = range.split(/ *, */);
      }
    }
    return generateMFs(ranges, options);
  }

  var jszip_min = {exports: {}};

  /*!

  JSZip v3.10.1 - A JavaScript class for generating and reading zip files
  <http://stuartk.com/jszip>

  (c) 2009-2016 Stuart Knightley <stuart [at] stuartk.com>
  Dual licenced under the MIT license or GPLv3. See https://raw.github.com/Stuk/jszip/main/LICENSE.markdown.

  JSZip uses the library pako released under the MIT license :
  https://github.com/nodeca/pako/blob/main/LICENSE
  */
  (function (module, exports) {
    !function (e) {
      module.exports = e();
    }(function () {
      return function s(a, o, h) {
        function u(r, e) {
          if (!o[r]) {
            if (!a[r]) {
              var t = "function" == typeof commonjsRequire && commonjsRequire;
              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 commonjsRequire && commonjsRequire, e = 0; e < h.length; e++) u(h[e]);
        return u;
      }({
        1: [function (e, t, r) {

          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) {

          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) {

          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) {

          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) {

          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) {

          var n = null;
          n = "undefined" != typeof Promise ? Promise : e("lie"), t.exports = {
            Promise: n
          };
        }, {
          lie: 37
        }],
        7: [function (e, t, r) {

          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) {

          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) {

          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) {

          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) {

          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) {

          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) {

          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) {

          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) {

          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) {

          t.exports = e("stream");
        }, {
          stream: void 0
        }],
        17: [function (e, t, r) {

          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) {

          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) {

          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) {

          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) {

          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) {

          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) {

          r.LOCAL_FILE_HEADER = "PK", r.CENTRAL_FILE_HEADER = "PK", r.CENTRAL_DIRECTORY_END = "PK", r.ZIP64_CENTRAL_DIRECTORY_LOCATOR = "PK", r.ZIP64_CENTRAL_DIRECTORY_END = "PK", r.DATA_DESCRIPTOR = "PK\b";
        }, {}],
        24: [function (e, t, r) {

          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) {

          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) {

          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) {

          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) {

          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) {

          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) {

          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) {

          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) {

          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) {

          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) {

          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) {

          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) {

            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 commonjsGlobal ? commonjsGlobal : "undefined" != typeof self ? self : "undefined" != typeof window ? window : {});
        }, {}],
        37: [function (e, t, r) {

          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) {

          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) {

          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) {

          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) {

          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) {

          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) {

          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) {

          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) {

          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) {

          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) {

          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) {

          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) {

          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) {

          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) {

          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) {

          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) {

          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) {

              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 commonjsGlobal ? commonjsGlobal : "undefined" != typeof self ? self : "undefined" != typeof window ? window : {});
        }, {}]
      }, {}, [10])(10);
    });
  })(jszip_min);
  var JSZip = jszip_min.exports;

  var browserPonyfill$1 = {exports: {}};

  (function (module, exports) {
    var global = typeof self !== 'undefined' ? self : commonjsGlobal;
    var __self__ = function () {
      function F() {
        this.fetch = false;
        this.DOMException = global.DOMException;
      }
      F.prototype = global;
      return new F();
    }();
    (function (self) {
      (function (exports) {
        var support = {
          searchParams: 'URLSearchParams' in self,
          iterable: 'Symbol' in self && 'iterator' in Symbol,
          blob: 'FileReader' in self && 'Blob' in self && function () {
            try {
              new Blob();
              return true;
            } catch (e) {
              return false;
            }
          }(),
          formData: 'FormData' in self,
          arrayBuffer: 'ArrayBuffer' in self
        };
        function isDataView(obj) {
          return obj && DataView.prototype.isPrototypeOf(obj);
        }
        if (support.arrayBuffer) {
          var viewClasses = ['[object Int8Array]', '[object Uint8Array]', '[object Uint8ClampedArray]', '[object Int16Array]', '[object Uint16Array]', '[object Int32Array]', '[object Uint32Array]', '[object Float32Array]', '[object Float64Array]'];
          var isArrayBufferView = ArrayBuffer.isView || function (obj) {
            return obj && viewClasses.indexOf(Object.prototype.toString.call(obj)) > -1;
          };
        }
        function normalizeName(name) {
          if (typeof name !== 'string') {
            name = String(name);
          }
          if (/[^a-z0-9\-#$%&'*+.^_`|~]/i.test(name)) {
            throw new TypeError('Invalid character in header field name');
          }
          return name.toLowerCase();
        }
        function normalizeValue(value) {
          if (typeof value !== 'string') {
            value = String(value);
          }
          return value;
        }

        // Build a destructive iterator for the value list
        function iteratorFor(items) {
          var iterator = {
            next: function () {
              var value = items.shift();
              return {
                done: value === undefined,
                value: value
              };
            }
          };
          if (support.iterable) {
            iterator[Symbol.iterator] = function () {
              return iterator;
            };
          }
          return iterator;
        }
        function Headers(headers) {
          this.map = {};
          if (headers instanceof Headers) {
            headers.forEach(function (value, name) {
              this.append(name, value);
            }, this);
          } else if (Array.isArray(headers)) {
            headers.forEach(function (header) {
              this.append(header[0], header[1]);
            }, this);
          } else if (headers) {
            Object.getOwnPropertyNames(headers).forEach(function (name) {
              this.append(name, headers[name]);
            }, this);
          }
        }
        Headers.prototype.append = function (name, value) {
          name = normalizeName(name);
          value = normalizeValue(value);
          var oldValue = this.map[name];
          this.map[name] = oldValue ? oldValue + ', ' + value : value;
        };
        Headers.prototype['delete'] = function (name) {
          delete this.map[normalizeName(name)];
        };
        Headers.prototype.get = function (name) {
          name = normalizeName(name);
          return this.has(name) ? this.map[name] : null;
        };
        Headers.prototype.has = function (name) {
          return this.map.hasOwnProperty(normalizeName(name));
        };
        Headers.prototype.set = function (name, value) {
          this.map[normalizeName(name)] = normalizeValue(value);
        };
        Headers.prototype.forEach = function (callback, thisArg) {
          for (var name in this.map) {
            if (this.map.hasOwnProperty(name)) {
              callback.call(thisArg, this.map[name], name, this);
            }
          }
        };
        Headers.prototype.keys = function () {
          var items = [];
          this.forEach(function (value, name) {
            items.push(name);
          });
          return iteratorFor(items);
        };
        Headers.prototype.values = function () {
          var items = [];
          this.forEach(function (value) {
            items.push(value);
          });
          return iteratorFor(items);
        };
        Headers.prototype.entries = function () {
          var items = [];
          this.forEach(function (value, name) {
            items.push([name, value]);
          });
          return iteratorFor(items);
        };
        if (support.iterable) {
          Headers.prototype[Symbol.iterator] = Headers.prototype.entries;
        }
        function consumed(body) {
          if (body.bodyUsed) {
            return Promise.reject(new TypeError('Already read'));
          }
          body.bodyUsed = true;
        }
        function fileReaderReady(reader) {
          return new Promise(function (resolve, reject) {
            reader.onload = function () {
              resolve(reader.result);
            };
            reader.onerror = function () {
              reject(reader.error);
            };
          });
        }
        function readBlobAsArrayBuffer(blob) {
          var reader = new FileReader();
          var promise = fileReaderReady(reader);
          reader.readAsArrayBuffer(blob);
          return promise;
        }
        function readBlobAsText(blob) {
          var reader = new FileReader();
          var promise = fileReaderReady(reader);
          reader.readAsText(blob);
          return promise;
        }
        function readArrayBufferAsText(buf) {
          var view = new Uint8Array(buf);
          var chars = new Array(view.length);
          for (var i = 0; i < view.length; i++) {
            chars[i] = String.fromCharCode(view[i]);
          }
          return chars.join('');
        }
        function bufferClone(buf) {
          if (buf.slice) {
            return buf.slice(0);
          } else {
            var view = new Uint8Array(buf.byteLength);
            view.set(new Uint8Array(buf));
            return view.buffer;
          }
        }
        function Body() {
          this.bodyUsed = false;
          this._initBody = function (body) {
            this._bodyInit = body;
            if (!body) {
              this._bodyText = '';
            } else if (typeof body === 'string') {
              this._bodyText = body;
            } else if (support.blob && Blob.prototype.isPrototypeOf(body)) {
              this._bodyBlob = body;
            } else if (support.formData && FormData.prototype.isPrototypeOf(body)) {
              this._bodyFormData = body;
            } else if (support.searchParams && URLSearchParams.prototype.isPrototypeOf(body)) {
              this._bodyText = body.toString();
            } else if (support.arrayBuffer && support.blob && isDataView(body)) {
              this._bodyArrayBuffer = bufferClone(body.buffer);
              // IE 10-11 can't handle a DataView body.
              this._bodyInit = new Blob([this._bodyArrayBuffer]);
            } else if (support.arrayBuffer && (ArrayBuffer.prototype.isPrototypeOf(body) || isArrayBufferView(body))) {
              this._bodyArrayBuffer = bufferClone(body);
            } else {
              this._bodyText = body = Object.prototype.toString.call(body);
            }
            if (!this.headers.get('content-type')) {
              if (typeof body === 'string') {
                this.headers.set('content-type', 'text/plain;charset=UTF-8');
              } else if (this._bodyBlob && this._bodyBlob.type) {
                this.headers.set('content-type', this._bodyBlob.type);
              } else if (support.searchParams && URLSearchParams.prototype.isPrototypeOf(body)) {
                this.headers.set('content-type', 'application/x-www-form-urlencoded;charset=UTF-8');
              }
            }
          };
          if (support.blob) {
            this.blob = function () {
              var rejected = consumed(this);
              if (rejected) {
                return rejected;
              }
              if (this._bodyBlob) {
                return Promise.resolve(this._bodyBlob);
              } else if (this._bodyArrayBuffer) {
                return Promise.resolve(new Blob([this._bodyArrayBuffer]));
              } else if (this._bodyFormData) {
                throw new Error('could not read FormData body as blob');
              } else {
                return Promise.resolve(new Blob([this._bodyText]));
              }
            };
            this.arrayBuffer = function () {
              if (this._bodyArrayBuffer) {
                return consumed(this) || Promise.resolve(this._bodyArrayBuffer);
              } else {
                return this.blob().then(readBlobAsArrayBuffer);
              }
            };
          }
          this.text = function () {
            var rejected = consumed(this);
            if (rejected) {
              return rejected;
            }
            if (this._bodyBlob) {
              return readBlobAsText(this._bodyBlob);
            } else if (this._bodyArrayBuffer) {
              return Promise.resolve(readArrayBufferAsText(this._bodyArrayBuffer));
            } else if (this._bodyFormData) {
              throw new Error('could not read FormData body as text');
            } else {
              return Promise.resolve(this._bodyText);
            }
          };
          if (support.formData) {
            this.formData = function () {
              return this.text().then(decode);
            };
          }
          this.json = function () {
            return this.text().then(JSON.parse);
          };
          return this;
        }

        // HTTP methods whose capitalization should be normalized
        var methods = ['DELETE', 'GET', 'HEAD', 'OPTIONS', 'POST', 'PUT'];
        function normalizeMethod(method) {
          var upcased = method.toUpperCase();
          return methods.indexOf(upcased) > -1 ? upcased : method;
        }
        function Request(input, options) {
          options = options || {};
          var body = options.body;
          if (input instanceof Request) {
            if (input.bodyUsed) {
              throw new TypeError('Already read');
            }
            this.url = input.url;
            this.credentials = input.credentials;
            if (!options.headers) {
              this.headers = new Headers(input.headers);
            }
            this.method = input.method;
            this.mode = input.mode;
            this.signal = input.signal;
            if (!body && input._bodyInit != null) {
              body = input._bodyInit;
              input.bodyUsed = true;
            }
          } else {
            this.url = String(input);
          }
          this.credentials = options.credentials || this.credentials || 'same-origin';
          if (options.headers || !this.headers) {
            this.headers = new Headers(options.headers);
          }
          this.method = normalizeMethod(options.method || this.method || 'GET');
          this.mode = options.mode || this.mode || null;
          this.signal = options.signal || this.signal;
          this.referrer = null;
          if ((this.method === 'GET' || this.method === 'HEAD') && body) {
            throw new TypeError('Body not allowed for GET or HEAD requests');
          }
          this._initBody(body);
        }
        Request.prototype.clone = function () {
          return new Request(this, {
            body: this._bodyInit
          });
        };
        function decode(body) {
          var form = new FormData();
          body.trim().split('&').forEach(function (bytes) {
            if (bytes) {
              var split = bytes.split('=');
              var name = split.shift().replace(/\+/g, ' ');
              var value = split.join('=').replace(/\+/g, ' ');
              form.append(decodeURIComponent(name), decodeURIComponent(value));
            }
          });
          return form;
        }
        function parseHeaders(rawHeaders) {
          var headers = new Headers();
          // Replace instances of \r\n and \n followed by at least one space or horizontal tab with a space
          // https://tools.ietf.org/html/rfc7230#section-3.2
          var preProcessedHeaders = rawHeaders.replace(/\r?\n[\t ]+/g, ' ');
          preProcessedHeaders.split(/\r?\n/).forEach(function (line) {
            var parts = line.split(':');
            var key = parts.shift().trim();
            if (key) {
              var value = parts.join(':').trim();
              headers.append(key, value);
            }
          });
          return headers;
        }
        Body.call(Request.prototype);
        function Response(bodyInit, options) {
          if (!options) {
            options = {};
          }
          this.type = 'default';
          this.status = options.status === undefined ? 200 : options.status;
          this.ok = this.status >= 200 && this.status < 300;
          this.statusText = 'statusText' in options ? options.statusText : 'OK';
          this.headers = new Headers(options.headers);
          this.url = options.url || '';
          this._initBody(bodyInit);
        }
        Body.call(Response.prototype);
        Response.prototype.clone = function () {
          return new Response(this._bodyInit, {
            status: this.status,
            statusText: this.statusText,
            headers: new Headers(this.headers),
            url: this.url
          });
        };
        Response.error = function () {
          var response = new Response(null, {
            status: 0,
            statusText: ''
          });
          response.type = 'error';
          return response;
        };
        var redirectStatuses = [301, 302, 303, 307, 308];
        Response.redirect = function (url, status) {
          if (redirectStatuses.indexOf(status) === -1) {
            throw new RangeError('Invalid status code');
          }
          return new Response(null, {
            status: status,
            headers: {
              location: url
            }
          });
        };
        exports.DOMException = self.DOMException;
        try {
          new exports.DOMException();
        } catch (err) {
          exports.DOMException = function (message, name) {
            this.message = message;
            this.name = name;
            var error = Error(message);
            this.stack = error.stack;
          };
          exports.DOMException.prototype = Object.create(Error.prototype);
          exports.DOMException.prototype.constructor = exports.DOMException;
        }
        function fetch(input, init) {
          return new Promise(function (resolve, reject) {
            var request = new Request(input, init);
            if (request.signal && request.signal.aborted) {
              return reject(new exports.DOMException('Aborted', 'AbortError'));
            }
            var xhr = new XMLHttpRequest();
            function abortXhr() {
              xhr.abort();
            }
            xhr.onload = function () {
              var options = {
                status: xhr.status,
                statusText: xhr.statusText,
                headers: parseHeaders(xhr.getAllResponseHeaders() || '')
              };
              options.url = 'responseURL' in xhr ? xhr.responseURL : options.headers.get('X-Request-URL');
              var body = 'response' in xhr ? xhr.response : xhr.responseText;
              resolve(new Response(body, options));
            };
            xhr.onerror = function () {
              reject(new TypeError('Network request failed'));
            };
            xhr.ontimeout = function () {
              reject(new TypeError('Network request failed'));
            };
            xhr.onabort = function () {
              reject(new exports.DOMException('Aborted', 'AbortError'));
            };
            xhr.open(request.method, request.url, true);
            if (request.credentials === 'include') {
              xhr.withCredentials = true;
            } else if (request.credentials === 'omit') {
              xhr.withCredentials = false;
            }
            if ('responseType' in xhr && support.blob) {
              xhr.responseType = 'blob';
            }
            request.headers.forEach(function (value, name) {
              xhr.setRequestHeader(name, value);
            });
            if (request.signal) {
              request.signal.addEventListener('abort', abortXhr);
              xhr.onreadystatechange = function () {
                // DONE (success or failure)
                if (xhr.readyState === 4) {
                  request.signal.removeEventListener('abort', abortXhr);
                }
              };
            }
            xhr.send(typeof request._bodyInit === 'undefined' ? null : request._bodyInit);
          });
        }
        fetch.polyfill = true;
        if (!self.fetch) {
          self.fetch = fetch;
          self.Headers = Headers;
          self.Request = Request;
          self.Response = Response;
        }
        exports.Headers = Headers;
        exports.Request = Request;
        exports.Response = Response;
        exports.fetch = fetch;
        Object.defineProperty(exports, '__esModule', {
          value: true
        });
        return exports;
      })({});
    })(__self__);
    __self__.fetch.ponyfill = true;
    // Remove "polyfill" property added by whatwg-fetch
    delete __self__.fetch.polyfill;
    // Choose between native implementation (global) or custom implementation (__self__)
    // var ctx = global.fetch ? global : __self__;
    var ctx = __self__; // this line disable service worker support temporarily
    exports = ctx.fetch; // To enable: import fetch from 'cross-fetch'
    exports.default = ctx.fetch; // For TypeScript consumers without esModuleInterop.
    exports.fetch = ctx.fetch; // To enable: import {fetch} from 'cross-fetch'
    exports.Headers = ctx.Headers;
    exports.Request = ctx.Request;
    exports.Response = ctx.Response;
    module.exports = exports;
  })(browserPonyfill$1, browserPonyfill$1.exports);
  var crossFetch = /*@__PURE__*/getDefaultExportFromCjs(browserPonyfill$1.exports);

  async function fetchArrayBuffer(url) {
    const result = await crossFetch(url);
    return result.arrayBuffer();
  }

  const loadingPromises$1 = {};
  async function loadCommercials() {
    let options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
    const {
      url = 'https://couch.cheminfo.org/cheminfo-public/d2eb480198c80275a1d05dd3609414f9/upload/commercials.zip'
    } = options;
    if (!loadingPromises$1[url]) {
      loadingPromises$1[url] = fetchArrayBuffer(url);
    }
    const buffer = await loadingPromises$1[url];
    const jsZip = new JSZip();
    let zip = await jsZip.loadAsync(buffer);
    let fileData = await zip.files['commercials.json'].async('string');
    let data = JSON.parse(fileData);
    data.sort((a, b) => a.em - b.em);
    return data;
  }

  var papaparse_min = {exports: {}};

  /* @license
  Papa Parse
  v5.3.2
  https://github.com/mholt/PapaParse
  License: MIT
  */
  (function (module, exports) {
    !function (e, t) {
      module.exports = t() ;
    }(commonjsGlobal, function s() {

      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;
    });
  })(papaparse_min);
  var Papa = papaparse_min.exports;

  var browserPonyfill = {exports: {}};

  (function (module, exports) {
    var global = typeof self !== 'undefined' ? self : commonjsGlobal;
    var __self__ = function () {
      function F() {
        this.fetch = false;
        this.DOMException = global.DOMException;
      }
      F.prototype = global;
      return new F();
    }();
    (function (self) {
      (function (exports) {
        var support = {
          searchParams: 'URLSearchParams' in self,
          iterable: 'Symbol' in self && 'iterator' in Symbol,
          blob: 'FileReader' in self && 'Blob' in self && function () {
            try {
              new Blob();
              return true;
            } catch (e) {
              return false;
            }
          }(),
          formData: 'FormData' in self,
          arrayBuffer: 'ArrayBuffer' in self
        };
        function isDataView(obj) {
          return obj && DataView.prototype.isPrototypeOf(obj);
        }
        if (support.arrayBuffer) {
          var viewClasses = ['[object Int8Array]', '[object Uint8Array]', '[object Uint8ClampedArray]', '[object Int16Array]', '[object Uint16Array]', '[object Int32Array]', '[object Uint32Array]', '[object Float32Array]', '[object Float64Array]'];
          var isArrayBufferView = ArrayBuffer.isView || function (obj) {
            return obj && viewClasses.indexOf(Object.prototype.toString.call(obj)) > -1;
          };
        }
        function normalizeName(name) {
          if (typeof name !== 'string') {
            name = String(name);
          }
          if (/[^a-z0-9\-#$%&'*+.^_`|~]/i.test(name)) {
            throw new TypeError('Invalid character in header field name');
          }
          return name.toLowerCase();
        }
        function normalizeValue(value) {
          if (typeof value !== 'string') {
            value = String(value);
          }
          return value;
        }

        // Build a destructive iterator for the value list
        function iteratorFor(items) {
          var iterator = {
            next: function () {
              var value = items.shift();
              return {
                done: value === undefined,
                value: value
              };
            }
          };
          if (support.iterable) {
            iterator[Symbol.iterator] = function () {
              return iterator;
            };
          }
          return iterator;
        }
        function Headers(headers) {
          this.map = {};
          if (headers instanceof Headers) {
            headers.forEach(function (value, name) {
              this.append(name, value);
            }, this);
          } else if (Array.isArray(headers)) {
            headers.forEach(function (header) {
              this.append(header[0], header[1]);
            }, this);
          } else if (headers) {
            Object.getOwnPropertyNames(headers).forEach(function (name) {
              this.append(name, headers[name]);
            }, this);
          }
        }
        Headers.prototype.append = function (name, value) {
          name = normalizeName(name);
          value = normalizeValue(value);
          var oldValue = this.map[name];
          this.map[name] = oldValue ? oldValue + ', ' + value : value;
        };
        Headers.prototype['delete'] = function (name) {
          delete this.map[normalizeName(name)];
        };
        Headers.prototype.get = function (name) {
          name = normalizeName(name);
          return this.has(name) ? this.map[name] : null;
        };
        Headers.prototype.has = function (name) {
          return this.map.hasOwnProperty(normalizeName(name));
        };
        Headers.prototype.set = function (name, value) {
          this.map[normalizeName(name)] = normalizeValue(value);
        };
        Headers.prototype.forEach = function (callback, thisArg) {
          for (var name in this.map) {
            if (this.map.hasOwnProperty(name)) {
              callback.call(thisArg, this.map[name], name, this);
            }
          }
        };
        Headers.prototype.keys = function () {
          var items = [];
          this.forEach(function (value, name) {
            items.push(name);
          });
          return iteratorFor(items);
        };
        Headers.prototype.values = function () {
          var items = [];
          this.forEach(function (value) {
            items.push(value);
          });
          return iteratorFor(items);
        };
        Headers.prototype.entries = function () {
          var items = [];
          this.forEach(function (value, name) {
            items.push([name, value]);
          });
          return iteratorFor(items);
        };
        if (support.iterable) {
          Headers.prototype[Symbol.iterator] = Headers.prototype.entries;
        }
        function consumed(body) {
          if (body.bodyUsed) {
            return Promise.reject(new TypeError('Already read'));
          }
          body.bodyUsed = true;
        }
        function fileReaderReady(reader) {
          return new Promise(function (resolve, reject) {
            reader.onload = function () {
              resolve(reader.result);
            };
            reader.onerror = function () {
              reject(reader.error);
            };
          });
        }
        function readBlobAsArrayBuffer(blob) {
          var reader = new FileReader();
          var promise = fileReaderReady(reader);
          reader.readAsArrayBuffer(blob);
          return promise;
        }
        function readBlobAsText(blob) {
          var reader = new FileReader();
          var promise = fileReaderReady(reader);
          reader.readAsText(blob);
          return promise;
        }
        function readArrayBufferAsText(buf) {
          var view = new Uint8Array(buf);
          var chars = new Array(view.length);
          for (var i = 0; i < view.length; i++) {
            chars[i] = String.fromCharCode(view[i]);
          }
          return chars.join('');
        }
        function bufferClone(buf) {
          if (buf.slice) {
            return buf.slice(0);
          } else {
            var view = new Uint8Array(buf.byteLength);
            view.set(new Uint8Array(buf));
            return view.buffer;
          }
        }
        function Body() {
          this.bodyUsed = false;
          this._initBody = function (body) {
            this._bodyInit = body;
            if (!body) {
              this._bodyText = '';
            } else if (typeof body === 'string') {
              this._bodyText = body;
            } else if (support.blob && Blob.prototype.isPrototypeOf(body)) {
              this._bodyBlob = body;
            } else if (support.formData && FormData.prototype.isPrototypeOf(body)) {
              this._bodyFormData = body;
            } else if (support.searchParams && URLSearchParams.prototype.isPrototypeOf(body)) {
              this._bodyText = body.toString();
            } else if (support.arrayBuffer && support.blob && isDataView(body)) {
              this._bodyArrayBuffer = bufferClone(body.buffer);
              // IE 10-11 can't handle a DataView body.
              this._bodyInit = new Blob([this._bodyArrayBuffer]);
            } else if (support.arrayBuffer && (ArrayBuffer.prototype.isPrototypeOf(body) || isArrayBufferView(body))) {
              this._bodyArrayBuffer = bufferClone(body);
            } else {
              this._bodyText = body = Object.prototype.toString.call(body);
            }
            if (!this.headers.get('content-type')) {
              if (typeof body === 'string') {
                this.headers.set('content-type', 'text/plain;charset=UTF-8');
              } else if (this._bodyBlob && this._bodyBlob.type) {
                this.headers.set('content-type', this._bodyBlob.type);
              } else if (support.searchParams && URLSearchParams.prototype.isPrototypeOf(body)) {
                this.headers.set('content-type', 'application/x-www-form-urlencoded;charset=UTF-8');
              }
            }
          };
          if (support.blob) {
            this.blob = function () {
              var rejected = consumed(this);
              if (rejected) {
                return rejected;
              }
              if (this._bodyBlob) {
                return Promise.resolve(this._bodyBlob);
              } else if (this._bodyArrayBuffer) {
                return Promise.resolve(new Blob([this._bodyArrayBuffer]));
              } else if (this._bodyFormData) {
                throw new Error('could not read FormData body as blob');
              } else {
                return Promise.resolve(new Blob([this._bodyText]));
              }
            };
            this.arrayBuffer = function () {
              if (this._bodyArrayBuffer) {
                return consumed(this) || Promise.resolve(this._bodyArrayBuffer);
              } else {
                return this.blob().then(readBlobAsArrayBuffer);
              }
            };
          }
          this.text = function () {
            var rejected = consumed(this);
            if (rejected) {
              return rejected;
            }
            if (this._bodyBlob) {
              return readBlobAsText(this._bodyBlob);
            } else if (this._bodyArrayBuffer) {
              return Promise.resolve(readArrayBufferAsText(this._bodyArrayBuffer));
            } else if (this._bodyFormData) {
              throw new Error('could not read FormData body as text');
            } else {
              return Promise.resolve(this._bodyText);
            }
          };
          if (support.formData) {
            this.formData = function () {
              return this.text().then(decode);
            };
          }
          this.json = function () {
            return this.text().then(JSON.parse);
          };
          return this;
        }

        // HTTP methods whose capitalization should be normalized
        var methods = ['DELETE', 'GET', 'HEAD', 'OPTIONS', 'POST', 'PUT'];
        function normalizeMethod(method) {
          var upcased = method.toUpperCase();
          return methods.indexOf(upcased) > -1 ? upcased : method;
        }
        function Request(input, options) {
          options = options || {};
          var body = options.body;
          if (input instanceof Request) {
            if (input.bodyUsed) {
              throw new TypeError('Already read');
            }
            this.url = input.url;
            this.credentials = input.credentials;
            if (!options.headers) {
              this.headers = new Headers(input.headers);
            }
            this.method = input.method;
            this.mode = input.mode;
            this.signal = input.signal;
            if (!body && input._bodyInit != null) {
              body = input._bodyInit;
              input.bodyUsed = true;
            }
          } else {
            this.url = String(input);
          }
          this.credentials = options.credentials || this.credentials || 'same-origin';
          if (options.headers || !this.headers) {
            this.headers = new Headers(options.headers);
          }
          this.method = normalizeMethod(options.method || this.method || 'GET');
          this.mode = options.mode || this.mode || null;
          this.signal = options.signal || this.signal;
          this.referrer = null;
          if ((this.method === 'GET' || this.method === 'HEAD') && body) {
            throw new TypeError('Body not allowed for GET or HEAD requests');
          }
          this._initBody(body);
        }
        Request.prototype.clone = function () {
          return new Request(this, {
            body: this._bodyInit
          });
        };
        function decode(body) {
          var form = new FormData();
          body.trim().split('&').forEach(function (bytes) {
            if (bytes) {
              var split = bytes.split('=');
              var name = split.shift().replace(/\+/g, ' ');
              var value = split.join('=').replace(/\+/g, ' ');
              form.append(decodeURIComponent(name), decodeURIComponent(value));
            }
          });
          return form;
        }
        function parseHeaders(rawHeaders) {
          var headers = new Headers();
          // Replace instances of \r\n and \n followed by at least one space or horizontal tab with a space
          // https://tools.ietf.org/html/rfc7230#section-3.2
          var preProcessedHeaders = rawHeaders.replace(/\r?\n[\t ]+/g, ' ');
          preProcessedHeaders.split(/\r?\n/).forEach(function (line) {
            var parts = line.split(':');
            var key = parts.shift().trim();
            if (key) {
              var value = parts.join(':').trim();
              headers.append(key, value);
            }
          });
          return headers;
        }
        Body.call(Request.prototype);
        function Response(bodyInit, options) {
          if (!options) {
            options = {};
          }
          this.type = 'default';
          this.status = options.status === undefined ? 200 : options.status;
          this.ok = this.status >= 200 && this.status < 300;
          this.statusText = 'statusText' in options ? options.statusText : 'OK';
          this.headers = new Headers(options.headers);
          this.url = options.url || '';
          this._initBody(bodyInit);
        }
        Body.call(Response.prototype);
        Response.prototype.clone = function () {
          return new Response(this._bodyInit, {
            status: this.status,
            statusText: this.statusText,
            headers: new Headers(this.headers),
            url: this.url
          });
        };
        Response.error = function () {
          var response = new Response(null, {
            status: 0,
            statusText: ''
          });
          response.type = 'error';
          return response;
        };
        var redirectStatuses = [301, 302, 303, 307, 308];
        Response.redirect = function (url, status) {
          if (redirectStatuses.indexOf(status) === -1) {
            throw new RangeError('Invalid status code');
          }
          return new Response(null, {
            status: status,
            headers: {
              location: url
            }
          });
        };
        exports.DOMException = self.DOMException;
        try {
          new exports.DOMException();
        } catch (err) {
          exports.DOMException = function (message, name) {
            this.message = message;
            this.name = name;
            var error = Error(message);
            this.stack = error.stack;
          };
          exports.DOMException.prototype = Object.create(Error.prototype);
          exports.DOMException.prototype.constructor = exports.DOMException;
        }
        function fetch(input, init) {
          return new Promise(function (resolve, reject) {
            var request = new Request(input, init);
            if (request.signal && request.signal.aborted) {
              return reject(new exports.DOMException('Aborted', 'AbortError'));
            }
            var xhr = new XMLHttpRequest();
            function abortXhr() {
              xhr.abort();
            }
            xhr.onload = function () {
              var options = {
                status: xhr.status,
                statusText: xhr.statusText,
                headers: parseHeaders(xhr.getAllResponseHeaders() || '')
              };
              options.url = 'responseURL' in xhr ? xhr.responseURL : options.headers.get('X-Request-URL');
              var body = 'response' in xhr ? xhr.response : xhr.responseText;
              resolve(new Response(body, options));
            };
            xhr.onerror = function () {
              reject(new TypeError('Network request failed'));
            };
            xhr.ontimeout = function () {
              reject(new TypeError('Network request failed'));
            };
            xhr.onabort = function () {
              reject(new exports.DOMException('Aborted', 'AbortError'));
            };
            xhr.open(request.method, request.url, true);
            if (request.credentials === 'include') {
              xhr.withCredentials = true;
            } else if (request.credentials === 'omit') {
              xhr.withCredentials = false;
            }
            if ('responseType' in xhr && support.blob) {
              xhr.responseType = 'blob';
            }
            request.headers.forEach(function (value, name) {
              xhr.setRequestHeader(name, value);
            });
            if (request.signal) {
              request.signal.addEventListener('abort', abortXhr);
              xhr.onreadystatechange = function () {
                // DONE (success or failure)
                if (xhr.readyState === 4) {
                  request.signal.removeEventListener('abort', abortXhr);
                }
              };
            }
            xhr.send(typeof request._bodyInit === 'undefined' ? null : request._bodyInit);
          });
        }
        fetch.polyfill = true;
        if (!self.fetch) {
          self.fetch = fetch;
          self.Headers = Headers;
          self.Request = Request;
          self.Response = Response;
        }
        exports.Headers = Headers;
        exports.Request = Request;
        exports.Response = Response;
        exports.fetch = fetch;
        Object.defineProperty(exports, '__esModule', {
          value: true
        });
        return exports;
      })({});
    })(__self__);
    __self__.fetch.ponyfill = true;
    // Remove "polyfill" property added by whatwg-fetch
    delete __self__.fetch.polyfill;
    // Choose between native implementation (global) or custom implementation (__self__)
    // var ctx = global.fetch ? global : __self__;
    var ctx = __self__; // this line disable service worker support temporarily
    exports = ctx.fetch; // To enable: import fetch from 'cross-fetch'
    exports.default = ctx.fetch; // For TypeScript consumers without esModuleInterop.
    exports.fetch = ctx.fetch; // To enable: import {fetch} from 'cross-fetch'
    exports.Headers = ctx.Headers;
    exports.Request = ctx.Request;
    exports.Response = ctx.Response;
    module.exports = exports;
  })(browserPonyfill, browserPonyfill.exports);
  var fetch = /*@__PURE__*/getDefaultExportFromCjs(browserPonyfill.exports);

  // eslint-disable-next-line no-shadow
  async function fetchText(url) {
    const result = await fetch(url);
    if (result.status !== 200) {
      throw new Error(String(result.status));
    }
    return result.text();
  }

  async function mfFromGoogleSheet(url) {
    let options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
    let {
      urlReferences
    } = options;
    if (urlReferences) {
      let results = await Promise.all([fetchText(url), fetchText(urlReferences)]);
      return parse(results[0], results[1]);
    } else {
      let result = await fetchText(url);
      return parse(result);
    }
    async function parse(tsv, tsvReferences) {
      let parsed = Papa.parse(tsv, {
        delimiter: '\t',
        header: true
      });
      let fields = parsed.meta.fields;
      let infoFields = fields.filter(a => !['mf', 'modif', 'ESI', 'MALDI', 'positive', 'negative'].includes(a));
      let formulas = parsed.data;
      let references = {};
      if (tsvReferences) {
        let referencesArray = Papa.parse(tsvReferences, {
          delimiter: '\t',
          header: true
        }).data;
        referencesArray.forEach(r => {
          references[r.label] = r;
        });
      }
      let results = [];
      for (let formula of formulas) {
        if (tsvReferences) {
          // we add references
          let refs = formula.references.split(/[ ,]+/);
          formula.references = [];
          for (let ref of refs) {
            formula.references.push(references[ref]);
          }
        }
        // we need to calculate all the possibilities
        try {
          let mfs = await generateMFs([formula.mf], {
            ionizations: formula.modif
          });
          for (let mf of mfs) {
            mf.info = {};
            for (let infoField of infoFields) {
              mf.info[infoField] = formula[infoField];
            }
            if (!formula.ESI && !formula.MALDI && !formula.positive && !formula.negative) {
              mf.filter = {
                ESI: true,
                MALDI: true,
                positive: true,
                negative: true
              };
            } else {
              mf.filter = {
                ESI: formula.ESI === 'X',
                MALDI: formula.MALDI === 'X',
                positive: formula.positive === 'X',
                negative: formula.negative === 'X'
              };
            }
            mf.mf = new MF(mf.mf).toMF();
            results.push(mf);
          }
        } catch (e) {
          // eslint-disable-next-line no-console
          console.warn('Non parsable molecular formula: ', formula.mf, formula.modif, e.toString());
        }
      }
      results = results.filter(a => {
        return a.ms.em !== 0;
      });
      results.sort((a, b) => {
        return a.ms.em - b.ms.em;
      });
      let uniqueResults = [results[0]];
      for (let i = 1; i < results.length; i++) {
        if (results[i - 1].ms.em !== results[i].ms.em) {
          uniqueResults.push(results[i]);
        }
      }
      return uniqueResults;
    }
  }

  async function loadGoogleSheet() {
    let options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
    let {
      refUUID = '1C_H9aiJyu9M9in7sHMOaz-d3Sv758rE72oLxEKH9ioA',
      uuid = '1LrJCl9-xSZKhGA9Y8nKVkYwB-mEOHBkTXg5qYXeFpZY'
    } = options;
    if (options.uuid && !options.refUUID) refUUID = '';
    let url = `https://googledocs.cheminfo.org/spreadsheets/d/${uuid}/export?format=tsv`;
    let refURL = refUUID ? `https://googledocs.cheminfo.org/spreadsheets/d/${refUUID}/export?format=tsv` : '';
    let data = await mfFromGoogleSheet(url, refURL);
    data.sort((a, b) => a.em - b.em);
    return data;
  }

  const loadingPromises = {};
  async function loadKnapSack() {
    let options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
    const {
      url = 'https://couch.cheminfo.org/cheminfo-public/d2eb480198c80275a1d05dd3609414f9/upload/ms.zip'
    } = options;
    if (!loadingPromises[url]) {
      loadingPromises[url] = fetchArrayBuffer(url);
    }
    const buffer = await loadingPromises[url];
    const jsZip = new JSZip();
    let zip = await jsZip.loadAsync(buffer);
    let fileData = await zip.files['ms.json'].async('string');
    let data = JSON.parse(fileData);
    data.forEach(d => {
      d.url = `http://kanaya.naist.jp/knapsack_jsp/information.jsp?word=${d.id}`;
    });
    data.sort((a, b) => a.em - b.em);
    return data;
  }

  /**
      Searching by various criteria. This mass will not take into account the mass
      of the mass of the electron
  * @param {object}   [filter={}]
  * @param {number}   [filter.minMW=0] - Minimal molecular weight
  * @param {number}   [filter.maxMW=+Infinity] - Maximal molecular weight
  * @param {number}   [filter.minEM=0] - Minimal monoisotopic mass
  * @param {number}   [filter.maxEM=+Infinity] - Maximal monoisotopic mass
  * @param {number}   [filter.minCharge=-Infinity] - Minimal charge
  * @param {number}   [filter.maxCharge=+Infinity] - Maximal charge
  * @param {object}   [filter.unsaturation={}]
  * @param {number}   [filter.unsaturation.min=-Infinity] - Minimal unsaturation
  * @param {number}   [filter.unsaturation.max=+Infinity] - Maximal unsaturation
  * @param {number}   [filter.unsaturation.onlyInteger=false] - Integer unsaturation
  * @param {number}   [filter.unsaturation.onlyNonInteger=false] - Non integer unsaturation
  * @param {object}   [filter.atoms] - object of atom:{min, max}

  * @param {object}   [options={}]
  * @param {array}    [options.databases] - an array containing the name of the databases so search, by default all
  * @param {boolean}  [options.flatten=false] - should we return the array as a flat result
  */

  function search(emdb, filter) {
    let options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
    let {
      databases = Object.keys(emdb.databases),
      flatten = false
    } = options;
    let results = {};
    for (let database of databases) {
      results[database] = emdb.databases[database].filter(entry => generalMatcher(entry, filter));
    }
    if (flatten) {
      let flattenResults = [];
      for (let database of databases) {
        for (let entry of results[database]) {
          entry.database = database;
          flattenResults.push(entry);
        }
      }
      return flattenResults;
    } else {
      return results;
    }
  }

  /**
  Search for an experimental monoisotopic mass
  * @param {number}   msem - The observed monoisotopic mass
  * @param {object}   [options={}]
  * @param {array}    [options.databases] - an array containing the name of the databases so search, by default all
  * @param {boolean}  [options.flatten=false] - should we return the array as a flat result
  * @param {string}   [options.ionizations] - list the allowed ionizations possibilities
  * @param {object}   [options.filter={}]
  * @param {number}         [options.filter.targetMass] - Target mass, allows to calculate error and filter results
  * @param {Array<number>}  [options.filter.targetMasses] - Target masses: SORTED array of numbers
  * @param {Array<number>}  [options.filter.targetIntensities] - Target intensities: SORTED array of numbers
  * @param {number}         [options.filter.minEM=0] - Minimal monoisotopic mass
  * @param {number}         [options.filter.maxEM=+Infinity] - Maximal monoisotopic mass
  * @param {number}         [options.filter.minMSEM=0] - Minimal monoisotopic mass observed by mass
  * @param {number}         [options.filter.maxMSEM=+Infinity] - Maximal monoisotopic mass observed by mass
  * @param {number}         [options.filter.minCharge=-Infinity] - Minimal charge
  * @param {number}         [options.filter.maxCharge=+Infinity] - Maximal charge
  * @param {object}         [options.filter.unsaturation={}]
  * @param {number}         [options.filter.unsaturation.min=-Infinity] - Minimal unsaturation
  * @param {number}         [options.filter.unsaturation.max=+Infinity] - Maximal unsaturation
  * @param {number}         [options.filter.unsaturation.onlyInteger=false] - Integer unsaturation
  * @param {number}         [options.filter.unsaturation.onlyNonInteger=false] - Non integer unsaturation
  * @param {object}         [options.filter.callback] - a function to filter the MF
  * @param {object}         [options.filter.atoms] - object of atom:{min, max}
  */

  function searchMSEM(emdb, msem) {
    let options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
    let filter = {
      ...(options.filter || {}),
      targetMass: msem
    };
    let {
      databases = Object.keys(emdb.databases),
      flatten = false
    } = options;
    let ionizations = preprocessIonizations(options.ionizations);
    let results = {};
    for (let database of databases) {
      results[database] = [];
    }
    for (let ionization of ionizations) {
      filter.ionization = ionization;
      for (let database of databases) {
        for (let entry of emdb.databases[database]) {
          let match = msemMatcher(entry, filter);
          if (match) {
            results[database].push({
              ...entry,
              ms: match.ms,
              ionization: match.ionization
            });
          }
        }
      }
    }
    if (flatten) {
      let flattenResults = [];
      for (let database of databases) {
        for (let entry of results[database]) {
          entry.database = database;
          flattenResults.push(entry);
        }
      }
      flattenResults.sort((a, b) => Math.abs(a.ms.ppm) - Math.abs(b.ms.ppm));
      return flattenResults;
    } else {
      Object.keys(results).forEach(k => results[k].sort((a, b) => Math.abs(a.ms.ppm) - Math.abs(b.ms.ppm)));
      return results;
    }
  }

  const GAUSSIAN_EXP_FACTOR = -4 * Math.LN2;
  const ROOT_PI_OVER_LN2 = Math.sqrt(Math.PI / Math.LN2);
  const ROOT_THREE = Math.sqrt(3);
  const ROOT_2LN2 = Math.sqrt(2 * Math.LN2);
  const ROOT_2LN2_MINUS_ONE = Math.sqrt(2 * Math.LN2) - 1;

  // https://en.wikipedia.org/wiki/Error_function#Inverse_functions
  // This code yields to a good approximation
  // If needed a better implementation using polynomial can be found on https://en.wikipedia.org/wiki/Error_function#Inverse_functions
  function erfinv(x) {
    let a = 0.147;
    if (x === 0) return 0;
    let ln1MinusXSqrd = Math.log(1 - x * x);
    let lnEtcBy2Plus2 = ln1MinusXSqrd / 2 + 2 / (Math.PI * a);
    let firstSqrt = Math.sqrt(lnEtcBy2Plus2 ** 2 - ln1MinusXSqrd / a);
    let secondSqrt = Math.sqrt(firstSqrt - lnEtcBy2Plus2);
    return secondSqrt * (x > 0 ? 1 : -1);
  }

  class Gaussian {
    constructor() {
      let options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
      const {
        fwhm = 500,
        sd
      } = options;
      this.fwhm = sd ? gaussianWidthToFWHM(2 * sd) : fwhm;
    }
    fwhmToWidth() {
      let fwhm = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : this.fwhm;
      return gaussianFwhmToWidth(fwhm);
    }
    widthToFWHM(width) {
      return gaussianWidthToFWHM(width);
    }
    fct(x) {
      return gaussianFct(x, this.fwhm);
    }
    getArea() {
      let height = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : calculateGaussianHeight({
        fwhm: this.fwhm
      });
      return getGaussianArea({
        fwhm: this.fwhm,
        height
      });
    }
    getFactor(area) {
      return getGaussianFactor(area);
    }
    getData() {
      let options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
      return getGaussianData(this, options);
    }
    calculateHeight() {
      let area = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 1;
      return calculateGaussianHeight({
        fwhm: this.fwhm,
        area
      });
    }
    getParameters() {
      return ['fwhm'];
    }
  }
  function calculateGaussianHeight(options) {
    let {
      fwhm = 500,
      area = 1,
      sd
    } = options;
    if (sd) fwhm = gaussianWidthToFWHM(2 * sd);
    return 2 * area / ROOT_PI_OVER_LN2 / fwhm;
  }
  /**
   * Calculate the height of the gaussian function of a specific width (fwhm) at a speicifc
   * x position (the gaussian is centered on x=0)
   * @param x
   * @param fwhm
   * @returns y
   */
  function gaussianFct(x, fwhm) {
    return Math.exp(GAUSSIAN_EXP_FACTOR * Math.pow(x / fwhm, 2));
  }
  function gaussianWidthToFWHM(width) {
    return width * ROOT_2LN2;
  }
  function gaussianFwhmToWidth(fwhm) {
    return fwhm / ROOT_2LN2;
  }
  function getGaussianArea(options) {
    let {
      fwhm = 500,
      sd,
      height = 1
    } = options;
    if (sd) fwhm = gaussianWidthToFWHM(2 * sd);
    return height * ROOT_PI_OVER_LN2 * fwhm / 2;
  }
  function getGaussianFactor() {
    let area = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 0.9999;
    return Math.sqrt(2) * erfinv(area);
  }
  function getGaussianData() {
    let shape = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
    let options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
    let {
      fwhm = 500,
      sd
    } = shape;
    if (sd) fwhm = gaussianWidthToFWHM(2 * sd);
    let {
      length,
      factor = getGaussianFactor(),
      height = calculateGaussianHeight({
        fwhm
      })
    } = options;
    if (!length) {
      length = Math.min(Math.ceil(fwhm * factor), Math.pow(2, 25) - 1);
      if (length % 2 === 0) length++;
    }
    const center = (length - 1) / 2;
    const data = new Float64Array(length);
    for (let i = 0; i <= center; i++) {
      data[i] = gaussianFct(i - center, fwhm) * height;
      data[length - 1 - i] = data[i];
    }
    return data;
  }

  class Lorentzian {
    constructor() {
      let options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
      const {
        fwhm = 500
      } = options;
      this.fwhm = fwhm;
    }
    fwhmToWidth() {
      let fwhm = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : this.fwhm;
      return lorentzianFwhmToWidth(fwhm);
    }
    widthToFWHM(width) {
      return lorentzianWidthToFWHM(width);
    }
    fct(x) {
      return lorentzianFct(x, this.fwhm);
    }
    getArea() {
      let height = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 1;
      return getLorentzianArea({
        fwhm: this.fwhm,
        height
      });
    }
    getFactor(area) {
      return getLorentzianFactor(area);
    }
    getData() {
      let options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
      return getLorentzianData(this, options);
    }
    calculateHeight() {
      let area = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 1;
      return calculateLorentzianHeight({
        fwhm: this.fwhm,
        area
      });
    }
    getParameters() {
      return ['fwhm'];
    }
  }
  const calculateLorentzianHeight = _ref => {
    let {
      fwhm = 1,
      area = 1
    } = _ref;
    return 2 * area / Math.PI / fwhm;
  };
  const getLorentzianArea = options => {
    const {
      fwhm = 500,
      height = 1
    } = options;
    return height * Math.PI * fwhm / 2;
  };
  const lorentzianFct = (x, fwhm) => {
    return fwhm ** 2 / (4 * x ** 2 + fwhm ** 2);
  };
  const lorentzianWidthToFWHM = width => {
    return width * ROOT_THREE;
  };
  const lorentzianFwhmToWidth = fwhm => {
    return fwhm / ROOT_THREE;
  };
  const getLorentzianFactor = function () {
    let area = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 0.9999;
    if (area >= 1) {
      throw new Error('area should be (0 - 1)');
    }
    const halfResidual = (1 - area) * 0.5;
    const quantileFunction = p => Math.tan(Math.PI * (p - 0.5));
    return (quantileFunction(1 - halfResidual) - quantileFunction(halfResidual)) / 2;
  };
  const getLorentzianData = function () {
    let shape = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
    let options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
    let {
      fwhm = 500
    } = shape;
    let {
      length,
      factor = getLorentzianFactor(),
      height = calculateLorentzianHeight({
        fwhm,
        area: 1
      })
    } = options;
    if (!length) {
      length = Math.min(Math.ceil(fwhm * factor), Math.pow(2, 25) - 1);
      if (length % 2 === 0) length++;
    }
    const center = (length - 1) / 2;
    const data = new Float64Array(length);
    for (let i = 0; i <= center; i++) {
      data[i] = lorentzianFct(i - center, fwhm) * height;
      data[length - 1 - i] = data[i];
    }
    return data;
  };

  class PseudoVoigt {
    constructor() {
      let options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
      const {
        fwhm = 500,
        mu = 0.5
      } = options;
      this.mu = mu;
      this.fwhm = fwhm;
    }
    fwhmToWidth() {
      let fwhm = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : this.fwhm;
      let mu = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : this.mu;
      return pseudoVoigtFwhmToWidth(fwhm, mu);
    }
    widthToFWHM(width) {
      let mu = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : this.mu;
      return pseudoVoigtWidthToFWHM(width, mu);
    }
    fct(x) {
      return pseudoVoigtFct(x, this.fwhm, this.mu);
    }
    getArea() {
      let height = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 1;
      return getPseudoVoigtArea({
        fwhm: this.fwhm,
        height,
        mu: this.mu
      });
    }
    getFactor(area) {
      return getPseudoVoigtFactor(area);
    }
    getData() {
      let options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
      const {
        length,
        factor,
        height = calculatePseudoVoigtHeight({
          fwhm: this.fwhm,
          mu: this.mu,
          area: 1
        })
      } = options;
      return getPseudoVoigtData(this, {
        factor,
        length,
        height
      });
    }
    calculateHeight() {
      let area = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 1;
      return calculatePseudoVoigtHeight({
        fwhm: this.fwhm,
        mu: this.mu,
        area
      });
    }
    getParameters() {
      return ['fwhm', 'mu'];
    }
  }
  const calculatePseudoVoigtHeight = function () {
    let options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
    let {
      fwhm = 1,
      mu = 0.5,
      area = 1
    } = options;
    return 2 * area / (fwhm * (mu * ROOT_PI_OVER_LN2 + (1 - mu) * Math.PI));
  };
  const pseudoVoigtFct = (x, fwhm, mu) => {
    return (1 - mu) * lorentzianFct(x, fwhm) + mu * gaussianFct(x, fwhm);
  };
  const pseudoVoigtWidthToFWHM = function (width) {
    let mu = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0.5;
    return width * (mu * ROOT_2LN2_MINUS_ONE + 1);
  };
  const pseudoVoigtFwhmToWidth = function (fwhm) {
    let mu = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0.5;
    return fwhm / (mu * ROOT_2LN2_MINUS_ONE + 1);
  };
  const getPseudoVoigtArea = options => {
    const {
      fwhm = 500,
      height = 1,
      mu = 0.5
    } = options;
    return fwhm * height * (mu * ROOT_PI_OVER_LN2 + (1 - mu) * Math.PI) / 2;
  };
  const getPseudoVoigtFactor = function () {
    let area = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 0.9999;
    let mu = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0.5;
    return mu < 1 ? getLorentzianFactor(area) : getGaussianFactor(area);
  };
  const getPseudoVoigtData = function () {
    let shape = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
    let options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
    let {
      fwhm = 500,
      mu = 0.5
    } = shape;
    let {
      length,
      factor = getPseudoVoigtFactor(0.999, mu),
      height = calculatePseudoVoigtHeight({
        fwhm,
        mu,
        area: 1
      })
    } = options;
    if (!height) {
      height = 1 / (mu / Math.sqrt(-GAUSSIAN_EXP_FACTOR / Math.PI) * fwhm + (1 - mu) * fwhm * Math.PI / 2);
    }
    if (!length) {
      length = Math.min(Math.ceil(fwhm * factor), Math.pow(2, 25) - 1);
      if (length % 2 === 0) length++;
    }
    const center = (length - 1) / 2;
    const data = new Float64Array(length);
    for (let i = 0; i <= center; i++) {
      data[i] = pseudoVoigtFct(i - center, fwhm, mu) * height;
      data[length - 1 - i] = data[i];
    }
    return data;
  };

  /**
   * Generate a instance of a specific kind of shape.
   */
  function getShape1D(shape) {
    const {
      kind
    } = shape;
    switch (kind) {
      case 'gaussian':
        return new Gaussian(shape);
      case 'lorentzian':
        return new Lorentzian(shape);
      case 'pseudoVoigt':
        return new PseudoVoigt(shape);
      default:
        {
          throw Error(`Unknown distribution ${kind}`);
        }
    }
  }

  function addBaseline(data, baselineFct) {
    if (!baselineFct) return data;
    let xs = data.x;
    let ys = data.y;
    for (let i = 0; i < xs.length; i++) {
      ys[i] += baselineFct(xs[i]);
    }
    return data;
  }

  const toString = Object.prototype.toString;
  /**
   * Checks if an object is an instance of an Array (array or typed array).
   *
   * @param {any} value - Object to check.
   * @returns {boolean} True if the object is an array.
   */
  function isAnyArray(value) {
    return toString.call(value).endsWith('Array]');
  }

  /**
   * This function xAdd the first array by the second array or a constant value to each element of the first array
   *
   * @param array1 - the first array
   * @param array2 - the second array or number
   */
  function xAdd(array1, array2) {
    let isConstant = false;
    let constant = 0;
    if (isAnyArray(array2)) {
      if (array1.length !== array2.length) {
        throw new Error('xAdd: size of array1 and array2 must be identical');
      }
    } else {
      isConstant = true;
      constant = array2;
    }
    let array3 = new Float64Array(array1.length);
    if (isConstant) {
      for (let i = 0; i < array1.length; i++) {
        array3[i] = array1[i] + constant;
      }
    } else {
      for (let i = 0; i < array1.length; i++) {
        array3[i] = array1[i] + array2[i];
      }
    }
    return array3;
  }

  /**
   * Checks if input is of type array
   *
   * @param input - input
   */
  function xCheck(input) {
    if (!isAnyArray(input)) {
      throw new TypeError('input must be an array');
    }
    if (input.length === 0) {
      throw new TypeError('input must not be empty');
    }
  }

  /**
   * Returns the closest index of a `target`
   *
   * @param array - array of numbers
   * @param target - target
   * @returns - closest index
   */
  function xFindClosestIndex(array, target) {
    let options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
    const {
      sorted = true
    } = options;
    if (sorted) {
      let low = 0;
      let high = array.length - 1;
      let middle = 0;
      while (high - low > 1) {
        middle = low + (high - low >> 1);
        if (array[middle] < target) {
          low = middle;
        } else if (array[middle] > target) {
          high = middle;
        } else {
          return middle;
        }
      }
      if (low < array.length - 1) {
        if (Math.abs(target - array[low]) < Math.abs(array[low + 1] - target)) {
          return low;
        } else {
          return low + 1;
        }
      } else {
        return low;
      }
    } else {
      let index = 0;
      let diff = Number.POSITIVE_INFINITY;
      for (let i = 0; i < array.length; i++) {
        const currentDiff = Math.abs(array[i] - target);
        if (currentDiff < diff) {
          diff = currentDiff;
          index = i;
        }
      }
      return index;
    }
  }

  /**
   * Returns an object with {fromIndex, toIndex} for a specific from / to
   *
   * @param x - array of numbers
   * @param options - Options
   */
  function xGetFromToIndex(x) {
    let options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
    let {
      fromIndex,
      toIndex,
      from,
      to
    } = options;
    if (fromIndex === undefined) {
      if (from !== undefined) {
        fromIndex = xFindClosestIndex(x, from);
      } else {
        fromIndex = 0;
      }
    }
    if (toIndex === undefined) {
      if (to !== undefined) {
        toIndex = xFindClosestIndex(x, to);
      } else {
        toIndex = x.length - 1;
      }
    }
    if (fromIndex < 0) fromIndex = 0;
    if (toIndex < 0) toIndex = 0;
    if (fromIndex >= x.length) fromIndex = x.length - 1;
    if (toIndex >= x.length) toIndex = x.length - 1;
    if (fromIndex > toIndex) [fromIndex, toIndex] = [toIndex, fromIndex];
    return {
      fromIndex,
      toIndex
    };
  }

  /**
   * Computes the maximal value of an array of values
   *
   * @param array - array of numbers
   * @param options - options
   */
  function xMaxValue(array) {
    let options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
    xCheck(array);
    const {
      fromIndex,
      toIndex
    } = xGetFromToIndex(array, options);
    let maxValue = array[fromIndex];
    for (let i = fromIndex + 1; i <= toIndex; i++) {
      if (array[i] > maxValue) {
        maxValue = array[i];
      }
    }
    return maxValue;
  }

  const LOOP = 8;
  const FLOAT_MUL = 1 / 16777216;
  const sh1 = 15;
  const sh2 = 18;
  const sh3 = 11;
  function multiply_uint32(n, m) {
    n >>>= 0;
    m >>>= 0;
    const nlo = n & 0xffff;
    const nhi = n - nlo;
    return (nhi * m >>> 0) + nlo * m >>> 0;
  }
  class XSadd {
    constructor() {
      let seed = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : Date.now();
      this.state = new Uint32Array(4);
      this.init(seed);
      this.random = this.getFloat.bind(this);
    }
    /**
     * Returns a 32-bit integer r (0 <= r < 2^32)
     */
    getUint32() {
      this.nextState();
      return this.state[3] + this.state[2] >>> 0;
    }
    /**
     * Returns a floating point number r (0.0 <= r < 1.0)
     */
    getFloat() {
      return (this.getUint32() >>> 8) * FLOAT_MUL;
    }
    init(seed) {
      if (!Number.isInteger(seed)) {
        throw new TypeError('seed must be an integer');
      }
      this.state[0] = seed;
      this.state[1] = 0;
      this.state[2] = 0;
      this.state[3] = 0;
      for (let i = 1; i < LOOP; i++) {
        this.state[i & 3] ^= i + multiply_uint32(1812433253, this.state[i - 1 & 3] ^ this.state[i - 1 & 3] >>> 30 >>> 0) >>> 0;
      }
      this.periodCertification();
      for (let i = 0; i < LOOP; i++) {
        this.nextState();
      }
    }
    periodCertification() {
      if (this.state[0] === 0 && this.state[1] === 0 && this.state[2] === 0 && this.state[3] === 0) {
        this.state[0] = 88; // X
        this.state[1] = 83; // S
        this.state[2] = 65; // A
        this.state[3] = 68; // D
      }
    }

    nextState() {
      let t = this.state[0];
      t ^= t << sh1;
      t ^= t >>> sh2;
      t ^= this.state[3] << sh3;
      this.state[0] = this.state[1];
      this.state[1] = this.state[2];
      this.state[2] = this.state[3];
      this.state[3] = t;
    }
  }

  /**
   * Create a random array of numbers of a specific length
   *
   * @return - array of random floats normally distributed
   */
  let spare;
  let hasSpare = false;
  function createRandomArray() {
    let options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
    let {
      mean = 0,
      standardDeviation = 1,
      length = 1000,
      range = 1,
      seed,
      distribution = 'normal'
    } = options;
    const generator = new XSadd(seed);
    let returnArray = new Float64Array(length);
    switch (distribution) {
      case 'normal':
        for (let i = 0; i < length; i++) {
          returnArray[i] = generateGaussian(mean, standardDeviation, generator);
        }
        break;
      case 'uniform':
        for (let i = 0; i < length; i++) {
          returnArray[i] = (generator.random() - 0.5) * range + mean;
        }
        break;
      default:
        // eslint-disable-next-line @typescript-eslint/restrict-template-expressions
        throw new Error(`unknown distribution: ${distribution}`);
    }
    return returnArray;
  }
  function generateGaussian(mean, standardDeviation, generator) {
    let val, u, v, s;
    if (hasSpare) {
      hasSpare = false;
      val = spare * standardDeviation + mean;
    } else {
      do {
        u = generator.random() * 2 - 1;
        v = generator.random() * 2 - 1;
        s = u * u + v * v;
      } while (s >= 1 || s === 0);
      s = Math.sqrt(-2 * Math.log(s) / s);
      spare = v * s;
      hasSpare = true;
      val = mean + standardDeviation * u * s;
    }
    return val;
  }

  function addNoise(data) {
    let options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
    const {
      seed = 0,
      distribution = 'normal',
      percent = 1
    } = options;
    const range = xMaxValue(data.y) * percent / 100;
    const noise = createRandomArray({
      distribution,
      seed,
      mean: 0,
      standardDeviation: range,
      range,
      length: data.x.length
    });
    data.y = xAdd(data.y, noise);
    return data;
  }

  class SpectrumGenerator {
    constructor() {
      let options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
      const {
        from = 0,
        to = 1000,
        nbPoints = 10001,
        peakWidthFct,
        shape = {
          kind: 'gaussian',
          fwhm: 5
        }
      } = options;
      this.from = from;
      this.to = to;
      this.nbPoints = nbPoints;
      this.interval = (this.to - this.from) / (this.nbPoints - 1);
      this.peakWidthFct = peakWidthFct;
      this.maxPeakHeight = Number.MIN_SAFE_INTEGER;
      this.data = {
        x: new Float64Array(this.nbPoints),
        y: new Float64Array(this.nbPoints)
      };
      let shapeGenerator = getShape1D(shape);
      this.shape = shapeGenerator;
      assertNumber(this.from, 'from');
      assertNumber(this.to, 'to');
      assertInteger(this.nbPoints, 'nbPoints');
      if (this.to <= this.from) {
        throw new RangeError('to option must be larger than from');
      }
      if (this.peakWidthFct && typeof this.peakWidthFct !== 'function') {
        throw new TypeError('peakWidthFct option must be a function');
      }
      this.reset();
    }
    /**
     * Add a series of peaks to the spectrum.
     * @param peaks - Peaks to add.
     */
    addPeaks(peaks, options) {
      if (!Array.isArray(peaks) && (typeof peaks !== 'object' || peaks.x === undefined || peaks.y === undefined || !Array.isArray(peaks.x) || !Array.isArray(peaks.y) || peaks.x.length !== peaks.y.length)) {
        throw new TypeError('peaks must be an array or an object containing x[] and y[]');
      }
      if (Array.isArray(peaks)) {
        for (const peak of peaks) {
          this.addPeak(peak, options);
        }
      } else {
        for (let i = 0; i < peaks.x.length; i++) {
          this.addPeak([peaks.x[i], peaks.y[i]], options);
        }
      }
    }
    /**
     * Add a single peak to the spectrum.
     * A peak may be either defined as [x,y,fwhm,...] or as {x, y, shape}
     * @param peak
     * @param options
     */
    addPeak(peak) {
      let options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
      if (Array.isArray(peak) && peak.length < 2) {
        throw new Error('peak must be an array with two (or three) values or an object with {x,y,width?}');
      }
      if (!Array.isArray(peak) && (peak.x === undefined || peak.y === undefined)) {
        throw new Error('peak must be an array with two (or three) values or an object with {x,y,width?}');
      }
      let xPosition;
      let intensity;
      let peakFWHM;
      let peakWidth;
      let peakShapeOptions;
      if (Array.isArray(peak)) {
        [xPosition, intensity, peakFWHM, peakShapeOptions] = peak;
      } else {
        xPosition = peak.x;
        intensity = peak.y;
        peakWidth = peak.width;
        peakShapeOptions = peak.shape;
      }
      if (intensity > this.maxPeakHeight) this.maxPeakHeight = intensity;
      let {
        shape: shapeOptions
      } = options;
      if (peakShapeOptions) {
        shapeOptions = shapeOptions ? {
          ...shapeOptions,
          ...peakShapeOptions
        } : peakShapeOptions;
      }
      const shape = shapeOptions ? getShape1D(shapeOptions) : Object.assign(Object.create(Object.getPrototypeOf(this.shape)), this.shape);
      let {
        width,
        widthLeft,
        widthRight
      } = options;
      /*
       if we don't force the fwhm we just take the one from the shape
       however we have many way to force it:
       - use [x,y,fwhm]
       - define `width` that will be converted to fwhm
       - define `widthLeft` and `widthRight` to define asymmetric peaks
       - have a callback `peakWidthFct`
       This should evolve in the future because we will not always have `fwhm`
       */
      const fwhm = peakFWHM !== undefined ? peakFWHM : peakWidth ? shape.widthToFWHM(peakWidth) : this.peakWidthFct ? this.peakWidthFct(xPosition) : width !== undefined ? width : shape.fwhm;
      if (!widthLeft) widthLeft = fwhm;
      if (!widthRight) widthRight = fwhm;
      if (!widthLeft || !widthRight) {
        throw new Error('Width left or right is undefined or zero');
      }
      let factor = options.factor === undefined ? shape.getFactor() : options.factor;
      const firstValue = xPosition - widthLeft / 2 * factor;
      const lastValue = xPosition + widthRight / 2 * factor;
      const firstPoint = Math.max(0, Math.floor((firstValue - this.from) / this.interval));
      const lastPoint = Math.min(this.nbPoints - 1, Math.ceil((lastValue - this.from) / this.interval));
      const middlePoint = Math.round((xPosition - this.from) / this.interval);
      // PEAK SHAPE MAY BE ASYMMETRC (widthLeft and widthRight) !
      // we calculate the left part of the shape
      shape.fwhm = widthLeft;
      for (let index = firstPoint; index < Math.max(middlePoint, 0); index++) {
        this.data.y[index] += intensity * shape.fct(this.data.x[index] - xPosition);
      }
      // we calculate the right part of the gaussian
      shape.fwhm = widthRight;
      for (let index = Math.min(middlePoint, lastPoint); index <= lastPoint; index++) {
        this.data.y[index] += intensity * shape.fct(this.data.x[index] - xPosition);
      }
    }
    /**
     * Add a baseline to the spectrum.
     * @param baselineFct - Mathematical function producing the baseline you want.
     */
    addBaseline(baselineFct) {
      addBaseline(this.data, baselineFct);
      return this;
    }
    /**
     * Add noise to the spectrum.
     *
     * @param percent - Noise's amplitude in percents of the spectrum max value. Default: 1.
     */
    addNoise(options) {
      addNoise(this.data, options);
      return this;
    }
    /**
     * Get the generated spectrum.
     */
    getSpectrum() {
      let options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
      if (typeof options === 'boolean') {
        options = {
          copy: options
        };
      }
      const {
        copy = true,
        threshold = 0
      } = options;
      if (threshold) {
        let minPeakHeight = this.maxPeakHeight * threshold;
        let x = [];
        let y = [];
        for (let i = 0; i < this.data.x.length; i++) {
          if (this.data.y[i] >= minPeakHeight) {
            x.push(this.data.x[i]);
            y.push(this.data.y[i]);
          }
        }
        return {
          x,
          y
        };
      }
      if (copy) {
        return {
          x: this.data.x.slice(),
          y: this.data.y.slice()
        };
      } else {
        return this.data;
      }
    }
    /**
     * Resets the generator with an empty spectrum.
     */
    reset() {
      const spectrum = this.data;
      for (let i = 0; i < this.nbPoints; i++) {
        spectrum.x[i] = this.from + i * this.interval;
      }
      return this;
    }
  }
  function assertInteger(value, name) {
    if (!Number.isInteger(value)) {
      throw new TypeError(`${name} option must be an integer`);
    }
  }
  function assertNumber(value, name) {
    if (!Number.isFinite(value)) {
      throw new TypeError(`${name} option must be a number`);
    }
  }

  function closestPointX(array, target) {
    let low = 0;
    let high = array.length - 1;
    let middle = 0;
    while (high - low > 1) {
      middle = low + (high - low >> 1);
      if (array[middle].x < target) {
        low = middle;
      } else if (array[middle].x > target) {
        high = middle;
      } else {
        return array[middle];
      }
    }
    if (low < array.length - 1) {
      if (Math.abs(target - array[low].x) < Math.abs(array[low + 1].x - target)) {
        return array[low];
      } else {
        return array[low + 1];
      }
    } else {
      return array[low];
    }
  }

  /**
   * Join x values if there are similar
   */

  function joinX(self) {
    let threshold = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : Number.EPSILON;
    // when we join we will use the center of mass
    if (self.array.length === 0) return [];
    self.sortX();
    let current = self.array[0];
    let result = [current];
    for (let i = 1; i < self.array.length; i++) {
      const item = self.array[i];
      if (item.x - current.x <= threshold) {
        // weighted sum
        current.x = item.y / (current.y + item.y) * (item.x - current.x) + current.x;
        current.y += item.y;
      } else {
        current = {
          x: item.x,
          y: item.y
        };
        if (item.composition) current.composition = item.composition;
        result.push(current);
      }
    }
    self.array = result;
    self.ySorted = false;
    return self;
  }

  function multiply(a, b) {
    let options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
    const {
      minY = 1e-8,
      maxLines = 5000,
      deltaX = 1e-2
    } = options;
    const result = new a.constructor();
    a.sortY();
    b.sortY();
    for (let entryA of a.array) {
      for (let entryB of b.array) {
        let y = entryA.y * entryB.y;
        if (y > minY) {
          const composition = calculateComposition(entryA, entryB);
          if (composition) {
            result.push({
              x: entryA.x + entryB.x,
              y,
              composition
            });
          } else {
            result.push({
              x: entryA.x + entryB.x,
              y
            });
          }
        }
        if (result.length > maxLines * 2) {
          result.joinX(deltaX);
          result.topY(maxLines);
        }
      }
    }
    result.joinX(deltaX);
    result.topY(maxLines);
    a.move(result);
    return a;
  }
  function calculateComposition(entryA, entryB) {
    if (!entryA.composition || !entryB.composition) return;
    let toReturn = {};
    const keys = [...new Set(Object.keys(entryA.composition).concat(Object.keys(entryB.composition)))];
    for (let key of keys) {
      toReturn[key] = (entryA.composition[key] || 0) + (entryB.composition[key] || 0);
    }
    return toReturn;
  }

  // https://en.wikipedia.org/wiki/Exponentiation_by_squaring

  function power(a, p) {
    let options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
    if (p <= 0) throw new Error('power must be larger than 0');
    if (p === 1) return a;
    if (p === 2) {
      return a.square();
    }
    p--;
    let base = a.copy(); // linear time
    while (p !== 0) {
      if ((p & 1) !== 0) {
        a.multiply(base, options); // executed <= log2(p) times
      }

      p >>= 1;
      if (p !== 0) base.square(options); // executed <= log2(p) times
    }

    return a;
  }

  /**
   * Internal class to deal with isotopic distribution calculations
   */
  class Distribution {
    constructor(array) {
      if (Array.isArray(array)) {
        this.array = array;
        this.xSorted = false;
        this.ySorted = false;
      } else {
        this.array = [];
        this.xSorted = true;
        this.ySorted = true;
      }
    }
    get length() {
      return this.array.length;
    }
    get xs() {
      return this.array.map(p => p.x);
    }
    get ys() {
      return this.array.map(p => p.y);
    }
    get minX() {
      if (!this.xSorted) this.sortX();
      return this.array[0].x;
    }
    get maxX() {
      if (!this.xSorted) this.sortX();
      return this.array[this.array.length - 1].x;
    }
    get minY() {
      if (!this.ySorted) this.sortY();
      return this.array[0].y;
    }
    get maxY() {
      if (!this.ySorted) this.sortY();
      return this.array[this.array.length - 1];
    }
    multiplyY(value) {
      this.array.forEach(item => item.y *= value);
    }
    setArray(array) {
      this.array = array;
      this.xSorted = false;
      this.ySorted = false;
    }
    move(other) {
      this.xSorted = other.xSorted;
      this.ySorted = other.ySorted;
      this.array = other.array;
    }
    push(point) {
      this.array.push(point);
      this.xSorted = false;
      this.ySorted = false;
    }
    sortX() {
      this.ySorted = false;
      if (this.xSorted) return this;
      this.array.sort((a, b) => a.x - b.x);
      this.xSorted = true;
      return this;
    }
    sortY() {
      this.xSorted = false;
      if (this.ySorted) return this;
      this.array.sort((a, b) => b.y - a.y);
      this.ySorted = true;
      return this;
    }
    normalize() {
      let sum = 0;
      for (let item of this.array) {
        sum += item.y;
      }
      for (let item of this.array) {
        item.y /= sum;
      }
      return this;
    }
    topY(limit) {
      if (!limit) return this;
      if (this.array.length <= limit) return this;
      this.sortY();
      this.array.splice(limit);
      return this;
    }
    square() {
      let options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
      return this.multiply(this, options);
    }
    multiply(b, options) {
      return multiply(this, b, options);
    }
    power(p, options) {
      return power(this, p, options);
    }
    copy() {
      let distCopy = new Distribution();
      distCopy.xSorted = this.xSorted;
      distCopy.ySorted = this.ySorted;
      distCopy.array = JSON.parse(JSON.stringify(this.array));
      return distCopy;
    }
    maxToOne() {
      if (this.array.length === 0) return this;
      let currentMax = this.array[0].y;
      for (let item of this.array) {
        if (item.y > currentMax) currentMax = item.y;
      }
      for (let item of this.array) {
        item.y /= currentMax;
      }
      return this;
    }
    joinX(threshold) {
      return joinX(this, threshold);
    }
    append(distribution) {
      for (let item of distribution.array) {
        this.array.push(item);
      }
      this.xSorted = false;
      this.ySorted = false;
    }
    closestPointX(target) {
      this.sortX();
      return closestPointX(this.array, target);
    }
  }

  function getDerivedCompositionInfo(composition) {
    const shortComposition = {};
    let label = '';
    let shortLabel = '';
    for (let key in composition) {
      let isotopeLabel = '';
      for (let i = 0; i < key.length; i++) {
        if (superscript[key[i]]) {
          isotopeLabel += superscript[key[i]];
        } else {
          isotopeLabel += key[i];
        }
      }
      if (composition[key] > 1) {
        const number = String(composition[key]);
        for (let i = 0; i < number.length; i++) {
          isotopeLabel += subscript[number[i]];
        }
      }
      label += isotopeLabel;
      if (stableIsotopesObject[key].mostAbundant) continue;
      shortLabel += isotopeLabel;
      shortComposition[key] = composition[key];
    }
    return {
      label,
      shortComposition,
      shortLabel
    };
  }

  const MINIMAL_FWHM = 1e-8;

  /**
   * An object containing two arrays
   * @typedef {object} XY
   * @property {Array<number>} x - The x array
   * @property {Array<number>} y - The y array
   */

  /**
   * A class that allows to manage isotopic distribution
   */
  class IsotopicDistribution {
    /**
     * Class that manage isotopic distribution
     * @param {string|array} value - Molecular formula or an array of parts
     * @param {object} [options={}]
     * @param {string} [options.ionizations=''] - string containing a comma separated list of modifications
     * @param {number} [options.fwhm=0.01] - Amount of Dalton under which 2 peaks are joined
     * @param {number} [options.maxLines=5000] - Maximal number of lines during calculations
     * @param {number} [options.minY=1e-8] - Minimal signal height during calculations
     * @param {number} [options.ensureCase=false] - Ensure uppercase / lowercase
     * @param {number} [options.allowNeutral=true] - Should we keep the distribution if the molecule has no charge
     */

    constructor(value) {
      let options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
      if (Array.isArray(value)) {
        this.parts = JSON.parse(JSON.stringify(value));
        for (let part of this.parts) {
          part.confidence = 0;
          part.isotopesInfo = new MF(`${part.mf}(${part.ionization.mf})`).getIsotopesInfo();
        }
      } else {
        let mf = new MF(value, {
          ensureCase: options.ensureCase
        });
        let mfInfo = mf.getInfo();
        let ionizations = preprocessIonizations(options.ionizations);
        let parts = mfInfo.parts || [mfInfo];
        this.parts = [];
        for (let partOriginal of parts) {
          // we calculate information for each part
          for (const ionization of ionizations) {
            let part = JSON.parse(JSON.stringify(partOriginal));
            part.em = part.monoisotopicMass; // TODO: To remove !!! we change the name !?
            part.isotopesInfo = new MF(`${part.mf}(${ionization.mf})`).getIsotopesInfo();
            part.confidence = 0;
            let msInfo = getMsInfo(part, {
              ionization
            });
            part.ionization = msInfo.ionization;
            part.ms = msInfo.ms;
            this.parts.push(part);
          }
        }
      }
      this.cachedDistribution = undefined;
      this.fwhm = options.fwhm === undefined ? 0.01 : options.fwhm;
      // if fwhm is under 1e-8 there are some artifacts in the spectra
      if (this.fwhm < MINIMAL_FWHM) this.fwhm = MINIMAL_FWHM;
      this.minY = options.minY === undefined ? MINIMAL_FWHM : options.minY;
      this.maxLines = options.maxLines || 5000;
      this.allowNeutral = options.allowNeutral === undefined ? true : options.allowNeutral;
    }
    getParts() {
      return this.parts;
    }

    /**
     * @return {Distribution} returns the total distribution (for all parts)
     */
    getDistribution() {
      if (this.cachedDistribution) return this.cachedDistribution;
      let options = {
        maxLines: this.maxLines,
        minY: this.minY,
        deltaX: this.fwhm
      };
      let finalDistribution = new Distribution();
      this.confidence = 0;
      // TODO need to cache each part without ionization
      // in case of many ionization we don't need to recalculate everything !
      for (let part of this.parts) {
        let totalDistribution = new Distribution([{
          x: 0,
          y: 1,
          composition: this.fwhm === MINIMAL_FWHM ? {} : undefined
        }]);
        let charge = part.ms.charge;
        let absoluteCharge = Math.abs(charge);
        if (charge || this.allowNeutral) {
          for (let isotope of part.isotopesInfo.isotopes) {
            if (isotope.number < 0) return {
              array: []
            };
            if (isotope.number > 0) {
              const newDistribution = JSON.parse(JSON.stringify(isotope.distribution));
              if (this.fwhm === MINIMAL_FWHM) {
                // add composition
                for (const entry of newDistribution) {
                  entry.composition = {
                    [Math.round(entry.x) + isotope.atom]: 1
                  };
                }
              }
              let distribution = new Distribution(newDistribution);
              distribution.power(isotope.number, options);
              totalDistribution.multiply(distribution, options);
            }
          }
          this.confidence += totalDistribution.array.reduce((sum, value) => sum + value.y, 0);

          // we finally deal with the charge

          if (charge) {
            totalDistribution.array.forEach(e => {
              e.x = (e.x - ELECTRON_MASS * charge) / absoluteCharge;
            });
          }
          if (totalDistribution.array && totalDistribution.array.length > 0) {
            totalDistribution.sortX();
            part.fromX = totalDistribution.array[0].x;
            part.toX = totalDistribution.array[totalDistribution.array.length - 1].x;
          }
          if (part.ms.target && part.ms.target.intensity && part.ms.target.intensity !== 1) {
            // intensity is the value of the monoisotopic mass !
            // need to find the intensity of the peak corresponding
            // to the monoisotopic mass
            if (part.ms.target.mass) {
              let target = totalDistribution.closestPointX(part.ms.target.mass);
              totalDistribution.multiplyY(part.ms.target.intensity / target.y);
            } else {
              totalDistribution.multiplyY(part.ms.target.intensity);
            }
          } else if (part.intensity && part.intensity !== 1) {
            totalDistribution.multiplyY(part.intensity);
          }
          part.isotopicDistribution = totalDistribution.array;
          if (finalDistribution.array.length === 0) {
            finalDistribution = totalDistribution;
          } else {
            finalDistribution.append(totalDistribution);
          }
        }
      }
      if (finalDistribution) finalDistribution.joinX(this.fwhm);
      for (let entry of finalDistribution.array) {
        if (!entry.composition) continue;
        Object.assign(entry, getDerivedCompositionInfo(entry.composition));
      }
      this.confidence /= this.parts.length;
      this.cachedDistribution = finalDistribution;
      return finalDistribution;
    }
    getCSV() {
      return this.getText({
        delimiter: ', '
      });
    }
    getTSV() {
      return this.getText({
        delimiter: '\t'
      });
    }
    getTable() {
      let options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
      const {
        maxValue,
        xLabel = 'x',
        yLabel = 'y'
      } = options;
      let points = this.getDistribution().array;
      let factor = 1;
      if (maxValue) {
        let maxY = this.getMaxY(points);
        factor = maxValue / maxY;
      }
      return points.map(point => {
        let newPoint = {};
        newPoint[xLabel] = point.x;
        newPoint[yLabel] = point.y * factor;
        return newPoint;
      });
    }
    getText() {
      let options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
      const {
        delimiter = '\t',
        numberDecimals = 3
      } = options;
      let points = this.getDistribution().array;
      let csv = [];
      for (let point of points) {
        csv.push(`${point.x.toFixed(5)}${delimiter}${(point.y * 100).toFixed(numberDecimals)}`);
      }
      return csv.join('\n');
    }
    getMaxY(points) {
      let maxY = points[0].y;
      for (let point of points) {
        if (point.y > maxY) maxY = point.y;
      }
      return maxY;
    }
    getSumY(points) {
      let sumY = 0;
      for (let point of points) {
        sumY += point.y;
      }
      return sumY;
    }

    /**
     * Returns the isotopic distribution as an array of peaks
     * @param {object} [options={}]
     * @param {number} [options.maxValue=100]
     * @param {number} [options.sumValue] // if sumValue is defined, maxValue is ignored
     * @return {Array<any>} an object containing at least the 2 properties: x:[] and y:[]
     */
    getPeaks() {
      let options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
      const {
        maxValue = 100,
        sumValue
      } = options;
      let peaks = this.getDistribution().array;
      if (peaks.length === 0) return [];
      let factor = 1;
      if (sumValue) {
        let sumY = this.getSumY(peaks);
        factor = sumY / sumValue;
      } else if (maxValue) {
        let maxY = this.getMaxY(peaks);
        factor = maxY / maxValue;
      }
      if (factor !== 1) {
        // we need to copy the array because we prefer no side effects
        peaks = JSON.parse(JSON.stringify(peaks));
        for (const peak of peaks) {
          peak.y = peak.y / factor;
        }
      }
      return peaks;
    }

    /**
     * Returns the isotopic distirubtion
     * @param {object} [options={}]
     * @param {number} [options.maxValue=100]
     * @param {number} [options.sumValue] // if sumValue is defined, maxValue is ignored
     * @return {XY} an object containing at least the 2 properties: x:[] and y:[]
     */
    getXY() {
      let options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
      let peaks = this.getPeaks(options);
      if (peaks.length === 0) {
        return {
          x: [],
          y: []
        };
      }
      const result = {
        x: peaks.map(a => a.x),
        y: peaks.map(a => a.y)
      };
      for (let key of Object.keys(peaks[0]).filter(k => k !== 'x' && k !== 'y')) {
        result[key] = peaks.map(a => a[key]);
      }
      return result;
    }

    /**
     * Returns the isotopic distribution as the sum of gaussian
     * @param {object} [options={}]
     * @param {number} [options.gaussianWidth=10]
     * @param {number} [options.threshold=0.00001] // minimal height to return point
     * @param {number} [options.maxLength=1e6] // minimal height to return point
     * @param {number} [options.maxValue] // rescale Y to reach maxValue
     * @param {function} [options.peakWidthFct=(mz)=>(this.fwhm)]
     * @return {XY} isotopic distribution as an object containing 2 properties: x:[] and y:[]
     */

    getGaussian() {
      let options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
      const {
        peakWidthFct = () => this.fwhm,
        threshold = 0.00001,
        gaussianWidth = 10,
        maxValue,
        maxLength = 1e6
      } = options;
      let points = this.getTable({
        maxValue
      });
      if (points.length === 0) return {
        x: [],
        y: []
      };
      const from = Math.floor(options.from || points[0].x - 2);
      const to = Math.ceil(options.to || points[points.length - 1].x + 2);
      const nbPoints = Math.round((to - from) * gaussianWidth / this.fwhm + 1);
      if (nbPoints > maxLength) {
        throw Error(`Number of points is over the maxLength: ${nbPoints}>${maxLength}`);
      }
      let gaussianOptions = {
        from,
        to,
        nbPoints,
        peakWidthFct
      };
      let spectrumGenerator = new SpectrumGenerator(gaussianOptions);
      for (let point of points) {
        spectrumGenerator.addPeak([point.x, point.y]);
      }
      let spectrum = spectrumGenerator.getSpectrum({
        threshold
      });
      return spectrum;
    }
  }

  function calculateOverlapFromDiff(diffs) {
    if (diffs[1].length === 0) return 0;
    let sumPos = 0;
    for (let i = 0; i < diffs[1].length; i++) {
      sumPos += Math.abs(diffs[1][i]);
    }
    return 1 - sumPos;
  }

  /**
   * This code requires the use of an array like  [[x1,y1],[x2,y2], ...]
   * If it is not the right format, we will just convert it
   * Otherwise we return the correct format
   * @param {Peaks} peaks
   * @returns [number[], number[]]
   */
  function checkPeaks(peaks) {
    // if it is already a 2D array of points, we just return them
    if (Array.isArray(peaks) && Array.isArray(peaks[0]) && peaks.length === 2) {
      return peaks;
    }
    if (Array.isArray(peaks.x) && Array.isArray(peaks.y)) {
      return [peaks.x, peaks.y];
    }
    const x = new Array(peaks.length);
    const y = new Array(peaks.length);
    for (let i = 0; i < peaks.length; i++) {
      x[i] = peaks[i][0];
      y[i] = peaks[i][1];
    }
    return [x, y];
  }

  function extract(array, from, to) {
    const newArray = [[], []];
    let j = 0;
    const length = array[0] ? array[0].length : 0;
    for (let i = 0; i < length; i++) {
      if ((!from || array[0][i] >= from) && (!to || array[0][i] <= to)) {
        newArray[0][j] = array[0][i];
        newArray[1][j] = array[1][i];
        j++;
      }
    }
    return newArray;
  }

  // returns an new array based on array1 where there is a peak of array2 at a distance under width/2
  function getCommonArray(array1, array2, width) {
    const newArray = [[], []];
    let pos2 = 0;
    width /= 2;
    let j = 0;
    const array1Length = array1[0] ? array1[0].length : 0;
    const array2Length = array2[0] ? array2[0].length : 0;
    for (let i = 0; i < array1Length; i++) {
      while (pos2 < array2Length && array1[0][i] > array2[0][pos2] + width) {
        pos2++;
      }
      if (pos2 < array2Length && array1[0][i] > array2[0][pos2] - width) {
        newArray[0][j] = array1[0][i];
        newArray[1][j] = array1[1][i];
        j++;
      }
    }
    return newArray;
  }

  var mlStat = {};

  var array$1 = {};

  (function (exports) {

    function compareNumbers(a, b) {
      return a - b;
    }

    /**
     * Computes the sum of the given values
     * @param {Array} values
     * @returns {number}
     */
    exports.sum = function sum(values) {
      var sum = 0;
      for (var i = 0; i < values.length; i++) {
        sum += values[i];
      }
      return sum;
    };

    /**
     * Computes the maximum of the given values
     * @param {Array} values
     * @returns {number}
     */
    exports.max = function max(values) {
      var max = values[0];
      var l = values.length;
      for (var i = 1; i < l; i++) {
        if (values[i] > max) max = values[i];
      }
      return max;
    };

    /**
     * Computes the minimum of the given values
     * @param {Array} values
     * @returns {number}
     */
    exports.min = function min(values) {
      var min = values[0];
      var l = values.length;
      for (var i = 1; i < l; i++) {
        if (values[i] < min) min = values[i];
      }
      return min;
    };

    /**
     * Computes the min and max of the given values
     * @param {Array} values
     * @returns {{min: number, max: number}}
     */
    exports.minMax = function minMax(values) {
      var min = values[0];
      var max = values[0];
      var l = values.length;
      for (var i = 1; i < l; i++) {
        if (values[i] < min) min = values[i];
        if (values[i] > max) max = values[i];
      }
      return {
        min: min,
        max: max
      };
    };

    /**
     * Computes the arithmetic mean of the given values
     * @param {Array} values
     * @returns {number}
     */
    exports.arithmeticMean = function arithmeticMean(values) {
      var sum = 0;
      var l = values.length;
      for (var i = 0; i < l; i++) {
        sum += values[i];
      }
      return sum / l;
    };

    /**
     * {@link arithmeticMean}
     */
    exports.mean = exports.arithmeticMean;

    /**
     * Computes the geometric mean of the given values
     * @param {Array} values
     * @returns {number}
     */
    exports.geometricMean = function geometricMean(values) {
      var mul = 1;
      var l = values.length;
      for (var i = 0; i < l; i++) {
        mul *= values[i];
      }
      return Math.pow(mul, 1 / l);
    };

    /**
     * Computes the mean of the log of the given values
     * If the return value is exponentiated, it gives the same result as the
     * geometric mean.
     * @param {Array} values
     * @returns {number}
     */
    exports.logMean = function logMean(values) {
      var lnsum = 0;
      var l = values.length;
      for (var i = 0; i < l; i++) {
        lnsum += Math.log(values[i]);
      }
      return lnsum / l;
    };

    /**
     * Computes the weighted grand mean for a list of means and sample sizes
     * @param {Array} means - Mean values for each set of samples
     * @param {Array} samples - Number of original values for each set of samples
     * @returns {number}
     */
    exports.grandMean = function grandMean(means, samples) {
      var sum = 0;
      var n = 0;
      var l = means.length;
      for (var i = 0; i < l; i++) {
        sum += samples[i] * means[i];
        n += samples[i];
      }
      return sum / n;
    };

    /**
     * Computes the truncated mean of the given values using a given percentage
     * @param {Array} values
     * @param {number} percent - The percentage of values to keep (range: [0,1])
     * @param {boolean} [alreadySorted=false]
     * @returns {number}
     */
    exports.truncatedMean = function truncatedMean(values, percent, alreadySorted) {
      if (alreadySorted === undefined) alreadySorted = false;
      if (!alreadySorted) {
        values = [].concat(values).sort(compareNumbers);
      }
      var l = values.length;
      var k = Math.floor(l * percent);
      var sum = 0;
      for (var i = k; i < l - k; i++) {
        sum += values[i];
      }
      return sum / (l - 2 * k);
    };

    /**
     * Computes the harmonic mean of the given values
     * @param {Array} values
     * @returns {number}
     */
    exports.harmonicMean = function harmonicMean(values) {
      var sum = 0;
      var l = values.length;
      for (var i = 0; i < l; i++) {
        if (values[i] === 0) {
          throw new RangeError('value at index ' + i + 'is zero');
        }
        sum += 1 / values[i];
      }
      return l / sum;
    };

    /**
     * Computes the contraharmonic mean of the given values
     * @param {Array} values
     * @returns {number}
     */
    exports.contraHarmonicMean = function contraHarmonicMean(values) {
      var r1 = 0;
      var r2 = 0;
      var l = values.length;
      for (var i = 0; i < l; i++) {
        r1 += values[i] * values[i];
        r2 += values[i];
      }
      if (r2 < 0) {
        throw new RangeError('sum of values is negative');
      }
      return r1 / r2;
    };

    /**
     * Computes the median of the given values
     * @param {Array} values
     * @param {boolean} [alreadySorted=false]
     * @returns {number}
     */
    exports.median = function median(values, alreadySorted) {
      if (alreadySorted === undefined) alreadySorted = false;
      if (!alreadySorted) {
        values = [].concat(values).sort(compareNumbers);
      }
      var l = values.length;
      var half = Math.floor(l / 2);
      if (l % 2 === 0) {
        return (values[half - 1] + values[half]) * 0.5;
      } else {
        return values[half];
      }
    };

    /**
     * Computes the variance of the given values
     * @param {Array} values
     * @param {boolean} [unbiased=true] - if true, divide by (n-1); if false, divide by n.
     * @returns {number}
     */
    exports.variance = function variance(values, unbiased) {
      if (unbiased === undefined) unbiased = true;
      var theMean = exports.mean(values);
      var theVariance = 0;
      var l = values.length;
      for (var i = 0; i < l; i++) {
        var x = values[i] - theMean;
        theVariance += x * x;
      }
      if (unbiased) {
        return theVariance / (l - 1);
      } else {
        return theVariance / l;
      }
    };

    /**
     * Computes the standard deviation of the given values
     * @param {Array} values
     * @param {boolean} [unbiased=true] - if true, divide by (n-1); if false, divide by n.
     * @returns {number}
     */
    exports.standardDeviation = function standardDeviation(values, unbiased) {
      return Math.sqrt(exports.variance(values, unbiased));
    };
    exports.standardError = function standardError(values) {
      return exports.standardDeviation(values) / Math.sqrt(values.length);
    };

    /**
     * IEEE Transactions on biomedical engineering, vol. 52, no. 1, january 2005, p. 76-
     * Calculate the standard deviation via the Median of the absolute deviation
     *  The formula for the standard deviation only holds for Gaussian random variables.
     * @returns {{mean: number, stdev: number}}
     */
    exports.robustMeanAndStdev = function robustMeanAndStdev(y) {
      var mean = 0,
        stdev = 0;
      var length = y.length,
        i = 0;
      for (i = 0; i < length; i++) {
        mean += y[i];
      }
      mean /= length;
      var averageDeviations = new Array(length);
      for (i = 0; i < length; i++) averageDeviations[i] = Math.abs(y[i] - mean);
      averageDeviations.sort(compareNumbers);
      if (length % 2 === 1) {
        stdev = averageDeviations[(length - 1) / 2] / 0.6745;
      } else {
        stdev = 0.5 * (averageDeviations[length / 2] + averageDeviations[length / 2 - 1]) / 0.6745;
      }
      return {
        mean: mean,
        stdev: stdev
      };
    };
    exports.quartiles = function quartiles(values, alreadySorted) {
      if (typeof alreadySorted === 'undefined') alreadySorted = false;
      if (!alreadySorted) {
        values = [].concat(values).sort(compareNumbers);
      }
      var quart = values.length / 4;
      var q1 = values[Math.ceil(quart) - 1];
      var q2 = exports.median(values, true);
      var q3 = values[Math.ceil(quart * 3) - 1];
      return {
        q1: q1,
        q2: q2,
        q3: q3
      };
    };
    exports.pooledStandardDeviation = function pooledStandardDeviation(samples, unbiased) {
      return Math.sqrt(exports.pooledVariance(samples, unbiased));
    };
    exports.pooledVariance = function pooledVariance(samples, unbiased) {
      if (typeof unbiased === 'undefined') unbiased = true;
      var sum = 0;
      var length = 0,
        l = samples.length;
      for (var i = 0; i < l; i++) {
        var values = samples[i];
        var vari = exports.variance(values);
        sum += (values.length - 1) * vari;
        if (unbiased) length += values.length - 1;else length += values.length;
      }
      return sum / length;
    };
    exports.mode = function mode(values) {
      var l = values.length,
        itemCount = new Array(l),
        i;
      for (i = 0; i < l; i++) {
        itemCount[i] = 0;
      }
      var itemArray = new Array(l);
      var count = 0;
      for (i = 0; i < l; i++) {
        var index = itemArray.indexOf(values[i]);
        if (index >= 0) itemCount[index]++;else {
          itemArray[count] = values[i];
          itemCount[count] = 1;
          count++;
        }
      }
      var maxValue = 0,
        maxIndex = 0;
      for (i = 0; i < count; i++) {
        if (itemCount[i] > maxValue) {
          maxValue = itemCount[i];
          maxIndex = i;
        }
      }
      return itemArray[maxIndex];
    };
    exports.covariance = function covariance(vector1, vector2, unbiased) {
      if (typeof unbiased === 'undefined') unbiased = true;
      var mean1 = exports.mean(vector1);
      var mean2 = exports.mean(vector2);
      if (vector1.length !== vector2.length) throw 'Vectors do not have the same dimensions';
      var cov = 0,
        l = vector1.length;
      for (var i = 0; i < l; i++) {
        var x = vector1[i] - mean1;
        var y = vector2[i] - mean2;
        cov += x * y;
      }
      if (unbiased) return cov / (l - 1);else return cov / l;
    };
    exports.skewness = function skewness(values, unbiased) {
      if (typeof unbiased === 'undefined') unbiased = true;
      var theMean = exports.mean(values);
      var s2 = 0,
        s3 = 0,
        l = values.length;
      for (var i = 0; i < l; i++) {
        var dev = values[i] - theMean;
        s2 += dev * dev;
        s3 += dev * dev * dev;
      }
      var m2 = s2 / l;
      var m3 = s3 / l;
      var g = m3 / Math.pow(m2, 3 / 2.0);
      if (unbiased) {
        var a = Math.sqrt(l * (l - 1));
        var b = l - 2;
        return a / b * g;
      } else {
        return g;
      }
    };
    exports.kurtosis = function kurtosis(values, unbiased) {
      if (typeof unbiased === 'undefined') unbiased = true;
      var theMean = exports.mean(values);
      var n = values.length,
        s2 = 0,
        s4 = 0;
      for (var i = 0; i < n; i++) {
        var dev = values[i] - theMean;
        s2 += dev * dev;
        s4 += dev * dev * dev * dev;
      }
      var m2 = s2 / n;
      var m4 = s4 / n;
      if (unbiased) {
        var v = s2 / (n - 1);
        var a = n * (n + 1) / ((n - 1) * (n - 2) * (n - 3));
        var b = s4 / (v * v);
        var c = (n - 1) * (n - 1) / ((n - 2) * (n - 3));
        return a * b - 3 * c;
      } else {
        return m4 / (m2 * m2) - 3;
      }
    };
    exports.entropy = function entropy(values, eps) {
      if (typeof eps === 'undefined') eps = 0;
      var sum = 0,
        l = values.length;
      for (var i = 0; i < l; i++) sum += values[i] * Math.log(values[i] + eps);
      return -sum;
    };
    exports.weightedMean = function weightedMean(values, weights) {
      var sum = 0,
        l = values.length;
      for (var i = 0; i < l; i++) sum += values[i] * weights[i];
      return sum;
    };
    exports.weightedStandardDeviation = function weightedStandardDeviation(values, weights) {
      return Math.sqrt(exports.weightedVariance(values, weights));
    };
    exports.weightedVariance = function weightedVariance(values, weights) {
      var theMean = exports.weightedMean(values, weights);
      var vari = 0,
        l = values.length;
      var a = 0,
        b = 0;
      for (var i = 0; i < l; i++) {
        var z = values[i] - theMean;
        var w = weights[i];
        vari += w * (z * z);
        b += w;
        a += w * w;
      }
      return vari * (b / (b * b - a));
    };
    exports.center = function center(values, inPlace) {
      if (typeof inPlace === 'undefined') inPlace = false;
      var result = values;
      if (!inPlace) result = [].concat(values);
      var theMean = exports.mean(result),
        l = result.length;
      for (var i = 0; i < l; i++) result[i] -= theMean;
    };
    exports.standardize = function standardize(values, standardDev, inPlace) {
      if (typeof standardDev === 'undefined') standardDev = exports.standardDeviation(values);
      if (typeof inPlace === 'undefined') inPlace = false;
      var l = values.length;
      var result = inPlace ? values : new Array(l);
      for (var i = 0; i < l; i++) result[i] = values[i] / standardDev;
      return result;
    };
    exports.cumulativeSum = function cumulativeSum(array) {
      var l = array.length;
      var result = new Array(l);
      result[0] = array[0];
      for (var i = 1; i < l; i++) result[i] = result[i - 1] + array[i];
      return result;
    };
  })(array$1);

  var matrix = {};

  (function (exports) {

    var arrayStat = array$1;
    function compareNumbers(a, b) {
      return a - b;
    }
    exports.max = function max(matrix) {
      var max = -Infinity;
      for (var i = 0; i < matrix.length; i++) {
        for (var j = 0; j < matrix[i].length; j++) {
          if (matrix[i][j] > max) max = matrix[i][j];
        }
      }
      return max;
    };
    exports.min = function min(matrix) {
      var min = Infinity;
      for (var i = 0; i < matrix.length; i++) {
        for (var j = 0; j < matrix[i].length; j++) {
          if (matrix[i][j] < min) min = matrix[i][j];
        }
      }
      return min;
    };
    exports.minMax = function minMax(matrix) {
      var min = Infinity;
      var max = -Infinity;
      for (var i = 0; i < matrix.length; i++) {
        for (var j = 0; j < matrix[i].length; j++) {
          if (matrix[i][j] < min) min = matrix[i][j];
          if (matrix[i][j] > max) max = matrix[i][j];
        }
      }
      return {
        min: min,
        max: max
      };
    };
    exports.entropy = function entropy(matrix, eps) {
      if (typeof eps === 'undefined') {
        eps = 0;
      }
      var sum = 0,
        l1 = matrix.length,
        l2 = matrix[0].length;
      for (var i = 0; i < l1; i++) {
        for (var j = 0; j < l2; j++) {
          sum += matrix[i][j] * Math.log(matrix[i][j] + eps);
        }
      }
      return -sum;
    };
    exports.mean = function mean(matrix, dimension) {
      if (typeof dimension === 'undefined') {
        dimension = 0;
      }
      var rows = matrix.length,
        cols = matrix[0].length,
        theMean,
        N,
        i,
        j;
      if (dimension === -1) {
        theMean = [0];
        N = rows * cols;
        for (i = 0; i < rows; i++) {
          for (j = 0; j < cols; j++) {
            theMean[0] += matrix[i][j];
          }
        }
        theMean[0] /= N;
      } else if (dimension === 0) {
        theMean = new Array(cols);
        N = rows;
        for (j = 0; j < cols; j++) {
          theMean[j] = 0;
          for (i = 0; i < rows; i++) {
            theMean[j] += matrix[i][j];
          }
          theMean[j] /= N;
        }
      } else if (dimension === 1) {
        theMean = new Array(rows);
        N = cols;
        for (j = 0; j < rows; j++) {
          theMean[j] = 0;
          for (i = 0; i < cols; i++) {
            theMean[j] += matrix[j][i];
          }
          theMean[j] /= N;
        }
      } else {
        throw new Error('Invalid dimension');
      }
      return theMean;
    };
    exports.sum = function sum(matrix, dimension) {
      if (typeof dimension === 'undefined') {
        dimension = 0;
      }
      var rows = matrix.length,
        cols = matrix[0].length,
        theSum,
        i,
        j;
      if (dimension === -1) {
        theSum = [0];
        for (i = 0; i < rows; i++) {
          for (j = 0; j < cols; j++) {
            theSum[0] += matrix[i][j];
          }
        }
      } else if (dimension === 0) {
        theSum = new Array(cols);
        for (j = 0; j < cols; j++) {
          theSum[j] = 0;
          for (i = 0; i < rows; i++) {
            theSum[j] += matrix[i][j];
          }
        }
      } else if (dimension === 1) {
        theSum = new Array(rows);
        for (j = 0; j < rows; j++) {
          theSum[j] = 0;
          for (i = 0; i < cols; i++) {
            theSum[j] += matrix[j][i];
          }
        }
      } else {
        throw new Error('Invalid dimension');
      }
      return theSum;
    };
    exports.product = function product(matrix, dimension) {
      if (typeof dimension === 'undefined') {
        dimension = 0;
      }
      var rows = matrix.length,
        cols = matrix[0].length,
        theProduct,
        i,
        j;
      if (dimension === -1) {
        theProduct = [1];
        for (i = 0; i < rows; i++) {
          for (j = 0; j < cols; j++) {
            theProduct[0] *= matrix[i][j];
          }
        }
      } else if (dimension === 0) {
        theProduct = new Array(cols);
        for (j = 0; j < cols; j++) {
          theProduct[j] = 1;
          for (i = 0; i < rows; i++) {
            theProduct[j] *= matrix[i][j];
          }
        }
      } else if (dimension === 1) {
        theProduct = new Array(rows);
        for (j = 0; j < rows; j++) {
          theProduct[j] = 1;
          for (i = 0; i < cols; i++) {
            theProduct[j] *= matrix[j][i];
          }
        }
      } else {
        throw new Error('Invalid dimension');
      }
      return theProduct;
    };
    exports.standardDeviation = function standardDeviation(matrix, means, unbiased) {
      var vari = exports.variance(matrix, means, unbiased),
        l = vari.length;
      for (var i = 0; i < l; i++) {
        vari[i] = Math.sqrt(vari[i]);
      }
      return vari;
    };
    exports.variance = function variance(matrix, means, unbiased) {
      if (typeof unbiased === 'undefined') {
        unbiased = true;
      }
      means = means || exports.mean(matrix);
      var rows = matrix.length;
      if (rows === 0) return [];
      var cols = matrix[0].length;
      var vari = new Array(cols);
      for (var j = 0; j < cols; j++) {
        var sum1 = 0,
          sum2 = 0,
          x = 0;
        for (var i = 0; i < rows; i++) {
          x = matrix[i][j] - means[j];
          sum1 += x;
          sum2 += x * x;
        }
        if (unbiased) {
          vari[j] = (sum2 - sum1 * sum1 / rows) / (rows - 1);
        } else {
          vari[j] = (sum2 - sum1 * sum1 / rows) / rows;
        }
      }
      return vari;
    };
    exports.median = function median(matrix) {
      var rows = matrix.length,
        cols = matrix[0].length;
      var medians = new Array(cols);
      for (var i = 0; i < cols; i++) {
        var data = new Array(rows);
        for (var j = 0; j < rows; j++) {
          data[j] = matrix[j][i];
        }
        data.sort(compareNumbers);
        var N = data.length;
        if (N % 2 === 0) {
          medians[i] = (data[N / 2] + data[N / 2 - 1]) * 0.5;
        } else {
          medians[i] = data[Math.floor(N / 2)];
        }
      }
      return medians;
    };
    exports.mode = function mode(matrix) {
      var rows = matrix.length,
        cols = matrix[0].length,
        modes = new Array(cols),
        i,
        j;
      for (i = 0; i < cols; i++) {
        var itemCount = new Array(rows);
        for (var k = 0; k < rows; k++) {
          itemCount[k] = 0;
        }
        var itemArray = new Array(rows);
        var count = 0;
        for (j = 0; j < rows; j++) {
          var index = itemArray.indexOf(matrix[j][i]);
          if (index >= 0) {
            itemCount[index]++;
          } else {
            itemArray[count] = matrix[j][i];
            itemCount[count] = 1;
            count++;
          }
        }
        var maxValue = 0,
          maxIndex = 0;
        for (j = 0; j < count; j++) {
          if (itemCount[j] > maxValue) {
            maxValue = itemCount[j];
            maxIndex = j;
          }
        }
        modes[i] = itemArray[maxIndex];
      }
      return modes;
    };
    exports.skewness = function skewness(matrix, unbiased) {
      if (typeof unbiased === 'undefined') unbiased = true;
      var means = exports.mean(matrix);
      var n = matrix.length,
        l = means.length;
      var skew = new Array(l);
      for (var j = 0; j < l; j++) {
        var s2 = 0,
          s3 = 0;
        for (var i = 0; i < n; i++) {
          var dev = matrix[i][j] - means[j];
          s2 += dev * dev;
          s3 += dev * dev * dev;
        }
        var m2 = s2 / n;
        var m3 = s3 / n;
        var g = m3 / Math.pow(m2, 3 / 2);
        if (unbiased) {
          var a = Math.sqrt(n * (n - 1));
          var b = n - 2;
          skew[j] = a / b * g;
        } else {
          skew[j] = g;
        }
      }
      return skew;
    };
    exports.kurtosis = function kurtosis(matrix, unbiased) {
      if (typeof unbiased === 'undefined') unbiased = true;
      var means = exports.mean(matrix);
      var n = matrix.length,
        m = matrix[0].length;
      var kurt = new Array(m);
      for (var j = 0; j < m; j++) {
        var s2 = 0,
          s4 = 0;
        for (var i = 0; i < n; i++) {
          var dev = matrix[i][j] - means[j];
          s2 += dev * dev;
          s4 += dev * dev * dev * dev;
        }
        var m2 = s2 / n;
        var m4 = s4 / n;
        if (unbiased) {
          var v = s2 / (n - 1);
          var a = n * (n + 1) / ((n - 1) * (n - 2) * (n - 3));
          var b = s4 / (v * v);
          var c = (n - 1) * (n - 1) / ((n - 2) * (n - 3));
          kurt[j] = a * b - 3 * c;
        } else {
          kurt[j] = m4 / (m2 * m2) - 3;
        }
      }
      return kurt;
    };
    exports.standardError = function standardError(matrix) {
      var samples = matrix.length;
      var standardDeviations = exports.standardDeviation(matrix);
      var l = standardDeviations.length;
      var standardErrors = new Array(l);
      var sqrtN = Math.sqrt(samples);
      for (var i = 0; i < l; i++) {
        standardErrors[i] = standardDeviations[i] / sqrtN;
      }
      return standardErrors;
    };
    exports.covariance = function covariance(matrix, dimension) {
      return exports.scatter(matrix, undefined, dimension);
    };
    exports.scatter = function scatter(matrix, divisor, dimension) {
      if (typeof dimension === 'undefined') {
        dimension = 0;
      }
      if (typeof divisor === 'undefined') {
        if (dimension === 0) {
          divisor = matrix.length - 1;
        } else if (dimension === 1) {
          divisor = matrix[0].length - 1;
        }
      }
      var means = exports.mean(matrix, dimension);
      var rows = matrix.length;
      if (rows === 0) {
        return [[]];
      }
      var cols = matrix[0].length,
        cov,
        i,
        j,
        s,
        k;
      if (dimension === 0) {
        cov = new Array(cols);
        for (i = 0; i < cols; i++) {
          cov[i] = new Array(cols);
        }
        for (i = 0; i < cols; i++) {
          for (j = i; j < cols; j++) {
            s = 0;
            for (k = 0; k < rows; k++) {
              s += (matrix[k][j] - means[j]) * (matrix[k][i] - means[i]);
            }
            s /= divisor;
            cov[i][j] = s;
            cov[j][i] = s;
          }
        }
      } else if (dimension === 1) {
        cov = new Array(rows);
        for (i = 0; i < rows; i++) {
          cov[i] = new Array(rows);
        }
        for (i = 0; i < rows; i++) {
          for (j = i; j < rows; j++) {
            s = 0;
            for (k = 0; k < cols; k++) {
              s += (matrix[j][k] - means[j]) * (matrix[i][k] - means[i]);
            }
            s /= divisor;
            cov[i][j] = s;
            cov[j][i] = s;
          }
        }
      } else {
        throw new Error('Invalid dimension');
      }
      return cov;
    };
    exports.correlation = function correlation(matrix) {
      var means = exports.mean(matrix),
        standardDeviations = exports.standardDeviation(matrix, true, means),
        scores = exports.zScores(matrix, means, standardDeviations),
        rows = matrix.length,
        cols = matrix[0].length,
        i,
        j;
      var cor = new Array(cols);
      for (i = 0; i < cols; i++) {
        cor[i] = new Array(cols);
      }
      for (i = 0; i < cols; i++) {
        for (j = i; j < cols; j++) {
          var c = 0;
          for (var k = 0, l = scores.length; k < l; k++) {
            c += scores[k][j] * scores[k][i];
          }
          c /= rows - 1;
          cor[i][j] = c;
          cor[j][i] = c;
        }
      }
      return cor;
    };
    exports.zScores = function zScores(matrix, means, standardDeviations) {
      means = means || exports.mean(matrix);
      if (typeof standardDeviations === 'undefined') standardDeviations = exports.standardDeviation(matrix, true, means);
      return exports.standardize(exports.center(matrix, means, false), standardDeviations, true);
    };
    exports.center = function center(matrix, means, inPlace) {
      means = means || exports.mean(matrix);
      var result = matrix,
        l = matrix.length,
        i,
        j,
        jj;
      if (!inPlace) {
        result = new Array(l);
        for (i = 0; i < l; i++) {
          result[i] = new Array(matrix[i].length);
        }
      }
      for (i = 0; i < l; i++) {
        var row = result[i];
        for (j = 0, jj = row.length; j < jj; j++) {
          row[j] = matrix[i][j] - means[j];
        }
      }
      return result;
    };
    exports.standardize = function standardize(matrix, standardDeviations, inPlace) {
      if (typeof standardDeviations === 'undefined') standardDeviations = exports.standardDeviation(matrix);
      var result = matrix,
        l = matrix.length,
        i,
        j,
        jj;
      if (!inPlace) {
        result = new Array(l);
        for (i = 0; i < l; i++) {
          result[i] = new Array(matrix[i].length);
        }
      }
      for (i = 0; i < l; i++) {
        var resultRow = result[i];
        var sourceRow = matrix[i];
        for (j = 0, jj = resultRow.length; j < jj; j++) {
          if (standardDeviations[j] !== 0 && !isNaN(standardDeviations[j])) {
            resultRow[j] = sourceRow[j] / standardDeviations[j];
          }
        }
      }
      return result;
    };
    exports.weightedVariance = function weightedVariance(matrix, weights) {
      var means = exports.mean(matrix);
      var rows = matrix.length;
      if (rows === 0) return [];
      var cols = matrix[0].length;
      var vari = new Array(cols);
      for (var j = 0; j < cols; j++) {
        var sum = 0;
        var a = 0,
          b = 0;
        for (var i = 0; i < rows; i++) {
          var z = matrix[i][j] - means[j];
          var w = weights[i];
          sum += w * (z * z);
          b += w;
          a += w * w;
        }
        vari[j] = sum * (b / (b * b - a));
      }
      return vari;
    };
    exports.weightedMean = function weightedMean(matrix, weights, dimension) {
      if (typeof dimension === 'undefined') {
        dimension = 0;
      }
      var rows = matrix.length;
      if (rows === 0) return [];
      var cols = matrix[0].length,
        means,
        i,
        ii,
        j,
        w,
        row;
      if (dimension === 0) {
        means = new Array(cols);
        for (i = 0; i < cols; i++) {
          means[i] = 0;
        }
        for (i = 0; i < rows; i++) {
          row = matrix[i];
          w = weights[i];
          for (j = 0; j < cols; j++) {
            means[j] += row[j] * w;
          }
        }
      } else if (dimension === 1) {
        means = new Array(rows);
        for (i = 0; i < rows; i++) {
          means[i] = 0;
        }
        for (j = 0; j < rows; j++) {
          row = matrix[j];
          w = weights[j];
          for (i = 0; i < cols; i++) {
            means[j] += row[i] * w;
          }
        }
      } else {
        throw new Error('Invalid dimension');
      }
      var weightSum = arrayStat.sum(weights);
      if (weightSum !== 0) {
        for (i = 0, ii = means.length; i < ii; i++) {
          means[i] /= weightSum;
        }
      }
      return means;
    };
    exports.weightedCovariance = function weightedCovariance(matrix, weights, means, dimension) {
      dimension = dimension || 0;
      means = means || exports.weightedMean(matrix, weights, dimension);
      var s1 = 0,
        s2 = 0;
      for (var i = 0, ii = weights.length; i < ii; i++) {
        s1 += weights[i];
        s2 += weights[i] * weights[i];
      }
      var factor = s1 / (s1 * s1 - s2);
      return exports.weightedScatter(matrix, weights, means, factor, dimension);
    };
    exports.weightedScatter = function weightedScatter(matrix, weights, means, factor, dimension) {
      dimension = dimension || 0;
      means = means || exports.weightedMean(matrix, weights, dimension);
      if (typeof factor === 'undefined') {
        factor = 1;
      }
      var rows = matrix.length;
      if (rows === 0) {
        return [[]];
      }
      var cols = matrix[0].length,
        cov,
        i,
        j,
        k,
        s;
      if (dimension === 0) {
        cov = new Array(cols);
        for (i = 0; i < cols; i++) {
          cov[i] = new Array(cols);
        }
        for (i = 0; i < cols; i++) {
          for (j = i; j < cols; j++) {
            s = 0;
            for (k = 0; k < rows; k++) {
              s += weights[k] * (matrix[k][j] - means[j]) * (matrix[k][i] - means[i]);
            }
            cov[i][j] = s * factor;
            cov[j][i] = s * factor;
          }
        }
      } else if (dimension === 1) {
        cov = new Array(rows);
        for (i = 0; i < rows; i++) {
          cov[i] = new Array(rows);
        }
        for (i = 0; i < rows; i++) {
          for (j = i; j < rows; j++) {
            s = 0;
            for (k = 0; k < cols; k++) {
              s += weights[k] * (matrix[j][k] - means[j]) * (matrix[i][k] - means[i]);
            }
            cov[i][j] = s * factor;
            cov[j][i] = s * factor;
          }
        }
      } else {
        throw new Error('Invalid dimension');
      }
      return cov;
    };
  })(matrix);

  var array = mlStat.array = array$1;
  mlStat.matrix = matrix;

  function normalize(array$1) {
    const min = array.min(array$1[1]);
    const max = array.max(array$1[1]);
    const sum = array.sum(array$1[1]);
    const length = array$1[1] ? array$1[1].length : 0;
    if (sum !== 0) {
      for (let i = 0; i < length; i++) {
        array$1[1][i] /= sum;
      }
    }
    return {
      sum,
      min,
      max
    };
  }

  // this method will systematically take care of both array
  function commonExtractAndNormalize(array1, array2, width, from, to, common) {
    if (!Array.isArray(array1) || !Array.isArray(array2)) {
      return {
        info: undefined,
        data: undefined
      };
    }
    const extract1 = extract(array1, from, to);
    const extract2 = extract(array2, from, to);
    let common1, common2, info1, info2;
    if (common & COMMON_SECOND) {
      common1 = getCommonArray(extract1, extract2, width);
      info1 = normalize(common1);
    } else {
      common1 = extract1;
      info1 = normalize(common1);
    }
    if (common & COMMON_FIRST) {
      common2 = getCommonArray(extract2, extract1, width);
      info2 = normalize(common2);
    } else {
      common2 = extract2;
      info2 = normalize(common2);
    }
    return {
      info1,
      info2,
      data1: common1,
      data2: common2
    };
  }

  function extractAndNormalize(array, from, to) {
    if (!Array.isArray(array)) {
      return {
        info: undefined,
        data: undefined
      };
    }
    const newArray = extract(array, from, to);
    const info = normalize(newArray);
    return {
      info,
      data: newArray
    };
  }

  // Adapted from: http://stackoverflow.com/questions/563198/how-do-you-detect-where-two-line-segments-intersect/1968345#1968345
  function getIntersection(segment1, segment2) {
    const p0X = segment1[0][0];
    const p0Y = segment1[0][1];
    const p1X = segment1[1][0];
    const p1Y = segment1[1][1];
    const p2X = segment2[0][0];
    const p2Y = segment2[0][1];
    const p3X = segment2[1][0];
    const p3Y = segment2[1][1];
    const s1X = p1X - p0X;
    const s1Y = p1Y - p0Y;
    const s2X = p3X - p2X;
    const s2Y = p3Y - p2Y;
    const s = (-s1Y * (p0X - p2X) + s1X * (p0Y - p2Y)) / (-s2X * s1Y + s1X * s2Y);
    const t = (s2X * (p0Y - p2Y) - s2Y * (p0X - p2X)) / (-s2X * s1Y + s1X * s2Y);
    if (s >= 0 && s <= 1 && t >= 0 && t <= 1) {
      return {
        x: p0X + t * s1X,
        y: p0Y + t * s1Y
      };
    }
    return null; // No collision
  }

  const COMMON_NO = 0;
  const COMMON_FIRST = 1;
  const COMMON_SECOND = 2;
  const COMMON_BOTH = 3;
  /**
   * A number, or a string containing a number.
   * @typedef {([number[],number[]]|[number,number][]|{x:number[],y:number[]})} Peaks
   */
  /**
   * Create a comparator class
   * {object} [options={}]
   * {string} [options.common=''] should we take only common peaks 'first', 'second', 'both', ''
   * {number} [options.widthBottom=2] bottom trapezoid width for similarity evaluation
   * {number} [options.widthTop=1] top trapezoid width for similarity evaluation
   * {number} [options.from] from region used for similarity calculation
   * {number} [options.to] to region used for similarity calculation
   */
  class Comparator {
    constructor() {
      let options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
      this.array1 = [];
      this.array2 = [];
      this.setOptions(options);
    }
    /*
       2 formats are allowed:
       [[x1,x2,...],[y1,y2,...]] or [[x1,y1],[x2,y2], ...]
      */
    setOptions() {
      let options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
      if (typeof options.common === 'string') {
        if (options.common.toLowerCase() === 'first') {
          this.common = COMMON_FIRST;
        } else if (options.common.toLowerCase() === 'second') {
          this.common = COMMON_SECOND;
        } else if (options.common.toLowerCase() === 'both') {
          this.common = COMMON_BOTH;
        } else {
          this.common = COMMON_NO;
        }
      } else if (options.common === true) {
        this.common = COMMON_BOTH;
      } else {
        this.common = COMMON_NO;
      }
      this.trapezoid = options.trapezoid;
      this.commonFactor = options.commonFactor || this.commonFactor || 4;
      const {
        widthBottom = this.widthBottom || 2,
        widthTop = this.widthTop || 1,
        from = this.from,
        to = this.to
      } = options;
      this.setTrapezoid(widthBottom, widthTop);
      this.setFromTo(from, to);
    }
    /**
     *
     * @param {Peaks} peaks
     */
    setPeaks1(peaks) {
      this.array1 = checkPeaks(peaks);
      if (this.common) {
        const extracts = commonExtractAndNormalize(this.array1, this.array2, this.widthBottom, this.from, this.to, this.common);
        this.array1Extract = extracts.data1;
        this.array1ExtractInfo = extracts.info1;
        this.array2Extract = extracts.data2;
        this.array2ExtractInfo = extracts.info2;
      } else {
        const extract = extractAndNormalize(this.array1, this.from, this.to);
        this.array1Extract = extract.data;
        this.array1ExtractInfo = extract.info;
      }
    }
    /**
     *
     * @param {Peaks} peaks
     */
    setPeaks2(peaks) {
      this.array2 = checkPeaks(peaks);
      if (this.common) {
        const extracts = commonExtractAndNormalize(this.array1, this.array2, this.widthBottom, this.from, this.to, this.common);
        this.array1Extract = extracts.data1;
        this.array1ExtractInfo = extracts.info1;
        this.array2Extract = extracts.data2;
        this.array2ExtractInfo = extracts.info2;
      } else {
        const extract = extractAndNormalize(this.array2, this.from, this.to);
        this.array2Extract = extract.data;
        this.array2ExtractInfo = extract.info;
      }
    }
    getExtract1() {
      return this.array1Extract;
    }
    getExtract2() {
      return this.array2Extract;
    }
    getExtractInfo1() {
      return this.array1ExtractInfo;
    }
    getExtractInfo2() {
      return this.array2ExtractInfo;
    }
    /**
     * Set the new bottom and top width of the trapezoid
     * @param {number} newWidthBottom
     * @param {number} newWidthTop
     */
    setTrapezoid(newWidthBottom, newWidthTop) {
      this.widthTop = newWidthTop;
      this.widthBottom = newWidthBottom;
      this.widthSlope = (this.widthBottom - this.widthTop) / 2;
      if (this.widthBottom < this.widthTop) {
        throw new Error('widthBottom has to be larger than widthTop');
      }
    }
    /**
     * Set the from / to for comparison
     * @param {number} newFrom - set the new from value
     * @param {number} newTo - set the new to value
     * @returns
     */
    setFromTo(newFrom, newTo) {
      if (newFrom === this.from && newTo === this.to) return;
      this.from = newFrom;
      this.to = newTo;
      if (this.common) {
        const extracts = commonExtractAndNormalize(this.array1, this.array2, this.widthBottom, this.from, this.to, this.common, this.commonFactor);
        this.array1Extract = extracts.data1;
        this.array1ExtractInfo = extracts.info1;
        this.array2Extract = extracts.data2;
        this.array2ExtractInfo = extracts.info2;
      } else {
        let extract1 = extractAndNormalize(this.array1, this.from, this.to);
        this.array1Extract = extract1.data;
        this.array1ExtractInfo = extract1.info;
        let extract2 = extractAndNormalize(this.array2, this.from, this.to);
        this.array2Extract = extract2.data;
        this.array2ExtractInfo = extract2.info;
      }
    }
    /**
     *
     * @param {number} x1
     * @param {number} y1
     * @param {number} x2
     * @param {number} y2
     * @returns
     */
    getOverlap(x1, y1, x2, y2) {
      if (y1 === 0 || y2 === 0) return 0;
      // TAKE CARE !!! We multiply the diff by 2 !!!
      const diff = Math.abs(x1 - x2) * 2;
      if (diff > this.widthBottom) return 0;
      if (diff <= this.widthTop) {
        return Math.min(y1, y2);
      }
      const maxValue = Math.max(y1, y2) * (this.widthBottom - diff) / (this.widthBottom - this.widthTop);
      return Math.min(y1, y2, maxValue);
    }
    /**
     * This is the old trapezoid similarity
     * @param {number} x1
     * @param {number} y1
     * @param {number} x2
     * @param {number} y2
     * @param {number} widthTop
     * @param {number} widthBottom
     * @returns
     */
    getOverlapTrapezoid(x1, y1, x2, y2, widthTop, widthBottom) {
      // eslint-disable-next-line no-console
      console.error('getOverlapTrapezoid should not be used anymore');
      const factor = 2 / (widthTop + widthBottom); // correction for surface=1
      if (y1 === 0 || y2 === 0) return 0;
      if (x1 === x2) {
        // they have the same position
        return Math.min(y1, y2);
      }
      const diff = Math.abs(x1 - x2);
      if (diff >= widthBottom) return 0;
      if (y1 === y2) {
        // do they have the same height ???
        // we need to find the common length
        if (diff <= widthTop) {
          return ((widthTop + widthBottom) / 2 - diff) * y1 * factor;
        } else if (diff <= widthBottom) {
          return (widthBottom - diff) * y1 / 2 * (diff - widthTop) / (widthBottom - widthTop) * factor;
        }
        return 0;
      } else {
        // the height are different and not the same position ...
        // we need to consider only one segment to find its intersection
        const small = Math.min(y1, y2);
        const big = Math.max(y1, y2);
        const targets = [[[0, 0], [this.widthSlope, small]], [[this.widthSlope, small], [this.widthSlope + widthTop, small]], [[widthTop + this.widthSlope, small], [widthBottom, 0]]];
        let segment;
        if (x1 > x2 && y1 > y2 || x1 < x2 && y1 < y2) {
          segment = [[diff, 0], [diff + this.widthSlope, big]];
        } else {
          segment = [[diff + this.widthSlope, big], [diff, 0]];
        }
        for (let i = 0; i < 3; i++) {
          const intersection = getIntersection(targets[i], segment);
          if (intersection) {
            switch (i) {
              case 0:
                return small - diff * intersection.y / 2 * factor;
              case 1:
                // to simplify ...
                //     console.log("           ",widthSlope,small,big,intersection.x)
                return (this.widthSlope * small / (2 * big) * small + (widthTop + this.widthSlope - intersection.x) * small + this.widthSlope * small / 2) * factor;
              case 2:
                return (widthBottom - diff) * intersection.y / 2 * factor;
              default:
                throw new Error(`unexpected intersection value: ${i}`);
            }
          }
        }
      }
      return NaN;
    }
    /**
     * This method calculates the total diff. The sum of positive value will yield to overlap
     * @returns
     */
    calculateDiff() {
      // we need to take 2 pointers
      // and travel progressively between them ...
      const newFirst = [this.array1Extract[0].slice(), this.array1Extract[1].slice()];
      const newSecond = [this.array2Extract[0].slice(), this.array2Extract[1].slice()];
      const array1Length = this.array1Extract[0] ? this.array1Extract[0].length : 0;
      const array2Length = this.array2Extract[0] ? this.array2Extract[0].length : 0;
      let pos1 = 0;
      let pos2 = 0;
      let previous2 = 0;
      while (pos1 < array1Length) {
        const diff = newFirst[0][pos1] - this.array2Extract[0][pos2];
        if (Math.abs(diff) < this.widthBottom) {
          // there is some overlap
          let overlap;
          if (this.trapezoid) {
            // old trapezoid overlap similarity
            overlap = this.getOverlapTrapezoid(newFirst[0][pos1], newFirst[1][pos1], newSecond[0][pos2], newSecond[1][pos2], this.widthTop, this.widthBottom);
          } else {
            overlap = this.getOverlap(newFirst[0][pos1], newFirst[1][pos1], newSecond[0][pos2], newSecond[1][pos2], this.widthTop, this.widthBottom);
          }
          newFirst[1][pos1] -= overlap;
          newSecond[1][pos2] -= overlap;
          if (pos2 < array2Length - 1) {
            pos2++;
          } else {
            pos1++;
            pos2 = previous2;
          }
        } else if (diff > 0 && pos2 < array2Length - 1) {
          pos2++;
          previous2 = pos2;
        } else {
          pos1++;
          pos2 = previous2;
        }
      }
      return newSecond;
    }
    /**
     * Set the new peaks and return info
     * @param {Peaks} newPeaks1
     * @param {Peaks} newPeaks2
     * @returns
     */
    getSimilarity(newPeaks1, newPeaks2) {
      if (newPeaks1) this.setPeaks1(newPeaks1);
      if (newPeaks2) this.setPeaks2(newPeaks2);
      const diff = this.calculateDiff();
      return {
        diff,
        extract1: this.getExtract1(),
        extract2: this.getExtract2(),
        extractInfo1: this.getExtractInfo1(),
        extractInfo2: this.getExtractInfo2(),
        similarity: calculateOverlapFromDiff(diff),
        widthBottom: this.widthBottom,
        widthTop: this.widthTop
      };
    }
    /**
     * This works mainly when you have a array1 that is fixed
     * newPeaks2 have to be normalized ! (sum to 1)
     * @param {Peaks} newPeaks2
     * @param {number} from
     * @param {number} to
     * @returns
     */
    fastSimilarity(newPeaks2, from, to) {
      this.array1Extract = extract(this.array1, from, to);
      this.array2Extract = newPeaks2;
      if (this.common & COMMON_SECOND) {
        this.array1Extract = getCommonArray(this.array1Extract, this.array2Extract, this.widthBottom);
      }
      normalize(this.array1Extract);
      const diff = this.calculateDiff();
      return calculateOverlapFromDiff(diff);
    }
  }

  /**
  Search for an experimental monoisotopic mass and calculate the similarity
  * @param {object}   [options={}]
  * @param {array}    [options.databases] - an array containing the name of the databases so search, by default all
  * @param {boolean}  [options.flatten] - should we return the array as a flat result
  * @param {function} [options.onStep] - Callback to do after each step
  * @param {string}   [options.ionizations=''] - Comma separated list of ionizations (to charge the molecule)
  * @param {object}   [options.minSimilarity=0.5] - min similarity value

  * @param {object}   [options.filter={}]
  * @param {boolean}  [options.filter.forceIonization=false] - If true ignore existing ionizations
  * @param {number}   [options.filter.msem] - Observed monoisotopic mass in mass spectrometer
  * @param {number}   [options.filter.precision=1000] - The precision on the experimental mass
  * @param {number}   [options.filter.minCharge=-Infinity] - Minimal charge
  * @param {number}   [options.filter.maxCharge=+Infinity] - Maximal charge
  * @param {object}   [options.filter.unsaturation={}]
  * @param {number}   [options.filter.unsaturation.min=-Infinity] - Minimal unsaturation
  * @param {number}   [options.filter.unsaturation.max=+Infinity] - Maximal unsaturation
  * @param {boolean}   [options.filter.unsaturation.onlyInteger=false] - Integer unsaturation
  * @param {boolean}   [options.filter.unsaturation.onlyNonInteger=false] - Non integer unsaturation
  * @param {object}   [options.filter.atoms] - object of atom:{min, max}
  * @param {object}   [options.filter.callback] - a function to filter the MF
  * @param {object}   [options.similarity={}]
  * @param {object}   [options.similarity.widthBottom]
  * @param {object}   [options.similarity.widthTop]
  * @param {object}   [options.similarity.widthFunction] - function called with mass that should return an object width containing top and bottom
  * @param {object}   [options.similarity.zone={}]
  * @param {object}   [options.similarity.zone.low=-0.5] - window shift based on observed monoisotopic mass
  * @param {object}   [options.similarity.zone.high=2.5] - to value for the comparison window
  * @param {object}   [options.similarity.common]
  * @returns {Promise}
  */

  async function searchSimilarity(emdb) {
    let options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
    const {
      similarity = {},
      minSimilarity = 0.5,
      filter = {},
      onStep
    } = options;
    let width = {
      bottom: similarity.widthBottom,
      top: similarity.widthTop
    };
    if (!emdb.experimentalSpectrum || !emdb.experimentalSpectrum.data.x.length > 0) {
      throw Error('You need to add an experimental spectrum first using setMassSpectrum');
    }
    let experimentalData = emdb.experimentalSpectrum.data;
    let sumY = emdb.experimentalSpectrum.sumY();

    // the result of emdb query will be stored in a property 'ms'

    let results = emdb.searchMSEM(filter.msem, options);
    let flatEntries = [];
    if (!options.flatten) {
      for (let database of Object.keys(results)) {
        for (let entry of results[database]) {
          flatEntries.push(entry);
        }
      }
    } else {
      flatEntries = results;
    }
    let {
      widthFunction,
      zone = {}
    } = similarity;
    if (widthFunction && typeof widthFunction === 'string') {
      // eslint-disable-next-line no-new-func
      widthFunction = new Function('mass', widthFunction);
      let checkTopBottom = widthFunction(123);
      if (!checkTopBottom.bottom || !checkTopBottom.top) {
        throw Error('widthFunction should return an object with bottom and top properties');
      }
    }
    const {
      low = -0.5,
      high = 2.5
    } = zone;

    // we need to calculate the similarity of the isotopic distribution
    let similarityProcessor = new Comparator(similarity);
    similarityProcessor.setPeaks1([experimentalData.x, experimentalData.y]);
    for (let i = 0; i < flatEntries.length; i++) {
      const entry = flatEntries[i];
      if (onStep) await onStep(i);
      let isotopicDistribution = new IsotopicDistribution(entry.mf, {
        allowNeutral: false,
        ionizations: [entry.ionization]
      });
      let distribution = isotopicDistribution.getDistribution();
      // we need to define the comparison zone that depends of the charge
      let from = entry.ms.em + low / Math.abs(entry.ms.charge);
      let to = entry.ms.em + high / Math.abs(entry.ms.charge);
      similarityProcessor.setFromTo(from, to);
      if (widthFunction) {
        width = widthFunction(entry.ms.em);
        similarityProcessor.setTrapezoid(width.bottom, width.top);
      }
      similarityProcessor.setPeaks2([distribution.xs, distribution.ys]);
      let result = similarityProcessor.getSimilarity();
      result.extractInfo1.from = from;
      result.extractInfo1.to = to;
      if (result.similarity > minSimilarity) {
        entry.ms.similarity = {
          value: result.similarity,
          experimental: result.extract1,
          theoretical: result.extract2,
          difference: result.diff,
          experimentalInfo: result.extractInfo1,
          thereoticalInfo: result.extractInfo2,
          quantity: result.extractInfo1.sum / sumY,
          width
        };
      }
    }
    if (!options.flatten) {
      for (let database of Object.keys(results)) {
        results[database] = results[database].filter(entry => entry.ms.similarity).sort((a, b) => b.ms.similarity.value - a.ms.similarity.value);
        for (let entry of results[database]) {
          flatEntries.push(entry);
        }
      }
    } else {
      results = results.filter(entry => entry.ms.similarity).sort((a, b) => b.ms.similarity.value - a.ms.similarity.value);
    }
    return results;
  }

  /**
   * A class that deals with database of monoisotopic mass and molecular formula
   */
  class EMDB {
    constructor() {
      this.databases = {};
      this.experimentalSpectrum = undefined;
    }

    /**
     *
     * @param {*} data
     * @param {object} [options={}]
     * @param {number} [options.normed=true] Should we normed (sum Y to 1) the experimental spectrum ?
     */
    setExperimentalSpectrum(data) {
      let options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
      const {
        normed = true
      } = options;
      if (normed) {
        this.experimentalSpectrum = new Spectrum(data).normedY();
      } else {
        this.experimentalSpectrum = new Spectrum(data);
      }
      return this.experimentalSpectrum;
    }

    /**
     * Add a new database using the KnapSack content
     * @param {*} options
     */
    async loadKnapSack() {
      let options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
      const {
        databaseName = 'knapSack',
        forceReload = false
      } = options;
      if (this.databases[databaseName] && !forceReload) return;
      this.databases[databaseName] = await loadKnapSack();
    }

    /**
     * Add a new database of 12000 commercial products
     * @param {*} options
     */
    async loadCommercials() {
      let options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
      const {
        databaseName = 'commercials',
        forceReload = false
      } = options;
      if (this.databases[databaseName] && !forceReload) return;
      this.databases[databaseName] = await loadCommercials();
    }
    get(databaseName) {
      return this.databases[databaseName];
    }

    /**
     * Load the contaminants database from a google sheet document
     * @param {object} [options={}]
     * @param {string} [options.databaseName='contaminants']
     * @param {string} [options.forceReload=false]
     */
    async loadContaminants() {
      let options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
      const {
        databaseName = 'contaminants',
        forceReload = false
      } = options;
      if (this.databases[databaseName] && !forceReload) return;
      this.databases[databaseName] = await loadGoogleSheet();
    }

    /**
     * Load a google sheet containing MF information
     * @param {object} [options={}]
     * @param {string} [options.databaseName='sheet']
     * @param {string} [options.forceReload=false]
     */

    async loadGoogleSheet() {
      let options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
      const {
        databaseName = 'sheet',
        forceReload = false
      } = options;
      if (this.databases[databaseName] && !forceReload) return;
      this.databases[databaseName] = await loadGoogleSheet();
    }
    async loadTest() {
      await this.fromArray(['C1-100'], {
        databaseName: 'test',
        ionizations: '+'
      });
    }
    async loadNeutralTest() {
      let options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
      const {
        maxC = 100
      } = options;
      await this.fromArray([`C1-${maxC}`], {
        databaseName: 'test'
      });
    }
    async fromMonoisotopicMass(mass) {
      let options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
      const {
        databaseName = 'monoisotopic',
        append = false
      } = options;
      let result = await fromMonoisotopicMass(mass, options);
      replaceOrAppend(this, databaseName, result.mfs, append);
      return result;
    }
    async fromArray(sequence) {
      let options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
      const {
        databaseName = 'generated',
        append = false,
        estimate
      } = options;
      const results = await fromArray(sequence, options);
      if (estimate) return results;
      replaceOrAppend(this, databaseName, results, append);
    }
    async fromRange(sequence) {
      let options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
      const {
        databaseName = 'generated',
        append = false,
        estimate
      } = options;
      const results = await fromRange(sequence, options);
      if (estimate) return results;
      replaceOrAppend(this, databaseName, results, append);
    }
    async fromPeptidicSequence(sequence) {
      let options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
      const {
        databaseName = 'peptidic',
        append = false,
        estimate
      } = options;
      const results = await fromPeptidicSequence(sequence, options);
      if (estimate) return results;
      replaceOrAppend(this, databaseName, results, append);
    }

    /**
     *
     * @param {string} databaseName
     * @param {object} [options={}]
     * @param {number} [options.precision=100]
     * @param {string} [options.ionizations='']
     * @returns
     */
    async appendFragmentsInfo(databaseName) {
      let options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
      const database = this.databases[databaseName];
      await appendFragmentsInfo(this.experimentalSpectrum, database, options);
      return database;
    }
    async fromNucleicSequence(sequence) {
      let options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
      const {
        databaseName = 'nucleic',
        append = false,
        estimate
      } = options;
      const results = await fromNucleicSequence(sequence, options);
      if (estimate) return results;
      replaceOrAppend(this, databaseName, results, append);
    }
    listDatabases() {
      return Object.keys(this.databases).sort();
    }
    getInfo() {
      return {
        databases: Object.keys(this.databases).sort().map(key => {
          return {
            name: key,
            nbEntries: this.databases[key].length
          };
        })
      };
    }
    search(filter) {
      let options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
      return search(this, filter, options);
    }
    searchMSEM(filter) {
      let options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
      return searchMSEM(this, filter, options);
    }
    searchSimilarity() {
      let options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
      return searchSimilarity(this, options);
    }
  }
  function replaceOrAppend(emdb, databaseName, results) {
    let append = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : false;
    if (!emdb.databases[databaseName] || !append) {
      emdb.databases[databaseName] = results;
      return;
    }
    emdb.databases[databaseName] = emdb.databases[databaseName].concat(results);
  }

  function preprocessEARanges(ranges, targetEA) {
    ranges = JSON.parse(JSON.stringify(ranges));
    if (typeof ranges === 'string') {
      // need to convert to ranges
      let parsed = parse(ranges.replace(/[\r\n\t ]/g, ''));
      let newRanges = [];
      let current = {
        mf: '',
        min: 1,
        max: 1
      };

      // example ClBr2(CH2)0-2NO
      // the idea is that has long as we don't have a range we don't really care
      // there is a limitation is that the range has to be first level of parenthesis
      let parenthesisLevel = 0;
      let currentMF = ''; // start at an atom first level or a parenthesis
      for (let item of parsed) {
        switch (item.kind) {
          case Kind.ATOM:
            if (parenthesisLevel === 0 && currentMF) {
              current.mf += currentMF;
              currentMF = '';
            }
            currentMF += item.value;
            break;
          case Kind.ISOTOPE:
            if (parenthesisLevel === 0 && currentMF) {
              current.mf += currentMF;
              currentMF = '';
            }
            currentMF += `[${item.value.isotope}${item.value.atom}]`;
            break;
          case Kind.MULTIPLIER:
            if (parenthesisLevel === 0 && currentMF) {
              current.mf += currentMF + item.value;
              currentMF = '';
            } else {
              currentMF += item.value;
            }
            break;
          case Kind.MULTIPLIER_RANGE:
            if (parenthesisLevel !== 0) {
              throw new Error('Range multiplier can only be at the first level');
            }
            newRanges.push({
              mf: currentMF,
              min: item.value.from,
              max: item.value.to
            });
            currentMF = '';
            break;
          case Kind.OPENING_PARENTHESIS:
            parenthesisLevel++;
            currentMF += '(';
            break;
          case Kind.CLOSING_PARENTHESIS:
            parenthesisLevel--;
            currentMF += ')';
            break;
          default:
            throw Error(`can not preprocess ${ranges}`);
        }
      }
      if (currentMF) {
        current.mf += currentMF;
      }
      if (current.mf) {
        newRanges.push(current);
      }
      ranges = newRanges;
    }
    let possibilities = [];
    for (let i = 0; i < ranges.length; i++) {
      let range = ranges[i];
      let min = range.min === undefined ? 0 : range.min;
      let max = range.max === undefined ? 1 : range.max;
      let possibility = {
        mf: range.mf,
        minCount: min,
        // value defined by the user
        maxCount: max,
        // value defined by the user
        targetEA: targetEA[range.mf],
        mw: 0,
        // mw till this level
        aw: 0,
        // atomic weight (mass * currentCount)
        maxRatio: 0,
        // maximum ratio possible if all next elements are 0
        currentValue: 0,
        currentCount: min - 1,
        currentUnsaturation: 0,
        initialOrder: i
      };
      possibilities.push(possibility);
      let info = new MF(range.mf).getInfo();
      possibility.mass = info.mass;
      possibility.unsaturation = range.unsaturation === undefined ? (info.unsaturation - 1) * 2 : range.unsaturation;
      if (possibility.mf !== info.mf) possibility.isGroup = true;
    }
    possibilities = possibilities.filter(r => r.minCount !== 0 || r.maxCount !== 0);
    possibilities.sort((a, b) => {
      return b.targetEA - a.targetEA;
    });
    return possibilities;
  }

  /**
   * Returns possible combinations
   * @param {object} [targetEA]
   * @param {object} [options={}]
   * @param {string} [options.ranges='C0-100 H0-100 O0-100 N0-100'] - range of mfs to search
   * @param {number} [options.maxElementError=0.003]
   * @param {number} [options.maxTotalError=0.01]
   * @param {number} [options.minMW=0] minimal molecular weight
   * @param {number} [options.maxMW=+Infinity] maximal molecular weight
   * @param {number} [options.unsaturation={}]
   * @param {number} [options.unsaturation.min=-Infinity] Minimal unsaturation
   * @param {number} [options.unsaturation.max=+Infinity] Maximal unsaturation
   * @param {number} [options.unsaturation.onlyInteger=false] Integer unsaturation
   * @param {number} [options.unsaturation.onlyNonInteger=false] Non integer unsaturation
   * @return {Array<object>}
   */

  function mfFromEA(targetEA) {
    let options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
    const {
      unsaturation = {},
      maxIterations = 1e8,
      minMW = 0,
      maxMW = +Infinity,
      ranges = [{
        mf: 'C',
        min: 0,
        max: 100
      }, {
        mf: 'H',
        min: 0,
        max: 100
      }, {
        mf: 'O',
        min: 0,
        max: 100
      }, {
        mf: 'N',
        min: 0,
        max: 100
      }],
      maxElementError = 0.003,
      maxTotalError = 0.01
    } = options;
    let filterUnsaturation = !!unsaturation;
    // we calculate not the real unsaturation but the one before dividing by 2 + 1
    let fakeMinUnsaturation = unsaturation.min === undefined ? Number.MIN_SAFE_INTEGER : (unsaturation.min - 1) * 2;
    let fakeMaxUnsaturation = unsaturation.max === undefined ? Number.MAX_SAFE_INTEGER : (unsaturation.max - 1) * 2;
    let results = {
      mfs: [],
      info: {
        numberMFEvaluated: 0,
        numberResults: 0
      }
    };
    let orderMapping = []; // used to sort the atoms

    let possibilities = preprocessEARanges(ranges, targetEA);
    orderMapping = getOrderMapping(possibilities);
    if (possibilities.length === 0) return {
      mfs: []
    };
    let currentPosition = 0;
    let currentAtom;

    //  if (DEBUG) console.log('possibilities', possibilities.map((a) => `${a.mf + a.originalMinCount}-${a.originalMaxCount}`));

    mfWhile: while (true) {
      while (currentPosition < possibilities.length && currentPosition >= 0) {
        let previousAtom = currentPosition === 0 ? {
          mw: 0
        } : possibilities[currentPosition - 1];
        currentAtom = possibilities[currentPosition];
        if (currentAtom.currentCount < currentAtom.maxCount) {
          currentAtom.currentCount++;
          currentAtom.aw = currentAtom.mass * currentAtom.currentCount;
          currentAtom.mw = currentAtom.aw + previousAtom.mw;
          currentAtom.maxRatio = currentAtom.aw / currentAtom.mw;
          // we should check if we can reach the target
          if (currentAtom.targetEA && currentAtom.aw / currentAtom.mw - currentAtom.targetEA < -maxElementError) {
            // we already don't have enough quantity of this element and it can only become worse
            continue;
          }
          if (currentPosition < possibilities.length - 1) {
            currentPosition++;
          } else {
            break;
          }
        } else {
          currentAtom.currentCount = currentAtom.minCount - 1;
          currentPosition--;
        }
      }
      if (currentPosition < 0) {
        break;
      }
      if (results.info.numberMFEvaluated++ > maxIterations) {
        throw Error(`Iteration number is over the current maximum of: ${maxIterations}`);
      }
      if (filterUnsaturation) {
        let unsaturationValue = 0;
        for (const possibility of possibilities) {
          unsaturationValue += possibility.unsaturation * possibility.currentCount;
        }
        let isOdd = Math.abs(unsaturationValue % 2);
        if (unsaturation.onlyInteger && isOdd === 1 || unsaturation.onlyNonInteger && isOdd === 0 || fakeMinUnsaturation > unsaturationValue || fakeMaxUnsaturation < unsaturationValue) {
          continue;
        }
      }
      let mw = currentAtom.mw;
      if (mw < minMW || mw > maxMW) continue;
      let totalError = 0;
      for (let i = 0; i < possibilities.length; i++) {
        const possibility = possibilities[i];
        let ratio = possibility.mass * possibility.currentCount / mw;
        if (possibility.targetEA !== undefined) {
          let error = Math.abs(possibility.targetEA - ratio);
          if (error > maxElementError) {
            continue mfWhile;
          }
          totalError += error;
        }
        possibility.currentValue = ratio;
      }
      if (isNaN(totalError) || totalError > maxTotalError) continue;
      results.mfs.push(getResult(possibilities, totalError, orderMapping));
      results.info.numberResults++;
    }
    results.mfs.sort((a, b) => Math.abs(a.totalError) - Math.abs(b.totalError));
    return results;
  }
  function getResult(possibilities, totalError, orderMapping) {
    const result = {
      mf: '',
      totalError
    };
    // we check that the first time we meet the ionization group it does not end
    // in the final result
    for (let i = 0; i < possibilities.length; i++) {
      let possibility = possibilities[orderMapping[i]];
      if (possibility.currentCount !== 0) {
        if (possibility.isGroup) {
          result.mf += `(${possibility.mf})`;
          if (possibility.currentCount !== 1) {
            result.mf += possibility.currentCount;
          }
        } else {
          result.mf += possibility.mf;
          if (possibility.currentCount !== 1) {
            result.mf += possibility.currentCount;
          }
        }
      }
      result.ea = possibilities.map(current => ({
        mf: current.mf,
        value: current.currentValue,
        expected: current.targetEA,
        error: current.targetEA === undefined ? undefined : Math.abs(current.targetEA - current.currentValue)
      }));
    }
    return result;
  }
  function getOrderMapping(possibilities) {
    let mapping = possibilities.map((p, i) => ({
      atom: p.mf,
      index: i
    }));
    mapping.sort((a, b) => {
      return atomSorter(a.atom, b.atom);
    });
    return mapping.map(a => a.index);
  }

  const Nucleotide = NucleotidePkg;
  const Report = ReportPkg;
  const Peptide = PeptidePkg;

  exports.EMDB = EMDB;
  exports.IsotopicDistribution = IsotopicDistribution;
  exports.MF = MF;
  exports.Nucleotide = Nucleotide;
  exports.Peptide = Peptide;
  exports.Report = Report;
  exports.Spectrum = Spectrum;
  exports.atomSorter = atomSorter;
  exports.elements = elements$2;
  exports.elementsObject = elementsObject;
  exports.generateMFs = generateMFs;
  exports.getBestPeaks = getBestPeaks;
  exports.getPeaks = getPeaks;
  exports.getRangesForFragment = getRangesForFragment;
  exports.groups = groups;
  exports.groupsObject = groupsObject;
  exports.mfFromEA = mfFromEA;
  exports.preprocessRanges = preprocessRanges;

  Object.defineProperty(exports, '__esModule', { value: true });

}));
//# sourceMappingURL=mass-tools.js.map
