/**
 * chromatography - Tools for storing, searching and analyzing GC/MS data
 * @version v7.0.0
 * @link https://github.com/cheminfo/chromatography#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.Chromatography = {}));
})(this, (function (exports) { 'use strict';

    // eslint-disable-next-line @typescript-eslint/unbound-method
    const toString$3 = Object.prototype.toString;
    /**
     * Checks if an object is an instance of an Array (array or typed array, except those that contain bigint values).
     *
     * @param value - Object to check.
     * @returns True if the object is an array or a typed array.
     */
    function isAnyArray$1(value) {
      const tag = toString$3.call(value);
      return tag.endsWith('Array]') && !tag.includes('Big');
    }

    var libEsm = /*#__PURE__*/Object.freeze({
        __proto__: null,
        isAnyArray: isAnyArray$1
    });

    /**
     * This function
     * @param output - undefined or a new array
     * @param length - length of the output array
     * @returns
     */
    function getOutputArray(output, length) {
      if (typeof 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);
      }
    }

    /**
     * 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');
      }
      const array = input.slice();
      let low = 0;
      let high = array.length - 1;
      let middle = 0;
      let currentLow = 0;
      let currentHigh = 0;
      const median = calcMiddle(low, high);
      while (true) {
        if (high <= low) {
          return array[median];
        }
        if (high === low + 1) {
          if (array[low] > array[high]) {
            swap(array, low, high);
          }
          return array[median];
        }
        // Find median of low, middle and high items; swap into position low
        middle = calcMiddle(low, high);
        if (array[middle] > array[high]) swap(array, middle, high);
        if (array[low] > array[high]) swap(array, low, high);
        if (array[middle] > array[low]) swap(array, middle, low);
        // Swap low item (now in position middle) into position (low+1)
        swap(array, middle, low + 1);
        // Nibble from each end towards middle, swapping items when stuck
        currentLow = low + 1;
        currentHigh = high;
        while (true) {
          do currentLow++; while (array[low] > array[currentLow]);
          do currentHigh--; while (array[currentHigh] > array[low]);
          if (currentHigh < currentLow) {
            break;
          }
          swap(array, currentLow, currentHigh);
        }
        // Swap middle item (in position low) back into correct position
        swap(array, low, currentHigh);
        // Re-set active partition
        if (currentHigh <= median) {
          low = currentLow;
        }
        if (currentHigh >= median) {
          high = currentHigh - 1;
        }
      }
    }
    function swap(array, i, j) {
      const temp = array[j];
      array[j] = array[i];
      array[i] = temp;
    }
    function calcMiddle(i, j) {
      return Math.floor((i + j) / 2);
    }

    /**
     * Checks if input is of type array.
     *
     * @param input - input
     * @param options
     */
    function xCheck(input, options = {}) {
      const {
        minLength
      } = options;
      if (!isAnyArray$1(input)) {
        throw new TypeError('input must be an array');
      }
      if (input.length === 0) {
        throw new TypeError('input must not be empty');
      }
      if (typeof input[0] !== 'number') {
        throw new TypeError('input must contain numbers');
      }
      if (minLength && input.length < minLength) {
        throw new Error(`input must have a length of at least ${minLength}`);
      }
    }

    /**
     * Returns the closest index of a `target`
     *
     * @param array - array of numbers
     * @param target - target
     * @param options
     * @returns - closest index
     */
    function xFindClosestIndex(array, target, options = {}) {
      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, options = {}) {
      let {
        fromIndex,
        toIndex
      } = options;
      const {
        from,
        to
      } = options;
      if (typeof fromIndex === 'undefined') {
        if (typeof from !== 'undefined') {
          fromIndex = xFindClosestIndex(x, from);
        } else {
          fromIndex = 0;
        }
      }
      if (typeof toIndex === 'undefined') {
        if (typeof 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
      };
    }

    function xAbsoluteSum(array, options = {}) {
      xCheck(array);
      const {
        fromIndex,
        toIndex
      } = xGetFromToIndex(array, options);
      let sum = 0;
      for (let i = fromIndex; i <= toIndex; i++) {
        if (array[i] < 0) {
          sum -= array[i];
        } else {
          sum += array[i];
        }
      }
      return sum;
    }

    /**
     * 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$1(array2)) {
        if (array1.length !== array2.length) {
          throw new Error('size of array1 and array2 must be identical');
        }
      } else {
        isConstant = true;
        constant = array2;
      }
      const 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;
    }

    /**
     * This function xMultiply the first array by the second array or a constant value to each element of the first array
     *
     * @param array1 - first array
     * @param array2 - second array
     * @param options - options
     */
    function xMultiply(array1, array2, options = {}) {
      let isConstant = false;
      let constant = 0;
      if (isAnyArray$1(array2)) {
        if (array1.length !== array2.length) {
          throw new Error('size of array1 and array2 must be identical');
        }
      } else {
        isConstant = true;
        constant = Number(array2);
      }
      const array3 = getOutputArray(options.output, 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;
    }

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

    function getAugmentedNamespace(n) {
    	if (n.__esModule) return n;
    	var a = Object.defineProperty({}, '__esModule', {value: true});
    	Object.keys(n).forEach(function (k) {
    		var d = Object.getOwnPropertyDescriptor(n, k);
    		Object.defineProperty(a, k, d.get ? d : {
    			enumerable: true,
    			get: function () {
    				return n[k];
    			}
    		});
    	});
    	return a;
    }

    var matrix = {};

    var require$$0 = /*@__PURE__*/getAugmentedNamespace(libEsm);

    function max(input) {
      var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
      if (!isAnyArray$1(input)) {
        throw new TypeError('input must be an array');
      }
      if (input.length === 0) {
        throw new TypeError('input must not be empty');
      }
      var _options$fromIndex = options.fromIndex,
        fromIndex = _options$fromIndex === void 0 ? 0 : _options$fromIndex,
        _options$toIndex = options.toIndex,
        toIndex = _options$toIndex === void 0 ? input.length : _options$toIndex;
      if (fromIndex < 0 || fromIndex >= input.length || !Number.isInteger(fromIndex)) {
        throw new Error('fromIndex must be a positive integer smaller than length');
      }
      if (toIndex <= fromIndex || toIndex > input.length || !Number.isInteger(toIndex)) {
        throw new Error('toIndex must be an integer greater than fromIndex and at most equal to length');
      }
      var maxValue = input[fromIndex];
      for (var i = fromIndex + 1; i < toIndex; i++) {
        if (input[i] > maxValue) maxValue = input[i];
      }
      return maxValue;
    }

    function min(input) {
      var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
      if (!isAnyArray$1(input)) {
        throw new TypeError('input must be an array');
      }
      if (input.length === 0) {
        throw new TypeError('input must not be empty');
      }
      var _options$fromIndex = options.fromIndex,
        fromIndex = _options$fromIndex === void 0 ? 0 : _options$fromIndex,
        _options$toIndex = options.toIndex,
        toIndex = _options$toIndex === void 0 ? input.length : _options$toIndex;
      if (fromIndex < 0 || fromIndex >= input.length || !Number.isInteger(fromIndex)) {
        throw new Error('fromIndex must be a positive integer smaller than length');
      }
      if (toIndex <= fromIndex || toIndex > input.length || !Number.isInteger(toIndex)) {
        throw new Error('toIndex must be an integer greater than fromIndex and at most equal to length');
      }
      var minValue = input[fromIndex];
      for (var i = fromIndex + 1; i < toIndex; i++) {
        if (input[i] < minValue) minValue = input[i];
      }
      return minValue;
    }

    function rescale$1(input) {
      var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
      if (!isAnyArray$1(input)) {
        throw new TypeError('input must be an array');
      } else if (input.length === 0) {
        throw new TypeError('input must not be empty');
      }
      var output;
      if (options.output !== undefined) {
        if (!isAnyArray$1(options.output)) {
          throw new TypeError('output option must be an array if specified');
        }
        output = options.output;
      } else {
        output = new Array(input.length);
      }
      var currentMin = min(input);
      var currentMax = max(input);
      if (currentMin === currentMax) {
        throw new RangeError('minimum and maximum input values are equal. Cannot rescale a constant array');
      }
      var _options$min = options.min,
        minValue = _options$min === void 0 ? options.autoMinMax ? currentMin : 0 : _options$min,
        _options$max = options.max,
        maxValue = _options$max === void 0 ? options.autoMinMax ? currentMax : 1 : _options$max;
      if (minValue >= maxValue) {
        throw new RangeError('min option must be smaller than max option');
      }
      var factor = (maxValue - minValue) / (currentMax - currentMin);
      for (var i = 0; i < input.length; i++) {
        output[i] = (input[i] - currentMin) * factor + minValue;
      }
      return output;
    }

    var libEs6 = /*#__PURE__*/Object.freeze({
        __proto__: null,
        'default': rescale$1
    });

    var require$$1 = /*@__PURE__*/getAugmentedNamespace(libEs6);

    Object.defineProperty(matrix, '__esModule', {
      value: true
    });
    var isAnyArray = require$$0;
    var rescale = require$$1;
    const indent = ' '.repeat(2);
    const indentData = ' '.repeat(4);

    /**
     * @this {Matrix}
     * @returns {string}
     */
    function inspectMatrix() {
      return inspectMatrixWithOptions(this);
    }
    function inspectMatrixWithOptions(matrix, options = {}) {
      const {
        maxRows = 15,
        maxColumns = 10,
        maxNumSize = 8,
        padMinus = 'auto'
      } = options;
      return `${matrix.constructor.name} {
${indent}[
${indentData}${inspectData(matrix, maxRows, maxColumns, maxNumSize, padMinus)}
${indent}]
${indent}rows: ${matrix.rows}
${indent}columns: ${matrix.columns}
}`;
    }
    function inspectData(matrix, maxRows, maxColumns, maxNumSize, padMinus) {
      const {
        rows,
        columns
      } = matrix;
      const maxI = Math.min(rows, maxRows);
      const maxJ = Math.min(columns, maxColumns);
      const result = [];
      if (padMinus === 'auto') {
        padMinus = false;
        loop: for (let i = 0; i < maxI; i++) {
          for (let j = 0; j < maxJ; j++) {
            if (matrix.get(i, j) < 0) {
              padMinus = true;
              break loop;
            }
          }
        }
      }
      for (let i = 0; i < maxI; i++) {
        let line = [];
        for (let j = 0; j < maxJ; j++) {
          line.push(formatNumber(matrix.get(i, j), maxNumSize, padMinus));
        }
        result.push(`${line.join(' ')}`);
      }
      if (maxJ !== columns) {
        result[result.length - 1] += ` ... ${columns - maxColumns} more columns`;
      }
      if (maxI !== rows) {
        result.push(`... ${rows - maxRows} more rows`);
      }
      return result.join(`\n${indentData}`);
    }
    function formatNumber(num, maxNumSize, padMinus) {
      return (num >= 0 && padMinus ? ` ${formatNumber2(num, maxNumSize - 1)}` : formatNumber2(num, maxNumSize)).padEnd(maxNumSize);
    }
    function formatNumber2(num, len) {
      // small.length numbers should be as is
      let str = num.toString();
      if (str.length <= len) return str;

      // (7)'0.00123' is better then (7)'1.23e-2'
      // (8)'0.000123' is worse then (7)'1.23e-3',
      let fix = num.toFixed(len);
      if (fix.length > len) {
        fix = num.toFixed(Math.max(0, len - (fix.length - len)));
      }
      if (fix.length <= len && !fix.startsWith('0.000') && !fix.startsWith('-0.000')) {
        return fix;
      }

      // well, if it's still too long the user should've used longer numbers
      let exp = num.toExponential(len);
      if (exp.length > len) {
        exp = num.toExponential(Math.max(0, len - (exp.length - len)));
      }
      return exp.slice(0);
    }
    function installMathOperations(AbstractMatrix, Matrix) {
      AbstractMatrix.prototype.add = function add(value) {
        if (typeof value === 'number') return this.addS(value);
        return this.addM(value);
      };
      AbstractMatrix.prototype.addS = function addS(value) {
        for (let i = 0; i < this.rows; i++) {
          for (let j = 0; j < this.columns; j++) {
            this.set(i, j, this.get(i, j) + value);
          }
        }
        return this;
      };
      AbstractMatrix.prototype.addM = function addM(matrix) {
        matrix = Matrix.checkMatrix(matrix);
        if (this.rows !== matrix.rows || this.columns !== matrix.columns) {
          throw new RangeError('Matrices dimensions must be equal');
        }
        for (let i = 0; i < this.rows; i++) {
          for (let j = 0; j < this.columns; j++) {
            this.set(i, j, this.get(i, j) + matrix.get(i, j));
          }
        }
        return this;
      };
      AbstractMatrix.add = function add(matrix, value) {
        const newMatrix = new Matrix(matrix);
        return newMatrix.add(value);
      };
      AbstractMatrix.prototype.sub = function sub(value) {
        if (typeof value === 'number') return this.subS(value);
        return this.subM(value);
      };
      AbstractMatrix.prototype.subS = function subS(value) {
        for (let i = 0; i < this.rows; i++) {
          for (let j = 0; j < this.columns; j++) {
            this.set(i, j, this.get(i, j) - value);
          }
        }
        return this;
      };
      AbstractMatrix.prototype.subM = function subM(matrix) {
        matrix = Matrix.checkMatrix(matrix);
        if (this.rows !== matrix.rows || this.columns !== matrix.columns) {
          throw new RangeError('Matrices dimensions must be equal');
        }
        for (let i = 0; i < this.rows; i++) {
          for (let j = 0; j < this.columns; j++) {
            this.set(i, j, this.get(i, j) - matrix.get(i, j));
          }
        }
        return this;
      };
      AbstractMatrix.sub = function sub(matrix, value) {
        const newMatrix = new Matrix(matrix);
        return newMatrix.sub(value);
      };
      AbstractMatrix.prototype.subtract = AbstractMatrix.prototype.sub;
      AbstractMatrix.prototype.subtractS = AbstractMatrix.prototype.subS;
      AbstractMatrix.prototype.subtractM = AbstractMatrix.prototype.subM;
      AbstractMatrix.subtract = AbstractMatrix.sub;
      AbstractMatrix.prototype.mul = function mul(value) {
        if (typeof value === 'number') return this.mulS(value);
        return this.mulM(value);
      };
      AbstractMatrix.prototype.mulS = function mulS(value) {
        for (let i = 0; i < this.rows; i++) {
          for (let j = 0; j < this.columns; j++) {
            this.set(i, j, this.get(i, j) * value);
          }
        }
        return this;
      };
      AbstractMatrix.prototype.mulM = function mulM(matrix) {
        matrix = Matrix.checkMatrix(matrix);
        if (this.rows !== matrix.rows || this.columns !== matrix.columns) {
          throw new RangeError('Matrices dimensions must be equal');
        }
        for (let i = 0; i < this.rows; i++) {
          for (let j = 0; j < this.columns; j++) {
            this.set(i, j, this.get(i, j) * matrix.get(i, j));
          }
        }
        return this;
      };
      AbstractMatrix.mul = function mul(matrix, value) {
        const newMatrix = new Matrix(matrix);
        return newMatrix.mul(value);
      };
      AbstractMatrix.prototype.multiply = AbstractMatrix.prototype.mul;
      AbstractMatrix.prototype.multiplyS = AbstractMatrix.prototype.mulS;
      AbstractMatrix.prototype.multiplyM = AbstractMatrix.prototype.mulM;
      AbstractMatrix.multiply = AbstractMatrix.mul;
      AbstractMatrix.prototype.div = function div(value) {
        if (typeof value === 'number') return this.divS(value);
        return this.divM(value);
      };
      AbstractMatrix.prototype.divS = function divS(value) {
        for (let i = 0; i < this.rows; i++) {
          for (let j = 0; j < this.columns; j++) {
            this.set(i, j, this.get(i, j) / value);
          }
        }
        return this;
      };
      AbstractMatrix.prototype.divM = function divM(matrix) {
        matrix = Matrix.checkMatrix(matrix);
        if (this.rows !== matrix.rows || this.columns !== matrix.columns) {
          throw new RangeError('Matrices dimensions must be equal');
        }
        for (let i = 0; i < this.rows; i++) {
          for (let j = 0; j < this.columns; j++) {
            this.set(i, j, this.get(i, j) / matrix.get(i, j));
          }
        }
        return this;
      };
      AbstractMatrix.div = function div(matrix, value) {
        const newMatrix = new Matrix(matrix);
        return newMatrix.div(value);
      };
      AbstractMatrix.prototype.divide = AbstractMatrix.prototype.div;
      AbstractMatrix.prototype.divideS = AbstractMatrix.prototype.divS;
      AbstractMatrix.prototype.divideM = AbstractMatrix.prototype.divM;
      AbstractMatrix.divide = AbstractMatrix.div;
      AbstractMatrix.prototype.mod = function mod(value) {
        if (typeof value === 'number') return this.modS(value);
        return this.modM(value);
      };
      AbstractMatrix.prototype.modS = function modS(value) {
        for (let i = 0; i < this.rows; i++) {
          for (let j = 0; j < this.columns; j++) {
            this.set(i, j, this.get(i, j) % value);
          }
        }
        return this;
      };
      AbstractMatrix.prototype.modM = function modM(matrix) {
        matrix = Matrix.checkMatrix(matrix);
        if (this.rows !== matrix.rows || this.columns !== matrix.columns) {
          throw new RangeError('Matrices dimensions must be equal');
        }
        for (let i = 0; i < this.rows; i++) {
          for (let j = 0; j < this.columns; j++) {
            this.set(i, j, this.get(i, j) % matrix.get(i, j));
          }
        }
        return this;
      };
      AbstractMatrix.mod = function mod(matrix, value) {
        const newMatrix = new Matrix(matrix);
        return newMatrix.mod(value);
      };
      AbstractMatrix.prototype.modulus = AbstractMatrix.prototype.mod;
      AbstractMatrix.prototype.modulusS = AbstractMatrix.prototype.modS;
      AbstractMatrix.prototype.modulusM = AbstractMatrix.prototype.modM;
      AbstractMatrix.modulus = AbstractMatrix.mod;
      AbstractMatrix.prototype.and = function and(value) {
        if (typeof value === 'number') return this.andS(value);
        return this.andM(value);
      };
      AbstractMatrix.prototype.andS = function andS(value) {
        for (let i = 0; i < this.rows; i++) {
          for (let j = 0; j < this.columns; j++) {
            this.set(i, j, this.get(i, j) & value);
          }
        }
        return this;
      };
      AbstractMatrix.prototype.andM = function andM(matrix) {
        matrix = Matrix.checkMatrix(matrix);
        if (this.rows !== matrix.rows || this.columns !== matrix.columns) {
          throw new RangeError('Matrices dimensions must be equal');
        }
        for (let i = 0; i < this.rows; i++) {
          for (let j = 0; j < this.columns; j++) {
            this.set(i, j, this.get(i, j) & matrix.get(i, j));
          }
        }
        return this;
      };
      AbstractMatrix.and = function and(matrix, value) {
        const newMatrix = new Matrix(matrix);
        return newMatrix.and(value);
      };
      AbstractMatrix.prototype.or = function or(value) {
        if (typeof value === 'number') return this.orS(value);
        return this.orM(value);
      };
      AbstractMatrix.prototype.orS = function orS(value) {
        for (let i = 0; i < this.rows; i++) {
          for (let j = 0; j < this.columns; j++) {
            this.set(i, j, this.get(i, j) | value);
          }
        }
        return this;
      };
      AbstractMatrix.prototype.orM = function orM(matrix) {
        matrix = Matrix.checkMatrix(matrix);
        if (this.rows !== matrix.rows || this.columns !== matrix.columns) {
          throw new RangeError('Matrices dimensions must be equal');
        }
        for (let i = 0; i < this.rows; i++) {
          for (let j = 0; j < this.columns; j++) {
            this.set(i, j, this.get(i, j) | matrix.get(i, j));
          }
        }
        return this;
      };
      AbstractMatrix.or = function or(matrix, value) {
        const newMatrix = new Matrix(matrix);
        return newMatrix.or(value);
      };
      AbstractMatrix.prototype.xor = function xor(value) {
        if (typeof value === 'number') return this.xorS(value);
        return this.xorM(value);
      };
      AbstractMatrix.prototype.xorS = function xorS(value) {
        for (let i = 0; i < this.rows; i++) {
          for (let j = 0; j < this.columns; j++) {
            this.set(i, j, this.get(i, j) ^ value);
          }
        }
        return this;
      };
      AbstractMatrix.prototype.xorM = function xorM(matrix) {
        matrix = Matrix.checkMatrix(matrix);
        if (this.rows !== matrix.rows || this.columns !== matrix.columns) {
          throw new RangeError('Matrices dimensions must be equal');
        }
        for (let i = 0; i < this.rows; i++) {
          for (let j = 0; j < this.columns; j++) {
            this.set(i, j, this.get(i, j) ^ matrix.get(i, j));
          }
        }
        return this;
      };
      AbstractMatrix.xor = function xor(matrix, value) {
        const newMatrix = new Matrix(matrix);
        return newMatrix.xor(value);
      };
      AbstractMatrix.prototype.leftShift = function leftShift(value) {
        if (typeof value === 'number') return this.leftShiftS(value);
        return this.leftShiftM(value);
      };
      AbstractMatrix.prototype.leftShiftS = function leftShiftS(value) {
        for (let i = 0; i < this.rows; i++) {
          for (let j = 0; j < this.columns; j++) {
            this.set(i, j, this.get(i, j) << value);
          }
        }
        return this;
      };
      AbstractMatrix.prototype.leftShiftM = function leftShiftM(matrix) {
        matrix = Matrix.checkMatrix(matrix);
        if (this.rows !== matrix.rows || this.columns !== matrix.columns) {
          throw new RangeError('Matrices dimensions must be equal');
        }
        for (let i = 0; i < this.rows; i++) {
          for (let j = 0; j < this.columns; j++) {
            this.set(i, j, this.get(i, j) << matrix.get(i, j));
          }
        }
        return this;
      };
      AbstractMatrix.leftShift = function leftShift(matrix, value) {
        const newMatrix = new Matrix(matrix);
        return newMatrix.leftShift(value);
      };
      AbstractMatrix.prototype.signPropagatingRightShift = function signPropagatingRightShift(value) {
        if (typeof value === 'number') return this.signPropagatingRightShiftS(value);
        return this.signPropagatingRightShiftM(value);
      };
      AbstractMatrix.prototype.signPropagatingRightShiftS = function signPropagatingRightShiftS(value) {
        for (let i = 0; i < this.rows; i++) {
          for (let j = 0; j < this.columns; j++) {
            this.set(i, j, this.get(i, j) >> value);
          }
        }
        return this;
      };
      AbstractMatrix.prototype.signPropagatingRightShiftM = function signPropagatingRightShiftM(matrix) {
        matrix = Matrix.checkMatrix(matrix);
        if (this.rows !== matrix.rows || this.columns !== matrix.columns) {
          throw new RangeError('Matrices dimensions must be equal');
        }
        for (let i = 0; i < this.rows; i++) {
          for (let j = 0; j < this.columns; j++) {
            this.set(i, j, this.get(i, j) >> matrix.get(i, j));
          }
        }
        return this;
      };
      AbstractMatrix.signPropagatingRightShift = function signPropagatingRightShift(matrix, value) {
        const newMatrix = new Matrix(matrix);
        return newMatrix.signPropagatingRightShift(value);
      };
      AbstractMatrix.prototype.rightShift = function rightShift(value) {
        if (typeof value === 'number') return this.rightShiftS(value);
        return this.rightShiftM(value);
      };
      AbstractMatrix.prototype.rightShiftS = function rightShiftS(value) {
        for (let i = 0; i < this.rows; i++) {
          for (let j = 0; j < this.columns; j++) {
            this.set(i, j, this.get(i, j) >>> value);
          }
        }
        return this;
      };
      AbstractMatrix.prototype.rightShiftM = function rightShiftM(matrix) {
        matrix = Matrix.checkMatrix(matrix);
        if (this.rows !== matrix.rows || this.columns !== matrix.columns) {
          throw new RangeError('Matrices dimensions must be equal');
        }
        for (let i = 0; i < this.rows; i++) {
          for (let j = 0; j < this.columns; j++) {
            this.set(i, j, this.get(i, j) >>> matrix.get(i, j));
          }
        }
        return this;
      };
      AbstractMatrix.rightShift = function rightShift(matrix, value) {
        const newMatrix = new Matrix(matrix);
        return newMatrix.rightShift(value);
      };
      AbstractMatrix.prototype.zeroFillRightShift = AbstractMatrix.prototype.rightShift;
      AbstractMatrix.prototype.zeroFillRightShiftS = AbstractMatrix.prototype.rightShiftS;
      AbstractMatrix.prototype.zeroFillRightShiftM = AbstractMatrix.prototype.rightShiftM;
      AbstractMatrix.zeroFillRightShift = AbstractMatrix.rightShift;
      AbstractMatrix.prototype.not = function not() {
        for (let i = 0; i < this.rows; i++) {
          for (let j = 0; j < this.columns; j++) {
            this.set(i, j, ~this.get(i, j));
          }
        }
        return this;
      };
      AbstractMatrix.not = function not(matrix) {
        const newMatrix = new Matrix(matrix);
        return newMatrix.not();
      };
      AbstractMatrix.prototype.abs = function abs() {
        for (let i = 0; i < this.rows; i++) {
          for (let j = 0; j < this.columns; j++) {
            this.set(i, j, Math.abs(this.get(i, j)));
          }
        }
        return this;
      };
      AbstractMatrix.abs = function abs(matrix) {
        const newMatrix = new Matrix(matrix);
        return newMatrix.abs();
      };
      AbstractMatrix.prototype.acos = function acos() {
        for (let i = 0; i < this.rows; i++) {
          for (let j = 0; j < this.columns; j++) {
            this.set(i, j, Math.acos(this.get(i, j)));
          }
        }
        return this;
      };
      AbstractMatrix.acos = function acos(matrix) {
        const newMatrix = new Matrix(matrix);
        return newMatrix.acos();
      };
      AbstractMatrix.prototype.acosh = function acosh() {
        for (let i = 0; i < this.rows; i++) {
          for (let j = 0; j < this.columns; j++) {
            this.set(i, j, Math.acosh(this.get(i, j)));
          }
        }
        return this;
      };
      AbstractMatrix.acosh = function acosh(matrix) {
        const newMatrix = new Matrix(matrix);
        return newMatrix.acosh();
      };
      AbstractMatrix.prototype.asin = function asin() {
        for (let i = 0; i < this.rows; i++) {
          for (let j = 0; j < this.columns; j++) {
            this.set(i, j, Math.asin(this.get(i, j)));
          }
        }
        return this;
      };
      AbstractMatrix.asin = function asin(matrix) {
        const newMatrix = new Matrix(matrix);
        return newMatrix.asin();
      };
      AbstractMatrix.prototype.asinh = function asinh() {
        for (let i = 0; i < this.rows; i++) {
          for (let j = 0; j < this.columns; j++) {
            this.set(i, j, Math.asinh(this.get(i, j)));
          }
        }
        return this;
      };
      AbstractMatrix.asinh = function asinh(matrix) {
        const newMatrix = new Matrix(matrix);
        return newMatrix.asinh();
      };
      AbstractMatrix.prototype.atan = function atan() {
        for (let i = 0; i < this.rows; i++) {
          for (let j = 0; j < this.columns; j++) {
            this.set(i, j, Math.atan(this.get(i, j)));
          }
        }
        return this;
      };
      AbstractMatrix.atan = function atan(matrix) {
        const newMatrix = new Matrix(matrix);
        return newMatrix.atan();
      };
      AbstractMatrix.prototype.atanh = function atanh() {
        for (let i = 0; i < this.rows; i++) {
          for (let j = 0; j < this.columns; j++) {
            this.set(i, j, Math.atanh(this.get(i, j)));
          }
        }
        return this;
      };
      AbstractMatrix.atanh = function atanh(matrix) {
        const newMatrix = new Matrix(matrix);
        return newMatrix.atanh();
      };
      AbstractMatrix.prototype.cbrt = function cbrt() {
        for (let i = 0; i < this.rows; i++) {
          for (let j = 0; j < this.columns; j++) {
            this.set(i, j, Math.cbrt(this.get(i, j)));
          }
        }
        return this;
      };
      AbstractMatrix.cbrt = function cbrt(matrix) {
        const newMatrix = new Matrix(matrix);
        return newMatrix.cbrt();
      };
      AbstractMatrix.prototype.ceil = function ceil() {
        for (let i = 0; i < this.rows; i++) {
          for (let j = 0; j < this.columns; j++) {
            this.set(i, j, Math.ceil(this.get(i, j)));
          }
        }
        return this;
      };
      AbstractMatrix.ceil = function ceil(matrix) {
        const newMatrix = new Matrix(matrix);
        return newMatrix.ceil();
      };
      AbstractMatrix.prototype.clz32 = function clz32() {
        for (let i = 0; i < this.rows; i++) {
          for (let j = 0; j < this.columns; j++) {
            this.set(i, j, Math.clz32(this.get(i, j)));
          }
        }
        return this;
      };
      AbstractMatrix.clz32 = function clz32(matrix) {
        const newMatrix = new Matrix(matrix);
        return newMatrix.clz32();
      };
      AbstractMatrix.prototype.cos = function cos() {
        for (let i = 0; i < this.rows; i++) {
          for (let j = 0; j < this.columns; j++) {
            this.set(i, j, Math.cos(this.get(i, j)));
          }
        }
        return this;
      };
      AbstractMatrix.cos = function cos(matrix) {
        const newMatrix = new Matrix(matrix);
        return newMatrix.cos();
      };
      AbstractMatrix.prototype.cosh = function cosh() {
        for (let i = 0; i < this.rows; i++) {
          for (let j = 0; j < this.columns; j++) {
            this.set(i, j, Math.cosh(this.get(i, j)));
          }
        }
        return this;
      };
      AbstractMatrix.cosh = function cosh(matrix) {
        const newMatrix = new Matrix(matrix);
        return newMatrix.cosh();
      };
      AbstractMatrix.prototype.exp = function exp() {
        for (let i = 0; i < this.rows; i++) {
          for (let j = 0; j < this.columns; j++) {
            this.set(i, j, Math.exp(this.get(i, j)));
          }
        }
        return this;
      };
      AbstractMatrix.exp = function exp(matrix) {
        const newMatrix = new Matrix(matrix);
        return newMatrix.exp();
      };
      AbstractMatrix.prototype.expm1 = function expm1() {
        for (let i = 0; i < this.rows; i++) {
          for (let j = 0; j < this.columns; j++) {
            this.set(i, j, Math.expm1(this.get(i, j)));
          }
        }
        return this;
      };
      AbstractMatrix.expm1 = function expm1(matrix) {
        const newMatrix = new Matrix(matrix);
        return newMatrix.expm1();
      };
      AbstractMatrix.prototype.floor = function floor() {
        for (let i = 0; i < this.rows; i++) {
          for (let j = 0; j < this.columns; j++) {
            this.set(i, j, Math.floor(this.get(i, j)));
          }
        }
        return this;
      };
      AbstractMatrix.floor = function floor(matrix) {
        const newMatrix = new Matrix(matrix);
        return newMatrix.floor();
      };
      AbstractMatrix.prototype.fround = function fround() {
        for (let i = 0; i < this.rows; i++) {
          for (let j = 0; j < this.columns; j++) {
            this.set(i, j, Math.fround(this.get(i, j)));
          }
        }
        return this;
      };
      AbstractMatrix.fround = function fround(matrix) {
        const newMatrix = new Matrix(matrix);
        return newMatrix.fround();
      };
      AbstractMatrix.prototype.log = function log() {
        for (let i = 0; i < this.rows; i++) {
          for (let j = 0; j < this.columns; j++) {
            this.set(i, j, Math.log(this.get(i, j)));
          }
        }
        return this;
      };
      AbstractMatrix.log = function log(matrix) {
        const newMatrix = new Matrix(matrix);
        return newMatrix.log();
      };
      AbstractMatrix.prototype.log1p = function log1p() {
        for (let i = 0; i < this.rows; i++) {
          for (let j = 0; j < this.columns; j++) {
            this.set(i, j, Math.log1p(this.get(i, j)));
          }
        }
        return this;
      };
      AbstractMatrix.log1p = function log1p(matrix) {
        const newMatrix = new Matrix(matrix);
        return newMatrix.log1p();
      };
      AbstractMatrix.prototype.log10 = function log10() {
        for (let i = 0; i < this.rows; i++) {
          for (let j = 0; j < this.columns; j++) {
            this.set(i, j, Math.log10(this.get(i, j)));
          }
        }
        return this;
      };
      AbstractMatrix.log10 = function log10(matrix) {
        const newMatrix = new Matrix(matrix);
        return newMatrix.log10();
      };
      AbstractMatrix.prototype.log2 = function log2() {
        for (let i = 0; i < this.rows; i++) {
          for (let j = 0; j < this.columns; j++) {
            this.set(i, j, Math.log2(this.get(i, j)));
          }
        }
        return this;
      };
      AbstractMatrix.log2 = function log2(matrix) {
        const newMatrix = new Matrix(matrix);
        return newMatrix.log2();
      };
      AbstractMatrix.prototype.round = function round() {
        for (let i = 0; i < this.rows; i++) {
          for (let j = 0; j < this.columns; j++) {
            this.set(i, j, Math.round(this.get(i, j)));
          }
        }
        return this;
      };
      AbstractMatrix.round = function round(matrix) {
        const newMatrix = new Matrix(matrix);
        return newMatrix.round();
      };
      AbstractMatrix.prototype.sign = function sign() {
        for (let i = 0; i < this.rows; i++) {
          for (let j = 0; j < this.columns; j++) {
            this.set(i, j, Math.sign(this.get(i, j)));
          }
        }
        return this;
      };
      AbstractMatrix.sign = function sign(matrix) {
        const newMatrix = new Matrix(matrix);
        return newMatrix.sign();
      };
      AbstractMatrix.prototype.sin = function sin() {
        for (let i = 0; i < this.rows; i++) {
          for (let j = 0; j < this.columns; j++) {
            this.set(i, j, Math.sin(this.get(i, j)));
          }
        }
        return this;
      };
      AbstractMatrix.sin = function sin(matrix) {
        const newMatrix = new Matrix(matrix);
        return newMatrix.sin();
      };
      AbstractMatrix.prototype.sinh = function sinh() {
        for (let i = 0; i < this.rows; i++) {
          for (let j = 0; j < this.columns; j++) {
            this.set(i, j, Math.sinh(this.get(i, j)));
          }
        }
        return this;
      };
      AbstractMatrix.sinh = function sinh(matrix) {
        const newMatrix = new Matrix(matrix);
        return newMatrix.sinh();
      };
      AbstractMatrix.prototype.sqrt = function sqrt() {
        for (let i = 0; i < this.rows; i++) {
          for (let j = 0; j < this.columns; j++) {
            this.set(i, j, Math.sqrt(this.get(i, j)));
          }
        }
        return this;
      };
      AbstractMatrix.sqrt = function sqrt(matrix) {
        const newMatrix = new Matrix(matrix);
        return newMatrix.sqrt();
      };
      AbstractMatrix.prototype.tan = function tan() {
        for (let i = 0; i < this.rows; i++) {
          for (let j = 0; j < this.columns; j++) {
            this.set(i, j, Math.tan(this.get(i, j)));
          }
        }
        return this;
      };
      AbstractMatrix.tan = function tan(matrix) {
        const newMatrix = new Matrix(matrix);
        return newMatrix.tan();
      };
      AbstractMatrix.prototype.tanh = function tanh() {
        for (let i = 0; i < this.rows; i++) {
          for (let j = 0; j < this.columns; j++) {
            this.set(i, j, Math.tanh(this.get(i, j)));
          }
        }
        return this;
      };
      AbstractMatrix.tanh = function tanh(matrix) {
        const newMatrix = new Matrix(matrix);
        return newMatrix.tanh();
      };
      AbstractMatrix.prototype.trunc = function trunc() {
        for (let i = 0; i < this.rows; i++) {
          for (let j = 0; j < this.columns; j++) {
            this.set(i, j, Math.trunc(this.get(i, j)));
          }
        }
        return this;
      };
      AbstractMatrix.trunc = function trunc(matrix) {
        const newMatrix = new Matrix(matrix);
        return newMatrix.trunc();
      };
      AbstractMatrix.pow = function pow(matrix, arg0) {
        const newMatrix = new Matrix(matrix);
        return newMatrix.pow(arg0);
      };
      AbstractMatrix.prototype.pow = function pow(value) {
        if (typeof value === 'number') return this.powS(value);
        return this.powM(value);
      };
      AbstractMatrix.prototype.powS = function powS(value) {
        for (let i = 0; i < this.rows; i++) {
          for (let j = 0; j < this.columns; j++) {
            this.set(i, j, this.get(i, j) ** value);
          }
        }
        return this;
      };
      AbstractMatrix.prototype.powM = function powM(matrix) {
        matrix = Matrix.checkMatrix(matrix);
        if (this.rows !== matrix.rows || this.columns !== matrix.columns) {
          throw new RangeError('Matrices dimensions must be equal');
        }
        for (let i = 0; i < this.rows; i++) {
          for (let j = 0; j < this.columns; j++) {
            this.set(i, j, this.get(i, j) ** matrix.get(i, j));
          }
        }
        return this;
      };
    }

    /**
     * @private
     * Check that a row index is not out of bounds
     * @param {Matrix} matrix
     * @param {number} index
     * @param {boolean} [outer]
     */
    function checkRowIndex(matrix, index, outer) {
      let max = outer ? matrix.rows : matrix.rows - 1;
      if (index < 0 || index > max) {
        throw new RangeError('Row index out of range');
      }
    }

    /**
     * @private
     * Check that a column index is not out of bounds
     * @param {Matrix} matrix
     * @param {number} index
     * @param {boolean} [outer]
     */
    function checkColumnIndex(matrix, index, outer) {
      let max = outer ? matrix.columns : matrix.columns - 1;
      if (index < 0 || index > max) {
        throw new RangeError('Column index out of range');
      }
    }

    /**
     * @private
     * Check that the provided vector is an array with the right length
     * @param {Matrix} matrix
     * @param {Array|Matrix} vector
     * @return {Array}
     * @throws {RangeError}
     */
    function checkRowVector(matrix, vector) {
      if (vector.to1DArray) {
        vector = vector.to1DArray();
      }
      if (vector.length !== matrix.columns) {
        throw new RangeError('vector size must be the same as the number of columns');
      }
      return vector;
    }

    /**
     * @private
     * Check that the provided vector is an array with the right length
     * @param {Matrix} matrix
     * @param {Array|Matrix} vector
     * @return {Array}
     * @throws {RangeError}
     */
    function checkColumnVector(matrix, vector) {
      if (vector.to1DArray) {
        vector = vector.to1DArray();
      }
      if (vector.length !== matrix.rows) {
        throw new RangeError('vector size must be the same as the number of rows');
      }
      return vector;
    }
    function checkRowIndices(matrix, rowIndices) {
      if (!isAnyArray.isAnyArray(rowIndices)) {
        throw new TypeError('row indices must be an array');
      }
      for (let i = 0; i < rowIndices.length; i++) {
        if (rowIndices[i] < 0 || rowIndices[i] >= matrix.rows) {
          throw new RangeError('row indices are out of range');
        }
      }
    }
    function checkColumnIndices(matrix, columnIndices) {
      if (!isAnyArray.isAnyArray(columnIndices)) {
        throw new TypeError('column indices must be an array');
      }
      for (let i = 0; i < columnIndices.length; i++) {
        if (columnIndices[i] < 0 || columnIndices[i] >= matrix.columns) {
          throw new RangeError('column indices are out of range');
        }
      }
    }
    function checkRange(matrix, startRow, endRow, startColumn, endColumn) {
      if (arguments.length !== 5) {
        throw new RangeError('expected 4 arguments');
      }
      checkNumber('startRow', startRow);
      checkNumber('endRow', endRow);
      checkNumber('startColumn', startColumn);
      checkNumber('endColumn', endColumn);
      if (startRow > endRow || startColumn > endColumn || startRow < 0 || startRow >= matrix.rows || endRow < 0 || endRow >= matrix.rows || startColumn < 0 || startColumn >= matrix.columns || endColumn < 0 || endColumn >= matrix.columns) {
        throw new RangeError('Submatrix indices are out of range');
      }
    }
    function newArray(length, value = 0) {
      let array = [];
      for (let i = 0; i < length; i++) {
        array.push(value);
      }
      return array;
    }
    function checkNumber(name, value) {
      if (typeof value !== 'number') {
        throw new TypeError(`${name} must be a number`);
      }
    }
    function checkNonEmpty(matrix) {
      if (matrix.isEmpty()) {
        throw new Error('Empty matrix has no elements to index');
      }
    }
    function sumByRow(matrix) {
      let sum = newArray(matrix.rows);
      for (let i = 0; i < matrix.rows; ++i) {
        for (let j = 0; j < matrix.columns; ++j) {
          sum[i] += matrix.get(i, j);
        }
      }
      return sum;
    }
    function sumByColumn(matrix) {
      let sum = newArray(matrix.columns);
      for (let i = 0; i < matrix.rows; ++i) {
        for (let j = 0; j < matrix.columns; ++j) {
          sum[j] += matrix.get(i, j);
        }
      }
      return sum;
    }
    function sumAll(matrix) {
      let v = 0;
      for (let i = 0; i < matrix.rows; i++) {
        for (let j = 0; j < matrix.columns; j++) {
          v += matrix.get(i, j);
        }
      }
      return v;
    }
    function productByRow(matrix) {
      let sum = newArray(matrix.rows, 1);
      for (let i = 0; i < matrix.rows; ++i) {
        for (let j = 0; j < matrix.columns; ++j) {
          sum[i] *= matrix.get(i, j);
        }
      }
      return sum;
    }
    function productByColumn(matrix) {
      let sum = newArray(matrix.columns, 1);
      for (let i = 0; i < matrix.rows; ++i) {
        for (let j = 0; j < matrix.columns; ++j) {
          sum[j] *= matrix.get(i, j);
        }
      }
      return sum;
    }
    function productAll(matrix) {
      let v = 1;
      for (let i = 0; i < matrix.rows; i++) {
        for (let j = 0; j < matrix.columns; j++) {
          v *= matrix.get(i, j);
        }
      }
      return v;
    }
    function varianceByRow(matrix, unbiased, mean) {
      const rows = matrix.rows;
      const cols = matrix.columns;
      const variance = [];
      for (let i = 0; i < rows; i++) {
        let sum1 = 0;
        let sum2 = 0;
        let x = 0;
        for (let j = 0; j < cols; j++) {
          x = matrix.get(i, j) - mean[i];
          sum1 += x;
          sum2 += x * x;
        }
        if (unbiased) {
          variance.push((sum2 - sum1 * sum1 / cols) / (cols - 1));
        } else {
          variance.push((sum2 - sum1 * sum1 / cols) / cols);
        }
      }
      return variance;
    }
    function varianceByColumn(matrix, unbiased, mean) {
      const rows = matrix.rows;
      const cols = matrix.columns;
      const variance = [];
      for (let j = 0; j < cols; j++) {
        let sum1 = 0;
        let sum2 = 0;
        let x = 0;
        for (let i = 0; i < rows; i++) {
          x = matrix.get(i, j) - mean[j];
          sum1 += x;
          sum2 += x * x;
        }
        if (unbiased) {
          variance.push((sum2 - sum1 * sum1 / rows) / (rows - 1));
        } else {
          variance.push((sum2 - sum1 * sum1 / rows) / rows);
        }
      }
      return variance;
    }
    function varianceAll(matrix, unbiased, mean) {
      const rows = matrix.rows;
      const cols = matrix.columns;
      const size = rows * cols;
      let sum1 = 0;
      let sum2 = 0;
      let x = 0;
      for (let i = 0; i < rows; i++) {
        for (let j = 0; j < cols; j++) {
          x = matrix.get(i, j) - mean;
          sum1 += x;
          sum2 += x * x;
        }
      }
      if (unbiased) {
        return (sum2 - sum1 * sum1 / size) / (size - 1);
      } else {
        return (sum2 - sum1 * sum1 / size) / size;
      }
    }
    function centerByRow(matrix, mean) {
      for (let i = 0; i < matrix.rows; i++) {
        for (let j = 0; j < matrix.columns; j++) {
          matrix.set(i, j, matrix.get(i, j) - mean[i]);
        }
      }
    }
    function centerByColumn(matrix, mean) {
      for (let i = 0; i < matrix.rows; i++) {
        for (let j = 0; j < matrix.columns; j++) {
          matrix.set(i, j, matrix.get(i, j) - mean[j]);
        }
      }
    }
    function centerAll(matrix, mean) {
      for (let i = 0; i < matrix.rows; i++) {
        for (let j = 0; j < matrix.columns; j++) {
          matrix.set(i, j, matrix.get(i, j) - mean);
        }
      }
    }
    function getScaleByRow(matrix) {
      const scale = [];
      for (let i = 0; i < matrix.rows; i++) {
        let sum = 0;
        for (let j = 0; j < matrix.columns; j++) {
          sum += matrix.get(i, j) ** 2 / (matrix.columns - 1);
        }
        scale.push(Math.sqrt(sum));
      }
      return scale;
    }
    function scaleByRow(matrix, scale) {
      for (let i = 0; i < matrix.rows; i++) {
        for (let j = 0; j < matrix.columns; j++) {
          matrix.set(i, j, matrix.get(i, j) / scale[i]);
        }
      }
    }
    function getScaleByColumn(matrix) {
      const scale = [];
      for (let j = 0; j < matrix.columns; j++) {
        let sum = 0;
        for (let i = 0; i < matrix.rows; i++) {
          sum += matrix.get(i, j) ** 2 / (matrix.rows - 1);
        }
        scale.push(Math.sqrt(sum));
      }
      return scale;
    }
    function scaleByColumn(matrix, scale) {
      for (let i = 0; i < matrix.rows; i++) {
        for (let j = 0; j < matrix.columns; j++) {
          matrix.set(i, j, matrix.get(i, j) / scale[j]);
        }
      }
    }
    function getScaleAll(matrix) {
      const divider = matrix.size - 1;
      let sum = 0;
      for (let j = 0; j < matrix.columns; j++) {
        for (let i = 0; i < matrix.rows; i++) {
          sum += matrix.get(i, j) ** 2 / divider;
        }
      }
      return Math.sqrt(sum);
    }
    function scaleAll(matrix, scale) {
      for (let i = 0; i < matrix.rows; i++) {
        for (let j = 0; j < matrix.columns; j++) {
          matrix.set(i, j, matrix.get(i, j) / scale);
        }
      }
    }
    class AbstractMatrix {
      static from1DArray(newRows, newColumns, newData) {
        let length = newRows * newColumns;
        if (length !== newData.length) {
          throw new RangeError('data length does not match given dimensions');
        }
        let newMatrix = new Matrix$1(newRows, newColumns);
        for (let row = 0; row < newRows; row++) {
          for (let column = 0; column < newColumns; column++) {
            newMatrix.set(row, column, newData[row * newColumns + column]);
          }
        }
        return newMatrix;
      }
      static rowVector(newData) {
        let vector = new Matrix$1(1, newData.length);
        for (let i = 0; i < newData.length; i++) {
          vector.set(0, i, newData[i]);
        }
        return vector;
      }
      static columnVector(newData) {
        let vector = new Matrix$1(newData.length, 1);
        for (let i = 0; i < newData.length; i++) {
          vector.set(i, 0, newData[i]);
        }
        return vector;
      }
      static zeros(rows, columns) {
        return new Matrix$1(rows, columns);
      }
      static ones(rows, columns) {
        return new Matrix$1(rows, columns).fill(1);
      }
      static rand(rows, columns, options = {}) {
        if (typeof options !== 'object') {
          throw new TypeError('options must be an object');
        }
        const {
          random = Math.random
        } = options;
        let matrix = new Matrix$1(rows, columns);
        for (let i = 0; i < rows; i++) {
          for (let j = 0; j < columns; j++) {
            matrix.set(i, j, random());
          }
        }
        return matrix;
      }
      static randInt(rows, columns, options = {}) {
        if (typeof options !== 'object') {
          throw new TypeError('options must be an object');
        }
        const {
          min = 0,
          max = 1000,
          random = Math.random
        } = options;
        if (!Number.isInteger(min)) throw new TypeError('min must be an integer');
        if (!Number.isInteger(max)) throw new TypeError('max must be an integer');
        if (min >= max) throw new RangeError('min must be smaller than max');
        let interval = max - min;
        let matrix = new Matrix$1(rows, columns);
        for (let i = 0; i < rows; i++) {
          for (let j = 0; j < columns; j++) {
            let value = min + Math.round(random() * interval);
            matrix.set(i, j, value);
          }
        }
        return matrix;
      }
      static eye(rows, columns, value) {
        if (columns === undefined) columns = rows;
        if (value === undefined) value = 1;
        let min = Math.min(rows, columns);
        let matrix = this.zeros(rows, columns);
        for (let i = 0; i < min; i++) {
          matrix.set(i, i, value);
        }
        return matrix;
      }
      static diag(data, rows, columns) {
        let l = data.length;
        if (rows === undefined) rows = l;
        if (columns === undefined) columns = rows;
        let min = Math.min(l, rows, columns);
        let matrix = this.zeros(rows, columns);
        for (let i = 0; i < min; i++) {
          matrix.set(i, i, data[i]);
        }
        return matrix;
      }
      static min(matrix1, matrix2) {
        matrix1 = this.checkMatrix(matrix1);
        matrix2 = this.checkMatrix(matrix2);
        let rows = matrix1.rows;
        let columns = matrix1.columns;
        let result = new Matrix$1(rows, columns);
        for (let i = 0; i < rows; i++) {
          for (let j = 0; j < columns; j++) {
            result.set(i, j, Math.min(matrix1.get(i, j), matrix2.get(i, j)));
          }
        }
        return result;
      }
      static max(matrix1, matrix2) {
        matrix1 = this.checkMatrix(matrix1);
        matrix2 = this.checkMatrix(matrix2);
        let rows = matrix1.rows;
        let columns = matrix1.columns;
        let result = new this(rows, columns);
        for (let i = 0; i < rows; i++) {
          for (let j = 0; j < columns; j++) {
            result.set(i, j, Math.max(matrix1.get(i, j), matrix2.get(i, j)));
          }
        }
        return result;
      }
      static checkMatrix(value) {
        return AbstractMatrix.isMatrix(value) ? value : new Matrix$1(value);
      }
      static isMatrix(value) {
        return value != null && value.klass === 'Matrix';
      }
      get size() {
        return this.rows * this.columns;
      }
      apply(callback) {
        if (typeof callback !== 'function') {
          throw new TypeError('callback must be a function');
        }
        for (let i = 0; i < this.rows; i++) {
          for (let j = 0; j < this.columns; j++) {
            callback.call(this, i, j);
          }
        }
        return this;
      }
      to1DArray() {
        let array = [];
        for (let i = 0; i < this.rows; i++) {
          for (let j = 0; j < this.columns; j++) {
            array.push(this.get(i, j));
          }
        }
        return array;
      }
      to2DArray() {
        let copy = [];
        for (let i = 0; i < this.rows; i++) {
          copy.push([]);
          for (let j = 0; j < this.columns; j++) {
            copy[i].push(this.get(i, j));
          }
        }
        return copy;
      }
      toJSON() {
        return this.to2DArray();
      }
      isRowVector() {
        return this.rows === 1;
      }
      isColumnVector() {
        return this.columns === 1;
      }
      isVector() {
        return this.rows === 1 || this.columns === 1;
      }
      isSquare() {
        return this.rows === this.columns;
      }
      isEmpty() {
        return this.rows === 0 || this.columns === 0;
      }
      isSymmetric() {
        if (this.isSquare()) {
          for (let i = 0; i < this.rows; i++) {
            for (let j = 0; j <= i; j++) {
              if (this.get(i, j) !== this.get(j, i)) {
                return false;
              }
            }
          }
          return true;
        }
        return false;
      }
      isDistance() {
        if (!this.isSymmetric()) return false;
        for (let i = 0; i < this.rows; i++) {
          if (this.get(i, i) !== 0) return false;
        }
        return true;
      }
      isEchelonForm() {
        let i = 0;
        let j = 0;
        let previousColumn = -1;
        let isEchelonForm = true;
        let checked = false;
        while (i < this.rows && isEchelonForm) {
          j = 0;
          checked = false;
          while (j < this.columns && checked === false) {
            if (this.get(i, j) === 0) {
              j++;
            } else if (this.get(i, j) === 1 && j > previousColumn) {
              checked = true;
              previousColumn = j;
            } else {
              isEchelonForm = false;
              checked = true;
            }
          }
          i++;
        }
        return isEchelonForm;
      }
      isReducedEchelonForm() {
        let i = 0;
        let j = 0;
        let previousColumn = -1;
        let isReducedEchelonForm = true;
        let checked = false;
        while (i < this.rows && isReducedEchelonForm) {
          j = 0;
          checked = false;
          while (j < this.columns && checked === false) {
            if (this.get(i, j) === 0) {
              j++;
            } else if (this.get(i, j) === 1 && j > previousColumn) {
              checked = true;
              previousColumn = j;
            } else {
              isReducedEchelonForm = false;
              checked = true;
            }
          }
          for (let k = j + 1; k < this.rows; k++) {
            if (this.get(i, k) !== 0) {
              isReducedEchelonForm = false;
            }
          }
          i++;
        }
        return isReducedEchelonForm;
      }
      echelonForm() {
        let result = this.clone();
        let h = 0;
        let k = 0;
        while (h < result.rows && k < result.columns) {
          let iMax = h;
          for (let i = h; i < result.rows; i++) {
            if (result.get(i, k) > result.get(iMax, k)) {
              iMax = i;
            }
          }
          if (result.get(iMax, k) === 0) {
            k++;
          } else {
            result.swapRows(h, iMax);
            let tmp = result.get(h, k);
            for (let j = k; j < result.columns; j++) {
              result.set(h, j, result.get(h, j) / tmp);
            }
            for (let i = h + 1; i < result.rows; i++) {
              let factor = result.get(i, k) / result.get(h, k);
              result.set(i, k, 0);
              for (let j = k + 1; j < result.columns; j++) {
                result.set(i, j, result.get(i, j) - result.get(h, j) * factor);
              }
            }
            h++;
            k++;
          }
        }
        return result;
      }
      reducedEchelonForm() {
        let result = this.echelonForm();
        let m = result.columns;
        let n = result.rows;
        let h = n - 1;
        while (h >= 0) {
          if (result.maxRow(h) === 0) {
            h--;
          } else {
            let p = 0;
            let pivot = false;
            while (p < n && pivot === false) {
              if (result.get(h, p) === 1) {
                pivot = true;
              } else {
                p++;
              }
            }
            for (let i = 0; i < h; i++) {
              let factor = result.get(i, p);
              for (let j = p; j < m; j++) {
                let tmp = result.get(i, j) - factor * result.get(h, j);
                result.set(i, j, tmp);
              }
            }
            h--;
          }
        }
        return result;
      }
      set() {
        throw new Error('set method is unimplemented');
      }
      get() {
        throw new Error('get method is unimplemented');
      }
      repeat(options = {}) {
        if (typeof options !== 'object') {
          throw new TypeError('options must be an object');
        }
        const {
          rows = 1,
          columns = 1
        } = options;
        if (!Number.isInteger(rows) || rows <= 0) {
          throw new TypeError('rows must be a positive integer');
        }
        if (!Number.isInteger(columns) || columns <= 0) {
          throw new TypeError('columns must be a positive integer');
        }
        let matrix = new Matrix$1(this.rows * rows, this.columns * columns);
        for (let i = 0; i < rows; i++) {
          for (let j = 0; j < columns; j++) {
            matrix.setSubMatrix(this, this.rows * i, this.columns * j);
          }
        }
        return matrix;
      }
      fill(value) {
        for (let i = 0; i < this.rows; i++) {
          for (let j = 0; j < this.columns; j++) {
            this.set(i, j, value);
          }
        }
        return this;
      }
      neg() {
        return this.mulS(-1);
      }
      getRow(index) {
        checkRowIndex(this, index);
        let row = [];
        for (let i = 0; i < this.columns; i++) {
          row.push(this.get(index, i));
        }
        return row;
      }
      getRowVector(index) {
        return Matrix$1.rowVector(this.getRow(index));
      }
      setRow(index, array) {
        checkRowIndex(this, index);
        array = checkRowVector(this, array);
        for (let i = 0; i < this.columns; i++) {
          this.set(index, i, array[i]);
        }
        return this;
      }
      swapRows(row1, row2) {
        checkRowIndex(this, row1);
        checkRowIndex(this, row2);
        for (let i = 0; i < this.columns; i++) {
          let temp = this.get(row1, i);
          this.set(row1, i, this.get(row2, i));
          this.set(row2, i, temp);
        }
        return this;
      }
      getColumn(index) {
        checkColumnIndex(this, index);
        let column = [];
        for (let i = 0; i < this.rows; i++) {
          column.push(this.get(i, index));
        }
        return column;
      }
      getColumnVector(index) {
        return Matrix$1.columnVector(this.getColumn(index));
      }
      setColumn(index, array) {
        checkColumnIndex(this, index);
        array = checkColumnVector(this, array);
        for (let i = 0; i < this.rows; i++) {
          this.set(i, index, array[i]);
        }
        return this;
      }
      swapColumns(column1, column2) {
        checkColumnIndex(this, column1);
        checkColumnIndex(this, column2);
        for (let i = 0; i < this.rows; i++) {
          let temp = this.get(i, column1);
          this.set(i, column1, this.get(i, column2));
          this.set(i, column2, temp);
        }
        return this;
      }
      addRowVector(vector) {
        vector = checkRowVector(this, vector);
        for (let i = 0; i < this.rows; i++) {
          for (let j = 0; j < this.columns; j++) {
            this.set(i, j, this.get(i, j) + vector[j]);
          }
        }
        return this;
      }
      subRowVector(vector) {
        vector = checkRowVector(this, vector);
        for (let i = 0; i < this.rows; i++) {
          for (let j = 0; j < this.columns; j++) {
            this.set(i, j, this.get(i, j) - vector[j]);
          }
        }
        return this;
      }
      mulRowVector(vector) {
        vector = checkRowVector(this, vector);
        for (let i = 0; i < this.rows; i++) {
          for (let j = 0; j < this.columns; j++) {
            this.set(i, j, this.get(i, j) * vector[j]);
          }
        }
        return this;
      }
      divRowVector(vector) {
        vector = checkRowVector(this, vector);
        for (let i = 0; i < this.rows; i++) {
          for (let j = 0; j < this.columns; j++) {
            this.set(i, j, this.get(i, j) / vector[j]);
          }
        }
        return this;
      }
      addColumnVector(vector) {
        vector = checkColumnVector(this, vector);
        for (let i = 0; i < this.rows; i++) {
          for (let j = 0; j < this.columns; j++) {
            this.set(i, j, this.get(i, j) + vector[i]);
          }
        }
        return this;
      }
      subColumnVector(vector) {
        vector = checkColumnVector(this, vector);
        for (let i = 0; i < this.rows; i++) {
          for (let j = 0; j < this.columns; j++) {
            this.set(i, j, this.get(i, j) - vector[i]);
          }
        }
        return this;
      }
      mulColumnVector(vector) {
        vector = checkColumnVector(this, vector);
        for (let i = 0; i < this.rows; i++) {
          for (let j = 0; j < this.columns; j++) {
            this.set(i, j, this.get(i, j) * vector[i]);
          }
        }
        return this;
      }
      divColumnVector(vector) {
        vector = checkColumnVector(this, vector);
        for (let i = 0; i < this.rows; i++) {
          for (let j = 0; j < this.columns; j++) {
            this.set(i, j, this.get(i, j) / vector[i]);
          }
        }
        return this;
      }
      mulRow(index, value) {
        checkRowIndex(this, index);
        for (let i = 0; i < this.columns; i++) {
          this.set(index, i, this.get(index, i) * value);
        }
        return this;
      }
      mulColumn(index, value) {
        checkColumnIndex(this, index);
        for (let i = 0; i < this.rows; i++) {
          this.set(i, index, this.get(i, index) * value);
        }
        return this;
      }
      max(by) {
        if (this.isEmpty()) {
          return NaN;
        }
        switch (by) {
          case 'row':
            {
              const max = new Array(this.rows).fill(Number.NEGATIVE_INFINITY);
              for (let row = 0; row < this.rows; row++) {
                for (let column = 0; column < this.columns; column++) {
                  if (this.get(row, column) > max[row]) {
                    max[row] = this.get(row, column);
                  }
                }
              }
              return max;
            }
          case 'column':
            {
              const max = new Array(this.columns).fill(Number.NEGATIVE_INFINITY);
              for (let row = 0; row < this.rows; row++) {
                for (let column = 0; column < this.columns; column++) {
                  if (this.get(row, column) > max[column]) {
                    max[column] = this.get(row, column);
                  }
                }
              }
              return max;
            }
          case undefined:
            {
              let max = this.get(0, 0);
              for (let row = 0; row < this.rows; row++) {
                for (let column = 0; column < this.columns; column++) {
                  if (this.get(row, column) > max) {
                    max = this.get(row, column);
                  }
                }
              }
              return max;
            }
          default:
            throw new Error(`invalid option: ${by}`);
        }
      }
      maxIndex() {
        checkNonEmpty(this);
        let v = this.get(0, 0);
        let idx = [0, 0];
        for (let i = 0; i < this.rows; i++) {
          for (let j = 0; j < this.columns; j++) {
            if (this.get(i, j) > v) {
              v = this.get(i, j);
              idx[0] = i;
              idx[1] = j;
            }
          }
        }
        return idx;
      }
      min(by) {
        if (this.isEmpty()) {
          return NaN;
        }
        switch (by) {
          case 'row':
            {
              const min = new Array(this.rows).fill(Number.POSITIVE_INFINITY);
              for (let row = 0; row < this.rows; row++) {
                for (let column = 0; column < this.columns; column++) {
                  if (this.get(row, column) < min[row]) {
                    min[row] = this.get(row, column);
                  }
                }
              }
              return min;
            }
          case 'column':
            {
              const min = new Array(this.columns).fill(Number.POSITIVE_INFINITY);
              for (let row = 0; row < this.rows; row++) {
                for (let column = 0; column < this.columns; column++) {
                  if (this.get(row, column) < min[column]) {
                    min[column] = this.get(row, column);
                  }
                }
              }
              return min;
            }
          case undefined:
            {
              let min = this.get(0, 0);
              for (let row = 0; row < this.rows; row++) {
                for (let column = 0; column < this.columns; column++) {
                  if (this.get(row, column) < min) {
                    min = this.get(row, column);
                  }
                }
              }
              return min;
            }
          default:
            throw new Error(`invalid option: ${by}`);
        }
      }
      minIndex() {
        checkNonEmpty(this);
        let v = this.get(0, 0);
        let idx = [0, 0];
        for (let i = 0; i < this.rows; i++) {
          for (let j = 0; j < this.columns; j++) {
            if (this.get(i, j) < v) {
              v = this.get(i, j);
              idx[0] = i;
              idx[1] = j;
            }
          }
        }
        return idx;
      }
      maxRow(row) {
        checkRowIndex(this, row);
        if (this.isEmpty()) {
          return NaN;
        }
        let v = this.get(row, 0);
        for (let i = 1; i < this.columns; i++) {
          if (this.get(row, i) > v) {
            v = this.get(row, i);
          }
        }
        return v;
      }
      maxRowIndex(row) {
        checkRowIndex(this, row);
        checkNonEmpty(this);
        let v = this.get(row, 0);
        let idx = [row, 0];
        for (let i = 1; i < this.columns; i++) {
          if (this.get(row, i) > v) {
            v = this.get(row, i);
            idx[1] = i;
          }
        }
        return idx;
      }
      minRow(row) {
        checkRowIndex(this, row);
        if (this.isEmpty()) {
          return NaN;
        }
        let v = this.get(row, 0);
        for (let i = 1; i < this.columns; i++) {
          if (this.get(row, i) < v) {
            v = this.get(row, i);
          }
        }
        return v;
      }
      minRowIndex(row) {
        checkRowIndex(this, row);
        checkNonEmpty(this);
        let v = this.get(row, 0);
        let idx = [row, 0];
        for (let i = 1; i < this.columns; i++) {
          if (this.get(row, i) < v) {
            v = this.get(row, i);
            idx[1] = i;
          }
        }
        return idx;
      }
      maxColumn(column) {
        checkColumnIndex(this, column);
        if (this.isEmpty()) {
          return NaN;
        }
        let v = this.get(0, column);
        for (let i = 1; i < this.rows; i++) {
          if (this.get(i, column) > v) {
            v = this.get(i, column);
          }
        }
        return v;
      }
      maxColumnIndex(column) {
        checkColumnIndex(this, column);
        checkNonEmpty(this);
        let v = this.get(0, column);
        let idx = [0, column];
        for (let i = 1; i < this.rows; i++) {
          if (this.get(i, column) > v) {
            v = this.get(i, column);
            idx[0] = i;
          }
        }
        return idx;
      }
      minColumn(column) {
        checkColumnIndex(this, column);
        if (this.isEmpty()) {
          return NaN;
        }
        let v = this.get(0, column);
        for (let i = 1; i < this.rows; i++) {
          if (this.get(i, column) < v) {
            v = this.get(i, column);
          }
        }
        return v;
      }
      minColumnIndex(column) {
        checkColumnIndex(this, column);
        checkNonEmpty(this);
        let v = this.get(0, column);
        let idx = [0, column];
        for (let i = 1; i < this.rows; i++) {
          if (this.get(i, column) < v) {
            v = this.get(i, column);
            idx[0] = i;
          }
        }
        return idx;
      }
      diag() {
        let min = Math.min(this.rows, this.columns);
        let diag = [];
        for (let i = 0; i < min; i++) {
          diag.push(this.get(i, i));
        }
        return diag;
      }
      norm(type = 'frobenius') {
        switch (type) {
          case 'max':
            return this.max();
          case 'frobenius':
            return Math.sqrt(this.dot(this));
          default:
            throw new RangeError(`unknown norm type: ${type}`);
        }
      }
      cumulativeSum() {
        let sum = 0;
        for (let i = 0; i < this.rows; i++) {
          for (let j = 0; j < this.columns; j++) {
            sum += this.get(i, j);
            this.set(i, j, sum);
          }
        }
        return this;
      }
      dot(vector2) {
        if (AbstractMatrix.isMatrix(vector2)) vector2 = vector2.to1DArray();
        let vector1 = this.to1DArray();
        if (vector1.length !== vector2.length) {
          throw new RangeError('vectors do not have the same size');
        }
        let dot = 0;
        for (let i = 0; i < vector1.length; i++) {
          dot += vector1[i] * vector2[i];
        }
        return dot;
      }
      mmul(other) {
        other = Matrix$1.checkMatrix(other);
        let m = this.rows;
        let n = this.columns;
        let p = other.columns;
        let result = new Matrix$1(m, p);
        let Bcolj = new Float64Array(n);
        for (let j = 0; j < p; j++) {
          for (let k = 0; k < n; k++) {
            Bcolj[k] = other.get(k, j);
          }
          for (let i = 0; i < m; i++) {
            let s = 0;
            for (let k = 0; k < n; k++) {
              s += this.get(i, k) * Bcolj[k];
            }
            result.set(i, j, s);
          }
        }
        return result;
      }
      strassen2x2(other) {
        other = Matrix$1.checkMatrix(other);
        let result = new Matrix$1(2, 2);
        const a11 = this.get(0, 0);
        const b11 = other.get(0, 0);
        const a12 = this.get(0, 1);
        const b12 = other.get(0, 1);
        const a21 = this.get(1, 0);
        const b21 = other.get(1, 0);
        const a22 = this.get(1, 1);
        const b22 = other.get(1, 1);

        // Compute intermediate values.
        const m1 = (a11 + a22) * (b11 + b22);
        const m2 = (a21 + a22) * b11;
        const m3 = a11 * (b12 - b22);
        const m4 = a22 * (b21 - b11);
        const m5 = (a11 + a12) * b22;
        const m6 = (a21 - a11) * (b11 + b12);
        const m7 = (a12 - a22) * (b21 + b22);

        // Combine intermediate values into the output.
        const c00 = m1 + m4 - m5 + m7;
        const c01 = m3 + m5;
        const c10 = m2 + m4;
        const c11 = m1 - m2 + m3 + m6;
        result.set(0, 0, c00);
        result.set(0, 1, c01);
        result.set(1, 0, c10);
        result.set(1, 1, c11);
        return result;
      }
      strassen3x3(other) {
        other = Matrix$1.checkMatrix(other);
        let result = new Matrix$1(3, 3);
        const a00 = this.get(0, 0);
        const a01 = this.get(0, 1);
        const a02 = this.get(0, 2);
        const a10 = this.get(1, 0);
        const a11 = this.get(1, 1);
        const a12 = this.get(1, 2);
        const a20 = this.get(2, 0);
        const a21 = this.get(2, 1);
        const a22 = this.get(2, 2);
        const b00 = other.get(0, 0);
        const b01 = other.get(0, 1);
        const b02 = other.get(0, 2);
        const b10 = other.get(1, 0);
        const b11 = other.get(1, 1);
        const b12 = other.get(1, 2);
        const b20 = other.get(2, 0);
        const b21 = other.get(2, 1);
        const b22 = other.get(2, 2);
        const m1 = (a00 + a01 + a02 - a10 - a11 - a21 - a22) * b11;
        const m2 = (a00 - a10) * (-b01 + b11);
        const m3 = a11 * (-b00 + b01 + b10 - b11 - b12 - b20 + b22);
        const m4 = (-a00 + a10 + a11) * (b00 - b01 + b11);
        const m5 = (a10 + a11) * (-b00 + b01);
        const m6 = a00 * b00;
        const m7 = (-a00 + a20 + a21) * (b00 - b02 + b12);
        const m8 = (-a00 + a20) * (b02 - b12);
        const m9 = (a20 + a21) * (-b00 + b02);
        const m10 = (a00 + a01 + a02 - a11 - a12 - a20 - a21) * b12;
        const m11 = a21 * (-b00 + b02 + b10 - b11 - b12 - b20 + b21);
        const m12 = (-a02 + a21 + a22) * (b11 + b20 - b21);
        const m13 = (a02 - a22) * (b11 - b21);
        const m14 = a02 * b20;
        const m15 = (a21 + a22) * (-b20 + b21);
        const m16 = (-a02 + a11 + a12) * (b12 + b20 - b22);
        const m17 = (a02 - a12) * (b12 - b22);
        const m18 = (a11 + a12) * (-b20 + b22);
        const m19 = a01 * b10;
        const m20 = a12 * b21;
        const m21 = a10 * b02;
        const m22 = a20 * b01;
        const m23 = a22 * b22;
        const c00 = m6 + m14 + m19;
        const c01 = m1 + m4 + m5 + m6 + m12 + m14 + m15;
        const c02 = m6 + m7 + m9 + m10 + m14 + m16 + m18;
        const c10 = m2 + m3 + m4 + m6 + m14 + m16 + m17;
        const c11 = m2 + m4 + m5 + m6 + m20;
        const c12 = m14 + m16 + m17 + m18 + m21;
        const c20 = m6 + m7 + m8 + m11 + m12 + m13 + m14;
        const c21 = m12 + m13 + m14 + m15 + m22;
        const c22 = m6 + m7 + m8 + m9 + m23;
        result.set(0, 0, c00);
        result.set(0, 1, c01);
        result.set(0, 2, c02);
        result.set(1, 0, c10);
        result.set(1, 1, c11);
        result.set(1, 2, c12);
        result.set(2, 0, c20);
        result.set(2, 1, c21);
        result.set(2, 2, c22);
        return result;
      }
      mmulStrassen(y) {
        y = Matrix$1.checkMatrix(y);
        let x = this.clone();
        let r1 = x.rows;
        let c1 = x.columns;
        let r2 = y.rows;
        let c2 = y.columns;
        if (c1 !== r2) {
          // eslint-disable-next-line no-console
          console.warn(`Multiplying ${r1} x ${c1} and ${r2} x ${c2} matrix: dimensions do not match.`);
        }

        // Put a matrix into the top left of a matrix of zeros.
        // `rows` and `cols` are the dimensions of the output matrix.
        function embed(mat, rows, cols) {
          let r = mat.rows;
          let c = mat.columns;
          if (r === rows && c === cols) {
            return mat;
          } else {
            let resultat = AbstractMatrix.zeros(rows, cols);
            resultat = resultat.setSubMatrix(mat, 0, 0);
            return resultat;
          }
        }

        // Make sure both matrices are the same size.
        // This is exclusively for simplicity:
        // this algorithm can be implemented with matrices of different sizes.

        let r = Math.max(r1, r2);
        let c = Math.max(c1, c2);
        x = embed(x, r, c);
        y = embed(y, r, c);

        // Our recursive multiplication function.
        function blockMult(a, b, rows, cols) {
          // For small matrices, resort to naive multiplication.
          if (rows <= 512 || cols <= 512) {
            return a.mmul(b); // a is equivalent to this
          }

          // Apply dynamic padding.
          if (rows % 2 === 1 && cols % 2 === 1) {
            a = embed(a, rows + 1, cols + 1);
            b = embed(b, rows + 1, cols + 1);
          } else if (rows % 2 === 1) {
            a = embed(a, rows + 1, cols);
            b = embed(b, rows + 1, cols);
          } else if (cols % 2 === 1) {
            a = embed(a, rows, cols + 1);
            b = embed(b, rows, cols + 1);
          }
          let halfRows = parseInt(a.rows / 2, 10);
          let halfCols = parseInt(a.columns / 2, 10);
          // Subdivide input matrices.
          let a11 = a.subMatrix(0, halfRows - 1, 0, halfCols - 1);
          let b11 = b.subMatrix(0, halfRows - 1, 0, halfCols - 1);
          let a12 = a.subMatrix(0, halfRows - 1, halfCols, a.columns - 1);
          let b12 = b.subMatrix(0, halfRows - 1, halfCols, b.columns - 1);
          let a21 = a.subMatrix(halfRows, a.rows - 1, 0, halfCols - 1);
          let b21 = b.subMatrix(halfRows, b.rows - 1, 0, halfCols - 1);
          let a22 = a.subMatrix(halfRows, a.rows - 1, halfCols, a.columns - 1);
          let b22 = b.subMatrix(halfRows, b.rows - 1, halfCols, b.columns - 1);

          // Compute intermediate values.
          let m1 = blockMult(AbstractMatrix.add(a11, a22), AbstractMatrix.add(b11, b22), halfRows, halfCols);
          let m2 = blockMult(AbstractMatrix.add(a21, a22), b11, halfRows, halfCols);
          let m3 = blockMult(a11, AbstractMatrix.sub(b12, b22), halfRows, halfCols);
          let m4 = blockMult(a22, AbstractMatrix.sub(b21, b11), halfRows, halfCols);
          let m5 = blockMult(AbstractMatrix.add(a11, a12), b22, halfRows, halfCols);
          let m6 = blockMult(AbstractMatrix.sub(a21, a11), AbstractMatrix.add(b11, b12), halfRows, halfCols);
          let m7 = blockMult(AbstractMatrix.sub(a12, a22), AbstractMatrix.add(b21, b22), halfRows, halfCols);

          // Combine intermediate values into the output.
          let c11 = AbstractMatrix.add(m1, m4);
          c11.sub(m5);
          c11.add(m7);
          let c12 = AbstractMatrix.add(m3, m5);
          let c21 = AbstractMatrix.add(m2, m4);
          let c22 = AbstractMatrix.sub(m1, m2);
          c22.add(m3);
          c22.add(m6);

          // Crop output to the desired size (undo dynamic padding).
          let result = AbstractMatrix.zeros(2 * c11.rows, 2 * c11.columns);
          result = result.setSubMatrix(c11, 0, 0);
          result = result.setSubMatrix(c12, c11.rows, 0);
          result = result.setSubMatrix(c21, 0, c11.columns);
          result = result.setSubMatrix(c22, c11.rows, c11.columns);
          return result.subMatrix(0, rows - 1, 0, cols - 1);
        }
        return blockMult(x, y, r, c);
      }
      scaleRows(options = {}) {
        if (typeof options !== 'object') {
          throw new TypeError('options must be an object');
        }
        const {
          min = 0,
          max = 1
        } = options;
        if (!Number.isFinite(min)) throw new TypeError('min must be a number');
        if (!Number.isFinite(max)) throw new TypeError('max must be a number');
        if (min >= max) throw new RangeError('min must be smaller than max');
        let newMatrix = new Matrix$1(this.rows, this.columns);
        for (let i = 0; i < this.rows; i++) {
          const row = this.getRow(i);
          if (row.length > 0) {
            rescale(row, {
              min,
              max,
              output: row
            });
          }
          newMatrix.setRow(i, row);
        }
        return newMatrix;
      }
      scaleColumns(options = {}) {
        if (typeof options !== 'object') {
          throw new TypeError('options must be an object');
        }
        const {
          min = 0,
          max = 1
        } = options;
        if (!Number.isFinite(min)) throw new TypeError('min must be a number');
        if (!Number.isFinite(max)) throw new TypeError('max must be a number');
        if (min >= max) throw new RangeError('min must be smaller than max');
        let newMatrix = new Matrix$1(this.rows, this.columns);
        for (let i = 0; i < this.columns; i++) {
          const column = this.getColumn(i);
          if (column.length) {
            rescale(column, {
              min,
              max,
              output: column
            });
          }
          newMatrix.setColumn(i, column);
        }
        return newMatrix;
      }
      flipRows() {
        const middle = Math.ceil(this.columns / 2);
        for (let i = 0; i < this.rows; i++) {
          for (let j = 0; j < middle; j++) {
            let first = this.get(i, j);
            let last = this.get(i, this.columns - 1 - j);
            this.set(i, j, last);
            this.set(i, this.columns - 1 - j, first);
          }
        }
        return this;
      }
      flipColumns() {
        const middle = Math.ceil(this.rows / 2);
        for (let j = 0; j < this.columns; j++) {
          for (let i = 0; i < middle; i++) {
            let first = this.get(i, j);
            let last = this.get(this.rows - 1 - i, j);
            this.set(i, j, last);
            this.set(this.rows - 1 - i, j, first);
          }
        }
        return this;
      }
      kroneckerProduct(other) {
        other = Matrix$1.checkMatrix(other);
        let m = this.rows;
        let n = this.columns;
        let p = other.rows;
        let q = other.columns;
        let result = new Matrix$1(m * p, n * q);
        for (let i = 0; i < m; i++) {
          for (let j = 0; j < n; j++) {
            for (let k = 0; k < p; k++) {
              for (let l = 0; l < q; l++) {
                result.set(p * i + k, q * j + l, this.get(i, j) * other.get(k, l));
              }
            }
          }
        }
        return result;
      }
      kroneckerSum(other) {
        other = Matrix$1.checkMatrix(other);
        if (!this.isSquare() || !other.isSquare()) {
          throw new Error('Kronecker Sum needs two Square Matrices');
        }
        let m = this.rows;
        let n = other.rows;
        let AxI = this.kroneckerProduct(Matrix$1.eye(n, n));
        let IxB = Matrix$1.eye(m, m).kroneckerProduct(other);
        return AxI.add(IxB);
      }
      transpose() {
        let result = new Matrix$1(this.columns, this.rows);
        for (let i = 0; i < this.rows; i++) {
          for (let j = 0; j < this.columns; j++) {
            result.set(j, i, this.get(i, j));
          }
        }
        return result;
      }
      sortRows(compareFunction = compareNumbers) {
        for (let i = 0; i < this.rows; i++) {
          this.setRow(i, this.getRow(i).sort(compareFunction));
        }
        return this;
      }
      sortColumns(compareFunction = compareNumbers) {
        for (let i = 0; i < this.columns; i++) {
          this.setColumn(i, this.getColumn(i).sort(compareFunction));
        }
        return this;
      }
      subMatrix(startRow, endRow, startColumn, endColumn) {
        checkRange(this, startRow, endRow, startColumn, endColumn);
        let newMatrix = new Matrix$1(endRow - startRow + 1, endColumn - startColumn + 1);
        for (let i = startRow; i <= endRow; i++) {
          for (let j = startColumn; j <= endColumn; j++) {
            newMatrix.set(i - startRow, j - startColumn, this.get(i, j));
          }
        }
        return newMatrix;
      }
      subMatrixRow(indices, startColumn, endColumn) {
        if (startColumn === undefined) startColumn = 0;
        if (endColumn === undefined) endColumn = this.columns - 1;
        if (startColumn > endColumn || startColumn < 0 || startColumn >= this.columns || endColumn < 0 || endColumn >= this.columns) {
          throw new RangeError('Argument out of range');
        }
        let newMatrix = new Matrix$1(indices.length, endColumn - startColumn + 1);
        for (let i = 0; i < indices.length; i++) {
          for (let j = startColumn; j <= endColumn; j++) {
            if (indices[i] < 0 || indices[i] >= this.rows) {
              throw new RangeError(`Row index out of range: ${indices[i]}`);
            }
            newMatrix.set(i, j - startColumn, this.get(indices[i], j));
          }
        }
        return newMatrix;
      }
      subMatrixColumn(indices, startRow, endRow) {
        if (startRow === undefined) startRow = 0;
        if (endRow === undefined) endRow = this.rows - 1;
        if (startRow > endRow || startRow < 0 || startRow >= this.rows || endRow < 0 || endRow >= this.rows) {
          throw new RangeError('Argument out of range');
        }
        let newMatrix = new Matrix$1(endRow - startRow + 1, indices.length);
        for (let i = 0; i < indices.length; i++) {
          for (let j = startRow; j <= endRow; j++) {
            if (indices[i] < 0 || indices[i] >= this.columns) {
              throw new RangeError(`Column index out of range: ${indices[i]}`);
            }
            newMatrix.set(j - startRow, i, this.get(j, indices[i]));
          }
        }
        return newMatrix;
      }
      setSubMatrix(matrix, startRow, startColumn) {
        matrix = Matrix$1.checkMatrix(matrix);
        if (matrix.isEmpty()) {
          return this;
        }
        let endRow = startRow + matrix.rows - 1;
        let endColumn = startColumn + matrix.columns - 1;
        checkRange(this, startRow, endRow, startColumn, endColumn);
        for (let i = 0; i < matrix.rows; i++) {
          for (let j = 0; j < matrix.columns; j++) {
            this.set(startRow + i, startColumn + j, matrix.get(i, j));
          }
        }
        return this;
      }
      selection(rowIndices, columnIndices) {
        checkRowIndices(this, rowIndices);
        checkColumnIndices(this, columnIndices);
        let newMatrix = new Matrix$1(rowIndices.length, columnIndices.length);
        for (let i = 0; i < rowIndices.length; i++) {
          let rowIndex = rowIndices[i];
          for (let j = 0; j < columnIndices.length; j++) {
            let columnIndex = columnIndices[j];
            newMatrix.set(i, j, this.get(rowIndex, columnIndex));
          }
        }
        return newMatrix;
      }
      trace() {
        let min = Math.min(this.rows, this.columns);
        let trace = 0;
        for (let i = 0; i < min; i++) {
          trace += this.get(i, i);
        }
        return trace;
      }
      clone() {
        return this.constructor.copy(this, new Matrix$1(this.rows, this.columns));
      }

      /**
       * @template {AbstractMatrix} M
       * @param {AbstractMatrix} from
       * @param {M} to
       * @return {M}
       */
      static copy(from, to) {
        for (const [row, column, value] of from.entries()) {
          to.set(row, column, value);
        }
        return to;
      }
      sum(by) {
        switch (by) {
          case 'row':
            return sumByRow(this);
          case 'column':
            return sumByColumn(this);
          case undefined:
            return sumAll(this);
          default:
            throw new Error(`invalid option: ${by}`);
        }
      }
      product(by) {
        switch (by) {
          case 'row':
            return productByRow(this);
          case 'column':
            return productByColumn(this);
          case undefined:
            return productAll(this);
          default:
            throw new Error(`invalid option: ${by}`);
        }
      }
      mean(by) {
        const sum = this.sum(by);
        switch (by) {
          case 'row':
            {
              for (let i = 0; i < this.rows; i++) {
                sum[i] /= this.columns;
              }
              return sum;
            }
          case 'column':
            {
              for (let i = 0; i < this.columns; i++) {
                sum[i] /= this.rows;
              }
              return sum;
            }
          case undefined:
            return sum / this.size;
          default:
            throw new Error(`invalid option: ${by}`);
        }
      }
      variance(by, options = {}) {
        if (typeof by === 'object') {
          options = by;
          by = undefined;
        }
        if (typeof options !== 'object') {
          throw new TypeError('options must be an object');
        }
        const {
          unbiased = true,
          mean = this.mean(by)
        } = options;
        if (typeof unbiased !== 'boolean') {
          throw new TypeError('unbiased must be a boolean');
        }
        switch (by) {
          case 'row':
            {
              if (!isAnyArray.isAnyArray(mean)) {
                throw new TypeError('mean must be an array');
              }
              return varianceByRow(this, unbiased, mean);
            }
          case 'column':
            {
              if (!isAnyArray.isAnyArray(mean)) {
                throw new TypeError('mean must be an array');
              }
              return varianceByColumn(this, unbiased, mean);
            }
          case undefined:
            {
              if (typeof mean !== 'number') {
                throw new TypeError('mean must be a number');
              }
              return varianceAll(this, unbiased, mean);
            }
          default:
            throw new Error(`invalid option: ${by}`);
        }
      }
      standardDeviation(by, options) {
        if (typeof by === 'object') {
          options = by;
          by = undefined;
        }
        const variance = this.variance(by, options);
        if (by === undefined) {
          return Math.sqrt(variance);
        } else {
          for (let i = 0; i < variance.length; i++) {
            variance[i] = Math.sqrt(variance[i]);
          }
          return variance;
        }
      }
      center(by, options = {}) {
        if (typeof by === 'object') {
          options = by;
          by = undefined;
        }
        if (typeof options !== 'object') {
          throw new TypeError('options must be an object');
        }
        const {
          center = this.mean(by)
        } = options;
        switch (by) {
          case 'row':
            {
              if (!isAnyArray.isAnyArray(center)) {
                throw new TypeError('center must be an array');
              }
              centerByRow(this, center);
              return this;
            }
          case 'column':
            {
              if (!isAnyArray.isAnyArray(center)) {
                throw new TypeError('center must be an array');
              }
              centerByColumn(this, center);
              return this;
            }
          case undefined:
            {
              if (typeof center !== 'number') {
                throw new TypeError('center must be a number');
              }
              centerAll(this, center);
              return this;
            }
          default:
            throw new Error(`invalid option: ${by}`);
        }
      }
      scale(by, options = {}) {
        if (typeof by === 'object') {
          options = by;
          by = undefined;
        }
        if (typeof options !== 'object') {
          throw new TypeError('options must be an object');
        }
        let scale = options.scale;
        switch (by) {
          case 'row':
            {
              if (scale === undefined) {
                scale = getScaleByRow(this);
              } else if (!isAnyArray.isAnyArray(scale)) {
                throw new TypeError('scale must be an array');
              }
              scaleByRow(this, scale);
              return this;
            }
          case 'column':
            {
              if (scale === undefined) {
                scale = getScaleByColumn(this);
              } else if (!isAnyArray.isAnyArray(scale)) {
                throw new TypeError('scale must be an array');
              }
              scaleByColumn(this, scale);
              return this;
            }
          case undefined:
            {
              if (scale === undefined) {
                scale = getScaleAll(this);
              } else if (typeof scale !== 'number') {
                throw new TypeError('scale must be a number');
              }
              scaleAll(this, scale);
              return this;
            }
          default:
            throw new Error(`invalid option: ${by}`);
        }
      }
      toString(options) {
        return inspectMatrixWithOptions(this, options);
      }
      [Symbol.iterator]() {
        return this.entries();
      }

      /**
       * iterator from left to right, from top to bottom
       * yield [row, column, value]
       * @returns {Generator<[number, number, number], void, *>}
       */
      *entries() {
        for (let row = 0; row < this.rows; row++) {
          for (let col = 0; col < this.columns; col++) {
            yield [row, col, this.get(row, col)];
          }
        }
      }

      /**
       * iterator from left to right, from top to bottom
       * yield value
       * @returns {Generator<number, void, *>}
       */
      *values() {
        for (let row = 0; row < this.rows; row++) {
          for (let col = 0; col < this.columns; col++) {
            yield this.get(row, col);
          }
        }
      }
    }
    AbstractMatrix.prototype.klass = 'Matrix';
    if (typeof Symbol !== 'undefined') {
      AbstractMatrix.prototype[Symbol.for('nodejs.util.inspect.custom')] = inspectMatrix;
    }
    function compareNumbers(a, b) {
      return a - b;
    }
    function isArrayOfNumbers(array) {
      return array.every(element => {
        return typeof element === 'number';
      });
    }

    // Synonyms
    AbstractMatrix.random = AbstractMatrix.rand;
    AbstractMatrix.randomInt = AbstractMatrix.randInt;
    AbstractMatrix.diagonal = AbstractMatrix.diag;
    AbstractMatrix.prototype.diagonal = AbstractMatrix.prototype.diag;
    AbstractMatrix.identity = AbstractMatrix.eye;
    AbstractMatrix.prototype.negate = AbstractMatrix.prototype.neg;
    AbstractMatrix.prototype.tensorProduct = AbstractMatrix.prototype.kroneckerProduct;
    class Matrix$1 extends AbstractMatrix {
      /**
       * @type {Float64Array[]}
       */
      data;

      /**
       * Init an empty matrix
       * @param {number} nRows
       * @param {number} nColumns
       */
      #initData(nRows, nColumns) {
        this.data = [];
        if (Number.isInteger(nColumns) && nColumns >= 0) {
          for (let i = 0; i < nRows; i++) {
            this.data.push(new Float64Array(nColumns));
          }
        } else {
          throw new TypeError('nColumns must be a positive integer');
        }
        this.rows = nRows;
        this.columns = nColumns;
      }
      constructor(nRows, nColumns) {
        super();
        if (Matrix$1.isMatrix(nRows)) {
          this.#initData(nRows.rows, nRows.columns);
          Matrix$1.copy(nRows, this);
        } else if (Number.isInteger(nRows) && nRows >= 0) {
          this.#initData(nRows, nColumns);
        } else if (isAnyArray.isAnyArray(nRows)) {
          // Copy the values from the 2D array
          const arrayData = nRows;
          nRows = arrayData.length;
          nColumns = nRows ? arrayData[0].length : 0;
          if (typeof nColumns !== 'number') {
            throw new TypeError('Data must be a 2D array with at least one element');
          }
          this.data = [];
          for (let i = 0; i < nRows; i++) {
            if (arrayData[i].length !== nColumns) {
              throw new RangeError('Inconsistent array dimensions');
            }
            if (!isArrayOfNumbers(arrayData[i])) {
              throw new TypeError('Input data contains non-numeric values');
            }
            this.data.push(Float64Array.from(arrayData[i]));
          }
          this.rows = nRows;
          this.columns = nColumns;
        } else {
          throw new TypeError('First argument must be a positive number or an array');
        }
      }
      set(rowIndex, columnIndex, value) {
        this.data[rowIndex][columnIndex] = value;
        return this;
      }
      get(rowIndex, columnIndex) {
        return this.data[rowIndex][columnIndex];
      }
      removeRow(index) {
        checkRowIndex(this, index);
        this.data.splice(index, 1);
        this.rows -= 1;
        return this;
      }
      addRow(index, array) {
        if (array === undefined) {
          array = index;
          index = this.rows;
        }
        checkRowIndex(this, index, true);
        array = Float64Array.from(checkRowVector(this, array));
        this.data.splice(index, 0, array);
        this.rows += 1;
        return this;
      }
      removeColumn(index) {
        checkColumnIndex(this, index);
        for (let i = 0; i < this.rows; i++) {
          const newRow = new Float64Array(this.columns - 1);
          for (let j = 0; j < index; j++) {
            newRow[j] = this.data[i][j];
          }
          for (let j = index + 1; j < this.columns; j++) {
            newRow[j - 1] = this.data[i][j];
          }
          this.data[i] = newRow;
        }
        this.columns -= 1;
        return this;
      }
      addColumn(index, array) {
        if (typeof array === 'undefined') {
          array = index;
          index = this.columns;
        }
        checkColumnIndex(this, index, true);
        array = checkColumnVector(this, array);
        for (let i = 0; i < this.rows; i++) {
          const newRow = new Float64Array(this.columns + 1);
          let j = 0;
          for (; j < index; j++) {
            newRow[j] = this.data[i][j];
          }
          newRow[j++] = array[i];
          for (; j < this.columns + 1; j++) {
            newRow[j] = this.data[i][j - 1];
          }
          this.data[i] = newRow;
        }
        this.columns += 1;
        return this;
      }
    }
    installMathOperations(AbstractMatrix, Matrix$1);

    /**
     * @typedef {0 | 1 | number | boolean} Mask
     */

    class SymmetricMatrix extends AbstractMatrix {
      /** @type {Matrix} */
      #matrix;
      get size() {
        return this.#matrix.size;
      }
      get rows() {
        return this.#matrix.rows;
      }
      get columns() {
        return this.#matrix.columns;
      }
      get diagonalSize() {
        return this.rows;
      }

      /**
       * not the same as matrix.isSymmetric()
       * Here is to check if it's instanceof SymmetricMatrix without bundling issues
       *
       * @param value
       * @returns {boolean}
       */
      static isSymmetricMatrix(value) {
        return Matrix$1.isMatrix(value) && value.klassType === 'SymmetricMatrix';
      }

      /**
       * @param diagonalSize
       * @return {SymmetricMatrix}
       */
      static zeros(diagonalSize) {
        return new this(diagonalSize);
      }

      /**
       * @param diagonalSize
       * @return {SymmetricMatrix}
       */
      static ones(diagonalSize) {
        return new this(diagonalSize).fill(1);
      }

      /**
       * @param {number | AbstractMatrix | ArrayLike<ArrayLike<number>>} diagonalSize
       * @return {this}
       */
      constructor(diagonalSize) {
        super();
        if (Matrix$1.isMatrix(diagonalSize)) {
          if (!diagonalSize.isSymmetric()) {
            throw new TypeError('not symmetric data');
          }
          this.#matrix = Matrix$1.copy(diagonalSize, new Matrix$1(diagonalSize.rows, diagonalSize.rows));
        } else if (Number.isInteger(diagonalSize) && diagonalSize >= 0) {
          this.#matrix = new Matrix$1(diagonalSize, diagonalSize);
        } else {
          this.#matrix = new Matrix$1(diagonalSize);
          if (!this.isSymmetric()) {
            throw new TypeError('not symmetric data');
          }
        }
      }
      clone() {
        const matrix = new SymmetricMatrix(this.diagonalSize);
        for (const [row, col, value] of this.upperRightEntries()) {
          matrix.set(row, col, value);
        }
        return matrix;
      }
      toMatrix() {
        return new Matrix$1(this);
      }
      get(rowIndex, columnIndex) {
        return this.#matrix.get(rowIndex, columnIndex);
      }
      set(rowIndex, columnIndex, value) {
        // symmetric set
        this.#matrix.set(rowIndex, columnIndex, value);
        this.#matrix.set(columnIndex, rowIndex, value);
        return this;
      }
      removeCross(index) {
        // symmetric remove side
        this.#matrix.removeRow(index);
        this.#matrix.removeColumn(index);
        return this;
      }
      addCross(index, array) {
        if (array === undefined) {
          array = index;
          index = this.diagonalSize;
        }
        const row = array.slice();
        row.splice(index, 1);
        this.#matrix.addRow(index, row);
        this.#matrix.addColumn(index, array);
        return this;
      }

      /**
       * @param {Mask[]} mask
       */
      applyMask(mask) {
        if (mask.length !== this.diagonalSize) {
          throw new RangeError('Mask size do not match with matrix size');
        }

        // prepare sides to remove from matrix from mask
        /** @type {number[]} */
        const sidesToRemove = [];
        for (const [index, passthroughs] of mask.entries()) {
          if (passthroughs) continue;
          sidesToRemove.push(index);
        }
        // to remove from highest to lowest for no mutation shifting
        sidesToRemove.reverse();

        // remove sides
        for (const sideIndex of sidesToRemove) {
          this.removeCross(sideIndex);
        }
        return this;
      }

      /**
       * Compact format upper-right corner of matrix
       * iterate from left to right, from top to bottom.
       *
       * ```
       *   A B C D
       * A 1 2 3 4
       * B 2 5 6 7
       * C 3 6 8 9
       * D 4 7 9 10
       * ```
       *
       * will return compact 1D array `[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]`
       *
       * length is S(i=0, n=sideSize) => 10 for a 4 sideSized matrix
       *
       * @returns {number[]}
       */
      toCompact() {
        const {
          diagonalSize
        } = this;

        /** @type {number[]} */
        const compact = new Array(diagonalSize * (diagonalSize + 1) / 2);
        for (let col = 0, row = 0, index = 0; index < compact.length; index++) {
          compact[index] = this.get(row, col);
          if (++col >= diagonalSize) col = ++row;
        }
        return compact;
      }

      /**
       * @param {number[]} compact
       * @return {SymmetricMatrix}
       */
      static fromCompact(compact) {
        const compactSize = compact.length;
        // compactSize = (sideSize * (sideSize + 1)) / 2
        // https://mathsolver.microsoft.com/fr/solve-problem/y%20%3D%20%20x%20%60cdot%20%20%20%60frac%7B%20%20%60left(%20x%2B1%20%20%60right)%20%20%20%20%7D%7B%202%20%20%7D
        // sideSize = (Sqrt(8 × compactSize + 1) - 1) / 2
        const diagonalSize = (Math.sqrt(8 * compactSize + 1) - 1) / 2;
        if (!Number.isInteger(diagonalSize)) {
          throw new TypeError(`This array is not a compact representation of a Symmetric Matrix, ${JSON.stringify(compact)}`);
        }
        const matrix = new SymmetricMatrix(diagonalSize);
        for (let col = 0, row = 0, index = 0; index < compactSize; index++) {
          matrix.set(col, row, compact[index]);
          if (++col >= diagonalSize) col = ++row;
        }
        return matrix;
      }

      /**
       * half iterator upper-right-corner from left to right, from top to bottom
       * yield [row, column, value]
       *
       * @returns {Generator<[number, number, number], void, *>}
       */
      *upperRightEntries() {
        for (let row = 0, col = 0; row < this.diagonalSize; void 0) {
          const value = this.get(row, col);
          yield [row, col, value];

          // at the end of row, move cursor to next row at diagonal position
          if (++col >= this.diagonalSize) col = ++row;
        }
      }

      /**
       * half iterator upper-right-corner from left to right, from top to bottom
       * yield value
       *
       * @returns {Generator<[number, number, number], void, *>}
       */
      *upperRightValues() {
        for (let row = 0, col = 0; row < this.diagonalSize; void 0) {
          const value = this.get(row, col);
          yield value;

          // at the end of row, move cursor to next row at diagonal position
          if (++col >= this.diagonalSize) col = ++row;
        }
      }
    }
    SymmetricMatrix.prototype.klassType = 'SymmetricMatrix';
    class DistanceMatrix extends SymmetricMatrix {
      /**
       * not the same as matrix.isSymmetric()
       * Here is to check if it's instanceof SymmetricMatrix without bundling issues
       *
       * @param value
       * @returns {boolean}
       */
      static isDistanceMatrix(value) {
        return SymmetricMatrix.isSymmetricMatrix(value) && value.klassSubType === 'DistanceMatrix';
      }
      constructor(sideSize) {
        super(sideSize);
        if (!this.isDistance()) {
          throw new TypeError('Provided arguments do no produce a distance matrix');
        }
      }
      set(rowIndex, columnIndex, value) {
        // distance matrix diagonal is 0
        if (rowIndex === columnIndex) value = 0;
        return super.set(rowIndex, columnIndex, value);
      }
      addCross(index, array) {
        if (array === undefined) {
          array = index;
          index = this.diagonalSize;
        }

        // ensure distance
        array = array.slice();
        array[index] = 0;
        return super.addCross(index, array);
      }
      toSymmetricMatrix() {
        return new SymmetricMatrix(this);
      }
      clone() {
        const matrix = new DistanceMatrix(this.diagonalSize);
        for (const [row, col, value] of this.upperRightEntries()) {
          if (row === col) continue;
          matrix.set(row, col, value);
        }
        return matrix;
      }

      /**
       * Compact format upper-right corner of matrix
       * no diagonal (only zeros)
       * iterable from left to right, from top to bottom.
       *
       * ```
       *   A B C D
       * A 0 1 2 3
       * B 1 0 4 5
       * C 2 4 0 6
       * D 3 5 6 0
       * ```
       *
       * will return compact 1D array `[1, 2, 3, 4, 5, 6]`
       *
       * length is S(i=0, n=sideSize-1) => 6 for a 4 side sized matrix
       *
       * @returns {number[]}
       */
      toCompact() {
        const {
          diagonalSize
        } = this;
        const compactLength = (diagonalSize - 1) * diagonalSize / 2;

        /** @type {number[]} */
        const compact = new Array(compactLength);
        for (let col = 1, row = 0, index = 0; index < compact.length; index++) {
          compact[index] = this.get(row, col);
          if (++col >= diagonalSize) col = ++row + 1;
        }
        return compact;
      }

      /**
       * @param {number[]} compact
       */
      static fromCompact(compact) {
        const compactSize = compact.length;
        if (compactSize === 0) {
          return new this(0);
        }

        // compactSize in Natural integer range ]0;∞]
        // compactSize = (sideSize * (sideSize - 1)) / 2
        // sideSize = (Sqrt(8 × compactSize + 1) + 1) / 2
        const diagonalSize = (Math.sqrt(8 * compactSize + 1) + 1) / 2;
        if (!Number.isInteger(diagonalSize)) {
          throw new TypeError(`This array is not a compact representation of a DistanceMatrix, ${JSON.stringify(compact)}`);
        }
        const matrix = new this(diagonalSize);
        for (let col = 1, row = 0, index = 0; index < compactSize; index++) {
          matrix.set(col, row, compact[index]);
          if (++col >= diagonalSize) col = ++row + 1;
        }
        return matrix;
      }
    }
    DistanceMatrix.prototype.klassSubType = 'DistanceMatrix';
    class BaseView extends AbstractMatrix {
      constructor(matrix, rows, columns) {
        super();
        this.matrix = matrix;
        this.rows = rows;
        this.columns = columns;
      }
    }
    class MatrixColumnView extends BaseView {
      constructor(matrix, column) {
        checkColumnIndex(matrix, column);
        super(matrix, matrix.rows, 1);
        this.column = column;
      }
      set(rowIndex, columnIndex, value) {
        this.matrix.set(rowIndex, this.column, value);
        return this;
      }
      get(rowIndex) {
        return this.matrix.get(rowIndex, this.column);
      }
    }
    class MatrixColumnSelectionView extends BaseView {
      constructor(matrix, columnIndices) {
        checkColumnIndices(matrix, columnIndices);
        super(matrix, matrix.rows, columnIndices.length);
        this.columnIndices = columnIndices;
      }
      set(rowIndex, columnIndex, value) {
        this.matrix.set(rowIndex, this.columnIndices[columnIndex], value);
        return this;
      }
      get(rowIndex, columnIndex) {
        return this.matrix.get(rowIndex, this.columnIndices[columnIndex]);
      }
    }
    class MatrixFlipColumnView extends BaseView {
      constructor(matrix) {
        super(matrix, matrix.rows, matrix.columns);
      }
      set(rowIndex, columnIndex, value) {
        this.matrix.set(rowIndex, this.columns - columnIndex - 1, value);
        return this;
      }
      get(rowIndex, columnIndex) {
        return this.matrix.get(rowIndex, this.columns - columnIndex - 1);
      }
    }
    class MatrixFlipRowView extends BaseView {
      constructor(matrix) {
        super(matrix, matrix.rows, matrix.columns);
      }
      set(rowIndex, columnIndex, value) {
        this.matrix.set(this.rows - rowIndex - 1, columnIndex, value);
        return this;
      }
      get(rowIndex, columnIndex) {
        return this.matrix.get(this.rows - rowIndex - 1, columnIndex);
      }
    }
    class MatrixRowView extends BaseView {
      constructor(matrix, row) {
        checkRowIndex(matrix, row);
        super(matrix, 1, matrix.columns);
        this.row = row;
      }
      set(rowIndex, columnIndex, value) {
        this.matrix.set(this.row, columnIndex, value);
        return this;
      }
      get(rowIndex, columnIndex) {
        return this.matrix.get(this.row, columnIndex);
      }
    }
    class MatrixRowSelectionView extends BaseView {
      constructor(matrix, rowIndices) {
        checkRowIndices(matrix, rowIndices);
        super(matrix, rowIndices.length, matrix.columns);
        this.rowIndices = rowIndices;
      }
      set(rowIndex, columnIndex, value) {
        this.matrix.set(this.rowIndices[rowIndex], columnIndex, value);
        return this;
      }
      get(rowIndex, columnIndex) {
        return this.matrix.get(this.rowIndices[rowIndex], columnIndex);
      }
    }
    class MatrixSelectionView extends BaseView {
      constructor(matrix, rowIndices, columnIndices) {
        checkRowIndices(matrix, rowIndices);
        checkColumnIndices(matrix, columnIndices);
        super(matrix, rowIndices.length, columnIndices.length);
        this.rowIndices = rowIndices;
        this.columnIndices = columnIndices;
      }
      set(rowIndex, columnIndex, value) {
        this.matrix.set(this.rowIndices[rowIndex], this.columnIndices[columnIndex], value);
        return this;
      }
      get(rowIndex, columnIndex) {
        return this.matrix.get(this.rowIndices[rowIndex], this.columnIndices[columnIndex]);
      }
    }
    class MatrixSubView extends BaseView {
      constructor(matrix, startRow, endRow, startColumn, endColumn) {
        checkRange(matrix, startRow, endRow, startColumn, endColumn);
        super(matrix, endRow - startRow + 1, endColumn - startColumn + 1);
        this.startRow = startRow;
        this.startColumn = startColumn;
      }
      set(rowIndex, columnIndex, value) {
        this.matrix.set(this.startRow + rowIndex, this.startColumn + columnIndex, value);
        return this;
      }
      get(rowIndex, columnIndex) {
        return this.matrix.get(this.startRow + rowIndex, this.startColumn + columnIndex);
      }
    }
    class MatrixTransposeView$1 extends BaseView {
      constructor(matrix) {
        super(matrix, matrix.columns, matrix.rows);
      }
      set(rowIndex, columnIndex, value) {
        this.matrix.set(columnIndex, rowIndex, value);
        return this;
      }
      get(rowIndex, columnIndex) {
        return this.matrix.get(columnIndex, rowIndex);
      }
    }
    class WrapperMatrix1D extends AbstractMatrix {
      constructor(data, options = {}) {
        const {
          rows = 1
        } = options;
        if (data.length % rows !== 0) {
          throw new Error('the data length is not divisible by the number of rows');
        }
        super();
        this.rows = rows;
        this.columns = data.length / rows;
        this.data = data;
      }
      set(rowIndex, columnIndex, value) {
        let index = this._calculateIndex(rowIndex, columnIndex);
        this.data[index] = value;
        return this;
      }
      get(rowIndex, columnIndex) {
        let index = this._calculateIndex(rowIndex, columnIndex);
        return this.data[index];
      }
      _calculateIndex(row, column) {
        return row * this.columns + column;
      }
    }
    class WrapperMatrix2D extends AbstractMatrix {
      constructor(data) {
        super();
        this.data = data;
        this.rows = data.length;
        this.columns = data[0].length;
      }
      set(rowIndex, columnIndex, value) {
        this.data[rowIndex][columnIndex] = value;
        return this;
      }
      get(rowIndex, columnIndex) {
        return this.data[rowIndex][columnIndex];
      }
    }
    function wrap(array, options) {
      if (isAnyArray.isAnyArray(array)) {
        if (array[0] && isAnyArray.isAnyArray(array[0])) {
          return new WrapperMatrix2D(array);
        } else {
          return new WrapperMatrix1D(array, options);
        }
      } else {
        throw new Error('the argument is not an array');
      }
    }
    class LuDecomposition {
      constructor(matrix) {
        matrix = WrapperMatrix2D.checkMatrix(matrix);
        let lu = matrix.clone();
        let rows = lu.rows;
        let columns = lu.columns;
        let pivotVector = new Float64Array(rows);
        let pivotSign = 1;
        let i, j, k, p, s, t, v;
        let LUcolj, kmax;
        for (i = 0; i < rows; i++) {
          pivotVector[i] = i;
        }
        LUcolj = new Float64Array(rows);
        for (j = 0; j < columns; j++) {
          for (i = 0; i < rows; i++) {
            LUcolj[i] = lu.get(i, j);
          }
          for (i = 0; i < rows; i++) {
            kmax = Math.min(i, j);
            s = 0;
            for (k = 0; k < kmax; k++) {
              s += lu.get(i, k) * LUcolj[k];
            }
            LUcolj[i] -= s;
            lu.set(i, j, LUcolj[i]);
          }
          p = j;
          for (i = j + 1; i < rows; i++) {
            if (Math.abs(LUcolj[i]) > Math.abs(LUcolj[p])) {
              p = i;
            }
          }
          if (p !== j) {
            for (k = 0; k < columns; k++) {
              t = lu.get(p, k);
              lu.set(p, k, lu.get(j, k));
              lu.set(j, k, t);
            }
            v = pivotVector[p];
            pivotVector[p] = pivotVector[j];
            pivotVector[j] = v;
            pivotSign = -pivotSign;
          }
          if (j < rows && lu.get(j, j) !== 0) {
            for (i = j + 1; i < rows; i++) {
              lu.set(i, j, lu.get(i, j) / lu.get(j, j));
            }
          }
        }
        this.LU = lu;
        this.pivotVector = pivotVector;
        this.pivotSign = pivotSign;
      }
      isSingular() {
        let data = this.LU;
        let col = data.columns;
        for (let j = 0; j < col; j++) {
          if (data.get(j, j) === 0) {
            return true;
          }
        }
        return false;
      }
      solve(value) {
        value = Matrix$1.checkMatrix(value);
        let lu = this.LU;
        let rows = lu.rows;
        if (rows !== value.rows) {
          throw new Error('Invalid matrix dimensions');
        }
        if (this.isSingular()) {
          throw new Error('LU matrix is singular');
        }
        let count = value.columns;
        let X = value.subMatrixRow(this.pivotVector, 0, count - 1);
        let columns = lu.columns;
        let i, j, k;
        for (k = 0; k < columns; k++) {
          for (i = k + 1; i < columns; i++) {
            for (j = 0; j < count; j++) {
              X.set(i, j, X.get(i, j) - X.get(k, j) * lu.get(i, k));
            }
          }
        }
        for (k = columns - 1; k >= 0; k--) {
          for (j = 0; j < count; j++) {
            X.set(k, j, X.get(k, j) / lu.get(k, k));
          }
          for (i = 0; i < k; i++) {
            for (j = 0; j < count; j++) {
              X.set(i, j, X.get(i, j) - X.get(k, j) * lu.get(i, k));
            }
          }
        }
        return X;
      }
      get determinant() {
        let data = this.LU;
        if (!data.isSquare()) {
          throw new Error('Matrix must be square');
        }
        let determinant = this.pivotSign;
        let col = data.columns;
        for (let j = 0; j < col; j++) {
          determinant *= data.get(j, j);
        }
        return determinant;
      }
      get lowerTriangularMatrix() {
        let data = this.LU;
        let rows = data.rows;
        let columns = data.columns;
        let X = new Matrix$1(rows, columns);
        for (let i = 0; i < rows; i++) {
          for (let j = 0; j < columns; j++) {
            if (i > j) {
              X.set(i, j, data.get(i, j));
            } else if (i === j) {
              X.set(i, j, 1);
            } else {
              X.set(i, j, 0);
            }
          }
        }
        return X;
      }
      get upperTriangularMatrix() {
        let data = this.LU;
        let rows = data.rows;
        let columns = data.columns;
        let X = new Matrix$1(rows, columns);
        for (let i = 0; i < rows; i++) {
          for (let j = 0; j < columns; j++) {
            if (i <= j) {
              X.set(i, j, data.get(i, j));
            } else {
              X.set(i, j, 0);
            }
          }
        }
        return X;
      }
      get pivotPermutationVector() {
        return Array.from(this.pivotVector);
      }
    }
    function hypotenuse(a, b) {
      let r = 0;
      if (Math.abs(a) > Math.abs(b)) {
        r = b / a;
        return Math.abs(a) * Math.sqrt(1 + r * r);
      }
      if (b !== 0) {
        r = a / b;
        return Math.abs(b) * Math.sqrt(1 + r * r);
      }
      return 0;
    }
    class QrDecomposition {
      constructor(value) {
        value = WrapperMatrix2D.checkMatrix(value);
        let qr = value.clone();
        let m = value.rows;
        let n = value.columns;
        let rdiag = new Float64Array(n);
        let i, j, k, s;
        for (k = 0; k < n; k++) {
          let nrm = 0;
          for (i = k; i < m; i++) {
            nrm = hypotenuse(nrm, qr.get(i, k));
          }
          if (nrm !== 0) {
            if (qr.get(k, k) < 0) {
              nrm = -nrm;
            }
            for (i = k; i < m; i++) {
              qr.set(i, k, qr.get(i, k) / nrm);
            }
            qr.set(k, k, qr.get(k, k) + 1);
            for (j = k + 1; j < n; j++) {
              s = 0;
              for (i = k; i < m; i++) {
                s += qr.get(i, k) * qr.get(i, j);
              }
              s = -s / qr.get(k, k);
              for (i = k; i < m; i++) {
                qr.set(i, j, qr.get(i, j) + s * qr.get(i, k));
              }
            }
          }
          rdiag[k] = -nrm;
        }
        this.QR = qr;
        this.Rdiag = rdiag;
      }
      solve(value) {
        value = Matrix$1.checkMatrix(value);
        let qr = this.QR;
        let m = qr.rows;
        if (value.rows !== m) {
          throw new Error('Matrix row dimensions must agree');
        }
        if (!this.isFullRank()) {
          throw new Error('Matrix is rank deficient');
        }
        let count = value.columns;
        let X = value.clone();
        let n = qr.columns;
        let i, j, k, s;
        for (k = 0; k < n; k++) {
          for (j = 0; j < count; j++) {
            s = 0;
            for (i = k; i < m; i++) {
              s += qr.get(i, k) * X.get(i, j);
            }
            s = -s / qr.get(k, k);
            for (i = k; i < m; i++) {
              X.set(i, j, X.get(i, j) + s * qr.get(i, k));
            }
          }
        }
        for (k = n - 1; k >= 0; k--) {
          for (j = 0; j < count; j++) {
            X.set(k, j, X.get(k, j) / this.Rdiag[k]);
          }
          for (i = 0; i < k; i++) {
            for (j = 0; j < count; j++) {
              X.set(i, j, X.get(i, j) - X.get(k, j) * qr.get(i, k));
            }
          }
        }
        return X.subMatrix(0, n - 1, 0, count - 1);
      }
      isFullRank() {
        let columns = this.QR.columns;
        for (let i = 0; i < columns; i++) {
          if (this.Rdiag[i] === 0) {
            return false;
          }
        }
        return true;
      }
      get upperTriangularMatrix() {
        let qr = this.QR;
        let n = qr.columns;
        let X = new Matrix$1(n, n);
        let i, j;
        for (i = 0; i < n; i++) {
          for (j = 0; j < n; j++) {
            if (i < j) {
              X.set(i, j, qr.get(i, j));
            } else if (i === j) {
              X.set(i, j, this.Rdiag[i]);
            } else {
              X.set(i, j, 0);
            }
          }
        }
        return X;
      }
      get orthogonalMatrix() {
        let qr = this.QR;
        let rows = qr.rows;
        let columns = qr.columns;
        let X = new Matrix$1(rows, columns);
        let i, j, k, s;
        for (k = columns - 1; k >= 0; k--) {
          for (i = 0; i < rows; i++) {
            X.set(i, k, 0);
          }
          X.set(k, k, 1);
          for (j = k; j < columns; j++) {
            if (qr.get(k, k) !== 0) {
              s = 0;
              for (i = k; i < rows; i++) {
                s += qr.get(i, k) * X.get(i, j);
              }
              s = -s / qr.get(k, k);
              for (i = k; i < rows; i++) {
                X.set(i, j, X.get(i, j) + s * qr.get(i, k));
              }
            }
          }
        }
        return X;
      }
    }
    class SingularValueDecomposition {
      constructor(value, options = {}) {
        value = WrapperMatrix2D.checkMatrix(value);
        if (value.isEmpty()) {
          throw new Error('Matrix must be non-empty');
        }
        let m = value.rows;
        let n = value.columns;
        const {
          computeLeftSingularVectors = true,
          computeRightSingularVectors = true,
          autoTranspose = false
        } = options;
        let wantu = Boolean(computeLeftSingularVectors);
        let wantv = Boolean(computeRightSingularVectors);
        let swapped = false;
        let a;
        if (m < n) {
          if (!autoTranspose) {
            a = value.clone();
            // eslint-disable-next-line no-console
            console.warn('Computing SVD on a matrix with more columns than rows. Consider enabling autoTranspose');
          } else {
            a = value.transpose();
            m = a.rows;
            n = a.columns;
            swapped = true;
            let aux = wantu;
            wantu = wantv;
            wantv = aux;
          }
        } else {
          a = value.clone();
        }
        let nu = Math.min(m, n);
        let ni = Math.min(m + 1, n);
        let s = new Float64Array(ni);
        let U = new Matrix$1(m, nu);
        let V = new Matrix$1(n, n);
        let e = new Float64Array(n);
        let work = new Float64Array(m);
        let si = new Float64Array(ni);
        for (let i = 0; i < ni; i++) si[i] = i;
        let nct = Math.min(m - 1, n);
        let nrt = Math.max(0, Math.min(n - 2, m));
        let mrc = Math.max(nct, nrt);
        for (let k = 0; k < mrc; k++) {
          if (k < nct) {
            s[k] = 0;
            for (let i = k; i < m; i++) {
              s[k] = hypotenuse(s[k], a.get(i, k));
            }
            if (s[k] !== 0) {
              if (a.get(k, k) < 0) {
                s[k] = -s[k];
              }
              for (let i = k; i < m; i++) {
                a.set(i, k, a.get(i, k) / s[k]);
              }
              a.set(k, k, a.get(k, k) + 1);
            }
            s[k] = -s[k];
          }
          for (let j = k + 1; j < n; j++) {
            if (k < nct && s[k] !== 0) {
              let t = 0;
              for (let i = k; i < m; i++) {
                t += a.get(i, k) * a.get(i, j);
              }
              t = -t / a.get(k, k);
              for (let i = k; i < m; i++) {
                a.set(i, j, a.get(i, j) + t * a.get(i, k));
              }
            }
            e[j] = a.get(k, j);
          }
          if (wantu && k < nct) {
            for (let i = k; i < m; i++) {
              U.set(i, k, a.get(i, k));
            }
          }
          if (k < nrt) {
            e[k] = 0;
            for (let i = k + 1; i < n; i++) {
              e[k] = hypotenuse(e[k], e[i]);
            }
            if (e[k] !== 0) {
              if (e[k + 1] < 0) {
                e[k] = 0 - e[k];
              }
              for (let i = k + 1; i < n; i++) {
                e[i] /= e[k];
              }
              e[k + 1] += 1;
            }
            e[k] = -e[k];
            if (k + 1 < m && e[k] !== 0) {
              for (let i = k + 1; i < m; i++) {
                work[i] = 0;
              }
              for (let i = k + 1; i < m; i++) {
                for (let j = k + 1; j < n; j++) {
                  work[i] += e[j] * a.get(i, j);
                }
              }
              for (let j = k + 1; j < n; j++) {
                let t = -e[j] / e[k + 1];
                for (let i = k + 1; i < m; i++) {
                  a.set(i, j, a.get(i, j) + t * work[i]);
                }
              }
            }
            if (wantv) {
              for (let i = k + 1; i < n; i++) {
                V.set(i, k, e[i]);
              }
            }
          }
        }
        let p = Math.min(n, m + 1);
        if (nct < n) {
          s[nct] = a.get(nct, nct);
        }
        if (m < p) {
          s[p - 1] = 0;
        }
        if (nrt + 1 < p) {
          e[nrt] = a.get(nrt, p - 1);
        }
        e[p - 1] = 0;
        if (wantu) {
          for (let j = nct; j < nu; j++) {
            for (let i = 0; i < m; i++) {
              U.set(i, j, 0);
            }
            U.set(j, j, 1);
          }
          for (let k = nct - 1; k >= 0; k--) {
            if (s[k] !== 0) {
              for (let j = k + 1; j < nu; j++) {
                let t = 0;
                for (let i = k; i < m; i++) {
                  t += U.get(i, k) * U.get(i, j);
                }
                t = -t / U.get(k, k);
                for (let i = k; i < m; i++) {
                  U.set(i, j, U.get(i, j) + t * U.get(i, k));
                }
              }
              for (let i = k; i < m; i++) {
                U.set(i, k, -U.get(i, k));
              }
              U.set(k, k, 1 + U.get(k, k));
              for (let i = 0; i < k - 1; i++) {
                U.set(i, k, 0);
              }
            } else {
              for (let i = 0; i < m; i++) {
                U.set(i, k, 0);
              }
              U.set(k, k, 1);
            }
          }
        }
        if (wantv) {
          for (let k = n - 1; k >= 0; k--) {
            if (k < nrt && e[k] !== 0) {
              for (let j = k + 1; j < n; j++) {
                let t = 0;
                for (let i = k + 1; i < n; i++) {
                  t += V.get(i, k) * V.get(i, j);
                }
                t = -t / V.get(k + 1, k);
                for (let i = k + 1; i < n; i++) {
                  V.set(i, j, V.get(i, j) + t * V.get(i, k));
                }
              }
            }
            for (let i = 0; i < n; i++) {
              V.set(i, k, 0);
            }
            V.set(k, k, 1);
          }
        }
        let pp = p - 1;
        let eps = Number.EPSILON;
        while (p > 0) {
          let k, kase;
          for (k = p - 2; k >= -1; k--) {
            if (k === -1) {
              break;
            }
            const alpha = Number.MIN_VALUE + eps * Math.abs(s[k] + Math.abs(s[k + 1]));
            if (Math.abs(e[k]) <= alpha || Number.isNaN(e[k])) {
              e[k] = 0;
              break;
            }
          }
          if (k === p - 2) {
            kase = 4;
          } else {
            let ks;
            for (ks = p - 1; ks >= k; ks--) {
              if (ks === k) {
                break;
              }
              let t = (ks !== p ? Math.abs(e[ks]) : 0) + (ks !== k + 1 ? Math.abs(e[ks - 1]) : 0);
              if (Math.abs(s[ks]) <= eps * t) {
                s[ks] = 0;
                break;
              }
            }
            if (ks === k) {
              kase = 3;
            } else if (ks === p - 1) {
              kase = 1;
            } else {
              kase = 2;
              k = ks;
            }
          }
          k++;
          switch (kase) {
            case 1:
              {
                let f = e[p - 2];
                e[p - 2] = 0;
                for (let j = p - 2; j >= k; j--) {
                  let t = hypotenuse(s[j], f);
                  let cs = s[j] / t;
                  let sn = f / t;
                  s[j] = t;
                  if (j !== k) {
                    f = -sn * e[j - 1];
                    e[j - 1] = cs * e[j - 1];
                  }
                  if (wantv) {
                    for (let i = 0; i < n; i++) {
                      t = cs * V.get(i, j) + sn * V.get(i, p - 1);
                      V.set(i, p - 1, -sn * V.get(i, j) + cs * V.get(i, p - 1));
                      V.set(i, j, t);
                    }
                  }
                }
                break;
              }
            case 2:
              {
                let f = e[k - 1];
                e[k - 1] = 0;
                for (let j = k; j < p; j++) {
                  let t = hypotenuse(s[j], f);
                  let cs = s[j] / t;
                  let sn = f / t;
                  s[j] = t;
                  f = -sn * e[j];
                  e[j] = cs * e[j];
                  if (wantu) {
                    for (let i = 0; i < m; i++) {
                      t = cs * U.get(i, j) + sn * U.get(i, k - 1);
                      U.set(i, k - 1, -sn * U.get(i, j) + cs * U.get(i, k - 1));
                      U.set(i, j, t);
                    }
                  }
                }
                break;
              }
            case 3:
              {
                const scale = Math.max(Math.abs(s[p - 1]), Math.abs(s[p - 2]), Math.abs(e[p - 2]), Math.abs(s[k]), Math.abs(e[k]));
                const sp = s[p - 1] / scale;
                const spm1 = s[p - 2] / scale;
                const epm1 = e[p - 2] / scale;
                const sk = s[k] / scale;
                const ek = e[k] / scale;
                const b = ((spm1 + sp) * (spm1 - sp) + epm1 * epm1) / 2;
                const c = sp * epm1 * (sp * epm1);
                let shift = 0;
                if (b !== 0 || c !== 0) {
                  if (b < 0) {
                    shift = 0 - Math.sqrt(b * b + c);
                  } else {
                    shift = Math.sqrt(b * b + c);
                  }
                  shift = c / (b + shift);
                }
                let f = (sk + sp) * (sk - sp) + shift;
                let g = sk * ek;
                for (let j = k; j < p - 1; j++) {
                  let t = hypotenuse(f, g);
                  if (t === 0) t = Number.MIN_VALUE;
                  let cs = f / t;
                  let sn = g / t;
                  if (j !== k) {
                    e[j - 1] = t;
                  }
                  f = cs * s[j] + sn * e[j];
                  e[j] = cs * e[j] - sn * s[j];
                  g = sn * s[j + 1];
                  s[j + 1] = cs * s[j + 1];
                  if (wantv) {
                    for (let i = 0; i < n; i++) {
                      t = cs * V.get(i, j) + sn * V.get(i, j + 1);
                      V.set(i, j + 1, -sn * V.get(i, j) + cs * V.get(i, j + 1));
                      V.set(i, j, t);
                    }
                  }
                  t = hypotenuse(f, g);
                  if (t === 0) t = Number.MIN_VALUE;
                  cs = f / t;
                  sn = g / t;
                  s[j] = t;
                  f = cs * e[j] + sn * s[j + 1];
                  s[j + 1] = -sn * e[j] + cs * s[j + 1];
                  g = sn * e[j + 1];
                  e[j + 1] = cs * e[j + 1];
                  if (wantu && j < m - 1) {
                    for (let i = 0; i < m; i++) {
                      t = cs * U.get(i, j) + sn * U.get(i, j + 1);
                      U.set(i, j + 1, -sn * U.get(i, j) + cs * U.get(i, j + 1));
                      U.set(i, j, t);
                    }
                  }
                }
                e[p - 2] = f;
                break;
              }
            case 4:
              {
                if (s[k] <= 0) {
                  s[k] = s[k] < 0 ? -s[k] : 0;
                  if (wantv) {
                    for (let i = 0; i <= pp; i++) {
                      V.set(i, k, -V.get(i, k));
                    }
                  }
                }
                while (k < pp) {
                  if (s[k] >= s[k + 1]) {
                    break;
                  }
                  let t = s[k];
                  s[k] = s[k + 1];
                  s[k + 1] = t;
                  if (wantv && k < n - 1) {
                    for (let i = 0; i < n; i++) {
                      t = V.get(i, k + 1);
                      V.set(i, k + 1, V.get(i, k));
                      V.set(i, k, t);
                    }
                  }
                  if (wantu && k < m - 1) {
                    for (let i = 0; i < m; i++) {
                      t = U.get(i, k + 1);
                      U.set(i, k + 1, U.get(i, k));
                      U.set(i, k, t);
                    }
                  }
                  k++;
                }
                p--;
                break;
              }
            // no default
          }
        }
        if (swapped) {
          let tmp = V;
          V = U;
          U = tmp;
        }
        this.m = m;
        this.n = n;
        this.s = s;
        this.U = U;
        this.V = V;
      }
      solve(value) {
        let Y = value;
        let e = this.threshold;
        let scols = this.s.length;
        let Ls = Matrix$1.zeros(scols, scols);
        for (let i = 0; i < scols; i++) {
          if (Math.abs(this.s[i]) <= e) {
            Ls.set(i, i, 0);
          } else {
            Ls.set(i, i, 1 / this.s[i]);
          }
        }
        let U = this.U;
        let V = this.rightSingularVectors;
        let VL = V.mmul(Ls);
        let vrows = V.rows;
        let urows = U.rows;
        let VLU = Matrix$1.zeros(vrows, urows);
        for (let i = 0; i < vrows; i++) {
          for (let j = 0; j < urows; j++) {
            let sum = 0;
            for (let k = 0; k < scols; k++) {
              sum += VL.get(i, k) * U.get(j, k);
            }
            VLU.set(i, j, sum);
          }
        }
        return VLU.mmul(Y);
      }
      solveForDiagonal(value) {
        return this.solve(Matrix$1.diag(value));
      }
      inverse() {
        let V = this.V;
        let e = this.threshold;
        let vrows = V.rows;
        let vcols = V.columns;
        let X = new Matrix$1(vrows, this.s.length);
        for (let i = 0; i < vrows; i++) {
          for (let j = 0; j < vcols; j++) {
            if (Math.abs(this.s[j]) > e) {
              X.set(i, j, V.get(i, j) / this.s[j]);
            }
          }
        }
        let U = this.U;
        let urows = U.rows;
        let ucols = U.columns;
        let Y = new Matrix$1(vrows, urows);
        for (let i = 0; i < vrows; i++) {
          for (let j = 0; j < urows; j++) {
            let sum = 0;
            for (let k = 0; k < ucols; k++) {
              sum += X.get(i, k) * U.get(j, k);
            }
            Y.set(i, j, sum);
          }
        }
        return Y;
      }
      get condition() {
        return this.s[0] / this.s[Math.min(this.m, this.n) - 1];
      }
      get norm2() {
        return this.s[0];
      }
      get rank() {
        let tol = Math.max(this.m, this.n) * this.s[0] * Number.EPSILON;
        let r = 0;
        let s = this.s;
        for (let i = 0, ii = s.length; i < ii; i++) {
          if (s[i] > tol) {
            r++;
          }
        }
        return r;
      }
      get diagonal() {
        return Array.from(this.s);
      }
      get threshold() {
        return Number.EPSILON / 2 * Math.max(this.m, this.n) * this.s[0];
      }
      get leftSingularVectors() {
        return this.U;
      }
      get rightSingularVectors() {
        return this.V;
      }
      get diagonalMatrix() {
        return Matrix$1.diag(this.s);
      }
    }
    function inverse(matrix, useSVD = false) {
      matrix = WrapperMatrix2D.checkMatrix(matrix);
      if (useSVD) {
        return new SingularValueDecomposition(matrix).inverse();
      } else {
        return solve$1(matrix, Matrix$1.eye(matrix.rows));
      }
    }
    function solve$1(leftHandSide, rightHandSide, useSVD = false) {
      leftHandSide = WrapperMatrix2D.checkMatrix(leftHandSide);
      rightHandSide = WrapperMatrix2D.checkMatrix(rightHandSide);
      if (useSVD) {
        return new SingularValueDecomposition(leftHandSide).solve(rightHandSide);
      } else {
        return leftHandSide.isSquare() ? new LuDecomposition(leftHandSide).solve(rightHandSide) : new QrDecomposition(leftHandSide).solve(rightHandSide);
      }
    }
    function determinant(matrix) {
      matrix = Matrix$1.checkMatrix(matrix);
      if (matrix.isSquare()) {
        if (matrix.columns === 0) {
          return 1;
        }
        let a, b, c, d;
        if (matrix.columns === 2) {
          // 2 x 2 matrix
          a = matrix.get(0, 0);
          b = matrix.get(0, 1);
          c = matrix.get(1, 0);
          d = matrix.get(1, 1);
          return a * d - b * c;
        } else if (matrix.columns === 3) {
          // 3 x 3 matrix
          let subMatrix0, subMatrix1, subMatrix2;
          subMatrix0 = new MatrixSelectionView(matrix, [1, 2], [1, 2]);
          subMatrix1 = new MatrixSelectionView(matrix, [1, 2], [0, 2]);
          subMatrix2 = new MatrixSelectionView(matrix, [1, 2], [0, 1]);
          a = matrix.get(0, 0);
          b = matrix.get(0, 1);
          c = matrix.get(0, 2);
          return a * determinant(subMatrix0) - b * determinant(subMatrix1) + c * determinant(subMatrix2);
        } else {
          // general purpose determinant using the LU decomposition
          return new LuDecomposition(matrix).determinant;
        }
      } else {
        throw Error('determinant can only be calculated for a square matrix');
      }
    }
    function xrange(n, exception) {
      let range = [];
      for (let i = 0; i < n; i++) {
        if (i !== exception) {
          range.push(i);
        }
      }
      return range;
    }
    function dependenciesOneRow(error, matrix, index, thresholdValue = 10e-10, thresholdError = 10e-10) {
      if (error > thresholdError) {
        return new Array(matrix.rows + 1).fill(0);
      } else {
        let returnArray = matrix.addRow(index, [0]);
        for (let i = 0; i < returnArray.rows; i++) {
          if (Math.abs(returnArray.get(i, 0)) < thresholdValue) {
            returnArray.set(i, 0, 0);
          }
        }
        return returnArray.to1DArray();
      }
    }
    function linearDependencies(matrix, options = {}) {
      const {
        thresholdValue = 10e-10,
        thresholdError = 10e-10
      } = options;
      matrix = Matrix$1.checkMatrix(matrix);
      let n = matrix.rows;
      let results = new Matrix$1(n, n);
      for (let i = 0; i < n; i++) {
        let b = Matrix$1.columnVector(matrix.getRow(i));
        let Abis = matrix.subMatrixRow(xrange(n, i)).transpose();
        let svd = new SingularValueDecomposition(Abis);
        let x = svd.solve(b);
        let error = Matrix$1.sub(b, Abis.mmul(x)).abs().max();
        results.setRow(i, dependenciesOneRow(error, x, i, thresholdValue, thresholdError));
      }
      return results;
    }
    function pseudoInverse(matrix, threshold = Number.EPSILON) {
      matrix = Matrix$1.checkMatrix(matrix);
      if (matrix.isEmpty()) {
        // with a zero dimension, the pseudo-inverse is the transpose, since all 0xn and nx0 matrices are singular
        // (0xn)*(nx0)*(0xn) = 0xn
        // (nx0)*(0xn)*(nx0) = nx0
        return matrix.transpose();
      }
      let svdSolution = new SingularValueDecomposition(matrix, {
        autoTranspose: true
      });
      let U = svdSolution.leftSingularVectors;
      let V = svdSolution.rightSingularVectors;
      let s = svdSolution.diagonal;
      for (let i = 0; i < s.length; i++) {
        if (Math.abs(s[i]) > threshold) {
          s[i] = 1.0 / s[i];
        } else {
          s[i] = 0.0;
        }
      }
      return V.mmul(Matrix$1.diag(s).mmul(U.transpose()));
    }
    function covariance(xMatrix, yMatrix = xMatrix, options = {}) {
      xMatrix = new Matrix$1(xMatrix);
      let yIsSame = false;
      if (typeof yMatrix === 'object' && !Matrix$1.isMatrix(yMatrix) && !isAnyArray.isAnyArray(yMatrix)) {
        options = yMatrix;
        yMatrix = xMatrix;
        yIsSame = true;
      } else {
        yMatrix = new Matrix$1(yMatrix);
      }
      if (xMatrix.rows !== yMatrix.rows) {
        throw new TypeError('Both matrices must have the same number of rows');
      }
      const {
        center = true
      } = options;
      if (center) {
        xMatrix = xMatrix.center('column');
        if (!yIsSame) {
          yMatrix = yMatrix.center('column');
        }
      }
      const cov = xMatrix.transpose().mmul(yMatrix);
      for (let i = 0; i < cov.rows; i++) {
        for (let j = 0; j < cov.columns; j++) {
          cov.set(i, j, cov.get(i, j) * (1 / (xMatrix.rows - 1)));
        }
      }
      return cov;
    }
    function correlation(xMatrix, yMatrix = xMatrix, options = {}) {
      xMatrix = new Matrix$1(xMatrix);
      let yIsSame = false;
      if (typeof yMatrix === 'object' && !Matrix$1.isMatrix(yMatrix) && !isAnyArray.isAnyArray(yMatrix)) {
        options = yMatrix;
        yMatrix = xMatrix;
        yIsSame = true;
      } else {
        yMatrix = new Matrix$1(yMatrix);
      }
      if (xMatrix.rows !== yMatrix.rows) {
        throw new TypeError('Both matrices must have the same number of rows');
      }
      const {
        center = true,
        scale = true
      } = options;
      if (center) {
        xMatrix.center('column');
        if (!yIsSame) {
          yMatrix.center('column');
        }
      }
      if (scale) {
        xMatrix.scale('column');
        if (!yIsSame) {
          yMatrix.scale('column');
        }
      }
      const sdx = xMatrix.standardDeviation('column', {
        unbiased: true
      });
      const sdy = yIsSame ? sdx : yMatrix.standardDeviation('column', {
        unbiased: true
      });
      const corr = xMatrix.transpose().mmul(yMatrix);
      for (let i = 0; i < corr.rows; i++) {
        for (let j = 0; j < corr.columns; j++) {
          corr.set(i, j, corr.get(i, j) * (1 / (sdx[i] * sdy[j])) * (1 / (xMatrix.rows - 1)));
        }
      }
      return corr;
    }
    class EigenvalueDecomposition {
      constructor(matrix, options = {}) {
        const {
          assumeSymmetric = false
        } = options;
        matrix = WrapperMatrix2D.checkMatrix(matrix);
        if (!matrix.isSquare()) {
          throw new Error('Matrix is not a square matrix');
        }
        if (matrix.isEmpty()) {
          throw new Error('Matrix must be non-empty');
        }
        let n = matrix.columns;
        let V = new Matrix$1(n, n);
        let d = new Float64Array(n);
        let e = new Float64Array(n);
        let value = matrix;
        let i, j;
        let isSymmetric = false;
        if (assumeSymmetric) {
          isSymmetric = true;
        } else {
          isSymmetric = matrix.isSymmetric();
        }
        if (isSymmetric) {
          for (i = 0; i < n; i++) {
            for (j = 0; j < n; j++) {
              V.set(i, j, value.get(i, j));
            }
          }
          tred2(n, e, d, V);
          tql2(n, e, d, V);
        } else {
          let H = new Matrix$1(n, n);
          let ort = new Float64Array(n);
          for (j = 0; j < n; j++) {
            for (i = 0; i < n; i++) {
              H.set(i, j, value.get(i, j));
            }
          }
          orthes(n, H, ort, V);
          hqr2(n, e, d, V, H);
        }
        this.n = n;
        this.e = e;
        this.d = d;
        this.V = V;
      }
      get realEigenvalues() {
        return Array.from(this.d);
      }
      get imaginaryEigenvalues() {
        return Array.from(this.e);
      }
      get eigenvectorMatrix() {
        return this.V;
      }
      get diagonalMatrix() {
        let n = this.n;
        let e = this.e;
        let d = this.d;
        let X = new Matrix$1(n, n);
        let i, j;
        for (i = 0; i < n; i++) {
          for (j = 0; j < n; j++) {
            X.set(i, j, 0);
          }
          X.set(i, i, d[i]);
          if (e[i] > 0) {
            X.set(i, i + 1, e[i]);
          } else if (e[i] < 0) {
            X.set(i, i - 1, e[i]);
          }
        }
        return X;
      }
    }
    function tred2(n, e, d, V) {
      let f, g, h, i, j, k, hh, scale;
      for (j = 0; j < n; j++) {
        d[j] = V.get(n - 1, j);
      }
      for (i = n - 1; i > 0; i--) {
        scale = 0;
        h = 0;
        for (k = 0; k < i; k++) {
          scale = scale + Math.abs(d[k]);
        }
        if (scale === 0) {
          e[i] = d[i - 1];
          for (j = 0; j < i; j++) {
            d[j] = V.get(i - 1, j);
            V.set(i, j, 0);
            V.set(j, i, 0);
          }
        } else {
          for (k = 0; k < i; k++) {
            d[k] /= scale;
            h += d[k] * d[k];
          }
          f = d[i - 1];
          g = Math.sqrt(h);
          if (f > 0) {
            g = -g;
          }
          e[i] = scale * g;
          h = h - f * g;
          d[i - 1] = f - g;
          for (j = 0; j < i; j++) {
            e[j] = 0;
          }
          for (j = 0; j < i; j++) {
            f = d[j];
            V.set(j, i, f);
            g = e[j] + V.get(j, j) * f;
            for (k = j + 1; k <= i - 1; k++) {
              g += V.get(k, j) * d[k];
              e[k] += V.get(k, j) * f;
            }
            e[j] = g;
          }
          f = 0;
          for (j = 0; j < i; j++) {
            e[j] /= h;
            f += e[j] * d[j];
          }
          hh = f / (h + h);
          for (j = 0; j < i; j++) {
            e[j] -= hh * d[j];
          }
          for (j = 0; j < i; j++) {
            f = d[j];
            g = e[j];
            for (k = j; k <= i - 1; k++) {
              V.set(k, j, V.get(k, j) - (f * e[k] + g * d[k]));
            }
            d[j] = V.get(i - 1, j);
            V.set(i, j, 0);
          }
        }
        d[i] = h;
      }
      for (i = 0; i < n - 1; i++) {
        V.set(n - 1, i, V.get(i, i));
        V.set(i, i, 1);
        h = d[i + 1];
        if (h !== 0) {
          for (k = 0; k <= i; k++) {
            d[k] = V.get(k, i + 1) / h;
          }
          for (j = 0; j <= i; j++) {
            g = 0;
            for (k = 0; k <= i; k++) {
              g += V.get(k, i + 1) * V.get(k, j);
            }
            for (k = 0; k <= i; k++) {
              V.set(k, j, V.get(k, j) - g * d[k]);
            }
          }
        }
        for (k = 0; k <= i; k++) {
          V.set(k, i + 1, 0);
        }
      }
      for (j = 0; j < n; j++) {
        d[j] = V.get(n - 1, j);
        V.set(n - 1, j, 0);
      }
      V.set(n - 1, n - 1, 1);
      e[0] = 0;
    }
    function tql2(n, e, d, V) {
      let g, h, i, j, k, l, m, p, r, dl1, c, c2, c3, el1, s, s2;
      for (i = 1; i < n; i++) {
        e[i - 1] = e[i];
      }
      e[n - 1] = 0;
      let f = 0;
      let tst1 = 0;
      let eps = Number.EPSILON;
      for (l = 0; l < n; l++) {
        tst1 = Math.max(tst1, Math.abs(d[l]) + Math.abs(e[l]));
        m = l;
        while (m < n) {
          if (Math.abs(e[m]) <= eps * tst1) {
            break;
          }
          m++;
        }
        if (m > l) {
          do {
            g = d[l];
            p = (d[l + 1] - g) / (2 * e[l]);
            r = hypotenuse(p, 1);
            if (p < 0) {
              r = -r;
            }
            d[l] = e[l] / (p + r);
            d[l + 1] = e[l] * (p + r);
            dl1 = d[l + 1];
            h = g - d[l];
            for (i = l + 2; i < n; i++) {
              d[i] -= h;
            }
            f = f + h;
            p = d[m];
            c = 1;
            c2 = c;
            c3 = c;
            el1 = e[l + 1];
            s = 0;
            s2 = 0;
            for (i = m - 1; i >= l; i--) {
              c3 = c2;
              c2 = c;
              s2 = s;
              g = c * e[i];
              h = c * p;
              r = hypotenuse(p, e[i]);
              e[i + 1] = s * r;
              s = e[i] / r;
              c = p / r;
              p = c * d[i] - s * g;
              d[i + 1] = h + s * (c * g + s * d[i]);
              for (k = 0; k < n; k++) {
                h = V.get(k, i + 1);
                V.set(k, i + 1, s * V.get(k, i) + c * h);
                V.set(k, i, c * V.get(k, i) - s * h);
              }
            }
            p = -s * s2 * c3 * el1 * e[l] / dl1;
            e[l] = s * p;
            d[l] = c * p;
          } while (Math.abs(e[l]) > eps * tst1);
        }
        d[l] = d[l] + f;
        e[l] = 0;
      }
      for (i = 0; i < n - 1; i++) {
        k = i;
        p = d[i];
        for (j = i + 1; j < n; j++) {
          if (d[j] < p) {
            k = j;
            p = d[j];
          }
        }
        if (k !== i) {
          d[k] = d[i];
          d[i] = p;
          for (j = 0; j < n; j++) {
            p = V.get(j, i);
            V.set(j, i, V.get(j, k));
            V.set(j, k, p);
          }
        }
      }
    }
    function orthes(n, H, ort, V) {
      let low = 0;
      let high = n - 1;
      let f, g, h, i, j, m;
      let scale;
      for (m = low + 1; m <= high - 1; m++) {
        scale = 0;
        for (i = m; i <= high; i++) {
          scale = scale + Math.abs(H.get(i, m - 1));
        }
        if (scale !== 0) {
          h = 0;
          for (i = high; i >= m; i--) {
            ort[i] = H.get(i, m - 1) / scale;
            h += ort[i] * ort[i];
          }
          g = Math.sqrt(h);
          if (ort[m] > 0) {
            g = -g;
          }
          h = h - ort[m] * g;
          ort[m] = ort[m] - g;
          for (j = m; j < n; j++) {
            f = 0;
            for (i = high; i >= m; i--) {
              f += ort[i] * H.get(i, j);
            }
            f = f / h;
            for (i = m; i <= high; i++) {
              H.set(i, j, H.get(i, j) - f * ort[i]);
            }
          }
          for (i = 0; i <= high; i++) {
            f = 0;
            for (j = high; j >= m; j--) {
              f += ort[j] * H.get(i, j);
            }
            f = f / h;
            for (j = m; j <= high; j++) {
              H.set(i, j, H.get(i, j) - f * ort[j]);
            }
          }
          ort[m] = scale * ort[m];
          H.set(m, m - 1, scale * g);
        }
      }
      for (i = 0; i < n; i++) {
        for (j = 0; j < n; j++) {
          V.set(i, j, i === j ? 1 : 0);
        }
      }
      for (m = high - 1; m >= low + 1; m--) {
        if (H.get(m, m - 1) !== 0) {
          for (i = m + 1; i <= high; i++) {
            ort[i] = H.get(i, m - 1);
          }
          for (j = m; j <= high; j++) {
            g = 0;
            for (i = m; i <= high; i++) {
              g += ort[i] * V.get(i, j);
            }
            g = g / ort[m] / H.get(m, m - 1);
            for (i = m; i <= high; i++) {
              V.set(i, j, V.get(i, j) + g * ort[i]);
            }
          }
        }
      }
    }
    function hqr2(nn, e, d, V, H) {
      let n = nn - 1;
      let low = 0;
      let high = nn - 1;
      let eps = Number.EPSILON;
      let exshift = 0;
      let norm = 0;
      let p = 0;
      let q = 0;
      let r = 0;
      let s = 0;
      let z = 0;
      let iter = 0;
      let i, j, k, l, m, t, w, x, y;
      let ra, sa, vr, vi;
      let notlast, cdivres;
      for (i = 0; i < nn; i++) {
        if (i < low || i > high) {
          d[i] = H.get(i, i);
          e[i] = 0;
        }
        for (j = Math.max(i - 1, 0); j < nn; j++) {
          norm = norm + Math.abs(H.get(i, j));
        }
      }
      while (n >= low) {
        l = n;
        while (l > low) {
          s = Math.abs(H.get(l - 1, l - 1)) + Math.abs(H.get(l, l));
          if (s === 0) {
            s = norm;
          }
          if (Math.abs(H.get(l, l - 1)) < eps * s) {
            break;
          }
          l--;
        }
        if (l === n) {
          H.set(n, n, H.get(n, n) + exshift);
          d[n] = H.get(n, n);
          e[n] = 0;
          n--;
          iter = 0;
        } else if (l === n - 1) {
          w = H.get(n, n - 1) * H.get(n - 1, n);
          p = (H.get(n - 1, n - 1) - H.get(n, n)) / 2;
          q = p * p + w;
          z = Math.sqrt(Math.abs(q));
          H.set(n, n, H.get(n, n) + exshift);
          H.set(n - 1, n - 1, H.get(n - 1, n - 1) + exshift);
          x = H.get(n, n);
          if (q >= 0) {
            z = p >= 0 ? p + z : p - z;
            d[n - 1] = x + z;
            d[n] = d[n - 1];
            if (z !== 0) {
              d[n] = x - w / z;
            }
            e[n - 1] = 0;
            e[n] = 0;
            x = H.get(n, n - 1);
            s = Math.abs(x) + Math.abs(z);
            p = x / s;
            q = z / s;
            r = Math.sqrt(p * p + q * q);
            p = p / r;
            q = q / r;
            for (j = n - 1; j < nn; j++) {
              z = H.get(n - 1, j);
              H.set(n - 1, j, q * z + p * H.get(n, j));
              H.set(n, j, q * H.get(n, j) - p * z);
            }
            for (i = 0; i <= n; i++) {
              z = H.get(i, n - 1);
              H.set(i, n - 1, q * z + p * H.get(i, n));
              H.set(i, n, q * H.get(i, n) - p * z);
            }
            for (i = low; i <= high; i++) {
              z = V.get(i, n - 1);
              V.set(i, n - 1, q * z + p * V.get(i, n));
              V.set(i, n, q * V.get(i, n) - p * z);
            }
          } else {
            d[n - 1] = x + p;
            d[n] = x + p;
            e[n - 1] = z;
            e[n] = -z;
          }
          n = n - 2;
          iter = 0;
        } else {
          x = H.get(n, n);
          y = 0;
          w = 0;
          if (l < n) {
            y = H.get(n - 1, n - 1);
            w = H.get(n, n - 1) * H.get(n - 1, n);
          }
          if (iter === 10) {
            exshift += x;
            for (i = low; i <= n; i++) {
              H.set(i, i, H.get(i, i) - x);
            }
            s = Math.abs(H.get(n, n - 1)) + Math.abs(H.get(n - 1, n - 2));
            // eslint-disable-next-line no-multi-assign
            x = y = 0.75 * s;
            w = -0.4375 * s * s;
          }
          if (iter === 30) {
            s = (y - x) / 2;
            s = s * s + w;
            if (s > 0) {
              s = Math.sqrt(s);
              if (y < x) {
                s = -s;
              }
              s = x - w / ((y - x) / 2 + s);
              for (i = low; i <= n; i++) {
                H.set(i, i, H.get(i, i) - s);
              }
              exshift += s;
              // eslint-disable-next-line no-multi-assign
              x = y = w = 0.964;
            }
          }
          iter = iter + 1;
          m = n - 2;
          while (m >= l) {
            z = H.get(m, m);
            r = x - z;
            s = y - z;
            p = (r * s - w) / H.get(m + 1, m) + H.get(m, m + 1);
            q = H.get(m + 1, m + 1) - z - r - s;
            r = H.get(m + 2, m + 1);
            s = Math.abs(p) + Math.abs(q) + Math.abs(r);
            p = p / s;
            q = q / s;
            r = r / s;
            if (m === l) {
              break;
            }
            if (Math.abs(H.get(m, m - 1)) * (Math.abs(q) + Math.abs(r)) < eps * (Math.abs(p) * (Math.abs(H.get(m - 1, m - 1)) + Math.abs(z) + Math.abs(H.get(m + 1, m + 1))))) {
              break;
            }
            m--;
          }
          for (i = m + 2; i <= n; i++) {
            H.set(i, i - 2, 0);
            if (i > m + 2) {
              H.set(i, i - 3, 0);
            }
          }
          for (k = m; k <= n - 1; k++) {
            notlast = k !== n - 1;
            if (k !== m) {
              p = H.get(k, k - 1);
              q = H.get(k + 1, k - 1);
              r = notlast ? H.get(k + 2, k - 1) : 0;
              x = Math.abs(p) + Math.abs(q) + Math.abs(r);
              if (x !== 0) {
                p = p / x;
                q = q / x;
                r = r / x;
              }
            }
            if (x === 0) {
              break;
            }
            s = Math.sqrt(p * p + q * q + r * r);
            if (p < 0) {
              s = -s;
            }
            if (s !== 0) {
              if (k !== m) {
                H.set(k, k - 1, -s * x);
              } else if (l !== m) {
                H.set(k, k - 1, -H.get(k, k - 1));
              }
              p = p + s;
              x = p / s;
              y = q / s;
              z = r / s;
              q = q / p;
              r = r / p;
              for (j = k; j < nn; j++) {
                p = H.get(k, j) + q * H.get(k + 1, j);
                if (notlast) {
                  p = p + r * H.get(k + 2, j);
                  H.set(k + 2, j, H.get(k + 2, j) - p * z);
                }
                H.set(k, j, H.get(k, j) - p * x);
                H.set(k + 1, j, H.get(k + 1, j) - p * y);
              }
              for (i = 0; i <= Math.min(n, k + 3); i++) {
                p = x * H.get(i, k) + y * H.get(i, k + 1);
                if (notlast) {
                  p = p + z * H.get(i, k + 2);
                  H.set(i, k + 2, H.get(i, k + 2) - p * r);
                }
                H.set(i, k, H.get(i, k) - p);
                H.set(i, k + 1, H.get(i, k + 1) - p * q);
              }
              for (i = low; i <= high; i++) {
                p = x * V.get(i, k) + y * V.get(i, k + 1);
                if (notlast) {
                  p = p + z * V.get(i, k + 2);
                  V.set(i, k + 2, V.get(i, k + 2) - p * r);
                }
                V.set(i, k, V.get(i, k) - p);
                V.set(i, k + 1, V.get(i, k + 1) - p * q);
              }
            }
          }
        }
      }
      if (norm === 0) {
        return;
      }
      for (n = nn - 1; n >= 0; n--) {
        p = d[n];
        q = e[n];
        if (q === 0) {
          l = n;
          H.set(n, n, 1);
          for (i = n - 1; i >= 0; i--) {
            w = H.get(i, i) - p;
            r = 0;
            for (j = l; j <= n; j++) {
              r = r + H.get(i, j) * H.get(j, n);
            }
            if (e[i] < 0) {
              z = w;
              s = r;
            } else {
              l = i;
              if (e[i] === 0) {
                H.set(i, n, w !== 0 ? -r / w : -r / (eps * norm));
              } else {
                x = H.get(i, i + 1);
                y = H.get(i + 1, i);
                q = (d[i] - p) * (d[i] - p) + e[i] * e[i];
                t = (x * s - z * r) / q;
                H.set(i, n, t);
                H.set(i + 1, n, Math.abs(x) > Math.abs(z) ? (-r - w * t) / x : (-s - y * t) / z);
              }
              t = Math.abs(H.get(i, n));
              if (eps * t * t > 1) {
                for (j = i; j <= n; j++) {
                  H.set(j, n, H.get(j, n) / t);
                }
              }
            }
          }
        } else if (q < 0) {
          l = n - 1;
          if (Math.abs(H.get(n, n - 1)) > Math.abs(H.get(n - 1, n))) {
            H.set(n - 1, n - 1, q / H.get(n, n - 1));
            H.set(n - 1, n, -(H.get(n, n) - p) / H.get(n, n - 1));
          } else {
            cdivres = cdiv(0, -H.get(n - 1, n), H.get(n - 1, n - 1) - p, q);
            H.set(n - 1, n - 1, cdivres[0]);
            H.set(n - 1, n, cdivres[1]);
          }
          H.set(n, n - 1, 0);
          H.set(n, n, 1);
          for (i = n - 2; i >= 0; i--) {
            ra = 0;
            sa = 0;
            for (j = l; j <= n; j++) {
              ra = ra + H.get(i, j) * H.get(j, n - 1);
              sa = sa + H.get(i, j) * H.get(j, n);
            }
            w = H.get(i, i) - p;
            if (e[i] < 0) {
              z = w;
              r = ra;
              s = sa;
            } else {
              l = i;
              if (e[i] === 0) {
                cdivres = cdiv(-ra, -sa, w, q);
                H.set(i, n - 1, cdivres[0]);
                H.set(i, n, cdivres[1]);
              } else {
                x = H.get(i, i + 1);
                y = H.get(i + 1, i);
                vr = (d[i] - p) * (d[i] - p) + e[i] * e[i] - q * q;
                vi = (d[i] - p) * 2 * q;
                if (vr === 0 && vi === 0) {
                  vr = eps * norm * (Math.abs(w) + Math.abs(q) + Math.abs(x) + Math.abs(y) + Math.abs(z));
                }
                cdivres = cdiv(x * r - z * ra + q * sa, x * s - z * sa - q * ra, vr, vi);
                H.set(i, n - 1, cdivres[0]);
                H.set(i, n, cdivres[1]);
                if (Math.abs(x) > Math.abs(z) + Math.abs(q)) {
                  H.set(i + 1, n - 1, (-ra - w * H.get(i, n - 1) + q * H.get(i, n)) / x);
                  H.set(i + 1, n, (-sa - w * H.get(i, n) - q * H.get(i, n - 1)) / x);
                } else {
                  cdivres = cdiv(-r - y * H.get(i, n - 1), -s - y * H.get(i, n), z, q);
                  H.set(i + 1, n - 1, cdivres[0]);
                  H.set(i + 1, n, cdivres[1]);
                }
              }
              t = Math.max(Math.abs(H.get(i, n - 1)), Math.abs(H.get(i, n)));
              if (eps * t * t > 1) {
                for (j = i; j <= n; j++) {
                  H.set(j, n - 1, H.get(j, n - 1) / t);
                  H.set(j, n, H.get(j, n) / t);
                }
              }
            }
          }
        }
      }
      for (i = 0; i < nn; i++) {
        if (i < low || i > high) {
          for (j = i; j < nn; j++) {
            V.set(i, j, H.get(i, j));
          }
        }
      }
      for (j = nn - 1; j >= low; j--) {
        for (i = low; i <= high; i++) {
          z = 0;
          for (k = low; k <= Math.min(j, high); k++) {
            z = z + V.get(i, k) * H.get(k, j);
          }
          V.set(i, j, z);
        }
      }
    }
    function cdiv(xr, xi, yr, yi) {
      let r, d;
      if (Math.abs(yr) > Math.abs(yi)) {
        r = yi / yr;
        d = yr + r * yi;
        return [(xr + r * xi) / d, (xi - r * xr) / d];
      } else {
        r = yr / yi;
        d = yi + r * yr;
        return [(r * xr + xi) / d, (r * xi - xr) / d];
      }
    }
    class CholeskyDecomposition {
      constructor(value) {
        value = WrapperMatrix2D.checkMatrix(value);
        if (!value.isSymmetric()) {
          throw new Error('Matrix is not symmetric');
        }
        let a = value;
        let dimension = a.rows;
        let l = new Matrix$1(dimension, dimension);
        let positiveDefinite = true;
        let i, j, k;
        for (j = 0; j < dimension; j++) {
          let d = 0;
          for (k = 0; k < j; k++) {
            let s = 0;
            for (i = 0; i < k; i++) {
              s += l.get(k, i) * l.get(j, i);
            }
            s = (a.get(j, k) - s) / l.get(k, k);
            l.set(j, k, s);
            d = d + s * s;
          }
          d = a.get(j, j) - d;
          positiveDefinite &= d > 0;
          l.set(j, j, Math.sqrt(Math.max(d, 0)));
          for (k = j + 1; k < dimension; k++) {
            l.set(j, k, 0);
          }
        }
        this.L = l;
        this.positiveDefinite = Boolean(positiveDefinite);
      }
      isPositiveDefinite() {
        return this.positiveDefinite;
      }
      solve(value) {
        value = WrapperMatrix2D.checkMatrix(value);
        let l = this.L;
        let dimension = l.rows;
        if (value.rows !== dimension) {
          throw new Error('Matrix dimensions do not match');
        }
        if (this.isPositiveDefinite() === false) {
          throw new Error('Matrix is not positive definite');
        }
        let count = value.columns;
        let B = value.clone();
        let i, j, k;
        for (k = 0; k < dimension; k++) {
          for (j = 0; j < count; j++) {
            for (i = 0; i < k; i++) {
              B.set(k, j, B.get(k, j) - B.get(i, j) * l.get(k, i));
            }
            B.set(k, j, B.get(k, j) / l.get(k, k));
          }
        }
        for (k = dimension - 1; k >= 0; k--) {
          for (j = 0; j < count; j++) {
            for (i = k + 1; i < dimension; i++) {
              B.set(k, j, B.get(k, j) - B.get(i, j) * l.get(i, k));
            }
            B.set(k, j, B.get(k, j) / l.get(k, k));
          }
        }
        return B;
      }
      get lowerTriangularMatrix() {
        return this.L;
      }
    }
    class nipals {
      constructor(X, options = {}) {
        X = WrapperMatrix2D.checkMatrix(X);
        let {
          Y
        } = options;
        const {
          scaleScores = false,
          maxIterations = 1000,
          terminationCriteria = 1e-10
        } = options;
        let u;
        if (Y) {
          if (isAnyArray.isAnyArray(Y) && typeof Y[0] === 'number') {
            Y = Matrix$1.columnVector(Y);
          } else {
            Y = WrapperMatrix2D.checkMatrix(Y);
          }
          if (Y.rows !== X.rows) {
            throw new Error('Y should have the same number of rows as X');
          }
          u = Y.getColumnVector(0);
        } else {
          u = X.getColumnVector(0);
        }
        let diff = 1;
        let t, q, w, tOld;
        for (let counter = 0; counter < maxIterations && diff > terminationCriteria; counter++) {
          w = X.transpose().mmul(u).div(u.transpose().mmul(u).get(0, 0));
          w = w.div(w.norm());
          t = X.mmul(w).div(w.transpose().mmul(w).get(0, 0));
          if (counter > 0) {
            diff = t.clone().sub(tOld).pow(2).sum();
          }
          tOld = t.clone();
          if (Y) {
            q = Y.transpose().mmul(t).div(t.transpose().mmul(t).get(0, 0));
            q = q.div(q.norm());
            u = Y.mmul(q).div(q.transpose().mmul(q).get(0, 0));
          } else {
            u = t;
          }
        }
        if (Y) {
          let p = X.transpose().mmul(t).div(t.transpose().mmul(t).get(0, 0));
          p = p.div(p.norm());
          let xResidual = X.clone().sub(t.clone().mmul(p.transpose()));
          let residual = u.transpose().mmul(t).div(t.transpose().mmul(t).get(0, 0));
          let yResidual = Y.clone().sub(t.clone().mulS(residual.get(0, 0)).mmul(q.transpose()));
          this.t = t;
          this.p = p.transpose();
          this.w = w.transpose();
          this.q = q;
          this.u = u;
          this.s = t.transpose().mmul(t);
          this.xResidual = xResidual;
          this.yResidual = yResidual;
          this.betas = residual;
        } else {
          this.w = w.transpose();
          this.s = t.transpose().mmul(t).sqrt();
          if (scaleScores) {
            this.t = t.clone().div(this.s.get(0, 0));
          } else {
            this.t = t;
          }
          this.xResidual = X.sub(t.mmul(w.transpose()));
        }
      }
    }
    matrix.AbstractMatrix = AbstractMatrix;
    matrix.CHO = CholeskyDecomposition;
    matrix.CholeskyDecomposition = CholeskyDecomposition;
    matrix.DistanceMatrix = DistanceMatrix;
    var EVD$1 = matrix.EVD = EigenvalueDecomposition;
    matrix.EigenvalueDecomposition = EigenvalueDecomposition;
    matrix.LU = LuDecomposition;
    matrix.LuDecomposition = LuDecomposition;
    var Matrix_1 = matrix.Matrix = Matrix$1;
    matrix.MatrixColumnSelectionView = MatrixColumnSelectionView;
    matrix.MatrixColumnView = MatrixColumnView;
    matrix.MatrixFlipColumnView = MatrixFlipColumnView;
    matrix.MatrixFlipRowView = MatrixFlipRowView;
    matrix.MatrixRowSelectionView = MatrixRowSelectionView;
    matrix.MatrixRowView = MatrixRowView;
    matrix.MatrixSelectionView = MatrixSelectionView;
    matrix.MatrixSubView = MatrixSubView;
    var MatrixTransposeView_1 = matrix.MatrixTransposeView = MatrixTransposeView$1;
    var NIPALS$1 = matrix.NIPALS = nipals;
    matrix.Nipals = nipals;
    matrix.QR = QrDecomposition;
    matrix.QrDecomposition = QrDecomposition;
    var SVD$1 = matrix.SVD = SingularValueDecomposition;
    matrix.SingularValueDecomposition = SingularValueDecomposition;
    matrix.SymmetricMatrix = SymmetricMatrix;
    matrix.WrapperMatrix1D = WrapperMatrix1D;
    matrix.WrapperMatrix2D = WrapperMatrix2D;
    matrix.correlation = correlation;
    matrix.covariance = covariance;
    var _default = matrix.default = Matrix$1;
    matrix.determinant = determinant;
    matrix.inverse = inverse;
    matrix.linearDependencies = linearDependencies;
    matrix.pseudoInverse = pseudoInverse;
    var solve_1 = matrix.solve = solve$1;
    matrix.wrap = wrap;

    const EVD = EVD$1;
    const Matrix = Matrix_1;
    const MatrixTransposeView = MatrixTransposeView_1;
    const NIPALS = NIPALS$1;
    const SVD = SVD$1;
    _default.Matrix ? _default.Matrix : Matrix_1;
    const solve = solve_1;

    /**
     * Create an array with numbers between "from" and "to" of length "length"
     *
     * @param options - options
     * @return - array of distributed numbers between "from" and "to"
     */
    function createFromToArray(options = {}) {
      const {
        from = 0,
        to = 1,
        length = 1000,
        includeFrom = true,
        includeTo = true,
        distribution = 'uniform'
      } = options;
      const array = new Float64Array(length);
      let div = length;
      if (includeFrom && includeTo) {
        div = length - 1;
      } else if (!includeFrom && includeTo || includeFrom && !includeTo) {
        div = length;
      } else if (!includeFrom && !includeTo) {
        div = length + 1;
      }
      const delta = (to - from) / div;
      if (distribution === 'uniform') {
        if (includeFrom) {
          let index = 0;
          while (index < length) {
            array[index] = from + delta * index;
            index++;
          }
        } else {
          let index = 0;
          while (index < length) {
            array[index] = from + delta * (index + 1);
            index++;
          }
        }
      } else if (distribution === 'log') {
        const base = (to / from) ** (1 / div);
        const firstExponent = Math.log(from) / Math.log(base);
        if (includeFrom) {
          let index = 0;
          while (index < length) {
            array[index] = base ** (firstExponent + index);
            index++;
          }
        } else {
          let index = 0;
          while (index < length) {
            array[index] = base ** (firstExponent + index + 1);
            index++;
          }
        }
      } else {
        throw new Error('distribution must be uniform or log');
      }
      return array;
    }

    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(seed = 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;
      }
    }

    var __classPrivateFieldSet = undefined && undefined.__classPrivateFieldSet || function (receiver, state, value, kind, f) {
      if (kind === "m") throw new TypeError("Private method is not writable");
      if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
      if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
      return kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value), value;
    };
    var __classPrivateFieldGet = undefined && undefined.__classPrivateFieldGet || function (receiver, state, kind, f) {
      if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
      if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
      return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
    };
    var _GaussianGenerator_spare, _GaussianGenerator_hasSpare, _GaussianGenerator_mean, _GaussianGenerator_standardDeviation, _GaussianGenerator_generator;
    /**
     * Create a random array of numbers of a specific length.
     *
     * @return - array of random floats normally distributed
     */
    function createRandomArray(options = {}) {
      const {
        mean = 0,
        standardDeviation = 1,
        length = 1000,
        range = 1,
        seed,
        distribution = 'normal'
      } = options;
      const generator = new XSadd(seed);
      const returnArray = new Float64Array(length);
      switch (distribution) {
        case 'normal':
          {
            const gaussianGenerator = new GaussianGenerator(mean, standardDeviation, generator);
            for (let i = 0; i < length; i++) {
              returnArray[i] = gaussianGenerator.generateGaussian();
            }
            break;
          }
        case 'uniform':
          {
            for (let i = 0; i < length; i++) {
              returnArray[i] = (generator.random() - 0.5) * range + mean;
            }
            break;
          }
        default:
          {
            throw new Error(`unknown distribution: ${String(distribution)}`);
          }
      }
      return returnArray;
    }
    class GaussianGenerator {
      constructor(mean, standardDeviation, generator) {
        _GaussianGenerator_spare.set(this, 0);
        _GaussianGenerator_hasSpare.set(this, false);
        _GaussianGenerator_mean.set(this, void 0);
        _GaussianGenerator_standardDeviation.set(this, void 0);
        _GaussianGenerator_generator.set(this, void 0);
        __classPrivateFieldSet(this, _GaussianGenerator_mean, mean, "f");
        __classPrivateFieldSet(this, _GaussianGenerator_standardDeviation, standardDeviation, "f");
        __classPrivateFieldSet(this, _GaussianGenerator_generator, generator, "f");
      }
      generateGaussian() {
        let val, u, v, s;
        if (__classPrivateFieldGet(this, _GaussianGenerator_hasSpare, "f")) {
          __classPrivateFieldSet(this, _GaussianGenerator_hasSpare, false, "f");
          val = __classPrivateFieldGet(this, _GaussianGenerator_spare, "f") * __classPrivateFieldGet(this, _GaussianGenerator_standardDeviation, "f") + __classPrivateFieldGet(this, _GaussianGenerator_mean, "f");
        } else {
          do {
            u = __classPrivateFieldGet(this, _GaussianGenerator_generator, "f").random() * 2 - 1;
            v = __classPrivateFieldGet(this, _GaussianGenerator_generator, "f").random() * 2 - 1;
            s = u * u + v * v;
          } while (s >= 1 || s === 0);
          s = Math.sqrt(-2 * Math.log(s) / s);
          __classPrivateFieldSet(this, _GaussianGenerator_spare, v * s, "f");
          __classPrivateFieldSet(this, _GaussianGenerator_hasSpare, true, "f");
          val = __classPrivateFieldGet(this, _GaussianGenerator_mean, "f") + __classPrivateFieldGet(this, _GaussianGenerator_standardDeviation, "f") * u * s;
        }
        return val;
      }
    }
    _GaussianGenerator_spare = new WeakMap(), _GaussianGenerator_hasSpare = new WeakMap(), _GaussianGenerator_mean = new WeakMap(), _GaussianGenerator_standardDeviation = new WeakMap(), _GaussianGenerator_generator = new WeakMap();

    /**
     * Computes the maximal value of an array of values
     *
     * @param array - array of numbers
     * @param options - options
     */
    function xMaxValue(array, options = {}) {
      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;
    }

    /**
     * Check if the values are separated always by the same difference
     *
     * @param array - Monotone growing array of number
     * @param options
     */
    function xIsEquallySpaced(array, options = {}) {
      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) {
        const 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 monotonic.
     *
     * @param array - array of numbers.
     * @returns 1 if monotonic increasing, -1 if monotonic decreasing, 0 if not monotonic.
     */
    function xIsMonotonic(array) {
      if (array.length <= 2) {
        return 1;
      }
      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 0;
        }
        return 1;
      }
      if (array[0] < array[array.length - 1]) {
        for (let i = 0; i < array.length - 1; i++) {
          if (array[i] >= array[i + 1]) return 0;
        }
        return 1;
      } else {
        for (let i = 0; i < array.length - 1; i++) {
          if (array[i] <= array[i + 1]) return 0;
        }
        return -1;
      }
    }

    /**
     * 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)
      };
    }

    /**
     * Return min and max values of an array.
     *
     * @param array - array of number
     * @returns - Object with 2 properties, min and max
     */
    function xMinMaxValues(array) {
      xCheck(array);
      let min = array[0];
      let max = array[0];
      for (const value of array) {
        if (value < min) min = value;
        if (value > max) max = value;
      }
      return {
        min,
        max
      };
    }

    var d3Array = {exports: {}};

    (function (module, exports) {
      (function (global, factory) {
        factory(exports) ;
      })(commonjsGlobal, function (exports) {

        function ascending(a, b) {
          return a < b ? -1 : a > b ? 1 : a >= b ? 0 : NaN;
        }
        function bisector(compare) {
          if (compare.length === 1) compare = ascendingComparator(compare);
          return {
            left: function (a, x, lo, hi) {
              if (lo == null) lo = 0;
              if (hi == null) hi = a.length;
              while (lo < hi) {
                var mid = lo + hi >>> 1;
                if (compare(a[mid], x) < 0) lo = mid + 1;else hi = mid;
              }
              return lo;
            },
            right: function (a, x, lo, hi) {
              if (lo == null) lo = 0;
              if (hi == null) hi = a.length;
              while (lo < hi) {
                var mid = lo + hi >>> 1;
                if (compare(a[mid], x) > 0) hi = mid;else lo = mid + 1;
              }
              return lo;
            }
          };
        }
        function ascendingComparator(f) {
          return function (d, x) {
            return ascending(f(d), x);
          };
        }
        var ascendingBisect = bisector(ascending);
        var bisectRight = ascendingBisect.right;
        var bisectLeft = ascendingBisect.left;
        function descending(a, b) {
          return b < a ? -1 : b > a ? 1 : b >= a ? 0 : NaN;
        }
        function number$1(x) {
          return x === null ? NaN : +x;
        }
        function variance(array, f) {
          var n = array.length,
            m = 0,
            a,
            d,
            s = 0,
            i = -1,
            j = 0;
          if (f == null) {
            while (++i < n) {
              if (!isNaN(a = number$1(array[i]))) {
                d = a - m;
                m += d / ++j;
                s += d * (a - m);
              }
            }
          } else {
            while (++i < n) {
              if (!isNaN(a = number$1(f(array[i], i, array)))) {
                d = a - m;
                m += d / ++j;
                s += d * (a - m);
              }
            }
          }
          if (j > 1) return s / (j - 1);
        }
        function deviation(array, f) {
          var v = variance(array, f);
          return v ? Math.sqrt(v) : v;
        }
        function extent(array, f) {
          var i = -1,
            n = array.length,
            a,
            b,
            c;
          if (f == null) {
            while (++i < n) if ((b = array[i]) != null && b >= b) {
              a = c = b;
              break;
            }
            while (++i < n) if ((b = array[i]) != null) {
              if (a > b) a = b;
              if (c < b) c = b;
            }
          } else {
            while (++i < n) if ((b = f(array[i], i, array)) != null && b >= b) {
              a = c = b;
              break;
            }
            while (++i < n) if ((b = f(array[i], i, array)) != null) {
              if (a > b) a = b;
              if (c < b) c = b;
            }
          }
          return [a, c];
        }
        function constant(x) {
          return function () {
            return x;
          };
        }
        function identity(x) {
          return x;
        }
        function range(start, stop, step) {
          start = +start, stop = +stop, step = (n = arguments.length) < 2 ? (stop = start, start = 0, 1) : n < 3 ? 1 : +step;
          var i = -1,
            n = Math.max(0, Math.ceil((stop - start) / step)) | 0,
            range = new Array(n);
          while (++i < n) {
            range[i] = start + i * step;
          }
          return range;
        }
        var e10 = Math.sqrt(50);
        var e5 = Math.sqrt(10);
        var e2 = Math.sqrt(2);
        function ticks(start, stop, count) {
          var step = tickStep(start, stop, count);
          return range(Math.ceil(start / step) * step, Math.floor(stop / step) * step + step / 2,
          // inclusive
          step);
        }
        function tickStep(start, stop, count) {
          var step0 = Math.abs(stop - start) / Math.max(0, count),
            step1 = Math.pow(10, Math.floor(Math.log(step0) / Math.LN10)),
            error = step0 / step1;
          if (error >= e10) step1 *= 10;else if (error >= e5) step1 *= 5;else if (error >= e2) step1 *= 2;
          return stop < start ? -step1 : step1;
        }
        function sturges(values) {
          return Math.ceil(Math.log(values.length) / Math.LN2) + 1;
        }
        function number(x) {
          return +x;
        }
        function histogram() {
          var value = identity,
            domain = extent,
            threshold = sturges;
          function histogram(data) {
            var i,
              n = data.length,
              x,
              values = new Array(n);

            // Coerce values to numbers.
            for (i = 0; i < n; ++i) {
              values[i] = +value(data[i], i, data);
            }
            var xz = domain(values),
              x0 = +xz[0],
              x1 = +xz[1],
              tz = threshold(values, x0, x1);

            // Convert number of thresholds into uniform thresholds.
            if (!Array.isArray(tz)) tz = ticks(x0, x1, +tz);

            // Coerce thresholds to numbers, ignoring any outside the domain.
            var m = tz.length;
            for (i = 0; i < m; ++i) tz[i] = +tz[i];
            while (tz[0] <= x0) tz.shift(), --m;
            while (tz[m - 1] >= x1) tz.pop(), --m;
            var bins = new Array(m + 1),
              bin;

            // Initialize bins.
            for (i = 0; i <= m; ++i) {
              bin = bins[i] = [];
              bin.x0 = i > 0 ? tz[i - 1] : x0;
              bin.x1 = i < m ? tz[i] : x1;
            }

            // Assign data to bins by value, ignoring any outside the domain.
            for (i = 0; i < n; ++i) {
              x = values[i];
              if (x0 <= x && x <= x1) {
                bins[bisectRight(tz, x, 0, m)].push(data[i]);
              }
            }
            return bins;
          }
          histogram.value = function (_) {
            return arguments.length ? (value = typeof _ === "function" ? _ : constant(+_), histogram) : value;
          };
          histogram.domain = function (_) {
            return arguments.length ? (domain = typeof _ === "function" ? _ : constant([+_[0], +_[1]]), histogram) : domain;
          };
          histogram.thresholds = function (_) {
            if (!arguments.length) return threshold;
            threshold = typeof _ === "function" ? _ : Array.isArray(_) ? constant(Array.prototype.map.call(_, number)) : constant(+_);
            return histogram;
          };
          return histogram;
        }
        function quantile(array, p, f) {
          if (f == null) f = number$1;
          if (!(n = array.length)) return;
          if ((p = +p) <= 0 || n < 2) return +f(array[0], 0, array);
          if (p >= 1) return +f(array[n - 1], n - 1, array);
          var n,
            h = (n - 1) * p,
            i = Math.floor(h),
            a = +f(array[i], i, array),
            b = +f(array[i + 1], i + 1, array);
          return a + (b - a) * (h - i);
        }
        function freedmanDiaconis(values, min, max) {
          values.sort(ascending);
          return Math.ceil((max - min) / (2 * (quantile(values, 0.75) - quantile(values, 0.25)) * Math.pow(values.length, -1 / 3)));
        }
        function scott(values, min, max) {
          return Math.ceil((max - min) / (3.5 * deviation(values) * Math.pow(values.length, -1 / 3)));
        }
        function max(array, f) {
          var i = -1,
            n = array.length,
            a,
            b;
          if (f == null) {
            while (++i < n) if ((b = array[i]) != null && b >= b) {
              a = b;
              break;
            }
            while (++i < n) if ((b = array[i]) != null && b > a) a = b;
          } else {
            while (++i < n) if ((b = f(array[i], i, array)) != null && b >= b) {
              a = b;
              break;
            }
            while (++i < n) if ((b = f(array[i], i, array)) != null && b > a) a = b;
          }
          return a;
        }
        function mean(array, f) {
          var s = 0,
            n = array.length,
            a,
            i = -1,
            j = n;
          if (f == null) {
            while (++i < n) if (!isNaN(a = number$1(array[i]))) s += a;else --j;
          } else {
            while (++i < n) if (!isNaN(a = number$1(f(array[i], i, array)))) s += a;else --j;
          }
          if (j) return s / j;
        }
        function median(array, f) {
          var numbers = [],
            n = array.length,
            a,
            i = -1;
          if (f == null) {
            while (++i < n) if (!isNaN(a = number$1(array[i]))) numbers.push(a);
          } else {
            while (++i < n) if (!isNaN(a = number$1(f(array[i], i, array)))) numbers.push(a);
          }
          return quantile(numbers.sort(ascending), 0.5);
        }
        function merge(arrays) {
          var n = arrays.length,
            m,
            i = -1,
            j = 0,
            merged,
            array;
          while (++i < n) j += arrays[i].length;
          merged = new Array(j);
          while (--n >= 0) {
            array = arrays[n];
            m = array.length;
            while (--m >= 0) {
              merged[--j] = array[m];
            }
          }
          return merged;
        }
        function min(array, f) {
          var i = -1,
            n = array.length,
            a,
            b;
          if (f == null) {
            while (++i < n) if ((b = array[i]) != null && b >= b) {
              a = b;
              break;
            }
            while (++i < n) if ((b = array[i]) != null && a > b) a = b;
          } else {
            while (++i < n) if ((b = f(array[i], i, array)) != null && b >= b) {
              a = b;
              break;
            }
            while (++i < n) if ((b = f(array[i], i, array)) != null && a > b) a = b;
          }
          return a;
        }
        function pairs(array) {
          var i = 0,
            n = array.length - 1,
            p = array[0],
            pairs = new Array(n < 0 ? 0 : n);
          while (i < n) pairs[i] = [p, p = array[++i]];
          return pairs;
        }
        function permute(array, indexes) {
          var i = indexes.length,
            permutes = new Array(i);
          while (i--) permutes[i] = array[indexes[i]];
          return permutes;
        }
        function scan(array, compare) {
          if (!(n = array.length)) return;
          var i = 0,
            n,
            j = 0,
            xi,
            xj = array[j];
          if (!compare) compare = ascending;
          while (++i < n) if (compare(xi = array[i], xj) < 0 || compare(xj, xj) !== 0) xj = xi, j = i;
          if (compare(xj, xj) === 0) return j;
        }
        function shuffle(array, i0, i1) {
          var m = (i1 == null ? array.length : i1) - (i0 = i0 == null ? 0 : +i0),
            t,
            i;
          while (m) {
            i = Math.random() * m-- | 0;
            t = array[m + i0];
            array[m + i0] = array[i + i0];
            array[i + i0] = t;
          }
          return array;
        }
        function sum(array, f) {
          var s = 0,
            n = array.length,
            a,
            i = -1;
          if (f == null) {
            while (++i < n) if (a = +array[i]) s += a; // Note: zero and null are equivalent.
          } else {
            while (++i < n) if (a = +f(array[i], i, array)) s += a;
          }
          return s;
        }
        function transpose(matrix) {
          if (!(n = matrix.length)) return [];
          for (var i = -1, m = min(matrix, length), transpose = new Array(m); ++i < m;) {
            for (var j = -1, n, row = transpose[i] = new Array(n); ++j < n;) {
              row[j] = matrix[j][i];
            }
          }
          return transpose;
        }
        function length(d) {
          return d.length;
        }
        function zip() {
          return transpose(arguments);
        }
        var version = "0.7.1";
        exports.version = version;
        exports.bisect = bisectRight;
        exports.bisectRight = bisectRight;
        exports.bisectLeft = bisectLeft;
        exports.ascending = ascending;
        exports.bisector = bisector;
        exports.descending = descending;
        exports.deviation = deviation;
        exports.extent = extent;
        exports.histogram = histogram;
        exports.thresholdFreedmanDiaconis = freedmanDiaconis;
        exports.thresholdScott = scott;
        exports.thresholdSturges = sturges;
        exports.max = max;
        exports.mean = mean;
        exports.median = median;
        exports.merge = merge;
        exports.min = min;
        exports.pairs = pairs;
        exports.permute = permute;
        exports.quantile = quantile;
        exports.range = range;
        exports.scan = scan;
        exports.shuffle = shuffle;
        exports.sum = sum;
        exports.ticks = ticks;
        exports.tickStep = tickStep;
        exports.transpose = transpose;
        exports.variance = variance;
        exports.zip = zip;
      });
    })(d3Array, d3Array.exports);

    const {
      bisectRight
    } = d3Array.exports;
    const quincunx = (u, v, w, q) => {
      const n = u.length - 1;
      u[0] = 0;
      v[0] = 0;
      w[0] = 0;
      v[1] = v[1] / u[1];
      w[1] = w[1] / u[1];
      for (let i = 2; i < n; ++i) {
        u[i] = u[i] - u[i - 2] * w[i - 2] * w[i - 2] - u[i - 1] * v[i - 1] * v[i - 1];
        v[i] = (v[i] - u[i - 1] * v[i - 1] * w[i - 1]) / u[i];
        w[i] = w[i] / u[i];
      }
      for (let i = 2; i < n; ++i) {
        q[i] = q[i] - v[i - 1] * q[i - 1] - w[i - 2] * q[i - 2];
      }
      for (let i = 1; i < n; ++i) {
        q[i] = q[i] / u[i];
      }
      q[n - 2] = q[n - 2] - v[n - 2] * q[n - 1];
      for (let i = n - 3; i > 0; --i) {
        q[i] = q[i] - v[i] * q[i + 1] - w[i] * q[i + 2];
      }
    };
    const smoothingSpline = (x, y, sigma, lambda) => {
      const n = x.length - 1;
      const h = new Array(n + 1);
      const r = new Array(n + 1);
      const f = new Array(n + 1);
      const p = new Array(n + 1);
      const q = new Array(n + 1);
      const u = new Array(n + 1);
      const v = new Array(n + 1);
      const w = new Array(n + 1);
      const params = x.map(() => [0, 0, 0, 0]);
      params.pop();
      const mu = 2 * (1 - lambda) / (3 * lambda);
      for (let i = 0; i < n; ++i) {
        h[i] = x[i + 1] - x[i];
        r[i] = 3 / h[i];
      }
      q[0] = 0;
      for (let i = 1; i < n; ++i) {
        f[i] = -(r[i - 1] + r[i]);
        p[i] = 2 * (x[i + 1] - x[i - 1]);
        q[i] = 3 * (y[i + 1] - y[i]) / h[i] - 3 * (y[i] - y[i - 1]) / h[i - 1];
      }
      q[n] = 0;
      for (let i = 1; i < n; ++i) {
        u[i] = r[i - 1] * r[i - 1] * sigma[i - 1] + f[i] * f[i] * sigma[i] + r[i] * r[i] * sigma[i + 1];
        u[i] = mu * u[i] + p[i];
      }
      for (let i = 1; i < n - 1; ++i) {
        v[i] = f[i] * r[i] * sigma[i] + r[i] * f[i + 1] * sigma[i + 1];
        v[i] = mu * v[i] + h[i];
      }
      for (let i = 1; i < n - 2; ++i) {
        w[i] = mu * r[i] * r[i + 1] * sigma[i + 1];
      }
      quincunx(u, v, w, q);
      params[0][3] = y[0] - mu * r[0] * q[1] * sigma[0];
      params[1][3] = y[1] - mu * (f[1] * q[1] + r[1] * q[2]) * sigma[0];
      params[0][0] = q[1] / (3 * h[0]);
      params[0][1] = 0;
      params[0][2] = (params[1][3] - params[0][3]) / h[0] - q[1] * h[0] / 3;
      r[0] = 0;
      for (let i = 1; i < n; ++i) {
        params[i][0] = (q[i + 1] - q[i]) / (3 * h[i]);
        params[i][1] = q[i];
        params[i][2] = (q[i] + q[i - 1]) * h[i - 1] + params[i - 1][2];
        params[i][3] = r[i - 1] * q[i - 1] + f[i] * q[i] + r[i] * q[i + 1];
        params[i][3] = y[i] - mu * params[i][3] * sigma[i];
      }
      return params;
    };
    class SplineInterpolator {
      constructor(xIn, yIn, lambda = 1) {
        const indices = xIn.map((_, i) => i);
        indices.sort((i, j) => xIn[i] - xIn[j]);
        const x = indices.map(i => xIn[i]);
        const y = indices.map(i => yIn[i]);
        const n = indices.length;
        const sigma = indices.map(() => 1);
        this.n = n;
        this.x = x;
        this.y = y;
        this.params = smoothingSpline(x, y, sigma, lambda);
      }
      interpolate(v) {
        if (v === this.x[this.n - 1]) {
          return this.y[this.n - 1];
        }
        const i = Math.min(Math.max(0, bisectRight(this.x, v) - 1), this.n - 2);
        const [a, b, c, d] = this.params[i];
        v = v - this.x[i];
        return a * v * v * v + b * v * v + c * v + d;
      }
      max(step = 100) {
        const xStart = this.x[0];
        const xStop = this.x[this.n - 1];
        const delta = (xStop - xStart) / step;
        let maxValue = -Infinity;
        for (let i = 0, x = xStart; i < step; ++i, x += delta) {
          const y = this.interpolate(x);
          if (y > maxValue) {
            maxValue = y;
          }
        }
        return maxValue;
      }
      min(step = 100) {
        const xStart = this.x[0];
        const xStop = this.x[this.n - 1];
        const delta = (xStop - xStart) / step;
        let minValue = Infinity;
        for (let i = 0, x = xStart; i < step; ++i, x += delta) {
          const y = this.interpolate(x);
          if (y < minValue) {
            minValue = y;
          }
        }
        return minValue;
      }
      domain() {
        return [this.x[0], this.x[this.x.length - 1]];
      }
      range() {
        return [this.min(), this.max()];
      }
      curve(nInterval, domain = null) {
        domain = domain || this.domain();
        const delta = (domain[1] - domain[0]) / (nInterval - 1);
        const vals = new Array(nInterval);
        for (let i = 0; i < nInterval; ++i) {
          const x = delta * i + domain[0];
          vals[i] = [x, this.interpolate(x)];
        }
        return vals;
      }
    }
    var splineInterpolator = SplineInterpolator;
    var SplineInterpolator$1 = splineInterpolator;

    /* eslint-disable @typescript-eslint/no-loss-of-precision */
    /*
    Adapted from: https://github.com/compute-io/erfcinv/blob/aa116e23883839359e310ad41a7c42f72815fc1e/lib/number.js

    The MIT License (MIT)

    Copyright (c) 2014-2015 The Compute.io Authors. All rights reserved.

    Permission is hereby granted, free of charge, to any person obtaining a copy
    of this software and associated documentation files (the "Software"), to deal
    in the Software without restriction, including without limitation the rights
    to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
    copies of the Software, and to permit persons to whom the Software is
    furnished to do so, subject to the following conditions:

    The above copyright notice and this permission notice shall be included in all
    copies or substantial portions of the Software.

    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
    SOFTWARE.


    Boost Software License - Version 1.0 - August 17th, 2003

    Permission is hereby granted, free of charge, to any person or organization obtaining a copy of the software and accompanying documentation covered by this license (the "Software") to use, reproduce, display, distribute, execute, and transmit the Software, and to prepare derivative works of the Software, and to permit third-parties to whom the Software is furnished to do so, all subject to the following:

    The copyright notices in the Software and this entire statement, including the above license grant, this restriction and the following disclaimer, must be included in all copies of the Software, in whole or in part, and all derivative works of the Software, unless such copies or derivative works are solely in the form of machine-executable object code generated by a source language processor.

    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
    */
    // Coefficients for erfcinv on [0, 0.5]:
    const Y1 = 8.91314744949340820313e-2;
    const P1 = [-5.38772965071242932965e-3, 8.22687874676915743155e-3, 2.19878681111168899165e-2, -3.65637971411762664006e-2, -1.26926147662974029034e-2, 3.34806625409744615033e-2, -8.36874819741736770379e-3, -5.08781949658280665617e-4];
    const Q1 = [8.86216390456424707504e-4, -2.33393759374190016776e-3, 7.95283687341571680018e-2, -5.27396382340099713954e-2, -7.1228902341542847553e-1, 6.62328840472002992063e-1, 1.56221558398423026363, -1.56574558234175846809, -9.70005043303290640362e-1, 1];
    // Coefficients for erfcinv for 0.5 > 1-x >= 0:
    const Y2 = 2.249481201171875;
    const P2 = [-3.67192254707729348546, 2.11294655448340526258e1, 1.7445385985570866523e1, -4.46382324441786960818e1, -1.88510648058714251895e1, 1.76447298408374015486e1, 8.37050328343119927838, 1.05264680699391713268e-1, -2.02433508355938759655e-1];
    const Q2 = [1.72114765761200282724, -2.26436933413139721736e1, 1.08268667355460159008e1, 4.85609213108739935468e1, -2.01432634680485188801e1, -2.86608180499800029974e1, 3.9713437953343869095, 6.24264124854247537712, 1];
    // Coefficients for erfcinv for sqrt( -log(1-x)):
    const Y3 = 8.07220458984375e-1;
    const P3 = [-6.81149956853776992068e-10, 2.85225331782217055858e-8, -6.79465575181126350155e-7, 2.14558995388805277169e-3, 2.90157910005329060432e-2, 1.42869534408157156766e-1, 3.37785538912035898924e-1, 3.87079738972604337464e-1, 1.17030156341995252019e-1, -1.63794047193317060787e-1, -1.31102781679951906451e-1];
    const Q3 = [1.105924229346489121e-2, 1.52264338295331783612e-1, 8.48854343457902036425e-1, 2.59301921623620271374, 4.77846592945843778382, 5.38168345707006855425, 3.46625407242567245975, 1];
    const Y4 = 9.3995571136474609375e-1;
    const P4 = [2.66339227425782031962e-12, -2.30404776911882601748e-10, 4.60469890584317994083e-6, 1.57544617424960554631e-4, 1.87123492819559223345e-3, 9.50804701325919603619e-3, 1.85573306514231072324e-2, -2.22426529213447927281e-3, -3.50353787183177984712e-2];
    const Q4 = [7.64675292302794483503e-5, 2.63861676657015992959e-3, 3.41589143670947727934e-2, 2.20091105764131249824e-1, 7.62059164553623404043e-1, 1.3653349817554063097, 1];
    const Y5 = 9.8362827301025390625e-1;
    const P5 = [9.9055709973310326855e-17, -2.81128735628831791805e-14, 4.62596163522878599135e-9, 4.49696789927706453732e-7, 1.49624783758342370182e-5, 2.09386317487588078668e-4, 1.05628862152492910091e-3, -1.12951438745580278863e-3, -1.67431005076633737133e-2];
    const Q5 = [2.82243172016108031869e-7, 2.75335474764726041141e-5, 9.64011807005165528527e-4, 1.60746087093676504695e-2, 1.38151865749083321638e-1, 5.91429344886417493481e-1, 1];
    /**
     * Polyval.
     *
     * @param c - Array of Number.
     * @param x - Number.
     * @returns Number.
     */
    function polyval(c, x) {
      let p = 0;
      for (const coef of c) {
        p = p * x + coef;
      }
      return p;
    }
    /**
     * Calculates a rational approximation.
     *
     * @private
     * @param x - Number.
     * @param v - Number.
     * @param P - Array of polynomial coefficients.
     * @param Q - Array of polynomial coefficients.
     * @param Y - Number.
     * @returns Rational approximation.
     */
    function calc(x, v, P, Q, Y) {
      const s = x - v;
      const r = polyval(P, s) / polyval(Q, s);
      return Y * x + r * x;
    }
    /**
     * Evaluates the complementary inverse error function for an input value.
     *
     * @private
     * @param x - Input value.
     * @returns Evaluated complementary inverse error function.
     */
    function erfcinv(x) {
      let sign = false;
      let val;
      let q;
      let g;
      let r;
      // [1] Special cases...
      // NaN:
      if (Number.isNaN(x)) {
        return Number.NaN;
      }
      // x not on the interval: [0,2]
      if (x < 0 || x > 2) {
        throw new RangeError(`erfcinv()::invalid input argument. Value must be on the interval [0,2]. Value: \`${x}\`.`);
      }
      if (x === 0) {
        return Number.POSITIVE_INFINITY;
      }
      if (x === 2) {
        return Number.NEGATIVE_INFINITY;
      }
      if (x === 1) {
        return 0;
      }
      // [2] Get the sign and make use of `erfc` reflection formula: `erfc(-z)=2 - erfc(z)`...
      if (x > 1) {
        q = 2 - x;
        x = 1 - q;
        sign = true;
      } else {
        q = x;
        x = 1 - x;
      }
      // [3] |x| <= 0.5
      if (x <= 0.5) {
        g = x * (x + 10);
        r = polyval(P1, x) / polyval(Q1, x);
        val = g * Y1 + g * r;
        return sign ? -val : val;
      }
      // [4] 1-|x| >= 0.25
      if (q >= 0.25) {
        g = Math.sqrt(-2 * Math.log(q));
        q = q - 0.25;
        r = polyval(P2, q) / polyval(Q2, q);
        val = g / (Y2 + r);
        return sign ? -val : val;
      }
      q = Math.sqrt(-Math.log(q));
      // [5] q < 3
      if (q < 3) {
        return calc(q, 1.125, P3, Q3, Y3);
      }
      // [6] q < 6
      if (q < 6) {
        return calc(q, 3, P4, Q4, Y4);
      }
      // Note that the smallest number in JavaScript is 5e-324. Math.sqrt( -Math.log( 5e-324 ) ) ~27.2844
      return calc(q, 6, P5, Q5, Y5);
      // Note that in the boost library, they are able to go to much smaller values, as 128 bit long doubles support ~1e-5000; something which JavaScript does not natively support.
    }

    /**
     * RayleighCdf.
     *
     * @param x - data
     * @param sigma - standard deviation
     * @returns - rayleigh cdf
     */
    function rayleighCdf(x, sigma = 1) {
      if (x < 0) {
        return 0;
      }
      return -Math.expm1(-(x ** 2) / (2 * sigma ** 2));
    }

    /* eslint-disable max-lines-per-function */
    /**
     * Determine noise level by san plot methodology (https://doi.org/10.1002/mrc.4882)
     *
     * @param array - real or magnitude spectra data.
     * @param options - options
     * @returns noise level
     */
    function xNoiseSanPlot(array, options = {}) {
      const {
        mask,
        cutOff,
        refine = true,
        magnitudeMode = false,
        scaleFactor = 1,
        factorStd = 5,
        fixOffset = true
      } = options;
      let input;
      if (Array.isArray(mask) && mask.length === array.length) {
        input = new Float64Array(array.filter((_e, i) => !mask[i]));
      } else {
        input = new Float64Array(array);
      }
      if (scaleFactor > 1) {
        for (let i = 0; i < input.length; i++) {
          input[i] *= scaleFactor;
        }
      }
      input = input.sort().reverse();
      if (fixOffset && !magnitudeMode) {
        const medianIndex = Math.floor(input.length / 2);
        const median = 0.5 * (input[medianIndex] + input[medianIndex + 1]);
        for (let i = 0; i < input.length; i++) {
          input[i] -= median;
        }
      }
      const firstNegativeValueIndex = input[input.length - 1] >= 0 ? input.length : input.findIndex(e => e < 0);
      let lastPositiveValueIndex = firstNegativeValueIndex - 1;
      for (let i = lastPositiveValueIndex; i >= 0; i--) {
        if (input[i] > 0) {
          lastPositiveValueIndex = i;
          break;
        }
      }
      const signPositive = input.slice(0, lastPositiveValueIndex + 1);
      const signNegative = input.slice(firstNegativeValueIndex);
      const cutOffDist = cutOff || determineCutOff(signPositive, {
        magnitudeMode
      });
      const pIndex = Math.floor(signPositive.length * cutOffDist);
      const initialNoiseLevelPositive = signPositive[pIndex];
      const skyPoint = signPositive[0];
      let initialNoiseLevelNegative;
      if (signNegative.length > 0) {
        const nIndex = Math.floor(signNegative.length * (1 - cutOffDist));
        initialNoiseLevelNegative = -1 * signNegative[nIndex];
      } else {
        initialNoiseLevelNegative = 0;
      }
      let noiseLevelPositive = initialNoiseLevelPositive;
      let noiseLevelNegative = initialNoiseLevelNegative;
      let cloneSignPositive = signPositive.slice();
      let cloneSignNegative = signNegative.slice();
      let cutOffSignalsIndexPlus = 0;
      let cutOffSignalsIndexNeg = 2;
      if (refine) {
        let cutOffSignals = noiseLevelPositive * factorStd;
        cutOffSignalsIndexPlus = signPositive.findIndex(e => e < cutOffSignals);
        if (cutOffSignalsIndexPlus > -1) {
          cloneSignPositive = signPositive.slice(cutOffSignalsIndexPlus);
          noiseLevelPositive = cloneSignPositive[Math.floor(cloneSignPositive.length * cutOffDist)];
        }
        cutOffSignals = noiseLevelNegative * factorStd;
        cutOffSignalsIndexNeg = signNegative.findIndex(e => e < cutOffSignals);
        if (cutOffSignalsIndexNeg > -1) {
          cloneSignNegative = signNegative.slice(cutOffSignalsIndexNeg);
          noiseLevelNegative = cloneSignPositive[Math.floor(cloneSignNegative.length * (1 - cutOffDist))];
        }
      }
      const correctionFactor = -simpleNormInvNumber(cutOffDist / 2, {
        magnitudeMode
      });
      let effectiveCutOffDist, refinedCorrectionFactor;
      if (refine && cutOffSignalsIndexPlus > -1) {
        effectiveCutOffDist = (cutOffDist * cloneSignPositive.length + cutOffSignalsIndexPlus) / (cloneSignPositive.length + cutOffSignalsIndexPlus);
        refinedCorrectionFactor = -1 * simpleNormInvNumber(effectiveCutOffDist / 2, {
          magnitudeMode
        });
        noiseLevelPositive /= refinedCorrectionFactor;
        if (cutOffSignalsIndexNeg > -1) {
          effectiveCutOffDist = (cutOffDist * cloneSignNegative.length + cutOffSignalsIndexNeg) / (cloneSignNegative.length + cutOffSignalsIndexNeg);
          refinedCorrectionFactor = -1 * simpleNormInvNumber(effectiveCutOffDist / 2, {
            magnitudeMode
          });
          if (noiseLevelNegative !== 0) {
            noiseLevelNegative /= refinedCorrectionFactor;
          }
        }
      } else {
        noiseLevelPositive /= correctionFactor;
        noiseLevelNegative /= correctionFactor;
      }
      return {
        positive: noiseLevelPositive,
        negative: noiseLevelNegative,
        snr: skyPoint / noiseLevelPositive,
        sanplot: generateSanPlot(input, {
          fromTo: {
            positive: {
              from: 0,
              to: lastPositiveValueIndex
            },
            negative: {
              from: firstNegativeValueIndex,
              to: input.length
            }
          }
        })
      };
    }
    /**
     * DetermineCutOff.
     *
     * @param signPositive - Array of numbers.
     * @param [options = {}] - Options.
     * @param [options.mask] - Boolean array to filter data, if the i-th element is true then the i-th element of the distribution will be ignored.
     * @param [options.scaleFactor=1] - Factor to scale the data input[i]*=scaleFactor.
     * @param [options.cutOff] - Percent of positive signal distribution where the noise level will be determined, if it is not defined the program calculate it.
     * @param [options.factorStd=5] - Factor times std to determine what will be marked as signals.
     * @param [options.refine=true] - If true the noise level will be recalculated get out the signals using factorStd.
     * @param [options.fixOffset=true] - If the baseline is correct, the midpoint of distribution should be zero. If true, the distribution will be centered.
     * @param [options.logBaseY=2] - Log scale to apply in the intensity axis in order to avoid big numbers.
     * @param options.magnitudeMode -
     * @param options.considerList -
     * @param options.considerList.from -
     * @param options.considerList.step -
     * @param options.considerList.to -
     * @param options.fromTo -
     * @returns Result.
     */
    function determineCutOff(signPositive, options = {}) {
      const {
        magnitudeMode = false,
        considerList = {
          from: 0.5,
          step: 0.1,
          to: 0.9
        }
      } = options;
      //generate a list of values for
      const cutOff = [];
      const indexMax = signPositive.length - 1;
      for (let i = 0.01; i <= 0.99; i += 0.01) {
        const index = Math.round(indexMax * i);
        const value = -signPositive[index] / simpleNormInvNumber(i / 2, {
          magnitudeMode
        });
        cutOff.push([i, value]);
      }
      let minKi = Number.MAX_SAFE_INTEGER;
      const {
        from,
        to,
        step
      } = considerList;
      const delta = step / 2;
      let whereToCutStat = 0.5;
      for (let i = from; i <= to; i += step) {
        const floor = i - delta;
        const top = i + delta;
        const elementsOfCutOff = cutOff.filter(e => e[0] < top && e[0] > floor);
        const averageValue = elementsOfCutOff.reduce((a, b) => a + Math.abs(b[1]), 0);
        let kiSqrt = 0;
        for (const element of elementsOfCutOff) {
          kiSqrt += (element[1] - averageValue) ** 2;
        }
        if (kiSqrt < minKi) {
          minKi = kiSqrt;
          whereToCutStat = i;
        }
      }
      return whereToCutStat;
    }
    function simpleNormInvNumber(data, options) {
      return simpleNormInv([data], options)[0];
    }
    /**
     * SimpleNormInvs.
     *
     * @param data - Data array.
     * @param options
     */
    function simpleNormInv(data, options = {}) {
      const {
        magnitudeMode = false
      } = options;
      const from = 0;
      const to = 2;
      const step = 0.01;
      const xTraining = createArray(from, to, step);
      const result = new Float64Array(data.length);
      const yTraining = new Float64Array(xTraining.length);
      if (magnitudeMode) {
        const factor = 1;
        for (let i = 0; i < yTraining.length; i++) {
          const finalInput = xTraining[i] * factor;
          yTraining[i] = 1 - rayleighCdf(finalInput);
        }
        const interp = new SplineInterpolator$1(xTraining, yTraining);
        for (let i = 0; i < result.length; i++) {
          const yValue = 2 * data[i];
          result[i] = -1 * interp.interpolate(yValue);
        }
      } else {
        for (let i = 0; i < result.length; i++) {
          result[i] = -1 * Math.SQRT2 * erfcinv(2 * data[i]);
        }
      }
      return result;
    }
    /**
     * CreateArray.
     *
     * @param from - From.
     * @param to - To.
     * @param step - Step.
     * @returns Array of results.
     */
    function createArray(from, to, step) {
      const length = Math.abs((from - to) / step + 1);
      const result = [];
      for (let i = 0; i < length; i++) {
        result.push(from + i * step);
      }
      return result;
    }
    /**
     * GenerateSanPlot.
     *
     * @param array - Array.
     * @param [options = {}] - Options.
     * @param [options.mask] - Boolean array to filter data, if the i-th element is true then the i-th element of the distribution will be ignored.
     * @param [options.scaleFactor=1] - Factor to scale the data input[i]*=scaleFactor.
     * @param [options.cutOff] - Percent of positive signal distribution where the noise level will be determined, if it is not defined the program calculate it.
     * @param [options.factorStd=5] - Factor times std to determine what will be marked as signals.
     * @param [options.refine=true] - If true the noise level will be recalculated get out the signals using factorStd.
     * @param [options.fixOffset=true] - If the baseline is correct, the midpoint of distribution should be zero. If true, the distribution will be centered.
     * @param [options.logBaseY=2] - Log scale to apply in the intensity axis in order to avoid big numbers.
     * @param options.magnitudeMode -
     * @param options.considerList -
     * @param options.considerList.from -
     * @param options.considerList.step -
     * @param options.considerList.to -
     * @param options.fromTo -
     * @returns Results.
     */
    function generateSanPlot(array, options = {}) {
      const {
        fromTo,
        logBaseY = 2
      } = options;
      const sanplot = {};
      for (const key in fromTo) {
        const {
          from,
          to
        } = fromTo[key];
        sanplot[key] = from !== to ? scale(array.slice(from, to), {
          logBaseY
        }) : {
          x: [],
          y: []
        };
        if (key === 'negative') {
          sanplot[key].y.reverse();
        }
      }
      return sanplot;
    }
    /**
     * Scale.
     *
     * @param array - Array.
     * @param [options = {}] - Options.
     * @param [options.mask] - Boolean array to filter data, if the i-th element is true then the i-th element of the distribution will be ignored.
     * @param [options.scaleFactor=1] - Factor to scale the data input[i]*=scaleFactor.
     * @param [options.cutOff] - Percent of positive signal distribution where the noise level will be determined, if it is not defined the program calculate it.
     * @param [options.factorStd=5] - Factor times std to determine what will be marked as signals.
     * @param [options.refine=true] - If true the noise level will be recalculated get out the signals using factorStd.
     * @param [options.fixOffset=true] - If the baseline is correct, the midpoint of distribution should be zero. If true, the distribution will be centered.
     * @param [options.logBaseY=2] - Log scale to apply in the intensity axis in order to avoid big numbers.
     * @param options.magnitudeMode -
     * @param options.considerList -
     * @param options.considerList.from -
     * @param options.considerList.step -
     * @param options.considerList.to -
     * @param options.fromTo -
     * @returns Results.
     */
    function scale(array, options = {}) {
      const {
        log10,
        abs
      } = Math;
      const {
        logBaseY
      } = options;
      if (logBaseY) {
        array = array.slice();
        const logOfBase = log10(logBaseY);
        for (let i = 0; i < array.length; i++) {
          array[i] = log10(abs(array[i])) / logOfBase;
        }
      }
      const xAxis = createFromToArray({
        from: 0,
        to: array.length - 1,
        length: array.length
      });
      return {
        x: xAxis,
        y: array
      };
    }

    /**
     * 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
      };
    }

    /**
     * Verify that `data` is an object of x,y arrays.
     * Throws an error if it's not.
     *
     * @param data
     * @param options
     */
    function xyCheck(data, options = {}) {
      const {
        minLength
      } = options;
      if (data === null || typeof data !== 'object' ||
      // @ts-expect-error Typechecking
      !isAnyArray$1(data.x) ||
      // @ts-expect-error Typechecking
      !isAnyArray$1(data.y)) {
        throw new Error('data must be an object of x and y arrays');
      }
      // @ts-expect-error Typechecking
      if (data.x.length !== data.y.length) {
        throw new Error('the x and y arrays must have the same length');
      }
      // @ts-expect-error Typechecking
      if (minLength && data.x.length < minLength) {
        throw new Error(`data.x must have a length of at least ${minLength}`);
      }
    }

    /**
     * Calculate integration
     *
     * @param data - Object that contains property x (an ordered increasing array) and y (an array)
     * @param options - Options
     * @returns - xyIntegration value on the specified range
     */
    function xyIntegration(data, options = {}) {
      xyCheck(data, {
        minLength: 1
      });
      const {
        x,
        y
      } = data;
      if (x.length === 1) return 0;
      const {
        fromIndex,
        toIndex
      } = xGetFromToIndex(x, options);
      let currentxyIntegration = 0;
      for (let i = fromIndex; i < toIndex; i++) {
        currentxyIntegration += (x[i + 1] - x[i]) * (y[i + 1] + y[i]) / 2;
      }
      return currentxyIntegration;
    }

    /**
     * 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 (xIsMonotonic(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()
          };
        }
      }
      const xyObject = Array.from(x, (val, index) => ({
        x: val,
        y: y[index]
      })).sort((a, b) => a.x - b.x);
      const 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;
    }

    /**
     * Ensure x values are unique
     *
     * @param data - Object that contains property x (Array) and y (Array)
     * @param options - Object containing a property algorithm (can be 'sum' or 'average', the latter being the default value), and a property isSorted (boolean indicating if the x-array is sorted).
     */
    function xyUniqueX(data, options = {}) {
      xyCheck(data);
      const {
        algorithm = 'average',
        isSorted = true
      } = options;
      if (!isSorted) {
        data = xySortX(data);
      }
      switch (algorithm) {
        case 'average':
          return average(data);
        case 'sum':
          return sum$3(data);
        default:
          throw new Error(`unknown algorithm: ${String(algorithm)}`);
      }
    }
    /**
     * Average.
     *
     * @param data - Input.
     * @returns Result.
     */
    function average(data) {
      const x = [];
      const y = [];
      let cumulativeY = data.y[0];
      let divider = 1;
      for (let i = 1; i < data.x.length; i++) {
        if (!(data.x[i] === data.x[i - 1])) {
          x.push(data.x[i - 1]);
          y.push(cumulativeY / divider);
          cumulativeY = 0;
          divider = 0;
        }
        cumulativeY += data.y[i];
        divider++;
      }
      x.push(data.x[data.x.length - 1]);
      y.push(cumulativeY / divider);
      return {
        x,
        y
      };
    }
    /**
     * Sum.
     *
     * @param data - Input.
     * @returns Result.
     */
    function sum$3(data) {
      const x = [];
      const y = [];
      let cumulativeY = data.y[0];
      for (let i = 1; i < data.x.length; i++) {
        if (!(data.x[i] === data.x[i - 1])) {
          x.push(data.x[i - 1]);
          y.push(cumulativeY);
          cumulativeY = 0;
        }
        cumulativeY += data.y[i];
      }
      x.push(data.x[data.x.length - 1]);
      y.push(cumulativeY);
      return {
        x,
        y
      };
    }

    /**
     * Merge DataXY
     * We have an array of DataXY and the goal is to merge all the values for which the deltaX is small or equal to delta.
     * X values are weighted average
     *
     * @param data - data
     * @param options - Options
     */
    function xyArrayWeightedMerge(data, options = {}) {
      let {
        delta = 1
      } = options;
      if (typeof delta === 'number') {
        const deltaNumber = delta;
        delta = () => deltaNumber;
      }
      data = data.filter(spectrum => spectrum.x.length > 0);
      if (data.length === 0) return {
        x: [],
        y: []
      };
      const x = [];
      const y = [];
      const positions = new Array(data.length).fill(0);
      const point = {
        x: 0,
        y: 0
      };
      nextValue(data, positions, point);
      const slot = {
        maxX: point.x + delta(point.x),
        sumY: point.y,
        sumXY: point.y * point.x
      };
      while (data.length > 0) {
        nextValue(data, positions, point);
        const sameSlot = point.x <= slot.maxX;
        if (!sameSlot) {
          if (slot.sumY > 0) {
            x.push(slot.sumXY / slot.sumY);
            y.push(slot.sumY);
          }
          slot.sumY = 0;
          slot.sumXY = 0;
        }
        slot.sumY += point.y;
        slot.sumXY += point.x * point.y;
        slot.maxX = point.x + delta(point.x);
        if (data.length === 0 && slot.sumY > 0) {
          x.push(slot.sumXY / slot.sumY);
          y.push(slot.sumY);
        }
      }
      return {
        x,
        y
      };
    }
    /**
     * NextValue.
     *
     * @param data - data.
     * @param positions - Positions array.
     * @param point - Point.
     */
    function nextValue(data, positions, point) {
      let minIndex = 0;
      let minX = data[0].x[positions[0]];
      for (let i = 1; i < data.length; i++) {
        const currentX = data[i].x[positions[i]];
        if (currentX < minX) {
          minX = currentX;
          minIndex = i;
        }
      }
      point.x = minX;
      point.y = data[minIndex].y[positions[minIndex]];
      positions[minIndex]++;
      if (positions[minIndex] === data[minIndex].x.length) {
        positions.splice(minIndex, 1);
        data.splice(minIndex, 1);
      }
    }

    /**
     * xyObjectSlotX
     *
     * @param points - Array of growing points {x,y}.
     * @param options - Options.
     */
    function xyObjectSlotX(points, options = {}) {
      const {
        slotWidth = 1
      } = options;
      const halfSlot = slotWidth / 2;
      // when we join we will use the center of mass
      const result = [];
      let current = {
        x: Number.NEGATIVE_INFINITY,
        y: 0
      };
      for (const point of points) {
        const slot = point.x - (point.x + halfSlot) % slotWidth + halfSlot;
        if (Math.abs(current.x - slot) > Number.EPSILON) {
          current = {
            x: slot,
            y: 0
          };
          result.push(current);
        }
        current.y += point.y;
      }
      return result;
    }

    function sum$2(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');
      }
      var sumValue = 0;
      for (var i = 0; i < input.length; i++) {
        sumValue += input[i];
      }
      return sumValue;
    }

    function mean(input) {
      return sum$2(input) / input.length;
    }

    /**
     * Class allowing to manage a Serie
     */
    class Series {
      constructor(array, dimension, options = {}) {
        let {
          meta = {}
        } = options;
        if (new.target === Series) {
          throw new Error('You need to create either a 1D or 2D series');
        }
        this.data = array;
        this.dimension = dimension;
        this.meta = meta;
        this.name = '';
      }
      getData() {
        return this.data;
      }
      is1D() {
        return this.dimension === 1;
      }
      is2D() {
        return this.dimension === 2;
      }
      toJSON() {
        return {
          data: this.data,
          meta: this.meta
        };
      }

      /**
       * Specify an array of index to keep
       * @param {Array} array
       */
      keep(array) {
        const newData = [];
        for (let i of array) {
          newData.push(this.data[i]);
        }
        this.data = newData;
        return this;
      }
    }

    /**
     * Class allowing to manage a Serie
     */
    class Series1D extends Series {
      constructor(array) {
        super(array, 1);
      }
    }

    /**
     * Class allowing to manage a 2D Serie
     */
    class Series2D extends Series {
      constructor(array) {
        super(array, 2);
      }
    }

    function seriesFromArray(array) {
      // need to check if it is a 1D or 2D array (or 3D ?)
      if (!isAnyArray$1(array)) {
        throw new TypeError('seriesFromArray requires as parameter an array of numbers or array');
      }
      if (array.length === 0 || typeof array[0] === 'number') {
        return new Series1D(array);
      }
      if (!isAnyArray$1(array[0])) {
        throw new TypeError('seriesFromArray requires as parameter an array of numbers or array');
      }
      return new Series2D(array);
    }

    function meanFilter(chromatogram, seriesName, options = {}) {
      const {
        factor = 2
      } = options;
      let series = chromatogram.getSeries(seriesName);
      let filtered = [];
      for (let i = 0; i < series.data.length; i++) {
        filtered.push(applyFilter$1(series.data[i], factor));
      }
      return seriesFromArray(filtered);
    }
    function applyFilter$1(series, factor) {
      let filtered = [[], []];
      if (series[1].length === 0) return filtered;
      const meanIntensity = factor * mean(series[1]);
      for (let i = 0; i < series[0].length; i++) {
        if (series[1][i] > meanIntensity) {
          filtered[0].push(series[0][i]);
          filtered[1].push(series[1][i]);
        }
      }
      return filtered;
    }

    function percentageFilter(chromatogram, seriesName, options = {}) {
      const {
        percentage = 0.1
      } = options;
      let series = chromatogram.getSeries(seriesName);
      let filtered = [];
      for (let i = 0; i < series.data.length; i++) {
        filtered.push(applyFilter(series.data[i], percentage));
      }
      return seriesFromArray(filtered);
    }
    function applyFilter(series, percentage) {
      let basePeak;
      try {
        basePeak = max(series[1]);
      } catch {
        basePeak = 0;
      }
      let filtered = [[], []];
      for (let i = 0; i < series[0].length; i++) {
        if (series[1][i] > percentage * basePeak) {
          filtered[0].push(series[0][i]);
          filtered[1].push(series[1][i]);
        }
      }
      return filtered;
    }

    /**
     * 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.replaceAll(/[()]/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(/^\d+[+-]$/)) {
        chargeNumber = Number(
        // eslint-disable-next-line unicorn/prefer-at
        charge.charAt(charge.length - 1) + charge.slice(0, -1));
      } else {
        chargeNumber = Number(charge);
      }
      return chargeNumber;
    }

    /* eslint-disable unicorn/prefer-code-point */

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

    function parse$2(mf) {
      return new MFParser().parse(mf);
    }
    class MFParser {
      parse(mf = '') {
        this.mf = mf;
        this.i = 0;
        this.result = [];
        let lastKind = Kind.BEGIN;
        while (this.i < mf.length) {
          if (this.result.length > 0 && this.result.at(-1).kind !== Kind.TEXT) {
            lastKind = this.result.at(-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.at(-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.at(-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.slice(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.slice(0, indexOfDash)),
            to: parseNumberWithDivision(number.slice(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.replaceAll(/[^A-Za-z]/g, '');
        let isotope = Number(substring.replaceAll(/\D/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(/^[\d,]+$/)) {
          return substring.split(',').map(Number);
        }
        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(/^\([\d+-]+$/)) {
          return parseCharge(substring.slice(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 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$1 = elementsAndIsotopes.map(element => ({
      number: element.number,
      symbol: element.symbol,
      mass: element.mass,
      name: element.name,
      monoisotopicMass: element.monoisotopicMass
    }));

    const elementsAndIsotopesObject = {};
    for (const element of elementsAndIsotopes) {
      elementsAndIsotopesObject[element.symbol] = element;
    }

    const elementsAndStableIsotopes = structuredClone(elementsAndIsotopes);
    for (const element of elementsAndStableIsotopes) {
      element.isotopes = element.isotopes.filter(i => i.abundance > 0);
    }

    const elementsAndStableIsotopesObject = {};
    for (const element of elementsAndStableIsotopes) {
      elementsAndStableIsotopesObject[element.symbol] = element;
    }

    const elementsObject = {};
    for (const element of elements$1) {
      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 = {};
    for (const key of Object.keys(elementsAndIsotopesObject)) {
      let e = elementsAndIsotopesObject[key];
      for (const i of e.isotopes) {
        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 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@BL"
      },
      "mass": 2.01410177812,
      "monoisotopicMass": 2.01410177812,
      "unsaturation": -1,
      "elements": [{
        "symbol": "H",
        "number": 1,
        "isotope": 2
      }]
    }, {
      "symbol": "Dde",
      "name": "Dde",
      "mf": "C10H13O2",
      "ocl": {
        "value": "dklD`FDEgHhihicIVZfZj@@",
        "coordinates": "!Bb@I~@Ha}upJH@m]}_`BH_Wx@b@I}bOrH"
      },
      "mass": 165.20939861871415,
      "monoisotopicMass": 165.09155465812998,
      "unsaturation": 7,
      "elements": [{
        "symbol": "C",
        "number": 10
      }, {
        "symbol": "H",
        "number": 13
      }, {
        "symbol": "O",
        "number": 2
      }]
    }, {
      "symbol": "Dnp",
      "name": "2,4-Dinitrophenyl",
      "mf": "C6H3N2O4",
      "ocl": {
        "value": "dkmB`bWatpVRd^VS{HhheEUFfBAbX@@",
        "coordinates": "!B_c~H_]]}b@I~Owx_`BH_]]}_c~H_]]}"
      },
      "mass": 167.09926376274353,
      "monoisotopicMass": 167.00928158383,
      "unsaturation": 11,
      "elements": [{
        "symbol": "C",
        "number": 6
      }, {
        "symbol": "H",
        "number": 3
      }, {
        "symbol": "N",
        "number": 2
      }, {
        "symbol": "O",
        "number": 4
      }]
    }, {
      "symbol": "Et",
      "name": "Ethyl",
      "mf": "C2H5",
      "ocl": {
        "value": "eMBAYRZ@",
        "coordinates": "!B@Fp@Xp"
      },
      "mass": 29.061175563749384,
      "monoisotopicMass": 29.03912516115,
      "unsaturation": -1,
      "elements": [{
        "symbol": "C",
        "number": 2
      }, {
        "symbol": "H",
        "number": 5
      }]
    }, {
      "symbol": "Fmoc",
      "name": "Fluorenylmethoxycarbonyl",
      "mf": "C15H11O2",
      "ocl": {
        "value": "fde@b@DX@liMkLrjxeVCzLuT@@@P@@@",
        "coordinates": "!BbOq~@Ha}bOrH_]ARcm}Tv~i`pAeKv|@fpB[j[~iozfAKvp"
      },
      "mass": 223.24719659427882,
      "monoisotopicMass": 223.07590459367,
      "unsaturation": 19,
      "elements": [{
        "symbol": "C",
        "number": 15
      }, {
        "symbol": "H",
        "number": 11
      }, {
        "symbol": "O",
        "number": 2
      }]
    }, {
      "symbol": "For",
      "name": "Formyl",
      "mf": "CHO",
      "ocl": {
        "value": "eMJDVTfP@",
        "coordinates": "!B@Fp@Xp"
      },
      "mass": 29.018081575109303,
      "monoisotopicMass": 29.0027396518,
      "unsaturation": 1,
      "elements": [{
        "symbol": "C",
        "number": 1
      }, {
        "symbol": "H",
        "number": 1
      }, {
        "symbol": "O",
        "number": 1
      }]
    }, {
      "symbol": "Gln",
      "name": "Glutamine diradical",
      "mf": "C5H8N2O2",
      "kind": "aa",
      "oneLetter": "Q",
      "alternativeOneLetter": "ξ",
      "ocl": {
        "value": "dmUDPBUICqYqR[evfjihr@@",
        "coordinates": "!Bb@I~@Ha}_c~H@m]}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@"
      },
      "mass": 183.24756861999438,
      "monoisotopicMass": 183.04797576807,
      "unsaturation": 7,
      "elements": [{
        "symbol": "C",
        "number": 9
      }, {
        "symbol": "H",
        "number": 11
      }, {
        "symbol": "O",
        "number": 2
      }, {
        "symbol": "S",
        "number": 1
      }]
    }, {
      "symbol": "Mtt",
      "name": "4-Methyltrityl",
      "mf": "C20H17",
      "ocl": {
        "value": "d@"
      },
      "mass": 257.3497107536532,
      "monoisotopicMass": 257.13302554791,
      "unsaturation": 23,
      "elements": [{
        "symbol": "C",
        "number": 20
      }, {
        "symbol": "H",
        "number": 17
      }]
    }, {
      "symbol": "Nle",
      "name": "Norleucine diradical",
      "mf": "C6H11NO",
      "ocl": {
        "value": "defHPBPOEgEInWYjjhr@@",
        "coordinates": "!Bb@I~@Ha}_c~H@m]}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@"
      },
      "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@"
      },
      "mass": 157.210511544801,
      "monoisotopicMass": 157.11027872702002,
      "unsaturation": 2,
      "elements": [{
        "symbol": "C",
        "number": 8
      }, {
        "symbol": "H",
        "number": 15
      }, {
        "symbol": "N",
        "number": 1
      }, {
        "symbol": "O",
        "number": 2
      }]
    }, {
      "symbol": "Stap",
      "name": "Statine triradical",
      "mf": "C8H14NO2",
      "ocl": {
        "value": "d@"
      },
      "mass": 156.2025707907452,
      "monoisotopicMass": 156.10245369479,
      "unsaturation": 3,
      "elements": [{
        "symbol": "C",
        "number": 8
      }, {
        "symbol": "H",
        "number": 14
      }, {
        "symbol": "N",
        "number": 1
      }, {
        "symbol": "O",
        "number": 2
      }]
    }, {
      "symbol": "Tacm",
      "name": "Trimethylacetamidomethyl",
      "mf": "C6H12NO",
      "ocl": {
        "value": "d@"
      },
      "mass": 114.16581256484488,
      "monoisotopicMass": 114.09188901076,
      "unsaturation": 1,
      "elements": [{
        "symbol": "C",
        "number": 6
      }, {
        "symbol": "H",
        "number": 12
      }, {
        "symbol": "N",
        "number": 1
      }, {
        "symbol": "O",
        "number": 1
      }]
    }, {
      "symbol": "Tbdms",
      "name": "t-Butyldimethylsilyl",
      "mf": "C6H15Si",
      "ocl": {
        "value": "d@"
      },
      "mass": 115.2690253969541,
      "monoisotopicMass": 115.09430201810001,
      "unsaturation": -1,
      "elements": [{
        "symbol": "C",
        "number": 6
      }, {
        "symbol": "H",
        "number": 15
      }, {
        "symbol": "Si",
        "number": 1
      }]
    }, {
      "symbol": "Tbu",
      "name": "t-Butyl",
      "mf": "C4H9",
      "ocl": {
        "value": "d@"
      },
      "mass": 57.114410373442986,
      "monoisotopicMass": 57.07042529007,
      "unsaturation": -1,
      "elements": [{
        "symbol": "C",
        "number": 4
      }, {
        "symbol": "H",
        "number": 9
      }]
    }, {
      "symbol": "Tbuo",
      "name": "t-Butoxy",
      "mf": "C4H9O",
      "ocl": {
        "value": "d@"
      },
      "mass": 73.11381529776126,
      "monoisotopicMass": 73.06533990964,
      "unsaturation": -1,
      "elements": [{
        "symbol": "C",
        "number": 4
      }, {
        "symbol": "H",
        "number": 9
      }, {
        "symbol": "O",
        "number": 1
      }]
    }, {
      "symbol": "Tbuthio",
      "name": "t-Butylthio",
      "mf": "C4H9S",
      "ocl": {
        "value": "d@"
      },
      "mass": 89.17919777957005,
      "monoisotopicMass": 89.04249646446999,
      "unsaturation": -1,
      "elements": [{
        "symbol": "C",
        "number": 4
      }, {
        "symbol": "H",
        "number": 9
      }, {
        "symbol": "S",
        "number": 1
      }]
    }, {
      "symbol": "Tfa",
      "name": "Trifluoroacetyl",
      "mf": "C2F3O",
      "ocl": {
        "value": "d@"
      },
      "mass": 97.01608620597878,
      "monoisotopicMass": 96.99012410776,
      "unsaturation": 1,
      "elements": [{
        "symbol": "C",
        "number": 2
      }, {
        "symbol": "F",
        "number": 3
      }, {
        "symbol": "O",
        "number": 1
      }]
    }, {
      "symbol": "Thr",
      "name": "Threonine diradical",
      "mf": "C4H7NO2",
      "kind": "aa",
      "oneLetter": "T",
      "alternativeOneLetter": "τ",
      "ocl": {
        "value": "d@"
      },
      "mass": 101.10404192541378,
      "monoisotopicMass": 101.04767846918,
      "unsaturation": 2,
      "elements": [{
        "symbol": "C",
        "number": 4
      }, {
        "symbol": "H",
        "number": 7
      }, {
        "symbol": "N",
        "number": 1
      }, {
        "symbol": "O",
        "number": 2
      }]
    }, {
      "symbol": "Thrp",
      "name": "Threonine triradical",
      "mf": "C4H6NO2",
      "ocl": {
        "value": "d@"
      },
      "mass": 100.09610117135801,
      "monoisotopicMass": 100.03985343695001,
      "unsaturation": 3,
      "elements": [{
        "symbol": "C",
        "number": 4
      }, {
        "symbol": "H",
        "number": 6
      }, {
        "symbol": "N",
        "number": 1
      }, {
        "symbol": "O",
        "number": 2
      }]
    }, {
      "symbol": "Tfsi",
      "name": "(Bis)(trifluoromethanesulfonyl)imide",
      "mf": "C2F6NO4S2",
      "ocl": {
        "value": "d@"
      },
      "mass": 280.1457884908235,
      "monoisotopicMass": 279.91729380789,
      "unsaturation": -1,
      "elements": [{
        "symbol": "C",
        "number": 2
      }, {
        "symbol": "F",
        "number": 6
      }, {
        "symbol": "N",
        "number": 1
      }, {
        "symbol": "O",
        "number": 4
      }, {
        "symbol": "S",
        "number": 2
      }]
    }, {
      "symbol": "Tips",
      "name": "Triisopropylsilyl",
      "mf": "C9H21Si",
      "ocl": {
        "value": "dmT@P@VX\\DffYjjjh@@",
        "coordinates": "!B_a@gHb\\]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
      }]
    }, {
      "symbol": "T",
      "name": "Tritium",
      "mf": "[3H]",
      "ocl": {
        "value": "eFAAYhBLCEX@",
        "coordinates": "!B@@L"
      },
      "mass": 3.0160492779,
      "monoisotopicMass": 3.0160492779,
      "unsaturation": -1,
      "elements": [{
        "symbol": "H",
        "number": 1,
        "isotope": 3
      }]
    }];

    const groupsObject = {};
    groups.forEach(e => {
      if (groupsObject[e.symbol]) {
        // eslint-disable-next-line no-console
        console.log(`The symbol ${e.symbol} is used more than once`);
      }
      groupsObject[e.symbol] = e;
    });

    const Style = {
      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.SUPERIMPOSE}">`, `<sup style="${Style.SUPERIMPOSE_SUP_SUB}">${line.over}</sup>`, `<sub style="${Style.SUPERIMPOSE_SUP_SUB}">${line.under}</sub>`, '</span>');
            break;
          default:
            html.push(line.value);
        }
      }
      return html.join('');
    }

    const elements = new Set(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++) {
        // eslint-disable-next-line unicorn/prefer-code-point
        if (mf.charCodeAt(i) > 64 && mf.charCodeAt(i) < 91) {
          return mf;
        }
      }
      let parts = mf.replaceAll(/([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].slice(j, 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.has(two)) {
                newPart += two;
                j++;
              } else if (elements.has(one)) {
                newPart += one;
              } else {
                return mf;
              }
            }
          }
          parts[i] = newPart;
        }
      }
      return parts.join('');
    }

    function flatten(parsed, options = {}) {
      const {
        groupIdentical = false,
        limit = 100000
      } = options;
      if (parsed.length === 0) return [''];
      let parts = [];
      let parenthesisLevel = 0;
      let currentPart;
      let comments = [];
      for (const entry of parsed) {
        if ((entry.kind === 'atom' || entry.kind === 'isotope' || entry.kind === 'openingParenthesis' || !currentPart) && parenthesisLevel === 0) {
          currentPart = {
            mf: '',
            min: 1,
            max: 1
          };
          parts.push(currentPart);
        }
        switch (entry.kind) {
          case 'atom':
            currentPart.mf += entry.value;
            break;
          case 'isotope':
            currentPart.mf += `[${entry.value.isotope}${entry.value.atom}]`;
            break;
          case 'multiplier':
            currentPart.mf += entry.value;
            break;
          case 'multiplierRange':
            if (parenthesisLevel !== 0) {
              throw new Error('Range definition inside parenthesis is not allowed.');
            }
            currentPart.min = entry.value.from;
            currentPart.max = entry.value.to;
            break;
          case 'openingParenthesis':
            parenthesisLevel++;
            currentPart.mf += entry.value;
            break;
          case 'charge':
            if (entry.value === 1) {
              currentPart.mf += '+';
            } else if (entry.value > 1) {
              currentPart.mf += `(+${entry.value})`;
            } else if (entry.value < 0) {
              currentPart.mf += `(${entry.value})`;
            }
            break;
          case 'closingParenthesis':
            parenthesisLevel--;
            currentPart.mf += entry.value;
            break;
          case 'comment':
            comments.push(entry.value);
            break;
          case 'text':
            break;
          default:
            throw new Error(`Could not flatten the parsed MF. Unknown kind: ${entry.kind}`);
        }
      }
      if (groupIdentical) {
        parts = optimizeRanges(parts);
      }
      const mfs = createMFs(parts, comments.join(' '), limit);
      return mfs;
    }

    /**
     * If we have many times the same mf we can combine them
     * This should only be applied if there are acutally some ranges
     */
    function optimizeRanges(parts) {
      let newParts = [];
      let mfsObject = {};
      let hasRange = false;
      for (const mf of parts) {
        if (mf.min !== mf.max) {
          hasRange = true;
          break;
        }
      }
      if (!hasRange) return parts;
      for (const mf of parts) {
        if (!mfsObject[mf.mf]) {
          mfsObject[mf.mf] = {
            mf: mf.mf,
            min: mf.min,
            max: mf.max
          };
          newParts.push(mfsObject[mf.mf]);
        } else {
          mfsObject[mf.mf].min = mfsObject[mf.mf].min + mf.min;
          mfsObject[mf.mf].max = mfsObject[mf.mf].max + mf.max;
        }
      }
      return newParts;
    }
    function createMFs(parts, comment, limit) {
      const currents = new Array(parts.length);
      for (let i = 0; i < currents.length; i++) {
        currents[i] = parts[i].min;
      }
      const mfs = [];
      let position = 0;
      while (position < currents.length) {
        if (currents[position] < parts[position].max) {
          mfs.push(getMF(parts, currents, comment));
          currents[position]++;
          for (let i = 0; i < position; i++) {
            currents[i] = parts[i].min;
          }
          position = 0;
        } else {
          position++;
        }
        if (mfs.length > limit) {
          throw new Error(`MF.flatten generates too many fragments (over ${limit})`);
        }
      }
      mfs.push(getMF(parts, currents, comment));
      return mfs;
    }
    function getMF(parts, currents, comment) {
      let mf = '';
      for (let i = 0; i < parts.length; i++) {
        if (currents[i] === 0) {
          continue;
        }
        mf += parts[i].mf;
        if (currents[i] !== 1) {
          mf += currents[i];
        }
      }
      if (comment) mf += `$${comment}`;
      return mf;
    }

    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$2(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$2(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 new 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]
        });
      }
      for (const ea of eas) {
        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.find(a => a.nominal === line.value.isotope);
                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);
    }

    /** @typedef {Record<string, number>} AtomsMap */

    /**
     * Convert a MF part to a map of atoms
     * This procedure will suppress the isotopes !
     * This is mainly used to make queries
     * @returns {AtomsMap}
     */
    function partToAtoms(part) {
      /** @type {AtomsMap} */
      const 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, options = {}) {
      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('');
    }

    /** @typedef {import('./getInfo.types').PartInfo} PartInfo */
    /** @typedef {import('./getInfo.types').PartInfoWithParts} PartInfoWithParts */

    /**
     *
     * @param {*} parts
     * @param {*} [options={}]
     * @returns {object|PartInfo|PartInfoWithParts}
     */
    function getInfo(parts, options = {}) {
      let {
        customUnsaturations = {},
        emFieldName = 'monoisotopicMass',
        msemFieldName = 'observedMonoisotopicMass'
      } = options;
      if (parts.length === 0) return {};
      if (parts.length === 1) {
        return getProcessedPart$1(parts[0], {
          customUnsaturations,
          emFieldName,
          msemFieldName
        });
      }
      let result = {
        parts: []
      };
      for (let part of parts) {
        result.parts.push(getProcessedPart$1(part, {
          customUnsaturations,
          emFieldName,
          msemFieldName
        }));
      }
      result[emFieldName] = 0;
      result.mass = 0;
      result.charge = 0;
      result.unsaturation = 0;
      result.atoms = {};
      result.mf = result.parts.map(a => a.mf).join('.');
      for (const part of result.parts) {
        result.mass += part.mass;
        result[emFieldName] += part[emFieldName];
        result.charge += part.charge;
        result.unsaturation += part.unsaturation;
        for (const atom in part.atoms) {
          if (!result.atoms[atom]) {
            result.atoms[atom] = 0;
          }
          result.atoms[atom] += part.atoms[atom];
        }
      }
      return result;
    }
    function getProcessedPart$1(part, options) {
      let {
        customUnsaturations,
        emFieldName,
        msemFieldName
      } = options;

      /** @type {PartInfo} */
      let currentPart = {
        mass: 0,
        charge: 0,
        mf: '',
        atoms: partToAtoms(part)
      };
      currentPart[emFieldName] = 0;
      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 new Error(`Unknown element: ${line.value}`);
                if (!customUnsaturations[line.value]) {
                  customUnsaturations[line.value] = element.unsaturation;
                }
              }
              if (!element) throw new Error(`Unknown element: ${line.value}`);
              currentPart[emFieldName] += 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[emFieldName] += 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[emFieldName] += isotopeRatioInfo[emFieldName] * 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[msemFieldName] = (currentPart[emFieldName] - currentPart.charge * ELECTRON_MASS) / Math.abs(currentPart.charge);
      }
      if (validUnsaturation) {
        currentPart.unsaturation = unsaturation / 2 + 1;
      }
      return currentPart;
    }

    /** @typedef {import('./getIsotopesInfo.types').IsotopesInfo} IsotopesInfo

    /**
     *
     * @param {*} parts
     * @returns {[]|IsotopesInfo}
     */
    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]);
    }

    /**
     * @returns {IsotopesInfo}
     */
    function getProcessedPart(part) {
      /** @type {IsotopesInfo} */
      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 new Error('unknown isotope:', line.value.atom, line.value.isotope);
              }
              result.isotopes.push({
                atom: 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,
                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$1(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$1(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(' . ');
    }

    var src = {};

    Object.defineProperty(src, "__esModule", {
      value: true
    });
    var atomSorter_1 = src.atomSorter = atomSorter;
    /**
     * Implementation of the Hill system for sorting atoms
     * https://en.wikipedia.org/wiki/Chemical_formula#Hill_system
     * @param a - first atom to compare
     * @param b - second atom to compare
     * @returns A value suitable for use in Array.prototype.sort.
     */
    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, options = {}) {
      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.at(-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(Boolean);
      }
    }
    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.at(-1).value += key.value.value * key.value.multiplier;
            }
          } else if (currentKey !== key.key) {
            result.push(key.value);
          } else {
            result.at(-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_1(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('');
    }

    /** @typedef {import('./util/getIsotopesInfo.types').IsotopesInfo} IsotopesInfo */
    /** @typedef {import('./util/getInfo.types').PartInfo} PartInfo */
    /** @typedef {import('./util/getInfo.types').PartInfoWithParts} PartInfoWithParts */

    /**
     * Class allowing to deal with molecular formula and derived information
     */
    class MF {
      constructor(mf, options = {}) {
        if (options.ensureCase) {
          mf = ensureCase(mf);
        }
        this.parsed = parse$2(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
       * @param {object} [options.customUnsaturations={}] custom unsaturations
       * @param {string} [options.emFieldName='monoisotopicMass'] name of the monoisotopic mass field
       * @param {string} [options.msemFieldName='observedMonoisotopicMass'] name of the observed monoisotopic mass field
       * @returns {PartInfo|PartInfoWithParts}
       */
      getInfo(options = {}) {
        if (!this.cache.info) {
          this.toParts();
          this.cache.info = getInfo(this.cache.parts, options);
        }
        return this.cache.info;
      }

      /**
       * Returns an object with the elemental analysis
       */
      getEA(options = {}) {
        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
       * @returns {IsotopesInfo}
       */
      getIsotopesInfo(options = {}) {
        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;
      }
      flatten(options) {
        return flatten(this.parsed, options);
      }
    }

    function applyLockMass(chromatogram, mfs, options = {}) {
      const {
        oddReference = true,
        maxShift = 0.1
      } = options;

      // allows mf as string or array
      if (typeof mfs === 'string') {
        mfs = [mfs];
      }

      // calculate the mass reference values
      const referenceMass = mfs.map(mf => {
        let info = new MF(mf).getInfo();
        return info.observedMonoisotopicMass || info.monoisotopicMass;
      });
      const ms = chromatogram.getSeries('ms').data;

      // check where is the reference values
      let referenceIndexShift = Number(oddReference);
      let msIndexShift = Number(!oddReference);
      const newSize = ms.length >> 1;
      let referencesCount = new Array(referenceMass.length).fill(0);

      // applying the changes for all the spectra
      let previousValidDifference = Number.MAX_VALUE;
      let usingPreviousValidDifference = false;
      for (let i = 0; i < newSize; i++) {
        let massIndex = 2 * i + msIndexShift;
        let referenceIndex = 2 * i + referenceIndexShift;

        // calculate the difference between theory and experimental (the smallest)
        let difference = Number.MAX_VALUE;
        let closestIndex = -1;
        for (let j = 0; j < referenceMass.length; j++) {
          for (let k = 0; k < ms[referenceIndex][0].length; k++) {
            if (Math.abs(difference) > Math.abs(referenceMass[j] - ms[referenceIndex][0][k])) {
              difference = referenceMass[j] - ms[referenceIndex][0][k];
              closestIndex = j;
            }
          }
        }
        if (Math.abs(difference) > maxShift && Math.abs(previousValidDifference) < maxShift) {
          difference = previousValidDifference;
          usingPreviousValidDifference = true;
        } else {
          usingPreviousValidDifference = false;
        }
        // apply identified lock mass
        if (Math.abs(difference) < maxShift) {
          previousValidDifference = difference;
          if (!usingPreviousValidDifference && closestIndex !== -1) {
            referencesCount[closestIndex] += 1;
          }
          for (let m = 0; m < ms[massIndex][0].length; m++) {
            ms[massIndex][0][m] += difference;
          }
        }
      }
      const referenceUsed = {
        total: newSize,
        totalFound: referencesCount.reduce((prev, current) => current + prev, 0),
        mfs: {},
        percent: 0
      };
      for (let r = 0; r < referenceMass.length; r++) {
        referenceUsed.mfs[mfs[r]] = referencesCount[r];
      }
      referenceUsed.percent = referenceUsed.totalFound / referenceUsed.total * 100;

      // remove the time and the mass spectra that contains the reference
      chromatogram.filter(index => index % 2 !== referenceIndexShift);
      return referenceUsed;
    }

    /**
     * Calculate bpc
     * @param {Chromatogram} chromatogram - GC/MS chromatogram where make the peak picking
     * @returns {Array} - Calculated bpc
     */
    function calculateBpc(chromatogram) {
      const ms = chromatogram.getSeries('ms');
      const massSpectra = ms.data;
      const bpc = [];
      for (const massSpectrum of massSpectra) {
        if (massSpectrum[1].length > 0) {
          bpc.push(max(massSpectrum[1]));
        } else {
          bpc.push(0);
        }
      }
      return bpc;
    }

    /**
     * Calculate the Extracted Ion Chromatogram
     * @param {object} chromatogram
     * @param {string|number} targetMass
     * @param {object} [options={}]
     * @param {number} [options.slotWidth=1]
     * @returns
     */

    function calculateEic(chromatogram, targetMass, options = {}) {
      const {
        slotWidth = 1
      } = options;
      if (!targetMass) {
        throw new Error('targetMass must be defined and a number or string of comma separated numbers');
      }
      let targetMasses;
      if (!Number.isNaN(Number(targetMass))) {
        targetMasses = [Number(targetMass)];
      } else if (typeof targetMass === 'string') {
        targetMasses = targetMass.split(/[\t\n\r ,;]+/).map(Number);
      }
      for (let mass of targetMasses) {
        if (Number.isNaN(mass)) {
          throw new Error('targetMass must be defined and a number or string of comma separated numbers');
        }
      }
      const halfWidth = slotWidth / 2;
      const ms = chromatogram.getSeries('ms');
      const massSpectra = ms.data;
      const result = new Array(massSpectra.length).fill(0);
      for (let mass of targetMasses) {
        for (let i = 0; i < massSpectra.length; i++) {
          let massSpectrum = massSpectra[i];
          for (let j = 0; j < massSpectrum[0].length; j++) {
            if (Math.abs(massSpectrum[0][j] - mass) <= halfWidth) {
              result[i] += massSpectrum[1][j];
            }
          }
        }
      }
      return result;
    }

    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, options = {}) {
      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 preprocessIonizations(ionizationsString = '') {
      if (Array.isArray(ionizationsString)) return ionizationsString;
      let ionizations = ionizationsString.split(/ *[\t\n\r,.;]+ */);

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

      let results = [];
      for (let ionization of ionizations) {
        let parts = new MF(ionization).flatten();
        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;
    }

    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(options = {}) {
        const {
          fwhm = 500,
          sd
        } = options;
        this.fwhm = sd ? gaussianWidthToFWHM(2 * sd) : fwhm;
      }
      fwhmToWidth(fwhm = this.fwhm) {
        return gaussianFwhmToWidth(fwhm);
      }
      widthToFWHM(width) {
        return gaussianWidthToFWHM(width);
      }
      fct(x) {
        return gaussianFct(x, this.fwhm);
      }
      getArea(height = calculateGaussianHeight({
        fwhm: this.fwhm
      })) {
        return getGaussianArea({
          fwhm: this.fwhm,
          height
        });
      }
      getFactor(area) {
        return getGaussianFactor(area);
      }
      getData(options = {}) {
        return getGaussianData(this, options);
      }
      calculateHeight(area = 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(area = 0.9999) {
      return Math.sqrt(2) * erfinv(area);
    }
    function getGaussianData(shape = {}, options = {}) {
      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(options = {}) {
        const {
          fwhm = 500
        } = options;
        this.fwhm = fwhm;
      }
      fwhmToWidth(fwhm = this.fwhm) {
        return lorentzianFwhmToWidth(fwhm);
      }
      widthToFWHM(width) {
        return lorentzianWidthToFWHM(width);
      }
      fct(x) {
        return lorentzianFct(x, this.fwhm);
      }
      getArea(height = 1) {
        return getLorentzianArea({
          fwhm: this.fwhm,
          height
        });
      }
      getFactor(area) {
        return getLorentzianFactor(area);
      }
      getData(options = {}) {
        return getLorentzianData(this, options);
      }
      calculateHeight(area = 1) {
        return calculateLorentzianHeight({
          fwhm: this.fwhm,
          area
        });
      }
      getParameters() {
        return ['fwhm'];
      }
    }
    const calculateLorentzianHeight = ({
      fwhm = 1,
      area = 1
    }) => {
      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 = (area = 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 = (shape = {}, options = {}) => {
      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(options = {}) {
        const {
          fwhm = 500,
          mu = 0.5
        } = options;
        this.mu = mu;
        this.fwhm = fwhm;
      }
      fwhmToWidth(fwhm = this.fwhm, mu = this.mu) {
        return pseudoVoigtFwhmToWidth(fwhm, mu);
      }
      widthToFWHM(width, mu = this.mu) {
        return pseudoVoigtWidthToFWHM(width, mu);
      }
      fct(x) {
        return pseudoVoigtFct(x, this.fwhm, this.mu);
      }
      getArea(height = 1) {
        return getPseudoVoigtArea({
          fwhm: this.fwhm,
          height,
          mu: this.mu
        });
      }
      getFactor(area) {
        return getPseudoVoigtFactor(area);
      }
      getData(options = {}) {
        const {
          length,
          factor,
          height = calculatePseudoVoigtHeight({
            fwhm: this.fwhm,
            mu: this.mu,
            area: 1
          })
        } = options;
        return getPseudoVoigtData(this, {
          factor,
          length,
          height
        });
      }
      calculateHeight(area = 1) {
        return calculatePseudoVoigtHeight({
          fwhm: this.fwhm,
          mu: this.mu,
          area
        });
      }
      getParameters() {
        return ['fwhm', 'mu'];
      }
    }
    const calculatePseudoVoigtHeight = (options = {}) => {
      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 = (width, mu = 0.5) => {
      return width * (mu * ROOT_2LN2_MINUS_ONE + 1);
    };
    const pseudoVoigtFwhmToWidth = (fwhm, mu = 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 = (area = 0.9999, mu = 0.5) => {
      return mu < 1 ? getLorentzianFactor(area) : getGaussianFactor(area);
    };
    const getPseudoVoigtData = (shape = {}, options = {}) => {
      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;
      const xs = data.x;
      const ys = data.y;
      for (let i = 0; i < xs.length; i++) {
        ys[i] += baselineFct(xs[i]);
      }
      return data;
    }

    function addNoise(data, options = {}) {
      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(options = {}) {
        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)
        };
        const 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, options = {}) {
        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);
        const {
          width
        } = options;
        let {
          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');
        }
        const 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(options = {}) {
        if (typeof options === 'boolean') {
          options = {
            copy: options
          };
        }
        const {
          copy = true,
          threshold = 0
        } = options;
        if (threshold) {
          const minPeakHeight = this.maxPeakHeight * threshold;
          const x = [];
          const 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: Float64Array.from(x),
            y: Float64Array.from(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, threshold = 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 = {
            ...item
          };
          result.push(current);
        }
      }
      self.array = result;
      self.ySorted = false;
      return self;
    }

    function multiply(a, b, options = {}) {
      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(array, p, options = {}) {
      if (p <= 0) throw new Error('power must be larger than 0');
      if (p === 1) return array;
      if (p === 2) {
        return array.square();
      }
      p--;
      let base = array.copy(); // linear time
      while (p !== 0) {
        if ((p & 1) !== 0) {
          array.multiply(base, options); // executed <= log2(p) times
        }
        p >>= 1;
        if (p !== 0) base.square(options); // executed <= log2(p) times
      }
      return array;
    }

    /**
     * Internal class to deal with isotopic distribution calculations
     */
    class Distribution {
      constructor(array = []) {
        this.array = array;
        this.cache = getEmptyCache();
      }
      emptyCache() {
        if (this.cache.isEmpty) return;
        this.cache = getEmptyCache();
      }
      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 sumY() {
        if (!Number.isNaN(this.cache.sumY)) return this.cache.sumY;
        let sumY = 0;
        for (let item of this.array) {
          sumY += item.y;
        }
        this.cache.sumY = sumY;
        this.cache.isEmpty = false;
        return sumY;
      }
      get minX() {
        if (!Number.isNaN(this.cache.minX)) return this.cache.minX;
        let minX = Number.POSITIVE_INFINITY;
        for (let item of this.array) {
          if (item.x < minX) {
            minX = item.x;
          }
        }
        this.cache.minX = minX;
        this.cache.isEmpty = false;
        return minX;
      }
      get maxX() {
        if (!Number.isNaN(this.cache.maxX)) return this.cache.maxX;
        let maxX = Number.NEGATIVE_INFINITY;
        for (let item of this.array) {
          if (item.x > maxX) {
            maxX = item.x;
          }
        }
        this.cache.maxX = maxX;
        this.cache.isEmpty = false;
        return maxX;
      }
      get minY() {
        if (!Number.isNaN(this.cache.minY)) return this.cache.minY;
        let minY = Number.POSITIVE_INFINITY;
        for (let item of this.array) {
          if (item.y < minY) {
            minY = item.y;
          }
        }
        this.cache.minY = minY;
        this.cache.isEmpty = false;
        return minY;
      }
      get maxY() {
        if (!Number.isNaN(this.cache.maxY)) return this.cache.maxY;
        let maxY = Number.NEGATIVE_INFINITY;
        for (let item of this.array) {
          if (item.y > maxY) {
            maxY = item.y;
          }
        }
        this.cache.maxY = maxY;
        this.cache.isEmpty = false;
        return maxY;
      }
      multiplyY(value) {
        for (const item of this.array) {
          item.y *= value;
        }
      }
      setArray(array) {
        this.array = array;
        this.emptyCache();
      }
      move(other) {
        this.array = other.array;
        this.emptyCache();
      }
      push(...points) {
        this.array.push(...points);
        this.emptyCache();
      }

      /**
       * Sort by ASCENDING x values
       * @returns {Distribution}
       */
      sortX() {
        this.cache.ySorted = false;
        if (this.cache.xSorted) return this;
        this.array.sort((a, b) => a.x - b.x);
        this.cache.xSorted = true;
        this.cache.isEmpty = false;
        return this;
      }

      /**
       * Sort by DESCENDING y values
       * @returns {Distribution}
       */
      sortY() {
        this.cache.xSorted = false;
        if (this.cache.ySorted) return this;
        this.array.sort((a, b) => b.y - a.y);
        this.cache.ySorted = true;
        this.cache.isEmpty = false;
        return this;
      }
      normalize() {
        const sum = this.sumY;
        for (let item of this.array) {
          item.y /= sum;
        }
        return this;
      }

      /**
       * Only keep a defined number of peaks
       * @param {number} limit
       * @returns
       */
      topY(limit) {
        if (!limit) return this;
        if (this.array.length <= limit) return this;
        this.sortY();
        this.array.splice(limit);
        return this;
      }

      /**
       * remove all the peaks under a defined relative threshold
       * @param {number} [relativeValue=0] Should be between 0 and 1. 0 means no peak will be removed, 1 means all peaks will be removed
       */
      threshold(relativeValue = 0) {
        if (!relativeValue) return this;
        const maxY = this.maxY;
        const threshold = maxY * relativeValue;
        this.array = this.array.filter(point => point.y >= threshold);
      }
      square(options = {}) {
        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.cache = {
          ...this.cache
        };
        distCopy.array = structuredClone(this.array);
        return distCopy;
      }
      maxToOne() {
        if (this.array.length === 0) return this;
        let currentMax = this.maxY;
        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.emptyCache();
      }
      closestPointX(target) {
        this.sortX();
        return closestPointX(this.array, target);
      }
    }
    function getEmptyCache() {
      return {
        isEmpty: true,
        xSorted: false,
        ySorted: false,
        minX: Number.NaN,
        maxX: Number.NaN,
        minY: Number.NaN,
        maxY: Number.NaN,
        sumY: Number.NaN
      };
    }

    /**
     * Calcultes informations about the isotopic composition explaining a specific mass
     * @param {Record<string, number>} composition
     * @returns
     */
    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;

    /** @typedef {import('mf-parser').IsotopesInfo} IsotopesInfo */
    /** @typedef {import('mf-parser').PartInfo} PartInfo */

    /** @typedef {import('./IsotopicDistribution.types').XY} XY */
    /** @typedef {import('./IsotopicDistribution.types').IsotopicDistributionPart} IsotopicDistributionPart */
    /** @typedef {import('./IsotopicDistribution.types').IsotopicDistributionOptions} IsotopicDistributionOptions */

    /**
     * A class that allows to manage isotopic distribution
     */
    class IsotopicDistribution {
      /**
       * Class that manages isotopic distribution
       * @param {string|Array<any>} value - Molecular formula or an array of parts.
       * @param {IsotopicDistributionOptions} [options]
       */
      constructor(value, options = {}) {
        this.threshold = options.threshold;
        this.limit = options.limit;
        if (Array.isArray(value)) {
          this.parts = structuredClone(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();
          const ionizations = preprocessIonizations(options.ionizations);
          /** @type {PartInfo} */
          const parts = 'parts' in mfInfo ? mfInfo.parts : [mfInfo];
          this.parts = [];
          for (let partOriginal of parts) {
            // we calculate information for each part
            for (const ionization of ionizations) {
              let part = structuredClone(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;
      }

      /**
       * @returns {Array<IsotopicDistributionPart>}
       */
      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 // should we calculate composition in isotopes of each peak
          }]);
          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 = structuredClone(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 = 0;
            for (const item of totalDistribution.array) {
              this.confidence += item.y;
            }

            // we finally deal with the charge

            if (charge) {
              for (const e of totalDistribution.array) {
                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.at(-1).x;
            }
            if (part?.ms.similarity?.factor) {
              totalDistribution.multiplyY(part.ms.similarity.factor);
            } else if (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;
            const absoluteChargeOrOne = absoluteCharge || 1;
            for (let entry of totalDistribution.array) {
              if (!entry.composition) continue;
              const deltaNeutrons = Math.round(entry.x * absoluteChargeOrOne - part.monoisotopicMass) + 0; // +0 to avoid -0
              Object.assign(entry, {
                ...getDerivedCompositionInfo(entry.composition),
                deltaNeutrons
              });
            }
            if (finalDistribution.array.length === 0) {
              finalDistribution = totalDistribution;
            } else {
              finalDistribution.append(totalDistribution);
            }
          }
        }
        if (finalDistribution) finalDistribution.joinX(this.fwhm);

        // if there is a threshold we will deal with it
        // and we will correct the confidence
        if (this.threshold || this.limit) {
          const sumBefore = finalDistribution.sumY;
          if (this.threshold) finalDistribution.threshold(this.threshold);
          if (this.limit) {
            finalDistribution.topY(this.limit);
            finalDistribution.sortX();
          }
          const sumAfter = finalDistribution.sumY;
          this.confidence = this.confidence * sumAfter / sumBefore;
        }
        this.confidence /= this.parts.length;
        this.cachedDistribution = finalDistribution;
        return finalDistribution;
      }
      getCSV() {
        return this.getText({
          delimiter: ', '
        });
      }
      getTSV() {
        return this.getText({
          delimiter: '\t'
        });
      }
      getTable(options = {}) {
        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(options = {}) {
        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<{x:number,y:number}|{x:number,y:number,label:string,shortComposition:string,shortLabel:string,deltaNeutrons:number}>}
       */
      getPeaks(options = {}) {
        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 = structuredClone(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 {{x:number[],y:number[]}|{x:number[],y:number[],label:string[],shortComposition:string[],shortLabel:string[],deltaNeutrons:[]}}
       */
      getXY(options = {}) {
        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 distirubtion
       * @param {object} [options={}]
       * @param {number} [options.maxValue=100]
       * @param {number} [options.sumValue] // if sumValue is defined, maxValue is ignored
       * @return {import('cheminfo-types').MeasurementXYVariables} an object containing at least the 2 properties: x:[] and y:[]
       */
      getVariables(options = {}) {
        const xy = this.getXY(options);
        return {
          x: {
            data: xy.x,
            label: 'm/z',
            units: 'u'
          },
          y: {
            data: xy.y,
            label: 'Relative intensity',
            units: '%'
          }
        };
      }

      /**
       * Returns the isotopic distribution as the sum of gaussian
       * @param {object} [options={}]
       * @param {number} [options.gaussianWidth=10] // how good should look the gaussian ? By default we take 10 times the fwhm as number of points
       * @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 {number} [options.from] // minimal x value, default to the first point - 2
       * @param {number} [options.to] // maximal x value, default to the last point + 2
       * @param {function} [options.peakWidthFct=(mz)=>(this.fwhm)]
       * @return {XY} isotopic distribution as an object containing 2 properties: x:[] and y:[]
       */

      getGaussian(options = {}) {
        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.at(-1).x + 2);
        const nbPoints = Math.round((to - from) * gaussianWidth / this.fwhm + 1);
        if (nbPoints > maxLength) {
          throw new 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;
      }
    }

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

    function unsaturationMatcher(entry, options = {}) {
      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;
    }

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

    /**
     * @param {object}             [entry={}]
     * @param {MSEMFilterOptions}  [options={}]
     * @return {boolean}
     */

    /**
     * We always recalculate msem
     */

    function msemMatcher(entry, options = {}) {
      const {
        ionization = {
          mf: '',
          em: 0,
          charge: 0,
          atoms: {}
        },
        forceIonization = false,
        precision = 1000,
        minCharge = Number.MIN_SAFE_INTEGER,
        maxCharge = Number.MAX_SAFE_INTEGER,
        absoluteCharge = false,
        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,
        minMW = -Infinity,
        maxMW = +Infinity,
        allowNegativeAtoms = false,
        atoms,
        callback
      } = options;
      if (entry.mw !== undefined && (entry.mw < minMW || entry.mw > maxMW)) {
        return false;
      }
      let msInfo = getMsInfo(entry, {
        ionization,
        forceIonization,
        targetMass
      });
      let ms = msInfo.ms;
      if (entry.em !== undefined && (entry.em < minEM || entry.em > maxEM)) {
        return false;
      }
      if (ms.em !== undefined && (ms.em < minMSEM || ms.em > maxMSEM)) {
        return false;
      }
      if (targetMass && Math.abs(ms.ppm) > precision) {
        return false;
      }
      if (ms.charge !== undefined) {
        let charge = absoluteCharge ? Math.abs(ms.charge) : ms.charge;
        if (charge < minCharge || charge > maxCharge) return false;
      }
      if (unsaturation !== undefined && entry.unsaturation !== undefined && !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(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 && !callback(entry)) return false;
      return msInfo;
    }

    /**
     * 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 monoisotopic mass (m/z)
     * In the molecular formula there may be a comment after the '$' symbol
     *
     * @param {Array}         ranges
     * @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 {import('mf-matcher').MSEMFilterOptions}        [options.filter={}]
     * @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(ranges, options = {}) {
      if (!Array.isArray(ranges)) {
        throw new Error('Ranges must be an array of string or object');
      }
      options = {
        ...options
      };
      let {
        limit = 100000,
        uniqueMFs = true,
        estimate = false,
        onStep
      } = options;
      options.filterFctVariables = {};
      for (let i = 0; i < ranges.length; i++) {
        const range = ranges[i];
        if (typeof range === 'object' && range.name) {
          options.filterFctVariables[range.name] = i;
          ranges[i] = range.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);

      // we allow String delimited by ". or ;" instead of an array
      for (let i = 0; i < ranges.length; i++) {
        if (!Array.isArray(ranges[i])) {
          ranges[i] = ranges[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 < ranges.length; i++) {
        let parts = ranges[i];
        let newParts = [];
        for (let j = 0; j < parts.length; j++) {
          let part = parts[j];
          if (part.match(/\d-[\d-]/)) {
            // deal with negative numbers
            // there are ranges ... we are in trouble !
            newParts = newParts.concat(new MF(part).flatten({
              groupIdentical: false,
              limit
            }));
          } else {
            newParts.push(parts[j]); // the part with the comments !
          }
        }
        ranges[i] = newParts;
      }
      if (estimate) {
        let total = ranges.reduce((previous, current) => previous * current.length, 1);
        return total * options.ionizations.length;
      }
      let results = [];
      let sizes = [];
      let currents = [];
      for (let i = 0; i < ranges.length; i++) {
        sizes.push(ranges[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, ranges, 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, ranges, options);
      if (uniqueMFs) {
        let uniqueMFsObject = {};
        for (const result of results) {
          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, options = {}) {
      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 sharps = [];
        for (let i = 0; i < keys.length; i++) {
          let anchors = keys[i][currents[i]].match(/#\d+/g);
          if (anchors) sharps.push(...anchors);
        }
        if (sharps.length % 2 === 1) return;
        sharps = sharps.sort();
        for (let i = 0; i < sharps.length; i += 2) {
          if (sharps[i] !== sharps[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.includes('$')) {
              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);
      }
    }

    /**
     * Calculate tic for specific molecular formula and ionizations
     *
     * The system will take all the peaks with an intensity over 5% (default value)
     * @param {Chromatogram} chromatogram - GC/MS chromatogram where make the peak picking
     * @param {string} targetMF - mass for which to extract the spectrum
     * @param {object} [options={}]
     * @param {number} [options.slotWidth=1] - Width of the slot around the mass of targetMF
     * @param {number} [options.threshold=0.05] - Minimal height for peaks
     * @param {number} [options.ionizations='H+'] - List of allowed ionisation
     * @returns {Promise<Array>} - Calculated mass for targetMass
     */
    async function calculateForMF(chromatogram, targetMF, options = {}) {
      const {
        threshold = 0.05,
        slotWidth = 1,
        ionizations = 'H+'
      } = options;
      if (typeof targetMF !== 'string') {
        throw new Error('targetMF must be defined and a string');
      }
      const mfInfos = await generateMFs([targetMF]);
      const mfs = mfInfos.map(info => info.mf);
      const halfWidth = slotWidth / 2;
      const ms = chromatogram.getSeries('ms');
      let masses = [];
      for (let mf of mfs) {
        let isotopicDistribution = new IsotopicDistribution(mf, {
          ionizations
        });
        // we add isotopicDistribution in all the parts
        isotopicDistribution.getDistribution();
        let parts = isotopicDistribution.getParts();
        masses = masses.concat(...parts.map(part => part.isotopicDistribution));
      }
      masses.sort((a, b) => a.x - b.x);
      masses = xyObjectSlotX(masses, {
        slotWidth
      }).filter(mass => mass.y > threshold);
      let massSpectra = ms.data;
      let result = new Array(massSpectra.length).fill(0);
      for (let targetMass of masses) {
        for (let i = 0; i < massSpectra.length; i++) {
          let massSpectrum = massSpectra[i];
          for (let j = 0; j < massSpectrum[0].length; j++) {
            if (Math.abs(massSpectrum[0][j] - targetMass.x) <= halfWidth) {
              result[i] += massSpectrum[1][j];
            }
          }
        }
      }
      return result;
    }

    function calculateLength(chromatogram, seriesName) {
      const series2D = chromatogram.getSeries(seriesName);
      const spectra = series2D.data;
      const length = spectra.map(spectrum => spectrum[0].length);
      return length;
    }

    function calculateTic(chromatogram) {
      const ms = chromatogram.getSeries('ms');
      const massSpectra = ms.data;
      const tic = [];
      for (const massSpectrum of massSpectra) {
        if (massSpectrum[1].length > 0) {
          tic.push(sum$2(massSpectrum[1]));
        } else {
          tic.push(0);
        }
      }
      return tic;
    }

    function zeroInsteadOfNegative(X) {
      let rows = X.rows;
      let columns = X.columns;
      let newMatrix = new Matrix(X);
      for (let r = 0; r < rows; r++) {
        for (let c = 0; c < columns; c++) {
          if (newMatrix.get(r, c) < 0) {
            newMatrix.set(r, c, 0);
          }
        }
      }
      return newMatrix;
    }

    function checkMatrixS(data, originalMatrix) {
      let {
        A,
        S
      } = data;
      //check if is there at least one element cero
      let indices = [];
      let sum = S.sum('row');
      for (let i = 0; i < sum.length; i++) {
        if (sum[i] === 0) {
          indices.push(i);
          continue;
        } else {
          for (let j = 0; j < S.columns; j++) {
            if (isNaN(S.get(i, j))) {
              indices.push(i);
              break;
            }
          }
        }
      }
      // if there than just one zero or NaN element
      // run a NMF with the residual matrix Y - A*B
      if (indices.length > 0) {
        let temp = fastExtractNMF(originalMatrix.clone().subM(A.mmul(S)), indices.length);
        for (let i = 0; i < indices.length; i++) {
          for (let j = 0; j < S.columns; j++) {
            S.set(indices[i], j, temp.S.get(i, j));
          }
          for (let j = 0; j < A.rows; j++) {
            A.set(j, indices[i], temp.A.get(j, i));
          }
        }
      }
      return Object.assign({}, data, {
        A,
        S
      });
    }
    function fastExtractNMF(residual, r) {
      if (r <= 0) return {
        A: [],
        S: []
      };
      const {
        columns,
        rows
      } = residual;
      let A = Matrix.zeros(rows, r);
      let S = Matrix.zeros(r, columns);
      for (let i = 0; i < r; i++) {
        residual = zeroInsteadOfNegative(residual);
        if (residual.sum() === 0) continue;
        let res2 = Matrix.pow(residual, 2).sum('column');
        //find the max of the first column

        let maxIndex = 0;
        for (let j = 1; j < res2.length; j++) {
          if (res2[maxIndex] < res2[j]) maxIndex = j;
        }
        if (res2[maxIndex] > 0) {
          let sqrtMaxValue = Math.sqrt(res2[maxIndex]);
          for (let j = 0; j < rows; j++) {
            let value = residual.get(j, maxIndex) / sqrtMaxValue;
            A.set(j, i, value);
          }
          let temp = A.getColumnVector(i).transpose().mmul(residual);
          for (let j = 0; j < columns; j++) {
            S.set(i, j, Math.max(temp.get(0, j), 0));
          }
          let subtracting = A.getColumnVector(i).mmul(S.getRowVector(i));
          residual = residual.sub(subtracting);
        }
      }
      return {
        A,
        S
      };
    }

    function normBy(x, by = 'column') {
      let norms = Matrix.mul(x, x).sum(by);
      let length = norms.length;
      for (let i = 0; i < length; i++) {
        norms[i] = Math.sqrt(norms[i]);
      }
      return by === 'row' ? Matrix.from1DArray(length, 1, norms) : Matrix.from1DArray(1, length, norms);
    }

    function normProj(X, normLimits) {
      let norms;
      let r = X.rows;
      let c = X.columns;
      if (normLimits.rows === r) {
        norms = normBy(X, 'row');
        //select rows with norm > 0 then multiply twise by the min
        for (let i = 0; i < r; i++) {
          if (norms.get(i, 0) <= 0) continue;
          for (let j = 0; j < c; j++) {
            let value = X.get(i, j) * Math.min(norms.get(i, 0), normLimits.get(i, 0) / norms.get(i, 0));
            X.set(i, j, value);
          }
        }
      } else {
        norms = normBy(X, 'column');
        for (let i = 0; i < c; i++) {
          if (norms.get(0, i) <= 0) continue;
          for (let j = 0; j < r; j++) {
            let value = X.get(j, i) * Math.min(norms.get(0, i), normLimits.get(0, i) / norms.get(0, i));
            X.set(j, i, value);
          }
        }
      }
      return X;
    }

    function updateMatrixA(Ainit, S, originalMatrix, options) {
      let {
        maxFBIteration,
        toleranceFB,
        normConstrained = false,
        lambda
      } = options;
      let St = S.transpose();
      let H = S.mmul(St);
      let YSt = originalMatrix.mmul(St);
      let evd = new EVD(H, {
        assumeSymmetric: true
      });
      let L = Math.max(...evd.realEigenvalues);
      let A = Ainit;
      let prevA = A.clone();
      let t = 1;
      let gradient = a => a.mmul(H).sub(YSt);
      let proximal;
      if (normConstrained) {
        let normLimits = normBy(Ainit, 'column');
        proximal = (x, threshold) => normProj(zeroInsteadOfNegative(x.subS(threshold)), normLimits);
      } else {
        proximal = (x, threshold) => zeroInsteadOfNegative(x.subS(threshold));
      }
      for (let i = 0; i < maxFBIteration; i++) {
        let tNext = (1 + Math.sqrt(1 + 4 * t * t)) / 2;
        let w = (t - 1) / tNext;
        t = tNext;
        let B = Matrix.mul(A, w + 1).sub(Matrix.mul(prevA, w));
        prevA = A.clone();
        A = proximal(B.sub(gradient(B).divS(L)), lambda / L);
        if (Matrix.sub(prevA, A).norm() / A.norm() < toleranceFB) {
          break;
        }
      }
      return A;
    }

    function getMax(array = []) {
      let max = Number.MIN_SAFE_INTEGER;
      for (let i = 0; i < array.length; i++) {
        if (max < array[i]) max = array[i];
      }
      return max;
    }

    function updateMatrixS(A, Sinit, originalMatrix, lambda, options) {
      let {
        maxFBIteration,
        toleranceFB
      } = options;
      let At = A.transpose();
      let H = At.mmul(A);
      let AtY = At.mmul(originalMatrix);
      let evd = new EVD(H, {
        assumeSymmetric: true
      });
      let L = getMax(evd.realEigenvalues);
      let t = 1;
      let S = Sinit.clone();
      let prevS = S.clone();
      let gradient = s => H.mmul(s).sub(AtY);
      let proximal = (x, threshold) => zeroInsteadOfNegative(x.subS(threshold));
      for (let i = 0; i < maxFBIteration; i++) {
        let tNext = (1 + Math.sqrt(1 + 4 * t * t)) / 2;
        let w = (t - 1) / tNext;
        t = tNext;
        // R = S_k + w [S_k - S_(k-1)] = (1 + w) .* S_k - w .* S_(k-1)
        let R = Matrix.mul(S, 1 + w).sub(Matrix.mul(prevS, w));
        prevS = S.clone();
        S = proximal(R.sub(gradient(R).divS(L)), lambda / L);
        if (Matrix.sub(prevS, S).norm() / S.norm() < toleranceFB) {
          break;
        }
      }
      return S;
    }

    function initialize(originalMatrix, options = {}) {
      const {
        rank,
        randGenerator,
        maxInitFBIteration,
        toleranceFBInit,
        maxFBIteration,
        toleranceFB,
        normConstrained
      } = options;
      let result = {};
      let rows = originalMatrix.rows;
      result.A = Matrix.rand(rows, rank, {
        random: randGenerator
      });
      for (let iter = 0; iter < maxInitFBIteration; iter++) {
        //select columns with sum positive from A
        let sumC = result.A.sum('column');
        for (let i = 0; i < sumC.length; i++) {
          while (sumC[i] === 0) {
            sumC[i] = 0;
            for (let j = 0; j < rows; j++) {
              result.A.set(j, i, randGenerator());
              sumC[i] += result.A.get(j, i);
            }
          }
        }

        //resolve the system of equation Lx = D for x, then select just non negative values;
        result.S = zeroInsteadOfNegative(solve(result.A, originalMatrix));

        //select rows with positive sum by row
        let sumR = result.S.sum('row');
        let positiveSumRowIndexS = [];
        let positiveSumRowS = [];
        for (let i = 0; i < sumR.length; i++) {
          if (sumR[i] > 0) {
            positiveSumRowIndexS.push(i);
            positiveSumRowS.push(result.S.getRow(i));
          }
        }
        positiveSumRowS = Matrix.checkMatrix(positiveSumRowS);

        // solve the system of linear equation xL = D for x. knowing that D/L = (L'\D')'.
        let candidateA = zeroInsteadOfNegative(solve(positiveSumRowS.transpose(), originalMatrix.transpose()));

        //then, set the columns of A with an index equal to the row index with sum > 0 into S
        //this step complete the last transpose of D/L = (L'\D')'.
        for (let i = 0; i < positiveSumRowIndexS.length; i++) {
          let colCandidate = candidateA.getRow(i);
          for (let j = 0; j < rows; j++) {
            result.A.set(j, positiveSumRowIndexS[i], colCandidate[j]);
          }
        }
        let prevS = result.S.clone();
        result.S = updateMatrixS(result.A, result.S, originalMatrix, 0, {
          maxFBIteration,
          toleranceFB
        });
        result = checkMatrixS(result, originalMatrix);
        result.A = updateMatrixA(result.A, result.S, originalMatrix, 0);
        if (Matrix.sub(prevS, result.S).norm() / result.S.norm() < toleranceFBInit) {
          break;
        }
      }
      return result;
    }

    function normalize(data, options) {
      const {
        normOnA
      } = options;
      let DS = normBy(data.S.transpose(), 'column');
      let DA = normBy(data.A, 'column');
      let D = Matrix.mul(DS, DA);
      let onS, onA;
      if (normOnA) {
        onS = (index, c) => data.S.get(index, c) * D.get(0, index) / DS.get(0, index);
        onA = (index, r) => data.A.get(r, index) / DA.get(0, index);
      } else {
        onS = (index, c) => data.S.get(index, c) / DS.get(0, index);
        onA = (index, r) => data.A.get(r, index) * D.get(0, index) / DA.get(0, index);
      }
      const sColumns = data.S.columns;
      const aRows = data.A.rows;
      for (let index = 0; index < D.columns; index++) {
        let valueForS, valueForA;
        if (D.get(0, index) > 0) {
          valueForS = onS;
          valueForA = onA;
        } else {
          valueForA = () => 0;
          valueForS = () => 0;
        }
        for (let c = 0; c < sColumns; c++) {
          data.S.set(index, c, valueForS(index, c));
        }
        for (let r = 0; r < aRows; r++) {
          data.A.set(r, index, valueForA(index, r));
        }
      }
      return data;
    }

    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;

    function getMedians(X, by) {
      let medians = [];
      let rows = X.rows;
      let columns = X.columns;
      switch (by) {
        case 'column':
          for (let i = 0; i < columns; i++) {
            medians.push(quickSelectMedian(X.getColumn(i)));
          }
          medians = Matrix.from1DArray(1, columns, medians);
          break;
        default:
          for (let i = 0; i < rows; i++) {
            medians.push(quickSelectMedian(X.getRow(i)));
          }
          medians = Matrix.from1DArray(rows, 1, medians);
      }
      return medians;
    }

    function dimMADstd(X, by) {
      let medians = getMedians(X, by);
      let matrix = X.clone();
      matrix = by === 'column' ? matrix.subRowVector(medians.to1DArray()) : matrix.subColumnVector(medians.to1DArray());
      return Matrix.mul(getMedians(matrix.abs(), by), 1.4826);
    }

    function updateLambda(data, originalMatrix, options = {}) {
      let {
        refinementBeginning,
        tauMAD
      } = options;
      let {
        iteration,
        lambda,
        A,
        S
      } = data;
      if (refinementBeginning <= iteration) return lambda;
      let sigmaResidue;
      if (options.lambdaInf !== undefined) {
        sigmaResidue = options.lambdaInf / options.tauMAD;
      } else if (options.addStd !== undefined) {
        sigmaResidue = options.addStd;
      } else {
        let alY = Matrix.sub(originalMatrix, A.mmul(S)).to1DArray();
        let result = dimMADstd(Matrix.from1DArray(1, alY.length, alY), 'row');
        sigmaResidue = result.get(0, 0);
      }
      let nextLambda = Math.max(tauMAD * sigmaResidue, lambda - 1 / (refinementBeginning - iteration));
      return nextLambda;
    }

    /**
     * Performing non-negative matrix factorization solving argmin_(A >= 0, S >= 0) 1 / 2 * ||Y - AS||_2^2 + lambda * ||S||_1
     * @param {Matrix||Array<Array>} originalMatrix - Matrix to be separated.
     * @param {Number} rank - The maximum number of linearly independent column/row vectors in the matrix.
     * @param {Object} [options = {}] - Options of ngmca factorization method.
     * @param {Number} [options.maximumIteration = 500] - Maximum number of iterations.
     * @param {Number} [options.maxFBIteration = 80] - Maximum number of iterations of the Forward-Backward subroutine.
     * @param {Object} [options.randGenerator = Math.random] - Random number generator for the subroutine of initialization.
     * @param {Number} [options.maxInitFBIteration = 50] - Maximum number of iterations of the Forward-Backward subroutine at the initialization.
     * @param {Number} [options.toleranceFB = 1e-5] - relative difference tolerance for convergence of the Forward-Backward sub-iterations.
     * @param {Number} [options.toleranceFBInit = 0] - relative difference tolerance for convergence of the Forward-Backward sub-iterations at the initialization.
     * @param {Number} [options.phaseRatio = 0.8] - transition between decreasing thresholding phase and refinement phase in percent of the iterations.
     * @param {Number} [options.tauMAD = 1] - constant coefficient for the final threshold computation.
     * @param {Boolean} [options.useTranspose = false] - if true the originalMatrix is transposed.
     */

    function nGMCA(originalMatrix, rank, options = {}) {
      const {
        maximumIteration = 500,
        maxFBIteration = 80,
        maxInitFBIteration = 50,
        toleranceFBInit = 0,
        toleranceFB = 0.00001,
        phaseRatio = 0.8,
        randGenerator = Math.random,
        tauMAD = 1,
        useTranspose = false
      } = options;
      let {
        normConstrained = false
      } = options;
      originalMatrix = Matrix.checkMatrix(originalMatrix);
      if (useTranspose) originalMatrix = originalMatrix.transpose();
      let refinementBeginning = Math.floor(phaseRatio * maximumIteration);
      let data = initialize(originalMatrix, {
        rank,
        randGenerator,
        maxInitFBIteration,
        toleranceFBInit,
        maxFBIteration,
        toleranceFB
      });
      data = normalize(data, {
        normOnA: true
      });
      data.lambda = data.A.transpose().mmul(data.A.mmul(data.S).sub(originalMatrix)).abs().max();
      for (let iter = 0; iter < maximumIteration; iter++) {
        data.iteration = iter;
        data.S = updateMatrixS(data.A, data.S, originalMatrix, data.lambda, options);
        data = checkMatrixS(data, originalMatrix);
        data = normalize(data, {
          normOnA: false
        });
        if (iter > refinementBeginning) normConstrained = true;
        data.A = updateMatrixA(data.A, data.S, originalMatrix, {
          maxFBIteration,
          toleranceFB,
          normConstrained,
          lambda: 0
        });
        data = normalize(data, {
          normOnA: true
        });
        data.lambda = updateLambda(data, originalMatrix, {
          refinementBeginning,
          tauMAD
        });
      }
      if (useTranspose) {
        let temp = data.A.transpose();
        data.A = data.S.transpose();
        data.S = temp;
      }
      return data;
    }

    /**
     * Creates new PCA (Principal Component Analysis) from the dataset
     * @param {MaybeMatrix} dataset - dataset or covariance matrix.
     * @param {PCAOptions} [options]
     * @param {boolean} [options.isCovarianceMatrix=false] - true if the dataset is a covariance matrix.
     * @param {string} [options.method='SVD'] - select which method to use: SVD (default), covarianceMatrirx or NIPALS.
     * @param {number} [options.nCompNIPALS=2] - number of components to be computed with NIPALS.
     * @param {boolean} [options.center=true] - should the data be centered (subtract the mean).
     * @param {boolean} [options.scale=false] - should the data be scaled (divide by the standard deviation).
     * @param {boolean} [options.ignoreZeroVariance=false] - ignore columns with zero variance if `scale` is `true`.
     * */
    class PCA {
      constructor(dataset, options = {}, model) {
        /* eslint-disable @typescript-eslint/naming-convention */
        this.U = null;
        this.S = null;
        if (model) {
          this.center = model.center;
          this.scale = model.scale;
          this.means = model.means;
          this.stdevs = model.stdevs;
          this.U = Matrix.checkMatrix(model.U);
          this.S = model.S;
          this.R = model.R;
          this.excludedFeatures = model.excludedFeatures || [];
          return;
        }
        let datasetMatrix;
        if (Array.isArray(dataset)) {
          datasetMatrix = new Matrix(dataset);
        } else {
          datasetMatrix = new Matrix(dataset);
        }
        const {
          isCovarianceMatrix = false,
          method = 'SVD',
          nCompNIPALS = 2,
          center = true,
          scale = false,
          ignoreZeroVariance = false
        } = options;
        this.center = center;
        this.scale = scale;
        this.means = null;
        this.stdevs = null;
        this.excludedFeatures = [];
        if (isCovarianceMatrix) {
          // User provided a covariance matrix instead of dataset.
          this._computeFromCovarianceMatrix(datasetMatrix);
          return;
        }
        this._adjust(datasetMatrix, ignoreZeroVariance);
        switch (method) {
          case 'covarianceMatrix':
            {
              // User provided a dataset but wants us to compute and use the covariance matrix.
              const covarianceMatrix = new MatrixTransposeView(datasetMatrix).mmul(datasetMatrix).div(datasetMatrix.rows - 1);
              this._computeFromCovarianceMatrix(covarianceMatrix);
              break;
            }
          case 'NIPALS':
            {
              this._computeWithNIPALS(datasetMatrix, nCompNIPALS);
              break;
            }
          case 'SVD':
            {
              const svd = new SVD(datasetMatrix, {
                computeLeftSingularVectors: false,
                computeRightSingularVectors: true,
                autoTranspose: true
              });
              this.U = svd.rightSingularVectors;
              const singularValues = svd.diagonal;
              const eigenvalues = [];
              for (const singularValue of singularValues) {
                eigenvalues.push(singularValue * singularValue / (datasetMatrix.rows - 1));
              }
              this.S = eigenvalues;
              break;
            }
          default:
            {
              throw new Error(`unknown method: ${method}`);
            }
        }
      }
      /**
       * Load a PCA model from JSON
       * @param {PCAModel} model
       * @return {PCA}
       */
      static load(model) {
        if (typeof model.name !== 'string') {
          throw new TypeError('model must have a name property');
        }
        if (model.name !== 'PCA') {
          throw new RangeError(`invalid model: ${model.name}`);
        }
        return new PCA(undefined, undefined, model);
      }
      /**
       * Project the dataset into the PCA space
       * @param {MaybeMatrix} dataset
       * @param {PredictOptions} options
       * @return {Matrix} dataset projected in the PCA space
       */
      predict(dataset, options = {}) {
        const {
          nComponents = this.U.columns
        } = options;
        let datasetmatrix;
        if (Array.isArray(dataset)) {
          datasetmatrix = new Matrix(dataset);
        } else {
          datasetmatrix = new Matrix(dataset);
        }
        if (this.center) {
          datasetmatrix.subRowVector(this.means);
          if (this.scale) {
            for (let i of this.excludedFeatures) {
              datasetmatrix.removeColumn(i);
            }
            datasetmatrix.divRowVector(this.stdevs);
          }
        }
        let predictions = datasetmatrix.mmul(this.U);
        return predictions.subMatrix(0, predictions.rows - 1, 0, nComponents - 1);
      }
      /**
       * Calculates the inverse PCA transform
       * @param {Matrix} dataset
       * @return {Matrix} dataset projected in the PCA space
       */
      invert(dataset) {
        dataset = Matrix.checkMatrix(dataset);
        let inverse = dataset.mmul(this.U.transpose());
        if (this.center) {
          if (this.scale) {
            inverse.mulRowVector(this.stdevs);
          }
          inverse.addRowVector(this.means);
        }
        return inverse;
      }
      /**
       * Returns the proportion of variance for each component
       * @return {[number]}
       */
      getExplainedVariance() {
        let sum = 0;
        if (this.S) {
          for (const s of this.S) {
            sum += s;
          }
        }
        if (this.S) {
          return this.S.map(value => value / sum);
        }
        return [];
      }
      /**
       * Returns the cumulative proportion of variance
       * @return {[number]}
       */
      getCumulativeVariance() {
        let explained = this.getExplainedVariance();
        for (let i = 1; i < explained.length; i++) {
          explained[i] += explained[i - 1];
        }
        return explained;
      }
      /**
       * Returns the Eigenvectors of the covariance matrix
       * @returns {Matrix}
       */
      getEigenvectors() {
        return this.U;
      }
      /**
       * Returns the Eigenvalues (on the diagonal)
       * @returns {[number]}
       */
      getEigenvalues() {
        return this.S;
      }
      /**
       * Returns the standard deviations of the principal components
       * @returns {[number]}
       */
      getStandardDeviations() {
        return this.S.map(x => Math.sqrt(x));
      }
      /**
       * Returns the loadings matrix
       * @return {Matrix}
       */
      getLoadings() {
        return this.U.transpose();
      }
      /**
       * Export the current model to a JSON object
       * @return {Object} model
       */
      toJSON() {
        return {
          name: 'PCA',
          center: this.center,
          scale: this.scale,
          means: this.means,
          stdevs: this.stdevs,
          U: this.U,
          S: this.S,
          excludedFeatures: this.excludedFeatures
        };
      }
      _adjust(dataset, ignoreZeroVariance) {
        if (this.center) {
          const mean = dataset.mean('column');
          const stdevs = this.scale ? dataset.standardDeviation('column', {
            mean
          }) : null;
          this.means = mean;
          dataset.subRowVector(mean);
          if (this.scale) {
            for (let i = 0; i < stdevs.length; i++) {
              if (stdevs[i] === 0) {
                if (ignoreZeroVariance) {
                  dataset.removeColumn(i);
                  stdevs.splice(i, 1);
                  this.excludedFeatures.push(i);
                  i--;
                } else {
                  throw new RangeError(`Cannot scale the dataset (standard deviation is zero at index ${i}`);
                }
              }
            }
            this.stdevs = stdevs;
            dataset.divRowVector(stdevs);
          }
        }
      }
      _computeFromCovarianceMatrix(dataset) {
        const evd = new EVD(dataset, {
          assumeSymmetric: true
        });
        this.U = evd.eigenvectorMatrix;
        this.U.flipRows();
        this.S = evd.realEigenvalues;
        this.S.reverse();
      }
      _computeWithNIPALS(dataset, nCompNIPALS) {
        this.U = new Matrix(nCompNIPALS, dataset.columns);
        this.S = [];
        let x = dataset;
        for (let i = 0; i < nCompNIPALS; i++) {
          let dc = new NIPALS(x);
          this.U.setRow(i, dc.w.transpose());
          this.S.push(Math.pow(dc.s.get(0, 0), 2));
          x = dc.xResidual;
        }
        this.U = this.U.transpose(); // to be compatible with API
      }
    }

    /**
     * Estimate the number of pure components of each range by NIPALS PCA
     * @param {Chromatogram} chromatogram - GC/MS chromatogram where make the estimation
     * @param {object} options - options with range and the matrix
     * @param {object} [options.range] - Range of retention times.
     * @param {number} [options.range.from] - lower limit in the retention time.
     * @param {number} [options.range.to] - upper limit in the retention time.
     * @param {Array<Array>} [options.matrix] - matrix to compute the number of pure components, if does not exist it will be computed.
     */

    function estimateNbPureComponents(chromatogram, options = {}) {
      let {
        range,
        matrix
      } = options;
      if (!matrix) matrix = chromatogram.getMzVsTimesMatrix(range).matrix;
      let pca = new PCA(matrix, {
        method: 'NIPALS',
        nCompNIPALS: 10,
        scale: true,
        ignoreZeroVariance: true
      });
      let s = pca.getExplainedVariance();
      let rank = 1;
      let cumulative = s[0];
      while ((cumulative - s[rank]) / cumulative < 0.88 && rank < s.length) {
        cumulative += s[rank];
        rank++;
      }
      return rank;
    }

    /**
     * Performing non-negative matrix factorization solving argmin_(A >= 0, S >= 0) 1 / 2 * ||Y - AS||_2^2 + lambda * ||S||_1
     * @param {Chromatogram} chromatogram - GC/MS chromatogram where make the estimation.
     * @param {object} [options = {}] - Options of ngmca factorization method
     * @param {number} [options.rank] - number of pure components, if it's undefined it will be estimated by explained variance of PCA.
     * @param {object} [options.range] - Range with from to.
     * @param {number} [options.range.from] - lower limit in the retention time.
     * @param {number} [options.range.to] - upper limit in the retention time.
     * @param {number} [options.nmfOptions = {}] - options to Non negative factorization (deconvolution method).
     * @param {number} [options.nmfOptions.maximumIteration = 500] - Maximum number of iterations.
     * @param {number} [options.nmfOptions.maxFBIteration = 80] - Maximum number of iterations of the Forward-Backward subroutine.
     * @param {number} [options.nmfOptions.toleranceFB = 1e-5] - relative difference tolerance for convergence of the Forward-Backward sub-iterations.
     * @param {number} [options.nmfOptions.phaseRatio = 0.8] - transition between decreasing thresholding phase and refinement phase in percent of the iterations.
     * @param {boolean} [options.useTranspose = false] - if true the originalMatrix is transposed.
     */

    function deconvolution(chromatogram, options = {}) {
      let {
        range,
        rank,
        nmfOptions = {}
      } = options;
      let {
        matrix,
        mzAxis,
        times
      } = chromatogram.getMzVsTimesMatrix(range);
      if (!rank) rank = estimateNbPureComponents(chromatogram, {
        range,
        matrix
      });
      if (rank < 1) {
        throw new RangeError(`Rank should be a positive number for ${range.from} - ${range.to}`);
      }
      let result = nGMCA(matrix, rank, nmfOptions);
      let maxByRow = [];
      for (let i = 0; i < result.S.rows; i++) {
        maxByRow.push(result.S.maxRow(i));
      }
      result.S.scale('row', {
        scale: maxByRow
      });
      result.A.scale('column', {
        scale: maxByRow.map(e => 1 / e)
      });
      return {
        matrix,
        times,
        mzAxis,
        rank,
        profile: result.A,
        component: result.S
      };
    }

    function merge$1(chromatogram, options = {}) {
      let {
        mergeThreshold = 0.3,
        seriesName = 'ms',
        range = {}
      } = options;
      const time = chromatogram.getTimes();
      chromatogram.requiresSeries(seriesName);
      let series = chromatogram.series[seriesName];
      if (series.dimension !== 2) {
        throw new Error(`The series "${seriesName}" is not of dimension 2`);
      }
      if (!range || range.from > time.at(-1) || range.to < time[0]) {
        return {
          x: [],
          y: []
        };
      }
      let {
        fromIndex,
        toIndex
      } = xGetFromToIndex(time, range);
      let data = series.data.slice(fromIndex, toIndex + 1).map(datum => xySortX({
        x: datum[0],
        y: datum[1]
      }));
      return {
        ...xyArrayWeightedMerge(data, {
          delta: mergeThreshold
        }),
        from: {
          index: fromIndex,
          time: time[fromIndex]
        },
        to: {
          index: toIndex,
          time: time[toIndex]
        }
      };
    }

    // Based on https://github.com/scijs/cholesky-solve

    /*
    The MIT License (MIT)

    Copyright (c) 2013 Eric Arnebäck

    Permission is hereby granted, free of charge, to any person obtaining a copy
    of this software and associated documentation files (the "Software"), to deal
    in the Software without restriction, including without limitation the rights
    to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
    copies of the Software, and to permit persons to whom the Software is
    furnished to do so, subject to the following conditions:

    The above copyright notice and this permission notice shall be included in
    all copies or substantial portions of the Software.

    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
    THE SOFTWARE.
    */

    function ldlSymbolic(n /* A and L are n-by-n, where n >= 0 */, Ap /* input of size n + 1, not modified */, Ai /* input of size nz=Ap[n], not modified */, Lp /* output of size n + 1, not defined on input */, Parent /* output of size n, not defined on input */, Lnz /* output of size n, not defined on input */, Flag /* workspace of size n, not defn. on input or output */) {
      let i, k, p, kk, p2;
      for (k = 0; k < n; k++) {
        /* L(k,:) pattern: all nodes reachable in etree from nz in A(0:k-1,k) */
        Parent[k] = -1; /* parent of k is not yet known */
        Flag[k] = k; /* mark node k as visited */
        Lnz[k] = 0; /* count of nonzeros in column k of L */
        kk = k; /* kth original, or permuted, column */
        p2 = Ap[kk + 1];
        for (p = Ap[kk]; p < p2; p++) {
          /* A (i,k) is nonzero (original or permuted A) */
          i = Ai[p];
          if (i < k) {
            /* follow path from i to root of etree, stop at flagged node */
            for (; Flag[i] !== k; i = Parent[i]) {
              /* find parent of i if not yet determined */
              if (Parent[i] === -1) Parent[i] = k;
              Lnz[i]++; /* L (k,i) is nonzero */
              Flag[i] = k; /* mark i as visited */
            }
          }
        }
      }
      /* construct Lp index array from Lnz column counts */
      Lp[0] = 0;
      for (k = 0; k < n; k++) {
        Lp[k + 1] = Lp[k] + Lnz[k];
      }
    }
    function ldlNumeric(n /* A and L are n-by-n, where n >= 0 */, Ap /* input of size n+1, not modified */, Ai /* input of size nz=Ap[n], not modified */, Ax /* input of size nz=Ap[n], not modified */, Lp /* input of size n+1, not modified */, Parent /* input of size n, not modified */, Lnz /* output of size n, not defn. on input */, Li /* output of size lnz=Lp[n], not defined on input */, Lx /* output of size lnz=Lp[n], not defined on input */, D /* output of size n, not defined on input */, Y /* workspace of size n, not defn. on input or output */, Pattern /* workspace of size n, not defn. on input or output */, Flag /* workspace of size n, not defn. on input or output */) {
      let yi, lKi;
      let i, k, p, kk, p2, len, top;
      for (k = 0; k < n; k++) {
        /* compute nonzero Pattern of kth row of L, in topological order */
        Y[k] = 0.0; /* Y(0:k) is now all zero */
        top = n; /* stack for pattern is empty */
        Flag[k] = k; /* mark node k as visited */
        Lnz[k] = 0; /* count of nonzeros in column k of L */
        kk = k; /* kth original, or permuted, column */
        p2 = Ap[kk + 1];
        for (p = Ap[kk]; p < p2; p++) {
          i = Ai[p]; /* get A(i,k) */
          if (i <= k) {
            Y[i] += Ax[p]; /* scatter A(i,k) into Y (sum duplicates) */
            for (len = 0; Flag[i] !== k; i = Parent[i]) {
              Pattern[len++] = i; /* L(k,i) is nonzero */
              Flag[i] = k; /* mark i as visited */
            }
            while (len > 0) Pattern[--top] = Pattern[--len];
          }
        }
        /* compute numerical values kth row of L (a sparse triangular solve) */
        D[k] = Y[k]; /* get D(k,k) and clear Y(k) */
        Y[k] = 0.0;
        for (; top < n; top++) {
          i = Pattern[top]; /* Pattern[top:n-1] is pattern of L(:,k) */
          yi = Y[i]; /* get and clear Y(i) */
          Y[i] = 0.0;
          p2 = Lp[i] + Lnz[i];
          for (p = Lp[i]; p < p2; p++) {
            Y[Li[p]] -= Lx[p] * yi;
          }
          lKi = yi / D[i]; /* the nonzero entry L(k,i) */
          D[k] -= lKi * yi;
          Li[p] = k; /* store L(k,i) in column form of L */
          Lx[p] = lKi;
          Lnz[i]++; /* increment count of nonzeros in col i */
        }
        if (D[k] === 0.0) return k; /* failure, D(k,k) is zero */
      }
      return n; /* success, diagonal of D is all nonzero */
    }
    function ldlLsolve(n /* L is n-by-n, where n >= 0 */, X /* size n. right-hand-side on input, soln. on output */, Lp /* input of size n+1, not modified */, Li /* input of size lnz=Lp[n], not modified */, Lx /* input of size lnz=Lp[n], not modified */) {
      let j, p, p2;
      for (j = 0; j < n; j++) {
        p2 = Lp[j + 1];
        for (p = Lp[j]; p < p2; p++) {
          X[Li[p]] -= Lx[p] * X[j];
        }
      }
    }
    function ldlDsolve(n /* D is n-by-n, where n >= 0 */, X /* size n. right-hand-side on input, soln. on output */, D /* input of size n, not modified */) {
      let j;
      for (j = 0; j < n; j++) {
        X[j] /= D[j];
      }
    }
    function ldlLTsolve(n /* L is n-by-n, where n >= 0 */, X /* size n. right-hand-side on input, soln. on output */, Lp /* input of size n+1, not modified */, Li /* input of size lnz=Lp[n], not modified */, Lx /* input of size lnz=Lp[n], not modified */) {
      let j, p, p2;
      for (j = n - 1; j >= 0; j--) {
        p2 = Lp[j + 1];
        for (p = Lp[j]; p < p2; p++) {
          X[j] -= Lx[p] * X[Li[p]];
        }
      }
    }
    function ldlPerm(n /* size of X, B, and P */, X /* output of size n. */, B /* input of size n. */, P /* input permutation array of size n. */) {
      let j;
      for (j = 0; j < n; j++) {
        X[j] = B[P[j]];
      }
    }
    function ldlPermt(n /* size of X, B, and P */, X /* output of size n. */, B /* input of size n. */, P /* input permutation array of size n. */) {
      let j;
      for (j = 0; j < n; j++) {
        X[P[j]] = B[j];
      }
    }
    function prepare(M, n, P) {
      // if a permutation was specified, apply it.
      if (P) {
        let Pinv = new Array(n);
        for (let k = 0; k < n; k++) {
          Pinv[P[k]] = k;
        }
        let Mt = []; // scratch memory
        // Apply permutation. We make M into P*M*P^T
        for (let a = 0; a < M.length; ++a) {
          let ar = Pinv[M[a][0]];
          let ac = Pinv[M[a][1]];

          // we only store the upper-diagonal elements(since we assume matrix is symmetric, we only need to store these)
          // if permuted element is below diagonal, we simply transpose it.
          if (ac < ar) {
            let t = ac;
            ac = ar;
            ar = t;
          }
          Mt[a] = [];
          Mt[a][0] = ar;
          Mt[a][1] = ac;
          Mt[a][2] = M[a][2];
        }
        M = Mt; // copy scratch memory.
      } else {
        // if P argument is null, we just use an identity permutation.
        P = [];
        for (let i = 0; i < n; ++i) {
          P[i] = i;
        }
      }

      // The sparse matrix we are decomposing is A.
      // Now we shall create A from M.
      let Ap = new Array(n + 1);
      let Ai = new Array(M.length);
      let Ax = new Array(M.length);

      // count number of non-zero elements in columns.
      let LNZ = [];
      for (let i = 0; i < n; ++i) {
        LNZ[i] = 0;
      }
      for (let a = 0; a < M.length; ++a) {
        LNZ[M[a][1]]++;
      }
      Ap[0] = 0;
      for (let i = 0; i < n; ++i) {
        Ap[i + 1] = Ap[i] + LNZ[i];
      }
      let coloffset = [];
      for (let a = 0; a < n; ++a) {
        coloffset[a] = 0;
      }

      // go through all elements in M, and add them to sparse matrix A.
      for (let i = 0; i < M.length; ++i) {
        let e = M[i];
        let col = e[1];
        let adr = Ap[col] + coloffset[col];
        Ai[adr] = e[0];
        Ax[adr] = e[2];
        coloffset[col]++;
      }
      let D = new Array(n);
      let Y = new Array(n);
      let Lp = new Array(n + 1);
      let Parent = new Array(n);
      let Lnz = new Array(n);
      let Flag = new Array(n);
      let Pattern = new Array(n);
      let bp1 = new Array(n);
      let x = new Array(n);
      let d;
      ldlSymbolic(n, Ap, Ai, Lp, Parent, Lnz, Flag);
      let Lx = new Array(Lp[n]);
      let Li = new Array(Lp[n]);
      d = ldlNumeric(n, Ap, Ai, Ax, Lp, Parent, Lnz, Li, Lx, D, Y, Pattern, Flag);
      if (d === n) {
        return b => {
          ldlPerm(n, bp1, b, P);
          ldlLsolve(n, bp1, Lp, Li, Lx);
          ldlDsolve(n, bp1, D);
          ldlLTsolve(n, bp1, Lp, Li, Lx);
          ldlPermt(n, x, bp1, P);
          return x;
        };
      } else {
        return null;
      }
    }

    var cuthillMckee_1 = cuthillMckee;
    function compareNum(a, b) {
      return a - b;
    }
    function cuthillMckee(list, n) {
      var adj = new Array(n);
      var visited = new Array(n);
      for (var i = 0; i < n; ++i) {
        adj[i] = [];
        visited[i] = false;
      }
      for (var i = 0; i < list.length; ++i) {
        var l = list[i];
        adj[l[0]].push(l[1]);
      }
      var toVisit = new Array(n);
      var eol = 0;
      var ptr = 0;
      for (var i = 0; i < n; ++i) {
        if (visited[i]) {
          continue;
        }
        toVisit[eol++] = i;
        visited[i] = true;
        while (ptr < eol) {
          var v = toVisit[ptr++];
          var nbhd = adj[v];
          nbhd.sort(compareNum);
          for (var j = 0; j < nbhd.length; ++j) {
            var u = nbhd[j];
            if (visited[u]) {
              continue;
            }
            visited[u] = true;
            toVisit[eol++] = u;
          }
        }
      }
      var result = new Array(n);
      for (var i = 0; i < n; ++i) {
        result[toVisit[i]] = i;
      }
      return result;
    }
    var cuthillMckee$1 = cuthillMckee_1;

    const getClosestNumber = (array = [], goal = 0) => {
      const closest = array.reduce((prev, curr) => {
        return Math.abs(curr - goal) < Math.abs(prev - goal) ? curr : prev;
      });
      return closest;
    };
    const getCloseIndex = (array = [], goal = 0) => {
      const closest = getClosestNumber(array, goal);
      return array.indexOf(closest);
    };
    const updateSystem = (matrix, y, weights) => {
      let nbPoints = y.length;
      let l = nbPoints - 1;
      let newMatrix = new Array(matrix.length);
      let newVector = new Float64Array(nbPoints);
      for (let i = 0; i < l; i++) {
        let w = weights[i];
        let diag = i * 2;
        let next = diag + 1;
        newMatrix[diag] = matrix[diag].slice();
        newMatrix[next] = matrix[next].slice();
        if (w === 0) {
          newVector[i] = 0;
        } else {
          newVector[i] = y[i] * w;
          newMatrix[diag][2] += w;
        }
      }
      newVector[l] = y[l] * weights[l];
      newMatrix[l * 2] = matrix[l * 2].slice();
      newMatrix[l * 2][2] += weights[l];
      return [newMatrix, newVector];
    };
    const getDeltaMatrix = (nbPoints, lambda) => {
      let matrix = [];
      let last = nbPoints - 1;
      for (let i = 0; i < last; i++) {
        matrix.push([i, i, lambda * 2]);
        matrix.push([i + 1, i, -1 * lambda]);
      }
      matrix[0][2] = lambda;
      matrix.push([last, last, lambda]);
      return {
        lowerTriangularNonZeros: matrix,
        permutationEncodedArray: cuthillMckee$1(matrix, nbPoints)
      };
    };

    function getControlPoints(x, y, options = {}) {
      const {
        length
      } = x;
      let {
        controlPoints = Int8Array.from({
          length
        }).fill(0)
      } = options;
      const {
        zones = [],
        weights = Float64Array.from({
          length
        }).fill(1)
      } = options;
      if (x.length !== y.length) {
        throw new RangeError('Y should match the length with X');
      } else if (controlPoints.length !== x.length) {
        throw new RangeError('controlPoints should match the length with X');
      } else if (weights.length !== x.length) {
        throw new RangeError('weights should match the length with X');
      }
      zones.forEach(range => {
        let indexFrom = getCloseIndex(x, range.from);
        let indexTo = getCloseIndex(x, range.to);
        if (indexFrom > indexTo) [indexFrom, indexTo] = [indexTo, indexFrom];
        for (let i = indexFrom; i < indexTo; i++) {
          controlPoints[i] = 1;
        }
      });
      return {
        weights: 'controlPoints' in options || zones.length > 0 ? xMultiply(weights, controlPoints) : weights,
        controlPoints
      };
    }

    /**
     * Fit the baseline drift by iteratively changing weights of sum square error between the fitted baseline and original signals,
     * for further information about the parameters you can get the [paper of airPLS](https://github.com/zmzhang/airPLS/blob/main/airPLS_manuscript.pdf)
     * @param {Array<number>} x - x axis data useful when control points or zones are submitted
     * @param {Array<number>} y - Original data
     * @param {object} [options={}] - Options object
     * @param {number} [options.maxIterations = 100] - Maximal number of iterations if the method does not reach the stop criterion
     * @param {number} [options.tolerance = 0.001] - Factor of the sum of absolute value of original data, to compute stop criterion
     * @param {Array<number>} [options.weights = [1,1,...]] - Initial weights vector, default each point has the same weight
     * @param {number} [options.lambda = 100] - Factor of weights matrix in -> [I + lambda D'D]z = x
     * @param {Array<number>} [options.controlPoints = []] - Array of 0|1 to force the baseline cross those points.
     * @param {Array<number>} [options.zones = []] - Array of x axis values (as from - to), to force that baseline cross those zones.
     * @returns {{corrected: Array<number>, error: number, iteration: number, baseline: Array<number>}}
     */

    function airPLS(x, y, options = {}) {
      const {
        weights,
        controlPoints
      } = getControlPoints(x, y, options);
      let {
        maxIterations = 100,
        lambda = 10,
        tolerance = 0.001
      } = options;
      let baseline, iteration;
      let sumNegDifferences = Number.MAX_SAFE_INTEGER;
      const corrected = Float64Array.from(y);
      let stopCriterion = getStopCriterion(y, tolerance);
      const {
        length
      } = y;
      let {
        lowerTriangularNonZeros,
        permutationEncodedArray
      } = getDeltaMatrix(length, lambda);
      let threshold = 1;
      const l = length - 1;
      let prevNegSum = Number.MAX_SAFE_INTEGER;
      for (iteration = 0; iteration < maxIterations && Math.abs(sumNegDifferences) > stopCriterion; iteration++) {
        let [leftHandSide, rightHandSide] = updateSystem(lowerTriangularNonZeros, y, weights);
        let cho = prepare(leftHandSide, length, permutationEncodedArray);
        baseline = cho(rightHandSide);
        sumNegDifferences = applyCorrection(y, baseline, corrected);
        if (iteration === 1) {
          const {
            positive
          } = xNoiseSanPlot(corrected);
          threshold = positive;
        } else {
          const absChange = Math.abs(prevNegSum / sumNegDifferences);
          if (absChange < 1.01 && absChange > 0.99) {
            break;
          }
        }
        prevNegSum = sumNegDifferences + 0;
        for (let i = 1; i < l; i++) {
          const diff = corrected[i];
          if (controlPoints[i] < 1 && Math.abs(diff) > threshold) {
            weights[i] = 0;
          } else {
            const factor = diff > 0 ? -1 : 1;
            weights[i] = Math.exp(factor * (iteration * diff) / Math.abs(sumNegDifferences));
          }
        }
        weights[0] = 1;
        weights[l] = 1;
      }
      return {
        corrected,
        baseline,
        iteration,
        error: sumNegDifferences
      };
      function applyCorrection(y, baseline, corrected) {
        let sumNegDifferences = 0;
        for (let i = 0; i < y.length; i++) {
          let diff = y[i] - baseline[i];
          if (diff < 0) sumNegDifferences += diff;
          corrected[i] = diff;
        }
        return sumNegDifferences;
      }
    }
    function getStopCriterion(y, tolerance) {
      let sum = xAbsoluteSum(y);
      return tolerance * sum;
    }

    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, options = {}) {
      const {
        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');
      }
      const half = Math.floor(windowSize / 2);
      const np = ys.length;
      const ans = new Float64Array(np);
      const weights = fullWeights(windowSize, polynomial, derivative);
      let hs = 0;
      let constantH = true;
      if (isAnyArray$1(xs)) {
        constantH = false;
      } else {
        hs = xs ** derivative;
      }
      //For the borders
      for (let i = 0; i < half; i++) {
        const wg1 = weights[half - i - 1];
        const 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
      const 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 (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) {
      const weights = new Array(m);
      const 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])) {
          const alpha = 20 * Math.log10(y[currentIndex - 1]);
          const beta = 20 * Math.log10(y[currentIndex]);
          const gamma = 20 * Math.log10(y[currentIndex + 1]);
          const 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, options = {}) {
      let {
        noiseLevel
      } = options;
      const {
        sgOptions = {
          windowSize: 9,
          polynomial: 3
        },
        smoothY = false,
        maxCriteria = true,
        minMaxRatio = 0.00025,
        realTopDetection = false
      } = options;
      const {
        x
      } = data;
      let {
        y
      } = data;
      if (xIsMonotonic(x) !== 1) {
        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
      const 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) {
        noiseLevel *= -1;
      }
      if (!maxCriteria) {
        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;
          }
        }
      }
      const xValue = equallySpaced ? x[1] - x[0] : x;
      const yData = smoothY ? sgg(y, xValue, {
        ...sgOptions,
        derivative: 0
      }) : y;
      const dY = sgg(y, xValue, {
        ...sgOptions,
        derivative: 1
      });
      const ddY = sgg(y, xValue, {
        ...sgOptions,
        derivative: 2
      });
      const {
        min: minY,
        max: maxY
      } = xMinMaxValues(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;
      const minddY = [];
      const intervalL = [];
      const 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) {
        const 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) {
            const 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;
    }

    /**
     * This method will allow to enlarge peaks while preventing overlap between peaks
     * A typical application in chromatography peak picking.
     * We should not make the hypothesis that x is equidistant
     * Because peaks may not be symmetric after we add 2 properties, from and to.
     * @return {Array} peakList
     */
    function broadenPeaks(peakList, options = {}) {
      const {
        factor = 2,
        overlap = false
      } = options;
      const peaks = mapPeaks(peakList, factor);
      if (!overlap) {
        for (let i = 0; i < peaks.length - 1; i++) {
          const peak = peaks[i];
          const nextPeak = peaks[i + 1];
          if (peak.to.x > nextPeak.from.x) {
            // we do it proportional to the width of the peaks
            peak.to.x = peak.width / (nextPeak.width + peak.width) * (nextPeak.x - peak.x) + peak.x;
            nextPeak.from.x = peak.to.x;
          }
        }
      }
      for (const peak of peaks) {
        peak.width = peak.to.x - peak.from.x;
        if (peak.shape) {
          const {
            shape,
            width
          } = peak;
          if (shape.fwhm !== undefined) {
            const shapeFct = getShape1D(shape);
            peak.shape.fwhm = shapeFct.widthToFWHM(width);
          }
        }
      }
      return peaks;
    }
    function mapPeaks(peaks, factor) {
      return peaks.map(peak => {
        const {
          id,
          shape
        } = peak;
        const xFrom = peak.x - (peak.x - peak.inflectionPoints.from.x) * factor;
        const xTo = peak.x + (peak.inflectionPoints.to.x - peak.x) * factor;
        let result = {
          x: peak.x,
          y: peak.y,
          index: peak.index,
          width: xTo - xFrom,
          from: {
            x: xFrom
          },
          to: {
            x: xTo
          }
        };
        if (id) {
          result = {
            ...result,
            id
          };
        }
        if (shape) {
          result = {
            ...result,
            shape
          };
        }
        return result;
      });
    }

    /**
     * Returns the result of a peak picking in the chromatogram
     *
     * Make a peak picking on a chromatogram is not obvious because the baseline is often not close to 0
     * and it is therefore difficult to filter by height.
     * We will therefore consider as height of a peak 2 times the height between the top and the middle
     * of the inflection points.
     *
     * In order to calculate the noise the same problem occurs, baseline is not horizontal.
     * We therefore calculate the median absolute deviation after baseline correction using airpls.
     * This noise will be used to filter the peak based on the 'heightFilter'.
     * @param {*} chromatogram
     * @param {object} [options={}]
     * @param {number} [options.heightFilter=5] - Peak height should be this factor times the noise (Median Absolute Deviation)
     * @param {string} [options.seriesName='tic']
     * @param {object} [options.broadenPeaksOptions='tic']
     * @returns
     */

    function getPeaks(chromatogram, options = {}) {
      const {
        heightFilter = 5,
        seriesName = 'tic',
        broadenPeaksOptions = {
          factor: 1,
          overlap: false
        }
      } = options;
      const series = chromatogram.getSeries(seriesName).data;
      const times = chromatogram.getTimes();
      const dataXY = {
        x: times,
        y: series
      };
      // first peak selection
      let peakList = gsd(dataXY, {
        noiseLevel: 0,
        realTopDetection: false,
        smoothY: true,
        sgOptions: {
          windowSize: 5,
          polynomial: 2
        }
      });
      const noiseHeight = xMedianAbsoluteDeviation(airPLS(dataXY.x, dataXY.y).corrected).mad * heightFilter;
      peakList = peakList.filter(peak => (peak.y - (dataXY.y[peak.inflectionPoints.from.index] + dataXY.y[peak.inflectionPoints.to.index]) / 2) * 2 > noiseHeight);
      peakList.sort((a, b) => a.x - b.x);
      const broadenPeaksList = broadenPeaks(peakList, broadenPeaksOptions);
      return broadenPeaksList.map(peak => ({
        from: peak.from.x,
        to: peak.to.x,
        width: peak.width,
        retentionTime: peak.x,
        intensity: peak.y
      }));
    }

    function filter(chromatogram, callback, options = {}) {
      const {
        copy = false
      } = options;
      if (copy) {
        chromatogram = chromatogram.copy();
      }
      let times = chromatogram.getTimes();
      let newTimes = [];
      let indexToKeep = [];
      for (let i = 0; i < times.length; i++) {
        if (callback(i, times[i])) {
          indexToKeep.push(i);
          newTimes.push(times[i]);
        }
      }
      chromatogram.times = newTimes;
      for (let key of chromatogram.getSeriesNames()) {
        const series = chromatogram.getSeries(key);
        series.keep(indexToKeep);
      }
      return chromatogram;
    }

    function getClosestData(chromatogram, time, options = {}) {
      const {
        seriesName = 'ms'
      } = options;
      chromatogram.requiresSeries(seriesName);
      let closest = chromatogram.getClosestTime(time);
      return {
        rt: chromatogram.getTimes()[closest],
        index: closest,
        data: chromatogram.getSeries(seriesName).data[closest]
      };
    }

    /**
     * Return the submatrix, times, and mass x axis for each range
     * @param chromatogram
     * @param {object} range - from - to of the TIC
     * @param {number} [range.from] - lower limit in the retention time
     * @param {number} [range.to] - upper limit in the retention time
     * @returns {object} - submatrix, times and m/z axis of the range.
     */
    function getMzVsTimesMatrix(chromatogram, range) {
      let {
        from,
        to
      } = range;
      let fromIndex = chromatogram.getClosestTime(from);
      let toIndex = chromatogram.getClosestTime(to);
      let data = chromatogram.series.ms.data.slice(fromIndex, toIndex);
      let times = chromatogram.times.slice(fromIndex, toIndex);
      let mzAxis = new Set();
      for (let i = 0; i < data.length; i++) {
        let spectrum = data[i];
        for (let j = 0; j < spectrum[0].length; j++) {
          mzAxis.add(Math.round(spectrum[0][j]));
        }
      }
      mzAxis = Array.from(mzAxis).sort((a, b) => a - b);
      const nbPoints = mzAxis.length;
      const matrix = new Matrix(data.length, nbPoints);
      for (let i = 0; i < data.length; i++) {
        let element = data[i];
        for (let j = 0; j < element[0].length; j++) {
          let xValue = Math.round(element[0][j]);
          let index = xFindClosestIndex(mzAxis, xValue);
          matrix.set(i, index, element[1][j]);
        }
      }
      return {
        times,
        mzAxis,
        matrix
      };
    }

    function baselineCorrection(points, fromTo, kind) {
      const deltaTime = points.x[fromTo.toIndex] - points.x[fromTo.fromIndex];
      const fromHeight = points.y[fromTo.fromIndex];
      const toHeight = points.y[fromTo.toIndex];
      let baseline = 0;
      let from = 0;
      let to = 0;
      switch (kind) {
        case 'trapezoid':
          baseline = deltaTime * (fromHeight + toHeight) / 2;
          from = fromHeight;
          to = toHeight;
          break;
        case 'min':
          from = Math.min(fromHeight, toHeight);
          to = from;
          baseline = deltaTime * from;
          break;
        default:
          throw new Error(`Unknown baseline method "${kind}"`);
      }
      return {
        value: baseline,
        from,
        to
      };
    }

    function integrate(chromatogram, ranges, options = {}) {
      const {
        baseline,
        seriesName = 'tic'
      } = options;
      if (!Array.isArray(ranges)) {
        throw new Error('Ranges must be an array of type [{from,to}]');
      }
      if (ranges.length === 0) {
        return [];
      }
      chromatogram.requiresSeries(seriesName);
      let series = chromatogram.series[seriesName];
      if (series.dimension !== 1) {
        throw new Error(`The series "${seriesName}" is not of dimension 1`);
      }
      const time = chromatogram.getTimes();
      let results = [];
      for (let range of ranges) {
        const fromTo = xGetFromToIndex(time, range);
        const integral = integrateRange({
          x: time,
          y: series.data
        }, fromTo, baseline);
        results.push(integral);
      }
      return results;
    }
    function integrateRange(points, fromTo, baseline) {
      let integration = xyIntegration(points, fromTo);
      if (baseline) {
        let correction = baselineCorrection(points, fromTo, baseline);
        return {
          integration: integration - correction.value,
          from: {
            time: points.x[fromTo.fromIndex],
            index: fromTo.fromIndex,
            baseline: correction.from
          },
          to: {
            time: points.x[fromTo.toIndex],
            index: fromTo.toIndex,
            baseline: correction.to
          }
        };
      } else {
        return {
          integration,
          from: {
            time: points.x[fromTo.fromIndex],
            index: fromTo.fromIndex,
            baseline: 0
          },
          to: {
            time: points.x[fromTo.toIndex],
            index: fromTo.toIndex,
            baseline: 0
          }
        };
      }
    }

    class Chromatogram {
      constructor(times, series, options = {}) {
        this.meta = options.meta || {};
        this.series = {};
        this.times = [];
        if (!isAnyArray$1(times)) {
          throw new TypeError('times must be an array');
        }
        this.times = times;
        if (series) {
          for (const [name, value] of Object.entries(series)) {
            this.addSeries(name, value);
          }
        }
      }
      get length() {
        return this.times.length;
      }
      getSeries(seriesName) {
        this.requiresSeries(seriesName);
        return this.series[seriesName];
      }
      getSeries1D(seriesName) {
        const series = this.getSeries(seriesName);
        if (!series.is1D()) {
          throw new Error(`series ${seriesName} is not a 1D series`);
        }
        return series;
      }
      getSeries2D(seriesName) {
        const series = this.getSeries(seriesName);
        if (!series.is2D()) {
          throw new Error(`series ${seriesName} is not a 2D series`);
        }
        return series;
      }
      getSeriesNames() {
        return Object.keys(this.series);
      }
      hasMass() {
        return this.hasSeries('ms');
      }
      deleteSeries(seriesName) {
        this.requiresSeries(seriesName);
        delete this.series[seriesName];
        return this;
      }
      addSeries(seriesName, array, options = {}) {
        if (this.hasSeries(seriesName) && !options.force) {
          throw new Error(`A series with name "${seriesName}" already exists`);
        }
        if (this.times.length !== array.length) {
          throw new Error(`The series size is not the same as the times size`);
        }
        this.series[seriesName] = seriesFromArray(array);
        this.series[seriesName].name = seriesName;
        return this;
      }
      hasSeries(seriesName) {
        return this.series[seriesName] !== undefined;
      }
      requiresSeries(seriesName) {
        if (!this.hasSeries(seriesName)) {
          throw new Error(`The series "${seriesName}" does not exist`);
        }
      }
      get firstTime() {
        return this.times[0];
      }
      get lastTime() {
        return this.times[this.length - 1];
      }
      getTimes() {
        return this.times;
      }
      setTimes(times) {
        if (times.length !== this.times.length) {
          throw new Error('New times must have the same length as the old ones');
        }
        this.times = times;
      }
      rescaleTime(conversionFunction) {
        this.times = this.times.map(time => conversionFunction(time));
        return this;
      }
      filter(callback, options) {
        return filter(this, callback, options);
      }
      getPeaks(options) {
        return getPeaks(this, options);
      }
      calculateTic(options = {}) {
        if (!this.hasSeries('tic') || options.force) {
          const tic = calculateTic(this);
          this.addSeries('tic', tic, {
            force: true
          });
        }
        return this;
      }
      calculateLength(seriesName, options = {}) {
        if (!this.hasSeries('length') || options.force) {
          const length = calculateLength(this, seriesName);
          this.addSeries('length', length, {
            force: true
          });
        }
        return this;
      }
      calculateBpc(options = {}) {
        if (!this.hasSeries('bpc') || options.force) {
          const bpc = calculateBpc(this);
          this.addSeries('bpc', bpc, {
            force: true
          });
        }
        return this;
      }
      calculateEic(targetMass, options = {}) {
        const {
          seriesName = `ms${targetMass}±${options.slotWidth / 2 || 0.5}`,
          cache = false
        } = options;
        if (cache && this.hasSeries(seriesName)) return this.getSeries(seriesName);
        const result = calculateEic(this, targetMass, options);
        this.addSeries(seriesName, result, options);
        return this.getSeries(seriesName);
      }
      async calculateForMF(targetMF, options = {}) {
        const {
          seriesName = `${targetMF}(${options.ionizations || 'H+'})±${options.slotWidth / 2 || 0.5}${options.threshold ? `(${options.threshold})` : ''}`,
          cache = false
        } = options;
        if (cache && this.hasSeries(seriesName)) return this.getSeries(seriesName);
        const result = await calculateForMF(this, targetMF, options);
        this.addSeries(seriesName, result, options);
        return this.getSeries(seriesName);
      }
      integrate(ranges, options) {
        return integrate(this, ranges, options);
      }
      merge(options) {
        return merge$1(this, options);
      }
      getClosestTime(time) {
        return xFindClosestIndex(this.getTimes(), time);
      }
      getClosestData(time, options = {}) {
        return getClosestData(this, time, options);
      }
      copy() {
        const json = structuredClone(this);
        return fromJSON(json);
      }
      meanFilter(seriesName, options = {}) {
        const {
          seriesName: newSeriesName = 'msMean'
        } = options;
        if (this.hasSeries(newSeriesName) && !options.force) {
          throw new Error(`A series with name "${seriesName}" already exists`);
        }
        const newSeries = meanFilter(this, seriesName, options);
        this.series[newSeriesName] = newSeries;
        return newSeries;
      }
      percentageFilter(seriesName, options = {}) {
        const {
          seriesName: newSeriesName = 'msPercentage'
        } = options;
        if (this.hasSeries(newSeriesName) && !options.force) {
          throw new Error(`A series with name "${seriesName}" already exists`);
        }
        const newSeries = percentageFilter(this, seriesName, options);
        this.series[newSeriesName] = newSeries;
        return newSeries;
      }
      applyLockMass(mfs, options) {
        return applyLockMass(this, mfs, options);
      }
      getMzVsTimesMatrix(range = {}) {
        return getMzVsTimesMatrix(this, range);
      }
      deconvolution(options = {}) {
        return deconvolution(this, options);
      }
      toJSON() {
        return {
          times: this.times,
          series: this.series
        };
      }
    }
    function fromJSON(json) {
      const {
        series,
        times,
        meta = {}
      } = json;
      let chromatogram = new Chromatogram(times, undefined, {
        meta
      });
      if (Array.isArray(series)) {
        for (let i = 0; i < series.length; i++) {
          chromatogram.addSeries(series[i].name, series[i].data);
        }
      } else {
        for (let key of Object.keys(series)) {
          chromatogram.addSeries(key, series[key].data, {
            meta: series[key].meta
          });
        }
      }
      return chromatogram;
    }

    /**
     * Append MS spectra to a list of peaks
     * @param {Chromatogram} chromatogram
     * @param {Array<object>} peaks - Array of range {from:, to:}
     * @param {object} [options={}] - Options for the integral filtering
     * @param {number} [options.mergeThreshold=0.3] - Peaks that are closer than this value (in Da) will be merged
     * @param {number} [options.seriesName='ms'] - Maximum number of peaks for each mass spectra (when is Number.MAX_VALUE there's no filter)
     * @returns {Array<object>} - A copy of ranges with ms appended
     */
    function appendMass(chromatogram, peaks, options = {}) {
      const {
        mergeThreshold = 0.3,
        seriesName = 'ms'
      } = options;
      const result = [];
      // integrate MS
      for (const peak of peaks) {
        const massSpectrum = merge$1(chromatogram, {
          mergeThreshold,
          seriesName,
          range: peak
        });
        result.push({
          ...peak,
          ms: massSpectrum
        });
      }
      return result;
    }

    function massFilter(massXYObject, options = {}) {
      const {
        thresholdFactor = 0,
        maxNumberPeaks = Number.MAX_VALUE,
        groupWidth = 0
      } = options;
      let max = -1;
      let massList = new Array(massXYObject.x.length);
      for (let i = 0; i < massXYObject.x.length; ++i) {
        massList[i] = {
          x: massXYObject.x[i],
          y: massXYObject.y[i]
        };
        if (massXYObject.y[i] > max) {
          max = massXYObject.y[i];
        }
      }

      // filters based in thresholdFactor
      max *= thresholdFactor;
      let filteredList = massList.filter(val => val.y > max);

      // filters based in maxNumberPeaks
      if (filteredList.length > maxNumberPeaks || groupWidth !== 0) {
        filteredList.sort((a, b) => b.y - a.y);

        // filters based in groupWidth
        filteredList = moreDistinct(filteredList, maxNumberPeaks, groupWidth);
        filteredList.sort((a, b) => a.x - b.x);
      }
      let ans = {
        x: new Array(filteredList.length),
        y: new Array(filteredList.length)
      };
      for (let i = 0; i < filteredList.length; ++i) {
        ans.x[i] = filteredList[i].x;
        ans.y[i] = filteredList[i].y;
      }
      return ans;
    }

    /**
     * Filters based in groupWidth
     * @ignore
     * @param {Array<object>} list - Sorted list of XY-objects to be filtered
     * @param {number} maxNumberPeaks - Maximum number of peaks for each mass spectra
     * @param {number} groupWidth - When find a max can't be another max in a radius of this size
     * @returns {Array<object>} - List of XY-objects filtered
     */
    function moreDistinct(list, maxNumberPeaks, groupWidth) {
      let len = 0;
      if (maxNumberPeaks > list.length) {
        maxNumberPeaks = list.length;
      }
      let filteredList = new Array(maxNumberPeaks);
      for (let i = 0; i < list.length && len < maxNumberPeaks; ++i) {
        let outRange = true;
        for (let j = 0; j < len && outRange; ++j) {
          outRange = outRange && !(list[i].x > filteredList[j].x - groupWidth && list[i].x < filteredList[j].x + groupWidth);
        }
        if (outRange) {
          filteredList[len++] = list[i];
        }
      }
      filteredList.length = len;
      return filteredList;
    }

    function vectorify(ranges, options = {}) {
      const {
        massPower = 3,
        intPower = 0.6
      } = options;
      let filter = options.thresholdFactor || options.maxNumberPeaks || options.groupWidth;
      let vector = new Array(ranges.length);
      if (filter) {
        const filterOptions = {
          thresholdFactor: options.thresholdFactor,
          maxNumberPeaks: options.maxNumberPeaks,
          groupWidth: options.groupWidth
        };
        for (let i = 0; i < ranges.length; ++i) {
          let len = ranges[i].ms.x.length;
          vector[i] = {
            x: ranges[i].ms.x,
            y: new Array(len)
          };
          for (let j = 0; j < len; ++j) {
            vector[i].y[j] = ranges[i].ms.x[j] ** massPower * ranges[i].ms.y[j] ** intPower;
          }
          vector[i] = massFilter(vector[i], filterOptions);
        }
      } else {
        for (let i = 0; i < ranges.length; ++i) {
          let len = ranges[i].ms.x.length;
          vector[i] = {
            x: ranges[i].ms.x,
            y: new Array(len)
          };
          for (let j = 0; j < len; ++j) {
            vector[i].y[j] = ranges[i].ms.x[j] ** massPower * ranges[i].ms.y[j] ** intPower;
          }
        }
      }
      return vector;
    }

    function cosineSimilarity(ms1x, ms1y, ms2x, ms2y) {
      let index1 = 0;
      let index2 = 0;
      let product = 0;
      let norm1 = 0;
      let norm2 = 0;
      while (index1 < ms1x.length || index2 < ms2x.length) {
        let w1 = ms1y[index1];
        let w2 = ms2y[index2];
        if (index2 === ms2x.length || ms1x[index1] < ms2x[index2]) {
          norm1 += w1 * w1;
          ++index1;
        } else if (index1 === ms1x.length || ms2x[index2] < ms1x[index1]) {
          norm2 += w2 * w2;
          ++index2;
        } else {
          product += w1 * w2;
          norm1 += w1 * w1;
          norm2 += w2 * w2;
          ++index1;
          ++index2;
        }
      }
      let norm1Norm2 = norm1 * norm2;
      if (norm1Norm2 === 0) {
        return 0;
      } else {
        return product * product / norm1Norm2;
      }
    }

    /**
     * Preprocessing task over the signalsj
     * @ignore
     * @param {Chromatogram} chromatogram - Chromatogram to process
     * @param {object} [options] - Options object (same as spectraComparison)
     * @returns {{peaks: Array<object>, integratedMs: Array<object>, vector: Array<object>}} - Array of peaks, integrated mass spectra and weighted mass spectra
     */
    function preprocessing(chromatogram, options) {
      // peak picking
      let peaks = getPeaks(chromatogram, options);
      peaks = peaks.sort((a, b) => a.from - b.to);

      // integrate mass in the peaks
      let integratedMs = appendMass(chromatogram, peaks, options);
      let vector = vectorify(integratedMs, options);
      return {
        peaks,
        integratedMs,
        vector
      };
    }
    const defaultOptions$1 = {
      thresholdFactor: 0,
      maxNumberPeaks: Number.MAX_VALUE,
      groupWidth: 0,
      heightFilter: 2,
      massPower: 3,
      intPower: 0.6,
      similarityThreshold: 0.7
    };
    function spectraComparison(chrom1, chrom2, options = {}) {
      options = {
        ...defaultOptions$1,
        ...options
      };

      // peak picking
      let reference = preprocessing(chrom1, options);
      let sample = preprocessing(chrom2, options);

      // similarity between MS
      const len = Math.max(sample.peaks.length, reference.peaks.length);
      let similarityPeaks = {
        chrom1: new Array(len),
        chrom2: new Array(len),
        similarity: new Array(len)
      };
      let similarLen = 0;

      // Similarity matrix
      for (let i = 0; i < sample.peaks.length; ++i) {
        let max = {
          similarity: -3
        };
        let biggerCounter = 0;
        for (let j = 0; j < reference.peaks.length; ++j) {
          let sim = cosineSimilarity(sample.vector[i].x, sample.vector[i].y, reference.vector[j].x, reference.vector[j].y);
          if (sim > options.similarityThreshold && sim > max.similarity) {
            max = {
              similarity: sim,
              chrom1: reference.peaks[j],
              chrom2: sample.peaks[i]
            };
          }
          if (sim > options.similarityThreshold) {
            ++biggerCounter;
          }
        }
        if (biggerCounter === 1) {
          similarityPeaks.chrom1[similarLen] = max.chrom1;
          similarityPeaks.chrom2[similarLen] = max.chrom2;
          similarityPeaks.similarity[similarLen++] = max.similarity;
        }
      }
      similarityPeaks.chrom1.length = similarLen;
      similarityPeaks.chrom2.length = similarLen;
      let duplicates = {};
      for (let i = 0; i < similarLen; ++i) {
        if (duplicates[similarityPeaks.chrom1[i].retentionTime]) {
          duplicates[similarityPeaks.chrom1[i].retentionTime].push(i);
        } else {
          duplicates[similarityPeaks.chrom1[i].retentionTime] = [i];
        }
      }
      let peaksChrom1 = [];
      let peaksChrom2 = [];
      let peaksSimilarity = [];
      for (let val in duplicates) {
        if (duplicates[val].length === 1) {
          peaksChrom1.push(similarityPeaks.chrom1[duplicates[val][0]]);
          peaksChrom2.push(similarityPeaks.chrom2[duplicates[val][0]]);
          peaksSimilarity.push(similarityPeaks.similarity[duplicates[val][0]]);
        }
      }
      return {
        peaksFirst: peaksChrom1,
        peaksSecond: peaksChrom2,
        peaksSimilarity
      };
    }

    /**
     * Check that x and y are arrays with the same length.
     * @param x - first array
     * @param y - second array
     * @throws if x or y are not the same length, or if they are not arrays
     */
    function checkArrayLength(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 (const xVal of x) {
            y.push(this._predict(xVal));
          }
          return y;
        } else {
          throw new TypeError('x must be a number or array');
        }
      }
      // eslint-disable-next-line @typescript-eslint/no-unused-vars
      _predict(x) {
        throw new Error('_predict must be implemented');
      }
      train() {
        // Do nothing for this package
      }
      // eslint-disable-next-line @typescript-eslint/no-unused-vars
      toString(precision) {
        return '';
      }
      // eslint-disable-next-line @typescript-eslint/no-unused-vars
      toLaTeX(precision) {
        return '';
      }
      /**
       * Return the correlation coefficient of determination (r) and chi-square.
       * @param x - explanatory variable
       * @param y - response variable
       * @return - Object with further statistics.
       */
      score(x, y) {
        checkArrayLength(x, y);
        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,
          r2: r * r,
          chi2,
          rmsd: Math.sqrt(rmsd / n)
        };
      }
    }

    /**
     * Cast `number` to string. Optionally `digits` specifies significant figures.
     * @param number
     * @param figures
     * @returns - A string representation of `number`.
     */
    function maybeToPrecision(number, figures) {
      if (number < 0) {
        number = 0 - number;
        if (typeof figures === 'number') {
          return `- ${number.toPrecision(figures)}`;
        } else {
          return `- ${number.toString()}`;
        }
      } else if (typeof figures === 'number') {
        return number.toPrecision(figures);
      } else {
        return number.toString();
      }
    }

    class PolynomialRegression extends BaseRegression {
      /**
       * @param x - independent or explanatory variable
       * @param y - dependent or response variable
       * @param degree - degree of the polynomial regression, or array of powers to be used. When degree is an array, intercept at zero is forced to false/ignored.
       * @example `new PolynomialRegression(x, y, 2)`, in this case, you can pass the option `interceptAtZero`, if you need it.
       * @example `new PolynomialRegression(x, y, [1, 3, 5])`
       * Each of the degrees corresponds to a column, so if you have them switched, just do:
       * @example `new PolynomialRegression(x, y, [3, 1, 5])`
       *
       * @param options.interceptAtZero - force the polynomial regression so that f(0) = 0
       */
      constructor(x, y, degree, options = {}) {
        super();
        // @ts-expect-error internal use only
        if (x === true) {
          // @ts-expect-error internal use only
          this.degree = y.degree;
          // @ts-expect-error internal use only
          this.powers = y.powers;
          // @ts-expect-error internal use only
          this.coefficients = y.coefficients;
        } else {
          checkArrayLength(x, y);
          const result = regress(x, y, degree, options);
          this.degree = result.degree;
          this.powers = result.powers;
          this.coefficients = result.coefficients;
        }
      }
      _predict(x) {
        let y = 0;
        for (let k = 0; k < this.powers.length; k++) {
          y += this.coefficients[k] * x ** this.powers[k];
        }
        return y;
      }
      toJSON() {
        return {
          name: 'polynomialRegression',
          degree: this.degree,
          powers: this.powers,
          coefficients: this.coefficients
        };
      }
      toString(precision) {
        return this._toFormula(precision, false);
      }
      toLaTeX(precision) {
        return this._toFormula(precision, true);
      }
      _toFormula(precision, isLaTeX) {
        let sup = '^';
        let closeSup = '';
        let times = ' * ';
        if (isLaTeX) {
          sup = '^{';
          closeSup = '}';
          times = '';
        }
        let fn = '';
        let str = '';
        for (let k = 0; k < this.coefficients.length; k++) {
          str = '';
          if (this.coefficients[k] !== 0) {
            if (this.powers[k] === 0) {
              str = maybeToPrecision(this.coefficients[k], precision);
            } else if (this.powers[k] === 1) {
              str = `${maybeToPrecision(this.coefficients[k], precision) + times}x`;
            } else {
              str = `${maybeToPrecision(this.coefficients[k], precision) + times}x${sup}${this.powers[k]}${closeSup}`;
            }
            if (this.coefficients[k] > 0 && k !== this.coefficients.length - 1) {
              str = ` + ${str}`;
            } else if (k !== this.coefficients.length - 1) {
              str = ` ${str}`;
            }
          }
          fn = str + fn;
        }
        if (fn.startsWith('+')) {
          fn = fn.slice(1);
        }
        return `f(x) = ${fn}`;
      }
      static load(json) {
        if (json.name !== 'polynomialRegression') {
          throw new TypeError('not a polynomial regression model');
        }
        // @ts-expect-error internal use only
        return new PolynomialRegression(true, json);
      }
    }
    /**
     * Perform a polynomial regression on the given data set.
     * This is an internal function.
     * @param x - independent or explanatory variable
     * @param y - dependent or response variable
     * @param degree - degree of the polynomial regression
     * @param options.interceptAtZero - force the polynomial regression so that $f(0) = 0$
     */
    function regress(x, y, degree, options = {}) {
      const n = x.length;
      let {
        interceptAtZero = false
      } = options;
      let powers = [];
      if (Array.isArray(degree)) {
        powers = degree;
        interceptAtZero = false; //must be false in this case
      } else if (typeof degree === 'number') {
        if (interceptAtZero) {
          powers = new Array(degree);
          for (let k = 0; k < degree; k++) {
            powers[k] = k + 1;
          }
        } else {
          powers = new Array(degree + 1);
          for (let k = 0; k <= degree; k++) {
            powers[k] = k;
          }
        }
      }
      const nCoefficients = powers.length; //1 per power, in any case.
      const F = new Matrix(n, nCoefficients);
      const Y = new Matrix([y]);
      for (let k = 0; k < nCoefficients; k++) {
        for (let i = 0; i < n; i++) {
          if (powers[k] === 0) {
            F.set(i, k, 1);
          } else {
            F.set(i, k, x[i] ** powers[k]);
          }
        }
      }
      const FT = new MatrixTransposeView(F);
      const A = FT.mmul(F);
      const B = FT.mmul(new MatrixTransposeView(Y));
      return {
        coefficients: solve(A, B).to1DArray(),
        degree: Math.max(...powers),
        powers
      };
    }

    function scaleAlignment(reference, sample, options = {}) {
      const {
        computeQuality = false,
        polynomialDegree = 3
      } = options;
      let referenceTime = reference.map(val => val.retentionTime);
      let sampleTime = sample.map(val => val.retentionTime);
      const regression = new PolynomialRegression(sampleTime, referenceTime, polynomialDegree);
      let error = new Array(sample.length);
      for (let i = 0; i < sample.length; i++) {
        error[i] = reference[i].retentionTime - regression.predict(sample[i].retentionTime);
      }
      let ans = {
        scaleRegression: regression
      };
      if (computeQuality) {
        let score = regression.score(sampleTime, referenceTime);
        ans.r2 = score.r2;
        ans.error = error;
      }
      return ans;
    }

    /**
     * @typedef KovatsReturn
     * @type {object}
     * @property {number} index - Kovats retention index
     * @property {number} numberFragments - Number of fragments
     * @property {number} percentFragments - Percentage of fragments
     */

    /**
     * Calculates the Kovats retention index for a mass spectra of a n-alkane
     * @param {object} ms - A mass spectra object
     * @param {Array<number>} ms.x - Array of masses
     * @param {Array<number>} ms.y - Array of intensities
     * @param options
     * @returns {KovatsReturn} - Kovats retention index
     */
    function kovats(ms, options = {}) {
      const {
        threshold = 0.01
      } = options;
      // we normalize the data and filter them
      let maxY = max(ms.y);
      let masses = [];
      let intensities = [];
      for (let i = 0; i < ms.x.length; i++) {
        if (ms.y[i] / maxY > threshold) {
          masses.push(ms.x[i]);
          intensities.push(ms.y[i] / maxY);
        }
      }

      // we find candidates
      let nbAlcaneMasses = [];
      let fragmentMasses = [];
      for (let i = 0; i < masses.length; i++) {
        if ((masses[i] - 2) % 14 === 0) {
          nbAlcaneMasses.push(masses[i]);
        }
        if ((masses[i] - 1) % 14 === 0) {
          fragmentMasses.push(masses[i]);
        }
      }
      if (nbAlcaneMasses.length === 0) return {};
      let biggestMass = nbAlcaneMasses.sort((a, b) => b - a)[0];
      fragmentMasses = fragmentMasses.filter(mass => mass < biggestMass);
      return {
        index: 100 * (biggestMass - 2) / 14,
        numberFragments: fragmentMasses.length,
        percentFragments: fragmentMasses.length / ((biggestMass - 2) / 14)
      };
    }

    function appendKovats(peaks) {
      const result = [];
      for (let peak of peaks) {
        result.push({
          ...peak,
          kovats: kovats(peak.ms)
        });
      }
      return result;
    }

    var binarySearch = function (haystack, needle, comparator, low, high) {
      var mid, cmp;
      if (low === undefined) low = 0;else {
        low = low | 0;
        if (low < 0 || low >= haystack.length) throw new RangeError("invalid lower bound");
      }
      if (high === undefined) high = haystack.length - 1;else {
        high = high | 0;
        if (high < low || high >= haystack.length) throw new RangeError("invalid upper bound");
      }
      while (low <= high) {
        // The naive `low + high >>> 1` could fail for array lengths > 2**31
        // because `>>>` converts its operands to int32. `low + (high - low >>> 1)`
        // works for array lengths <= 2**32-1 which is also Javascript's max array
        // length.
        mid = low + (high - low >>> 1);
        cmp = +comparator(haystack[mid], needle, mid, haystack);

        // Too low.
        if (cmp < 0.0) low = mid + 1;

        // Too high.
        else if (cmp > 0.0) high = mid - 1;

        // Key found.
        else return mid;
      }

      // Key not found.
      return ~low;
    };
    var binarySearch$1 = binarySearch;

    const ascValue = (a, b) => a.index - b.index;
    const ascTime = (a, b) => a.time - b.time;
    function getKovatsConversionFunction(peaks, options = {}) {
      const {
        revert = false
      } = options;
      const kovatsConversionTable = peaks.map(peak => ({
        time: peak.x,
        index: peak.kovats.index
      }));
      if (revert) {
        const indexes = kovatsConversionTable.sort(ascValue);
        return index => {
          let position = binarySearch$1(indexes, {
            index
          }, ascValue);
          if (position < 0) {
            position = ~position;

            // handle extreme cases
            if (position === 0 || position === indexes.length) {
              return 0;
            }
            let smallerAlcane = indexes[position - 1].time;
            let largerAlcane = indexes[position].time;
            return (index - indexes[position - 1].index) * (largerAlcane - smallerAlcane) / 100 + smallerAlcane;
          } else {
            return indexes[position].time;
          }
        };
      } else {
        const times = kovatsConversionTable.sort(ascTime);
        return time => {
          let position = binarySearch$1(times, {
            time
          }, ascTime);
          if (position < 0) {
            position = ~position;

            // handle extreme cases
            if (position === 0 || position === times.length) {
              return 0;
            }
            let smallerAlcane = times[position - 1].time;
            let largerAlcane = times[position].time;
            return 100 * (time - smallerAlcane) / (largerAlcane - smallerAlcane) + times[position - 1].index;
          } else {
            return times[position].index;
          }
        };
      }
    }

    /*
        https://tools.ietf.org/html/rfc3629

        UTF8-char = UTF8-1 / UTF8-2 / UTF8-3 / UTF8-4

        UTF8-1    = %x00-7F

        UTF8-2    = %xC2-DF UTF8-tail

        UTF8-3    = %xE0 %xA0-BF UTF8-tail
                    %xE1-EC 2( UTF8-tail )
                    %xED %x80-9F UTF8-tail
                    %xEE-EF 2( UTF8-tail )

        UTF8-4    = %xF0 %x90-BF 2( UTF8-tail )
                    %xF1-F3 3( UTF8-tail )
                    %xF4 %x80-8F 2( UTF8-tail )

        UTF8-tail = %x80-BF
    */
    /**
     * Check if a Node.js Buffer or Uint8Array is UTF-8.
     */
    function isUtf8(buf) {
      if (!buf) {
        return false;
      }
      var i = 0;
      var len = buf.length;
      while (i < len) {
        // UTF8-1 = %x00-7F
        if (buf[i] <= 0x7F) {
          i++;
          continue;
        }
        // UTF8-2 = %xC2-DF UTF8-tail
        if (buf[i] >= 0xC2 && buf[i] <= 0xDF) {
          // if(buf[i + 1] >= 0x80 && buf[i + 1] <= 0xBF) {
          if (buf[i + 1] >> 6 === 2) {
            i += 2;
            continue;
          } else {
            return false;
          }
        }
        // UTF8-3 = %xE0 %xA0-BF UTF8-tail
        // UTF8-3 = %xED %x80-9F UTF8-tail
        if ((buf[i] === 0xE0 && buf[i + 1] >= 0xA0 && buf[i + 1] <= 0xBF || buf[i] === 0xED && buf[i + 1] >= 0x80 && buf[i + 1] <= 0x9F) && buf[i + 2] >> 6 === 2) {
          i += 3;
          continue;
        }
        // UTF8-3 = %xE1-EC 2( UTF8-tail )
        // UTF8-3 = %xEE-EF 2( UTF8-tail )
        if ((buf[i] >= 0xE1 && buf[i] <= 0xEC || buf[i] >= 0xEE && buf[i] <= 0xEF) && buf[i + 1] >> 6 === 2 && buf[i + 2] >> 6 === 2) {
          i += 3;
          continue;
        }
        // UTF8-4 = %xF0 %x90-BF 2( UTF8-tail )
        //          %xF1-F3 3( UTF8-tail )
        //          %xF4 %x80-8F 2( UTF8-tail )
        if ((buf[i] === 0xF0 && buf[i + 1] >= 0x90 && buf[i + 1] <= 0xBF || buf[i] >= 0xF1 && buf[i] <= 0xF3 && buf[i + 1] >> 6 === 2 || buf[i] === 0xF4 && buf[i + 1] >= 0x80 && buf[i + 1] <= 0x8F) && buf[i + 2] >> 6 === 2 && buf[i + 3] >> 6 === 2) {
          i += 4;
          continue;
        }
        return false;
      }
      return true;
    }

    /**
     * Ensure that the data is string. If it is an ArrayBuffer it will be converted to string using TextDecoder.
     * @param blob
     * @param options
     * @returns
     */
    function ensureString(blob, options = {}) {
      if (typeof blob === 'string') {
        return blob;
      }
      if (ArrayBuffer.isView(blob) || blob instanceof ArrayBuffer) {
        const {
          encoding = guessEncoding(blob)
        } = options;
        const decoder = new TextDecoder(encoding);
        return decoder.decode(blob);
      }
      throw new TypeError(`blob must be a string, ArrayBuffer or ArrayBufferView`);
    }
    function guessEncoding(blob) {
      const uint8 = ArrayBuffer.isView(blob) ? new Uint8Array(blob.buffer, blob.byteOffset, blob.byteLength) : new Uint8Array(blob);
      if (uint8.length >= 2) {
        if (uint8[0] === 0xfe && uint8[1] === 0xff) {
          return 'utf-16be';
        }
        if (uint8[0] === 0xff && uint8[1] === 0xfe) {
          return 'utf-16le';
        }
      }
      //@ts-expect-error an ArrayBuffer is also ok
      if (!isUtf8(blob)) return 'latin1';
      return 'utf-8';
    }

    function parseString$1(value) {
      if (value.length === 4 || value.length === 5) {
        const lowercase = value.toLowerCase();
        if (lowercase === 'true') return true;
        if (lowercase === 'false') return false;
      }
      const number = Number(value);
      if (number === 0 && !value.includes('0')) {
        return value;
      }
      if (!Number.isNaN(number)) return number;
      return value;
    }

    const gyromagneticRatio = {
      '1H': 267.52218744e6,
      '2H': 41.065e6,
      '3H': 285.3508e6,
      '3He': -203.789e6,
      '7Li': 103.962e6,
      '13C': 67.28284e6,
      '14N': 19.331e6,
      '15N': -27.116e6,
      '17O': -36.264e6,
      '19F': 251.662e6,
      '23Na': 70.761e6,
      '27Al': 69.763e6,
      '29Si': -53.19e6,
      '31P': 108.291e6,
      '57Fe': 8.681e6,
      '63Cu': 71.118e6,
      '67Zn': 16.767e6,
      '129Xe': -73.997e6,
      '195Pt': 58.385e6,
      '199Hg': 48.457916e6,
      '187Os': 6.192895e6,
      '183W': 11.282403e6,
      '125Te': -85.108404e6,
      '123Te': -70.59098e6,
      '119Sn': -100.317e6,
      '117Sn': -95.8879e6,
      '115Sn': -88.013e6,
      '113Cd': -59.609155e6,
      '111Cd': -56.983131e6,
      '109Ag': -12.518634e6,
      '77Se': 51.253857e6,
      '89Y': -13.162791e6,
      '103Rh': -8.468e6,
      '107Ag': -10.889181e6,
      '203Tl': 155.393338e6,
      '205Tl': 156.921808e6,
      '207Pb': 55.8046e6
    };
    function getGyromagneticRatio(nucleus) {
      if (gyromagneticRatio[nucleus]) {
        return gyromagneticRatio[nucleus];
      }
      nucleus = nucleus.toLowerCase();
      if (nucleus === 'proton') return gyromagneticRatio['1H'];
      // we try to use only the numbers
      const nucleusNumber = nucleus.replace(/[^0-9]/g, '');
      if (!nucleusNumber) return null;
      for (const key in gyromagneticRatio) {
        if (key.replace(/[^0-9]/g, '') === nucleusNumber) {
          return gyromagneticRatio[key];
        }
      }
      return null;
    }

    function median(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');
      }
      return quickSelectMedian(input.slice());
    }

    var P = /\s*\$\$.*/;
    function M(e) {
      return e.replace(P, "");
    }
    var E = ["TIC", ".RIC", "SCANNUMBER"];
    function te(e) {
      let a = e.spectra,
        s = a.length,
        i = {
          times: new Array(s),
          series: {
            ms: {
              dimension: 2,
              data: new Array(s)
            }
          }
        },
        o = [];
      for (let t = 0; t < E.length; t++) {
        let r = I(E[t]);
        a[0][r] && (o.push(r), i.series[r] = {
          dimension: 1,
          data: new Array(s)
        });
      }
      for (let t = 0; t < s; t++) {
        let r = a[t];
        i.times[t] = r.pageValue;
        for (let l = 0; l < o.length; l++) i.series[o[l]].data[t] = Number(r[o[l]]);
        r.data && (i.series.ms.data[t] = [r.data.x, r.data.y]);
      }
      e.chromatogram = i;
    }
    function le(e) {
      return E.indexOf(e) !== -1;
    }
    function I(e) {
      return e.toLowerCase().replace(/[^a-z0-9]/g, "");
    }
    function Y(e) {
      let a = [];
      for (let s = 0; s < e.length; s++) a.push(Number(e[s]));
      return a;
    }
    function U(e, a) {
      let s = e.yFactor,
        i = e.deltaX;
      e.isXYdata = !0;
      let o = {
        x: [],
        y: []
      };
      e.data = o;
      let t = e.firstX,
        r = e.firstY,
        l = !1,
        n,
        p = 0;
      for (; p < a.length; p++) if (n = a.charCodeAt(p), n === 13 || n === 10) l = !0;else if (l) break;
      let c = !0,
        d = !1,
        u = !1,
        f = 0,
        m = !1,
        h = !1,
        g = 0,
        A = 0,
        N = !1,
        y = !1,
        C = !1,
        T = 0;
      for (; p <= a.length; p++) if (p === a.length ? n = 13 : n = a.charCodeAt(p), h) (n === 13 || n === 10) && (c = !0, h = !1);else if (n <= 57 && n >= 48) y = !0, T > 0 ? g += (n - 48) / 10 ** T++ : (g *= 10, g += n - 48);else if (n === 44 || n === 46) y = !0, T++;else {
        if (y) {
          if (c) c = !1, u && (C = !0);else if (C) C = !1;else {
            d ? (f = N ? 0 - g : g, u = !0, d = !1) : m || (A = N ? 0 - g : g);
            let O = m ? g - 1 : 1;
            for (let v = 0; v < O; v++) u ? r += f : r = A, o.x.push(t), o.y.push(r * s), t += i;
          }
          N = !1, g = 0, T = 0, y = !1, m = !1;
        }
        if (n < 74 && n > 63) y = !0, u = !1, g = n - 64;else if (n > 96 && n < 106) y = !0, u = !1, g = n - 96, N = !0;else if (n === 115) y = !0, m = !0, g = 9;else if (n > 82 && n < 91) y = !0, m = !0, g = n - 82;else if (n > 73 && n < 83) y = !0, d = !0, g = n - 73;else if (n > 105 && n < 115) y = !0, d = !0, g = n - 105, N = !0;else if (n === 36 && a.charCodeAt(p + 1) === 36) y = !0, h = !0;else if (n === 37) y = !0, d = !0, g = 0, N = !1;else if (n === 45) {
          let O = a.charCodeAt(p + 1);
          (O >= 48 && O <= 57 || O === 44 || O === 46) && (y = !0, c || (u = !1), N = !0);
        } else (n === 13 || n === 10) && (c = !0, h = !1);
      }
    }
    var se = /[,\t ]+/;
    function D(e, a, s) {
      if (e.isPeaktable = !0, !e.variables || Object.keys(e.variables) === 2 ? me(e, a, s) : ce(e, a, s), e.variables) for (let i in e.variables) e.variables[i].data = e.data[i];
    }
    function me(e, a, s) {
      let i = {
        x: [],
        y: []
      };
      e.data = i;
      let o = a.split(/,? *,?[;\r\n]+ */);
      for (let t = 1; t < o.length; t++) {
        let r = o[t].trim().replace(P, "").split(se);
        if (r.length % 2 === 0) for (let l = 0; l < r.length; l = l + 2) i.x.push(Number(r[l]) * e.xFactor), i.y.push(Number(r[l + 1]) * e.yFactor);else s.logs.push(`Format error: ${r}`);
      }
    }
    function ce(e, a, s) {
      let i = {},
        o = Object.keys(e.variables),
        t = o.length;
      o.forEach(l => i[l] = []), e.data = i;
      let r = a.split(/,? *,?[;\r\n]+ */);
      for (let l = 1; l < r.length; l++) {
        let n = r[l].trim().replace(P, "").split(se);
        if (n.length % t === 0) for (let p = 0; p < n.length; p++) i[o[p % t]].push(Number(n[p]));else s.logs.push(`Wrong number of columns: ${n}`);
      }
    }
    function V(e, a) {
      e.isXYAdata = !0;
      let s = {};
      e.data = s;
      let i = a.split(/\r?\n/),
        o = i[0].replace(/^.*?([A-Z]+).*$/, "$1").split("").map(t => t.toLowerCase());
      for (let t = 1; t < i.length; t++) {
        let r = i[t].replace(/^\((.*)\)$/, "$1").split(/ *, */);
        for (let l = 0; l < o.length; l++) {
          let n = r[l];
          switch (o[l]) {
            case "x":
            case "y":
            case "w":
              n = Number.parseFloat(n);
              break;
            case "a":
              n = n.replace(/^<(.*)>$/, "$1");
              break;
            case "m":
              break;
            default:
              continue;
          }
          s[o[l]] || (s[o[l]] = []), s[o[l]].push(n);
        }
      }
    }
    function ie(e) {
      return Array.isArray(e) ? e[0] : e;
    }
    function B(e) {
      let {
          spectra: a
        } = e,
        s = a[0].data.y[0],
        i = s,
        o = a.length,
        t = a[0].data.x.length,
        r = new Array(o);
      for (let f = 0; f < o; f++) {
        r[f] = a[f].data.y;
        for (let m = 0; m < t; m++) {
          let h = r[f][m];
          h < s && (s = h), h > i && (i = h);
        }
      }
      let l = a[0].data.x[0],
        n = a[0].data.x[a[0].data.x.length - 1],
        {
          firstY: p,
          lastY: c
        } = he(e);
      if (l > n) for (let f of r) f.reverse();
      p > c && r.reverse();
      let d = [];
      for (let f = 0; f < r.length; f++) {
        let m = Float64Array.from(r[f]);
        for (let h = 0; h < m.length; h++) m[h] < 0 && (m[h] = -m[h]);
        d.push(median(m));
      }
      let u = median(d);
      return {
        z: r,
        minX: Math.min(l, n),
        maxX: Math.max(l, n),
        minY: Math.min(p, c),
        maxY: Math.max(p, c),
        minZ: s,
        maxZ: i,
        noise: u
      };
    }
    function he(e) {
      let {
        spectra: a,
        ntuples: s,
        info: i
      } = e;
      if (s) for (let o = 0; o < s.length; o++) {
        let {
          symbol: t,
          nucleus: r,
          units: l
        } = s[o];
        if (t.match(/[F|T]1/) && l?.toUpperCase().match("HZ")) {
          let n = ie(i[".OBSERVEFREQUENCY"]),
            {
              nucleus: p
            } = s.find(h => h.symbol.match(/[F|T]2/));
          if ([n, p, r].some(h => !h)) break;
          let c = getGyromagneticRatio(p),
            d = getGyromagneticRatio(r),
            {
              first: u,
              last: f
            } = s[o],
            m = n * d / c;
          return {
            firstY: u / m,
            lastY: f / m
          };
        }
      }
      return {
        firstY: a[0].pageValue,
        lastY: a[a.length - 1].pageValue
      };
    }
    function k(e, a) {
      let s = e.noise,
        i = e.z,
        o,
        t,
        r,
        l,
        n,
        p,
        c,
        d,
        u = i.length,
        f = i[0].length,
        m,
        h,
        g,
        A,
        N = e.minX,
        C = (e.maxX - N) / (f - 1),
        T = e.minY,
        v = (e.maxY - T) / (u - 1),
        oe = e.minZ,
        K = e.maxZ,
        Q = a.nbContourLevels * 2,
        W = new Array(Q),
        x;
      for (let R = 0; R < Q; R++) {
        let $ = {};
        W[R] = $;
        let fe = R % 2,
          _ = (K - a.noiseMultiplier * s) * Math.exp((R >> 1) - a.nbContourLevels);
        fe === 0 ? x = _ + a.noiseMultiplier * s : x = 0 - _ - a.noiseMultiplier * s;
        let b = [];
        if ($.zValue = x, $.lines = b, !(x <= oe || x >= K)) for (let X = 0; X < u - 1; X++) {
          let z = i[X],
            ee = i[X + 1];
          for (let S = 0; S < f - 1; S++) o = z[S], t = z[S + 1], r = ee[S], l = ee[S + 1], n = o > x, p = t > x, c = r > x, d = l > x, n !== p && n !== c && (m = S + (x - o) / (t - o), h = X, g = S, A = X + (x - o) / (r - o), b.push(m * C + N), b.push(h * v + T), b.push(g * C + N), b.push(A * v + T)), d !== p && d !== c && (m = S + 1, h = X + 1 - (x - l) / (t - l), g = S + 1 - (x - l) / (r - l), A = X + 1, b.push(m * C + N), b.push(h * v + T), b.push(g * C + N), b.push(A * v + T)), p !== c && (m = (S + 1 - (x - t) / (r - t)) * C + N, h = (X + (x - t) / (r - t)) * v + T, p !== n && (g = S + 1 - (x - t) / (o - t), A = X, b.push(m), b.push(h), b.push(g * C + N), b.push(A * v + T)), c !== n && (g = S, A = X + 1 - (x - r) / (o - r), b.push(m), b.push(h), b.push(g * C + N), b.push(A * v + T)), p !== d && (g = S + 1, A = X + (x - t) / (l - t), b.push(m), b.push(h), b.push(g * C + N), b.push(A * v + T)), c !== d && (g = S + (x - r) / (l - r), A = X + 1, b.push(m), b.push(h), b.push(g * C + N), b.push(A * v + T)));
        }
      }
      return {
        minX: e.minX,
        maxX: e.maxX,
        minY: e.minY,
        maxY: e.maxY,
        segments: W
      };
    }
    function Z(e, a) {
      let s = B(e);
      a.noContour || (e.contourLines = k(s, a), delete s.z), e.minMax = s;
    }
    function J(e, a) {
      for (let s of e) {
        let i = 0,
          o = 0;
        for (let t of s.spectra) {
          if (s.ntuples && s.ntuples.symbol ? (!i && t.observeFrequency && (i = t.observeFrequency), !o && t.shiftOffsetVal && (o = t.shiftOffsetVal)) : (i = t.observeFrequency, o = t.shiftOffsetVal), i && t.xUnits && t.xUnits.toUpperCase().includes("HZ")) {
            t.xUnits = "PPM", t.xFactor = t.xFactor / i, t.firstX = t.firstX / i, t.lastX = t.lastX / i, t.deltaX = t.deltaX / i;
            for (let r = 0; r < t.data.x.length; r++) t.data.x[r] /= i;
          }
          if (o && t.xUnits.toLowerCase().includes("ppm")) {
            let r = t.firstX - o;
            t.firstX = t.firstX - r, t.lastX = t.lastX - r;
            for (let l = 0; l < t.data.x.length; l++) t.data.x[l] -= r;
          }
          if (s.ntuples && s.ntuples.nucleus && s.ntuples.symbol) for (let r = 0; r < s.ntuples.nucleus.length; r++) {
            let l = s.ntuples.symbol[r],
              n = s.ntuples.nucleus[r];
            if (l.match(/^[F|T]/) && !n) {
              if (l.match(/[F|T]1/)) if (s.tmp.$NUC2) s.ntuples.nucleus[r] = s.tmp.$NUC2;else {
                let p = s.ntuples.symbol.indexOf(l.replace(/^([F|T]).*/, "$12"));
                p && s.ntuples.nucleus[p] && (s.ntuples.nucleus[r] = s.ntuples.nucleus[p]);
              }
              l.match(/[F|T]2/) && (s.ntuples.nucleus[r] = s.tmp.$NUC1);
            }
            l.match(/[F|T]2/) && (s.yType = s.ntuples.nucleus[0], getGyromagneticRatio(s.xType) || (s.xType = s.ntuples.nucleus[1]));
          }
          if (i && s.ntuples && s.ntuples.symbol && s.ntuples.nucleus) {
            let r = "",
              l = s.ntuples.symbol.indexOf(t.pageSymbol);
            if (s.ntuples.units && s.ntuples.units[l] && (r = s.ntuples.units[l]), !r.toLowerCase().match(/(ppm|seconds)/)) {
              if (l !== 0) {
                let u = "Not sure about this ntuples format";
                if (a) a.warn(u);else throw Error(u);
              }
              let {
                  nucleus: n
                } = s.ntuples,
                p = getGyromagneticRatio(n[0]),
                c = getGyromagneticRatio(n[1]);
              if (!p || !c) {
                let u = `Problem with determination of gyromagnetic ratio for ${n.join("-")}`;
                if (a) a.error(u);else throw Error(u);
              }
              let d = p / c * i;
              t.pageValue /= d;
            }
          }
        }
      }
    }
    function L(e, a, s = {}) {
      s.logger?.trace(a), e.profiling && e.profiling.push({
        action: a,
        time: Date.now() - s.start
      });
    }
    function G(e) {
      let a = e.spectra[0].data;
      e.chromatogram = {
        times: a.x.slice(),
        series: {
          intensity: {
            dimension: 1,
            data: a.y.slice()
          }
        }
      };
    }
    function H(e, a, s) {
      J(e, s.logger), de(e, s);
      for (let i of e) {
        if (Object.keys(i.ntuples).length > 0) {
          let o = [],
            t = Object.keys(i.ntuples);
          for (let r = 0; r < t.length; r++) {
            let l = t[r],
              n = i.ntuples[l];
            for (let p = 0; p < n.length; p++) o[p] || (o[p] = {}), o[p][l] = n[p];
          }
          i.ntuples = o;
        }
        i.twoD && s.wantXY && (Z(i, s), L(a, "Finished countour plot calculation", s), s.keepSpectra || delete i.spectra), s.chromatogram && (i.spectra.length > 1 ? te(i) : G(i), L(a, "Finished chromatogram calculation", s)), delete i.tmp;
      }
    }
    function de(e, a) {
      for (let s of e) for (let i in s.meta) {
        let o = s.meta[i];
        if (typeof o == "string") {
          if (o[0] === "{") {
            if (!o.includes(":") && o[o.length - 1] === "}") {
              let t = o.slice(1, -1).split(/[,; ]+/).filter(r => r);
              for (let r = 0; r < t.length; r++) s.meta[i + String(r)] = a.dynamicTyping ? parseString$1(t[r]) : t[r];
            }
          } else if (o[0] === "(") {
            let t = o.split(/\r?\n/),
              r = /^\((?<from>\d+)\.\.(?<to>\d+)\).*$/;
            if (r.test(t[0])) {
              let [l, n] = t[0].match(r).slice(1).map(Number),
                p = t.slice(1).join(" ").split(/[,; ]+/).filter(c => c);
              for (let c = l; c <= n; c++) a.dynamicTyping && typeof p[c - l] == "string" ? s.meta[i + String(c)] = parseString$1(p[c - l]) : s.meta[i + String(c)] = p[c - l];
            }
          }
        }
      }
    }
    function q(e, a, s) {
      let i = -1,
        o = -1,
        t = "",
        r = "";
      if (s.indexOf("++") > 0) t = s.replace(/.*\(([a-zA-Z0-9]+)\+\+.*/, "$1"), r = s.replace(/.*\.\.([a-zA-Z0-9]+).*/, "$1");else {
        s = s.replace(/[^a-zA-Z%]/g, ""), t = s.charAt(0), r = s.charAt(1), a.variables = {};
        for (let l of s) {
          let n = l.toLowerCase(),
            p = e.ntuples.symbol.indexOf(l);
          if (p === -1) throw Error(`Symbol undefined: ${l}`);
          a.variables[n] = {};
          for (let c in e.ntuples) e.ntuples[c][p] && (a.variables[n][c.replace(/^var/, "")] = e.ntuples[c][p]);
        }
      }
      i = e.ntuples.symbol.indexOf(t), o = e.ntuples.symbol.indexOf(r), i === -1 && (i = 0), o === -1 && (o = 0), e.ntuples.first && (e.ntuples.first.length > i && (a.firstX = e.ntuples.first[i]), e.ntuples.first.length > o && (a.firstY = e.ntuples.first[o])), e.ntuples.last && (e.ntuples.last.length > i && (a.lastX = e.ntuples.last[i]), e.ntuples.last.length > o && (a.lastY = e.ntuples.last[o])), e.ntuples.vardim && e.ntuples.vardim.length > i && (a.nbPoints = e.ntuples.vardim[i]), e.ntuples.factor && (e.ntuples.factor.length > i && (a.xFactor = e.ntuples.factor[i]), e.ntuples.factor.length > o && (a.yFactor = e.ntuples.factor[o])), e.ntuples.units && (e.ntuples.units.length > i && (e.ntuples.varname && e.ntuples.varname[i] ? a.xUnits = `${e.ntuples.varname[i]} [${e.ntuples.units[i]}]` : a.xUnits = e.ntuples.units[i]), e.ntuples.units.length > o && (e.ntuples.varname && e.ntuples.varname[o] ? a.yUnits = `${e.ntuples.varname[o]} [${e.ntuples.units[o]}]` : a.yUnits = e.ntuples.units[o]));
    }
    function w(e) {
      e.xFactor || (e.xFactor = 1), e.yFactor || (e.yFactor = 1);
    }
    var F = /[ \t]*,[ \t]*/,
      xe = {
        keepRecordsRegExp: /^$/,
        canonicDataLabels: !0,
        canonicMetadataLabels: !1,
        dynamicTyping: !0,
        withoutXY: !1,
        noTrimRegExp: /^$/,
        chromatogram: !1,
        keepSpectra: !1,
        noContour: !1,
        nbContourLevels: 7,
        noiseMultiplier: 5,
        profiling: !1
      };
    function Ne(e, a = {}) {
      e = ensureString(e), a = {
        ...xe,
        ...a
      }, a.logger?.debug("Starting jcamp conversion"), a.wantXY = !a.withoutXY, a.start = Date.now();
      let s = [],
        i = {
          profiling: a.profiling ? [] : !1,
          logs: [],
          entries: []
        },
        o = {
          children: []
        },
        t = o,
        r = [],
        l = {};
      if (typeof e != "string") throw new TypeError("the JCAMP should be a string");
      L(i, "Before split to LDRS", a);
      let n = e.replace(/[\r\n]+##/g, `
##`).split(`
##`);
      L(i, "Split to LDRS", a), n[0] && (n[0] = n[0].replace(/^[\r\n ]*##/, ""));
      for (let p of n) {
        let c = p.indexOf("="),
          d = c > 0 ? p.substring(0, c) : p,
          u = c > 0 ? d.match(a.noTrimRegExp) ? p.substring(c + 1) : p.substring(c + 1).trim() : "",
          f = d.replace(/[_ -]/g, "").toUpperCase();
        if (f === "DATATABLE") {
          let m = u.indexOf(`
`);
          if (m === -1 && (m = u.indexOf("\r")), m > 0) {
            let h = u.substring(0, m).split(/[ ,;\t]+/);
            q(t, l, h[0]), l.datatable = h[0], h[1] && h[1].indexOf("PEAKS") > -1 ? f = "PEAKTABLE" : h[1] && (h[1].indexOf("XYDATA") || h[0].indexOf("++") > 0) && (f = "XYDATA", l.nbPoints && (l.deltaX = (l.lastX - l.firstX) / (l.nbPoints - 1)));
          }
        }
        if (f === "XYDATA") {
          a.wantXY && (w(l), u.match(/.*\+\+.*/) ? (l.nbPoints && (l.deltaX = (l.lastX - l.firstX) / (l.nbPoints - 1)), U(l, u)) : D(l, u, i), t.spectra.push(l), l = {});
          continue;
        } else if (f === "PEAKTABLE") {
          a.wantXY && (w(l), D(l, u, i), t.spectra.push(l), l = {});
          continue;
        }
        if (f === "PEAKASSIGNMENTS") {
          a.wantXY && (u.match(/.*([^A-Z]*).*/) && V(l, u), t.spectra.push(l), l = {});
          continue;
        }
        if (f === "TITLE") {
          let m = t;
          m.children || (m.children = []), t = {
            spectra: [],
            ntuples: {},
            info: {},
            meta: {},
            tmp: {}
          }, m.children.push(t), r.push(m), s.push(t), t.title = u;
        } else f === "DATATYPE" ? (t.dataType = u, u.match(/(^nd|\snd\s)/i) && (t.twoD = !0)) : f === "NTUPLES" ? u.match(/(^nd|\snd\s)/i) && (t.twoD = !0) : f === "DATACLASS" ? t.dataClass = u : f === "JCAMPDX" ? t.jcampDX = M(u) : f === "JCAMPCS" ? t.jcampCS = M(u) : f === "XUNITS" ? l.xUnits = u : f === "YUNITS" ? l.yUnits = u : f === "FIRSTX" ? l.firstX = Number(u) : f === "LASTX" ? l.lastX = Number(u) : f === "FIRSTY" ? l.firstY = Number(u) : f === "LASTY" ? l.lastY = Number(u) : f === "NPOINTS" ? l.nbPoints = Number(u) : f === "XFACTOR" ? l.xFactor = Number(u) : f === "YFACTOR" ? l.yFactor = Number(u) : f === "MAXX" ? l.maxX = Number(u) : f === "MINX" ? l.minX = Number(u) : f === "MAXY" ? l.maxY = Number(u) : f === "MINY" ? l.minY = Number(u) : f === "DELTAX" ? l.deltaX = Number(u) : f === ".OBSERVEFREQUENCY" || f === "$SFO1" ? l.observeFrequency || (l.observeFrequency = Number(u)) : f === ".OBSERVENUCLEUS" ? l.xType || (t.xType = u.replace(/[^a-zA-Z0-9]/g, "")) : f === "$OFFSET" ? (t.shiftOffsetNum = 0, l.shiftOffsetVal || (l.shiftOffsetVal = Number(u))) : f === "$REFERENCEPOINT" || (f === "VARNAME" ? t.ntuples.varname = u.split(F) : f === "SYMBOL" ? t.ntuples.symbol = u.split(F) : f === "VARTYPE" ? t.ntuples.vartype = u.split(F) : f === "VARFORM" ? t.ntuples.varform = u.split(F) : f === "VARDIM" ? t.ntuples.vardim = Y(u.split(F)) : f === "UNITS" ? t.ntuples.units = u.split(F) : f === "FACTOR" ? t.ntuples.factor = Y(u.split(F)) : f === "FIRST" ? t.ntuples.first = Y(u.split(F)) : f === "LAST" ? t.ntuples.last = Y(u.split(F)) : f === "MIN" ? t.ntuples.min = Y(u.split(F)) : f === "MAX" ? t.ntuples.max = Y(u.split(F)) : f === ".NUCLEUS" ? t.ntuples && (t.ntuples.nucleus = u.split(F).map(m => m.replace(/[^a-zA-Z0-9]/g, ""))) : f === "PAGE" ? (l.page = u.trim(), l.pageValue = Number(u.replace(/^.*=/, "")), l.pageSymbol = l.page.replace(/[=].*/, "")) : f === "RETENTIONTIME" ? l.pageValue = Number(u) : le(f) ? l[I(f)] = u : f === "SAMPLEDESCRIPTION" ? l.sampleDescription = u : f.startsWith("$NUC") ? !t.tmp[f] && !u.includes("off") && (t.tmp[f] = u.replace(/[<>]/g, "")) : f === "END" && (t = r.pop()));
        if (t && t.info && t.meta && f.match(a.keepRecordsRegExp)) {
          let m, h;
          d.startsWith("$") ? (h = a.canonicMetadataLabels ? f.substring(1) : d.substring(1), m = t.meta) : (h = a.canonicDataLabels ? f : d, m = t.info), a.dynamicTyping && (u = parseString$1(u)), m[h] ? (Array.isArray(m[h]) || (m[h] = [m[h]]), m[h].push(u)) : m[h] = u;
        }
      }
      if (L(i, "Finished parsing", a), H(s, i, a), L(i, "Total time", a), i.entries = o.children, i.flatten = s, a.logger) {
        a.logger.debug("Finished jcamp conversion");
        for (let p of i.flatten) a.logger.debug(`${p.dataType} - ${p.title}`);
      }
      return i;
    }

    function fromJcamp(jcamp) {
      const data = Ne(jcamp, {
        chromatogram: true
      }).flatten[0].chromatogram;
      return fromJSON(data);
    }

    /**
     * General internal parsing function
     * @param text - Csv or tsv strings.
     * @param options - Parsing options
     * @returns parsed text file with column information
     */
    function parse$1(text, options = {}) {
      const {
        rescale = false,
        uniqueX = false,
        bestGuess = false,
        //@ts-expect-error old library used this property and we want to throw an error so that people are forced to migrate
        keepInfo
      } = options;
      let {
        xColumn = 0,
        yColumn = 1,
        numberColumns = Number.MAX_SAFE_INTEGER,
        maxNumberColumns = Number.MAX_SAFE_INTEGER,
        minNumberColumns = 2
      } = options;
      if (keepInfo !== undefined) {
        throw new Error('keepInfo has been deprecated, pelase use the new method parseXYAndKeepInfo');
      }
      text = ensureString(text);
      maxNumberColumns = Math.max(maxNumberColumns, xColumn + 1, yColumn + 1);
      minNumberColumns = Math.max(xColumn + 1, yColumn + 1, minNumberColumns);
      const lines = text.split(/[\r\n]+/);
      let matrix = [];
      const info = [];
      let position = 0;
      lines.forEach(line => {
        line = line.trim();
        // we will consider only lines that contains only numbers
        if (/[0-9]+/.test(line) && /^[0-9eE,;. \t+-]+$/.test(line)) {
          let fields = line.split(/,[; \t]+|[; \t]+/);
          if (fields.length === 1) {
            fields = line.split(/[,; \t]+/);
          }
          if (fields && fields.length >= minNumberColumns &&
          // we filter lines that have not enough or too many columns
          fields.length <= maxNumberColumns) {
            matrix.push(fields.map(value => parseFloat(value.replace(',', '.'))));
            position++;
          }
        } else if (line) {
          info.push({
            position,
            value: line
          });
        }
      });
      if (bestGuess) {
        if (matrix[0] && matrix[0].length === 3 && options.xColumn === undefined && options.yColumn === undefined) {
          // is the first column a seuqnetial number ?
          let skipFirstColumn = true;
          for (let i = 0; i < matrix.length - 1; i++) {
            if (Math.abs(matrix[i][0] - matrix[i + 1][0]) !== 1) {
              skipFirstColumn = false;
            }
          }
          if (skipFirstColumn) {
            xColumn = 1;
            yColumn = 2;
          }
        }
        if (matrix[0] && matrix[0].length > 3) {
          const xs = [];
          for (const row of matrix) {
            for (let i = xColumn; i < row.length; i += 2) {
              xs.push(row[i]);
            }
          }
          if (xIsMonotonic(xs)) {
            numberColumns = 2;
          }
        }
      }
      if (numberColumns) {
        const newMatrix = [];
        for (const row of matrix) {
          for (let i = 0; i < row.length; i += numberColumns) {
            newMatrix.push(row.slice(i, i + numberColumns));
          }
        }
        matrix = newMatrix;
      }
      let result = {
        x: matrix.map(row => row[xColumn]),
        y: matrix.map(row => row[yColumn])
      };
      if (uniqueX) {
        result = xyUniqueX(result, {
          algorithm: 'sum'
        });
      }
      if (rescale) {
        const maxY = xMaxValue(result.y);
        for (let i = 0; i < result.y.length; i++) {
          result.y[i] /= maxY;
        }
      }
      return {
        info,
        data: result
      };
    }

    /**
     * Parse a text-file and convert it to an object {x:[], y:[]}
     * @param text - Csv or tsv strings.
     * @param options - Parsing options
     * @returns - The parsed data
     */
    function parseXY(text, options = {}) {
      return parse$1(text, options).data;
    }

    function fromText(text, options = {}) {
      const data = parseXY(text, options);
      const time = data.x;
      let series = {
        intensity: data.y
      };
      return new Chromatogram(time, series);
    }

    // eslint-disable-next-line import/no-unassigned-import
    function decode$1(bytes, encoding = 'utf8') {
      const decoder = new TextDecoder(encoding);
      return decoder.decode(bytes);
    }
    const encoder = new TextEncoder();
    function encode(str) {
      return encoder.encode(str);
    }

    const defaultByteLength = 1024 * 8;
    const hostBigEndian = (() => {
      const array = new Uint8Array(4);
      const view = new Uint32Array(array.buffer);
      return !((view[0] = 1) & array[0]);
    })();
    const typedArrays = {
      int8: globalThis.Int8Array,
      uint8: globalThis.Uint8Array,
      int16: globalThis.Int16Array,
      uint16: globalThis.Uint16Array,
      int32: globalThis.Int32Array,
      uint32: globalThis.Uint32Array,
      uint64: globalThis.BigUint64Array,
      int64: globalThis.BigInt64Array,
      float32: globalThis.Float32Array,
      float64: globalThis.Float64Array
    };
    class IOBuffer {
      /**
       * @param data - The data to construct the IOBuffer with.
       * If data is a number, it will be the new buffer's length<br>
       * If data is `undefined`, the buffer will be initialized with a default length of 8Kb<br>
       * If data is an ArrayBuffer, SharedArrayBuffer, an ArrayBufferView (Typed Array), an IOBuffer instance,
       * or a Node.js Buffer, a view will be created over the underlying ArrayBuffer.
       * @param options
       */
      constructor(data = defaultByteLength, options = {}) {
        let dataIsGiven = false;
        if (typeof data === 'number') {
          data = new ArrayBuffer(data);
        } else {
          dataIsGiven = true;
          this.lastWrittenByte = data.byteLength;
        }
        const offset = options.offset ? options.offset >>> 0 : 0;
        const byteLength = data.byteLength - offset;
        let dvOffset = offset;
        if (ArrayBuffer.isView(data) || data instanceof IOBuffer) {
          if (data.byteLength !== data.buffer.byteLength) {
            dvOffset = data.byteOffset + offset;
          }
          data = data.buffer;
        }
        if (dataIsGiven) {
          this.lastWrittenByte = byteLength;
        } else {
          this.lastWrittenByte = 0;
        }
        this.buffer = data;
        this.length = byteLength;
        this.byteLength = byteLength;
        this.byteOffset = dvOffset;
        this.offset = 0;
        this.littleEndian = true;
        this._data = new DataView(this.buffer, dvOffset, byteLength);
        this._mark = 0;
        this._marks = [];
      }
      /**
       * Checks if the memory allocated to the buffer is sufficient to store more
       * bytes after the offset.
       * @param byteLength - The needed memory in bytes.
       * @returns `true` if there is sufficient space and `false` otherwise.
       */
      available(byteLength = 1) {
        return this.offset + byteLength <= this.length;
      }
      /**
       * Check if little-endian mode is used for reading and writing multi-byte
       * values.
       * @returns `true` if little-endian mode is used, `false` otherwise.
       */
      isLittleEndian() {
        return this.littleEndian;
      }
      /**
       * Set little-endian mode for reading and writing multi-byte values.
       */
      setLittleEndian() {
        this.littleEndian = true;
        return this;
      }
      /**
       * Check if big-endian mode is used for reading and writing multi-byte values.
       * @returns `true` if big-endian mode is used, `false` otherwise.
       */
      isBigEndian() {
        return !this.littleEndian;
      }
      /**
       * Switches to big-endian mode for reading and writing multi-byte values.
       */
      setBigEndian() {
        this.littleEndian = false;
        return this;
      }
      /**
       * Move the pointer n bytes forward.
       * @param n - Number of bytes to skip.
       */
      skip(n = 1) {
        this.offset += n;
        return this;
      }
      /**
       * Move the pointer n bytes backward.
       * @param n - Number of bytes to move back.
       */
      back(n = 1) {
        this.offset -= n;
        return this;
      }
      /**
       * Move the pointer to the given offset.
       * @param offset
       */
      seek(offset) {
        this.offset = offset;
        return this;
      }
      /**
       * Store the current pointer offset.
       * @see {@link IOBuffer#reset}
       */
      mark() {
        this._mark = this.offset;
        return this;
      }
      /**
       * Move the pointer back to the last pointer offset set by mark.
       * @see {@link IOBuffer#mark}
       */
      reset() {
        this.offset = this._mark;
        return this;
      }
      /**
       * Push the current pointer offset to the mark stack.
       * @see {@link IOBuffer#popMark}
       */
      pushMark() {
        this._marks.push(this.offset);
        return this;
      }
      /**
       * Pop the last pointer offset from the mark stack, and set the current
       * pointer offset to the popped value.
       * @see {@link IOBuffer#pushMark}
       */
      popMark() {
        const offset = this._marks.pop();
        if (offset === undefined) {
          throw new Error('Mark stack empty');
        }
        this.seek(offset);
        return this;
      }
      /**
       * Move the pointer offset back to 0.
       */
      rewind() {
        this.offset = 0;
        return this;
      }
      /**
       * Make sure the buffer has sufficient memory to write a given byteLength at
       * the current pointer offset.
       * If the buffer's memory is insufficient, this method will create a new
       * buffer (a copy) with a length that is twice (byteLength + current offset).
       * @param byteLength
       */
      ensureAvailable(byteLength = 1) {
        if (!this.available(byteLength)) {
          const lengthNeeded = this.offset + byteLength;
          const newLength = lengthNeeded * 2;
          const newArray = new Uint8Array(newLength);
          newArray.set(new Uint8Array(this.buffer));
          this.buffer = newArray.buffer;
          this.length = this.byteLength = newLength;
          this._data = new DataView(this.buffer);
        }
        return this;
      }
      /**
       * Read a byte and return false if the byte's value is 0, or true otherwise.
       * Moves pointer forward by one byte.
       */
      readBoolean() {
        return this.readUint8() !== 0;
      }
      /**
       * Read a signed 8-bit integer and move pointer forward by 1 byte.
       */
      readInt8() {
        return this._data.getInt8(this.offset++);
      }
      /**
       * Read an unsigned 8-bit integer and move pointer forward by 1 byte.
       */
      readUint8() {
        return this._data.getUint8(this.offset++);
      }
      /**
       * Alias for {@link IOBuffer#readUint8}.
       */
      readByte() {
        return this.readUint8();
      }
      /**
       * Read `n` bytes and move pointer forward by `n` bytes.
       */
      readBytes(n = 1) {
        return this.readArray(n, 'uint8');
      }
      /**
       * Creates an array of corresponding to the type `type` and size `size`.
       * For example type `uint8` will create a `Uint8Array`.
       * @param size - size of the resulting array
       * @param type - number type of elements to read
       */
      readArray(size, type) {
        const bytes = typedArrays[type].BYTES_PER_ELEMENT * size;
        const offset = this.byteOffset + this.offset;
        const slice = this.buffer.slice(offset, offset + bytes);
        if (this.littleEndian === hostBigEndian && type !== 'uint8' && type !== 'int8') {
          const slice = new Uint8Array(this.buffer.slice(offset, offset + bytes));
          slice.reverse();
          const returnArray = new typedArrays[type](slice.buffer);
          this.offset += bytes;
          returnArray.reverse();
          return returnArray;
        }
        const returnArray = new typedArrays[type](slice);
        this.offset += bytes;
        return returnArray;
      }
      /**
       * Read a 16-bit signed integer and move pointer forward by 2 bytes.
       */
      readInt16() {
        const value = this._data.getInt16(this.offset, this.littleEndian);
        this.offset += 2;
        return value;
      }
      /**
       * Read a 16-bit unsigned integer and move pointer forward by 2 bytes.
       */
      readUint16() {
        const value = this._data.getUint16(this.offset, this.littleEndian);
        this.offset += 2;
        return value;
      }
      /**
       * Read a 32-bit signed integer and move pointer forward by 4 bytes.
       */
      readInt32() {
        const value = this._data.getInt32(this.offset, this.littleEndian);
        this.offset += 4;
        return value;
      }
      /**
       * Read a 32-bit unsigned integer and move pointer forward by 4 bytes.
       */
      readUint32() {
        const value = this._data.getUint32(this.offset, this.littleEndian);
        this.offset += 4;
        return value;
      }
      /**
       * Read a 32-bit floating number and move pointer forward by 4 bytes.
       */
      readFloat32() {
        const value = this._data.getFloat32(this.offset, this.littleEndian);
        this.offset += 4;
        return value;
      }
      /**
       * Read a 64-bit floating number and move pointer forward by 8 bytes.
       */
      readFloat64() {
        const value = this._data.getFloat64(this.offset, this.littleEndian);
        this.offset += 8;
        return value;
      }
      /**
       * Read a 64-bit signed integer number and move pointer forward by 8 bytes.
       */
      readBigInt64() {
        const value = this._data.getBigInt64(this.offset, this.littleEndian);
        this.offset += 8;
        return value;
      }
      /**
       * Read a 64-bit unsigned integer number and move pointer forward by 8 bytes.
       */
      readBigUint64() {
        const value = this._data.getBigUint64(this.offset, this.littleEndian);
        this.offset += 8;
        return value;
      }
      /**
       * Read a 1-byte ASCII character and move pointer forward by 1 byte.
       */
      readChar() {
        return String.fromCharCode(this.readInt8());
      }
      /**
       * Read `n` 1-byte ASCII characters and move pointer forward by `n` bytes.
       */
      readChars(n = 1) {
        let result = '';
        for (let i = 0; i < n; i++) {
          result += this.readChar();
        }
        return result;
      }
      /**
       * Read the next `n` bytes, return a UTF-8 decoded string and move pointer
       * forward by `n` bytes.
       */
      readUtf8(n = 1) {
        return decode$1(this.readBytes(n));
      }
      /**
       * Read the next `n` bytes, return a string decoded with `encoding` and move pointer
       * forward by `n` bytes.
       * If no encoding is passed, the function is equivalent to @see {@link IOBuffer#readUtf8}
       */
      decodeText(n = 1, encoding = 'utf-8') {
        return decode$1(this.readBytes(n), encoding);
      }
      /**
       * Write 0xff if the passed value is truthy, 0x00 otherwise and move pointer
       * forward by 1 byte.
       */
      writeBoolean(value) {
        this.writeUint8(value ? 0xff : 0x00);
        return this;
      }
      /**
       * Write `value` as an 8-bit signed integer and move pointer forward by 1 byte.
       */
      writeInt8(value) {
        this.ensureAvailable(1);
        this._data.setInt8(this.offset++, value);
        this._updateLastWrittenByte();
        return this;
      }
      /**
       * Write `value` as an 8-bit unsigned integer and move pointer forward by 1
       * byte.
       */
      writeUint8(value) {
        this.ensureAvailable(1);
        this._data.setUint8(this.offset++, value);
        this._updateLastWrittenByte();
        return this;
      }
      /**
       * An alias for {@link IOBuffer#writeUint8}.
       */
      writeByte(value) {
        return this.writeUint8(value);
      }
      /**
       * Write all elements of `bytes` as uint8 values and move pointer forward by
       * `bytes.length` bytes.
       */
      writeBytes(bytes) {
        this.ensureAvailable(bytes.length);
        for (let i = 0; i < bytes.length; i++) {
          this._data.setUint8(this.offset++, bytes[i]);
        }
        this._updateLastWrittenByte();
        return this;
      }
      /**
       * Write `value` as a 16-bit signed integer and move pointer forward by 2
       * bytes.
       */
      writeInt16(value) {
        this.ensureAvailable(2);
        this._data.setInt16(this.offset, value, this.littleEndian);
        this.offset += 2;
        this._updateLastWrittenByte();
        return this;
      }
      /**
       * Write `value` as a 16-bit unsigned integer and move pointer forward by 2
       * bytes.
       */
      writeUint16(value) {
        this.ensureAvailable(2);
        this._data.setUint16(this.offset, value, this.littleEndian);
        this.offset += 2;
        this._updateLastWrittenByte();
        return this;
      }
      /**
       * Write `value` as a 32-bit signed integer and move pointer forward by 4
       * bytes.
       */
      writeInt32(value) {
        this.ensureAvailable(4);
        this._data.setInt32(this.offset, value, this.littleEndian);
        this.offset += 4;
        this._updateLastWrittenByte();
        return this;
      }
      /**
       * Write `value` as a 32-bit unsigned integer and move pointer forward by 4
       * bytes.
       */
      writeUint32(value) {
        this.ensureAvailable(4);
        this._data.setUint32(this.offset, value, this.littleEndian);
        this.offset += 4;
        this._updateLastWrittenByte();
        return this;
      }
      /**
       * Write `value` as a 32-bit floating number and move pointer forward by 4
       * bytes.
       */
      writeFloat32(value) {
        this.ensureAvailable(4);
        this._data.setFloat32(this.offset, value, this.littleEndian);
        this.offset += 4;
        this._updateLastWrittenByte();
        return this;
      }
      /**
       * Write `value` as a 64-bit floating number and move pointer forward by 8
       * bytes.
       */
      writeFloat64(value) {
        this.ensureAvailable(8);
        this._data.setFloat64(this.offset, value, this.littleEndian);
        this.offset += 8;
        this._updateLastWrittenByte();
        return this;
      }
      /**
       * Write `value` as a 64-bit signed bigint and move pointer forward by 8
       * bytes.
       */
      writeBigInt64(value) {
        this.ensureAvailable(8);
        this._data.setBigInt64(this.offset, value, this.littleEndian);
        this.offset += 8;
        this._updateLastWrittenByte();
        return this;
      }
      /**
       * Write `value` as a 64-bit unsigned bigint and move pointer forward by 8
       * bytes.
       */
      writeBigUint64(value) {
        this.ensureAvailable(8);
        this._data.setBigUint64(this.offset, value, this.littleEndian);
        this.offset += 8;
        this._updateLastWrittenByte();
        return this;
      }
      /**
       * Write the charCode of `str`'s first character as an 8-bit unsigned integer
       * and move pointer forward by 1 byte.
       */
      writeChar(str) {
        return this.writeUint8(str.charCodeAt(0));
      }
      /**
       * Write the charCodes of all `str`'s characters as 8-bit unsigned integers
       * and move pointer forward by `str.length` bytes.
       */
      writeChars(str) {
        for (let i = 0; i < str.length; i++) {
          this.writeUint8(str.charCodeAt(i));
        }
        return this;
      }
      /**
       * UTF-8 encode and write `str` to the current pointer offset and move pointer
       * forward according to the encoded length.
       */
      writeUtf8(str) {
        return this.writeBytes(encode(str));
      }
      /**
       * Export a Uint8Array view of the internal buffer.
       * The view starts at the byte offset and its length
       * is calculated to stop at the last written byte or the original length.
       */
      toArray() {
        return new Uint8Array(this.buffer, this.byteOffset, this.lastWrittenByte);
      }
      /**
       * Update the last written byte offset
       * @private
       */
      _updateLastWrittenByte() {
        if (this.offset > this.lastWrittenByte) {
          this.lastWrittenByte = this.offset;
        }
      }
    }

    const types = {
      BYTE: 1,
      CHAR: 2,
      SHORT: 3,
      INT: 4,
      FLOAT: 5,
      DOUBLE: 6
    };
    /**
     * Parse a number into their respective type
     * @param type - integer that represents the type
     * @return - parsed value of the type
     */
    function num2str(type) {
      switch (Number(type)) {
        case types.BYTE:
          return 'byte';
        case types.CHAR:
          return 'char';
        case types.SHORT:
          return 'short';
        case types.INT:
          return 'int';
        case types.FLOAT:
          return 'float';
        case types.DOUBLE:
          return 'double';
        default:
          return 'undefined';
      }
    }
    /**
     * Parse a number type identifier to his size in bytes
     * @param type - integer that represents the type
     * @return size of the type
     */
    function num2bytes(type) {
      switch (Number(type)) {
        case types.BYTE:
          return 1;
        case types.CHAR:
          return 1;
        case types.SHORT:
          return 2;
        case types.INT:
          return 4;
        case types.FLOAT:
          return 4;
        case types.DOUBLE:
          return 8;
        default:
          return -1;
      }
    }
    /**
     * Reverse search of num2str
     * @param type - string that represents the type
     * @return parsed value of the type
     */
    function str2num(type) {
      switch (String(type)) {
        case 'byte':
          return types.BYTE;
        case 'char':
          return types.CHAR;
        case 'short':
          return types.SHORT;
        case 'int':
          return types.INT;
        case 'float':
          return types.FLOAT;
        case 'double':
          return types.DOUBLE;
        /* istanbul ignore next */
        default:
          return -1;
      }
    }
    /**
     * Auxiliary function to read numeric data
     * @param size - Size of the element to read
     * @param bufferReader - Function to read next value
     * @return
     */
    function readNumber(size, bufferReader) {
      if (size !== 1) {
        const numbers = new Array(size);
        for (let i = 0; i < size; i++) {
          numbers[i] = bufferReader();
        }
        return numbers;
      } else {
        return bufferReader();
      }
    }
    /**
     * Given a type and a size reads the next element
     * @param buffer - Buffer for the file data
     * @param type - Type of the data to read
     * @param size - Size of the element to read
     * @return
     */
    function readType(buffer, type, size) {
      switch (type) {
        case types.BYTE:
          return Array.from(buffer.readBytes(size));
        case types.CHAR:
          return trimNull(buffer.readChars(size));
        case types.SHORT:
          return readNumber(size, buffer.readInt16.bind(buffer));
        case types.INT:
          return readNumber(size, buffer.readInt32.bind(buffer));
        case types.FLOAT:
          return readNumber(size, buffer.readFloat32.bind(buffer));
        case types.DOUBLE:
          return readNumber(size, buffer.readFloat64.bind(buffer));
        default:
          throw new Error(`non valid type ${type}`);
      }
    }
    /**
     * Removes null terminate value
     * @param value - String to trim
     * @return - Trimmed string
     */
    function trimNull(value) {
      if (value.charCodeAt(value.length - 1) === 0) {
        return value.substring(0, value.length - 1);
      }
      return value;
    }

    // const STREAMING = 4294967295;
    /**
     * Read data for the given non-record variable
     * @param buffer - Buffer for the file data
     * @param variable - Variable metadata
     * @return - Data of the element
     */
    function nonRecord(buffer, variable) {
      // variable type
      const type = str2num(variable.type);
      // size of the data
      const size = variable.size / num2bytes(type);
      // iterates over the data
      const data = new Array(size);
      for (let i = 0; i < size; i++) {
        data[i] = readType(buffer, type, 1);
      }
      return data;
    }
    /**
     * Read data for the given record variable
     * @param buffer - Buffer for the file data
     * @param variable - Variable metadata
     * @param recordDimension - Record dimension metadata
     * @return - Data of the element
     */
    function record(buffer, variable, recordDimension) {
      // variable type
      const type = str2num(variable.type);
      const width = variable.size ? variable.size / num2bytes(type) : 1;
      // size of the data
      // TODO streaming data
      const size = recordDimension.length;
      // iterates over the data
      const data = new Array(size);
      const step = recordDimension.recordStep;
      if (step) {
        for (let i = 0; i < size; i++) {
          const currentOffset = buffer.offset;
          data[i] = readType(buffer, type, width);
          buffer.seek(currentOffset + step);
        }
      } else {
        throw new Error('recordDimension.recordStep is undefined');
      }
      return data;
    }

    /**
     * Throws a non-valid NetCDF exception if the statement it's true
     * @ignore
     * @param statement - Throws if true
     * @param reason - Reason to throw
     */
    function notNetcdf(statement, reason) {
      if (statement) {
        throw new TypeError(`Not a valid NetCDF v3.x file: ${reason}`);
      }
    }
    /**
     * Moves 1, 2, or 3 bytes to next 4-byte boundary
     * @param buffer - Buffer for the file data
     */
    function padding(buffer) {
      if (buffer.offset % 4 !== 0) {
        buffer.skip(4 - buffer.offset % 4);
      }
    }
    /**
     * Reads the name
     * @param buffer - Buffer for the file data
     * @return Name
     */
    function readName(buffer) {
      // Read name
      const nameLength = buffer.readUint32();
      const name = buffer.readChars(nameLength);
      // validate name
      // TODO
      // Apply padding
      padding(buffer);
      return name;
    }

    // Grammar constants
    const ZERO = 0;
    const NC_DIMENSION = 10;
    const NC_VARIABLE = 11;
    const NC_ATTRIBUTE = 12;
    const NC_UNLIMITED = 0;
    /**
     * Reads the file header as @see {@link Header}
     * @param buffer - Buffer for the file data
     * @param version - Version of the file
     * @returns
     */
    function header(buffer, version) {
      const header = {
        version
      };
      const recordDimension = {
        length: buffer.readUint32()
      };
      const dimList = dimensionsList(buffer);
      if (!Array.isArray(dimList)) {
        recordDimension.id = dimList.recordId;
        recordDimension.name = dimList.recordName;
        header.dimensions = dimList.dimensions;
      }
      header.globalAttributes = attributesList(buffer);
      const variables = variablesList(buffer, recordDimension?.id, version);
      if (!Array.isArray(variables)) {
        header.variables = variables.variables;
        recordDimension.recordStep = variables.recordStep;
      }
      header.recordDimension = recordDimension;
      return header;
    }
    /**
     * List of dimensions
     * @param buffer - Buffer for the file data
     * @return List of dimensions
     */
    function dimensionsList(buffer) {
      const result = {};
      let recordId, recordName;
      const dimList = buffer.readUint32();
      let dimensions;
      if (dimList === ZERO) {
        notNetcdf(buffer.readUint32() !== ZERO, 'wrong empty tag for list of dimensions');
        return [];
      } else {
        notNetcdf(dimList !== NC_DIMENSION, 'wrong tag for list of dimensions');
        // Length of dimensions
        const dimensionSize = buffer.readUint32();
        dimensions = new Array(dimensionSize);
        //populate `name` and `size` for each dimension
        for (let dim = 0; dim < dimensionSize; dim++) {
          // Read name
          const name = readName(buffer);
          // Read dimension size
          const size = buffer.readUint32();
          if (size === NC_UNLIMITED) {
            // in netcdf 3 one field can be of size unlimited
            recordId = dim;
            recordName = name;
          }
          dimensions[dim] = {
            name,
            size
          };
        }
      }
      if (recordId !== undefined) {
        result.recordId = recordId;
      }
      if (recordName !== undefined) {
        result.recordName = recordName;
      }
      result.dimensions = dimensions;
      return result;
    }
    /**
     * List of attributes
     * @param buffer - Buffer for the file data
     * @return - List of attributes with:
     */
    function attributesList(buffer) {
      const gAttList = buffer.readUint32();
      let attributes;
      if (gAttList === ZERO) {
        notNetcdf(buffer.readUint32() !== ZERO, 'wrong empty tag for list of attributes');
        return [];
      } else {
        notNetcdf(gAttList !== NC_ATTRIBUTE, 'wrong tag for list of attributes');
        // Length of attributes
        const attributeSize = buffer.readUint32();
        attributes = new Array(attributeSize);
        // Populate `name`, `type` and `value` for each attribute
        for (let gAtt = 0; gAtt < attributeSize; gAtt++) {
          // Read name
          const name = readName(buffer);
          // Read type
          const type = buffer.readUint32();
          notNetcdf(type < 1 || type > 6, `non valid type ${type}`);
          // Read attribute
          const size = buffer.readUint32();
          const value = readType(buffer, type, size);
          // Apply padding
          padding(buffer);
          attributes[gAtt] = {
            name,
            type: num2str(type),
            value
          };
        }
      }
      return attributes;
    }
    /**
     * @param buffer - Buffer for the file data
     * @param recordId - Id of the unlimited dimension (also called record dimension)
     * This value may be undefined if there is no unlimited dimension
     * @param version - Version of the file
     * @return - Number of recordStep and list of variables @see {@link Variables}
     */
    function variablesList(buffer, recordId, version) {
      const varList = buffer.readUint32();
      let recordStep = 0;
      let variables;
      if (varList === ZERO) {
        notNetcdf(buffer.readUint32() !== ZERO, 'wrong empty tag for list of variables');
        return [];
      } else {
        notNetcdf(varList !== NC_VARIABLE, 'wrong tag for list of variables');
        // Length of variables
        const variableSize = buffer.readUint32();
        variables = new Array(variableSize);
        for (let v = 0; v < variableSize; v++) {
          // Read name
          const name = readName(buffer);
          // Read dimensionality of the variable
          const dimensionality = buffer.readUint32();
          // Index into the list of dimensions
          const dimensionsIds = new Array(dimensionality);
          for (let dim = 0; dim < dimensionality; dim++) {
            dimensionsIds[dim] = buffer.readUint32();
          }
          // Read variables size
          const attributes = attributesList(buffer);
          // Read type
          const type = buffer.readUint32();
          notNetcdf(type < 1 && type > 6, `non valid type ${type}`);
          // Read variable size
          // The 32-bit varSize field is not large enough to contain the size of variables that require
          // more than 2^32 - 4 bytes, so 2^32 - 1 is used in the varSize field for such variables.
          const varSize = buffer.readUint32();
          // Read offset
          let offset = buffer.readUint32();
          if (version === 2) {
            notNetcdf(offset > 0, 'offsets larger than 4GB not supported');
            offset = buffer.readUint32();
          }
          let record = false;
          // Count amount of record variables
          if (typeof recordId !== 'undefined' && dimensionsIds[0] === recordId) {
            recordStep += varSize;
            record = true;
          }
          variables[v] = {
            name,
            dimensions: dimensionsIds,
            attributes,
            type: num2str(type),
            size: varSize,
            offset,
            record
          };
        }
      }
      return {
        variables,
        recordStep
      };
    }

    function toString$2() {
      const result = [];
      result.push('DIMENSIONS');
      for (const dimension of this.dimensions) {
        result.push(`  ${dimension.name.padEnd(30)} = size: ${dimension.size}`);
      }
      result.push('');
      result.push('GLOBAL ATTRIBUTES');
      for (const attribute of this.globalAttributes) {
        result.push(`  ${attribute.name.padEnd(30)} = ${attribute.value}`);
      }
      const variables = JSON.parse(JSON.stringify(this.variables));
      result.push('');
      result.push('VARIABLES:');
      for (const variable of variables) {
        variable.value = this.getDataVariable(variable);
        let stringify = JSON.stringify(variable.value);
        if (stringify.length > 50) stringify = stringify.substring(0, 50);
        if (!isNaN(variable.value.length)) {
          stringify += ` (length: ${variable.value.length})`;
        }
        result.push(`  ${variable.name.padEnd(30)} = ${stringify}`);
      }
      return result.join('\n');
    }

    /**
     * Reads a NetCDF v3.x file
     * [See specification](https://www.unidata.ucar.edu/software/netcdf/docs/file_format_specifications.html)
     * @param data - ArrayBuffer or any Typed Array (including Node.js' Buffer from v4) with the data
     * @constructor
     */
    class NetCDFReader {
      constructor(data) {
        this.toString = toString$2;
        const buffer = new IOBuffer(data);
        buffer.setBigEndian();
        // Validate that it's a NetCDF file
        notNetcdf(buffer.readChars(3) !== 'CDF', 'should start with CDF');
        // Check the NetCDF format
        const version = buffer.readByte();
        notNetcdf(version > 2, 'unknown version');
        // Read the header
        this.header = header(buffer, version);
        this.buffer = buffer;
      }
      /**
       * @return - Version for the NetCDF format
       */
      get version() {
        if (this.header.version === 1) {
          return 'classic format';
        } else {
          return '64-bit offset format';
        }
      }
      /**
       * @return {object} - Metadata for the record dimension
       *  * `length`: Number of elements in the record dimension
       *  * `id`: Id number in the list of dimensions for the record dimension
       *  * `name`: String with the name of the record dimension
       *  * `recordStep`: Number with the record variables step size
       */
      get recordDimension() {
        return this.header.recordDimension;
      }
      /**
       * @return - Array - List of dimensions with:
       *  * `name`: String with the name of the dimension
       *  * `size`: Number with the size of the dimension
       */
      get dimensions() {
        return this.header.dimensions;
      }
      /**
       * @return - Array - List of global attributes with:
       *  * `name`: String with the name of the attribute
       *  * `type`: String with the type of the attribute
       *  * `value`: A number or string with the value of the attribute
       */
      get globalAttributes() {
        return this.header.globalAttributes;
      }
      /**
       * Returns the value of an attribute
       * @param - AttributeName
       * @return - Value of the attributeName or null
       */
      getAttribute(attributeName) {
        const attribute = this.globalAttributes.find(val => val.name === attributeName);
        if (attribute) return attribute.value;
        return null;
      }
      /**
       * Returns the value of a variable as a string
       * @param - variableName
       * @return - Value of the variable as a string or null
       */
      getDataVariableAsString(variableName) {
        const variable = this.getDataVariable(variableName);
        if (variable) return variable.join('');
        return null;
      }
      get variables() {
        return this.header.variables;
      }
      /**
       * Retrieves the data for a given variable
       * @param variableName - Name of the variable to search or variable object
       * @return The variable values
       */
      getDataVariable(variableName) {
        let variable;
        if (typeof variableName === 'string') {
          // search the variable
          variable = this.header.variables.find(val => {
            return val.name === variableName;
          });
        } else {
          variable = variableName;
        }
        // throws if variable not found
        if (variable === undefined) {
          throw new Error('Not a valid NetCDF v3.x file: variable not found');
        }
        // go to the offset position
        this.buffer.seek(variable.offset);
        if (variable.record) {
          // record variable case
          return record(this.buffer, variable, this.header.recordDimension);
        } else {
          // non-record variable case
          return nonRecord(this.buffer, variable);
        }
      }
      /**
       * Check if a dataVariable exists
       * @param variableName - Name of the variable to find
       * @return boolean
       */
      dataVariableExists(variableName) {
        const variable = this.header.variables.find(val => {
          return val.name === variableName;
        });
        return variable !== undefined;
      }
      /**
       * Check if an attribute exists
       * @param attributeName - Name of the attribute to find
       * @return boolean
       */
      attributeExists(attributeName) {
        const attribute = this.globalAttributes.find(val => val.name === attributeName);
        return attribute !== undefined;
      }
    }

    /* reader.toString() provides the following information
        DIMENSIONS
          point_number                   = size: 0
          scan_number                    = size: 60
          error_number                   = size: 1
          _64_byte_string                = size: 64

        GLOBAL ATTRIBUTES
          dataset_completeness           = C1
          ms_template_revision           = 1.0.1
          netcdf_revision                = 4.2
          languages                      = English
          administrative_comments        =
          netcdf_file_date_time_stamp    = 202003031432433600000
          experiment_date_time_stamp     = 202003031432433600000
          source_file_reference          = JC-012_cleavage test_Scan1_is1.datx 2020.03.03 14:32:43
          source_file_format             = Advion ExpressIon Compact Mass Spectrometer Data System
          source_file_date_time_stamp    = 202003031432433600000
          experiment_title               =
          experiment_type                = Continuum Mass Spectrum
          test_ionization_mode           = Electrospray Ionization
          test_ionization_polarity       = Positive Polarity
          sample_state                   = Other State
          test_separation_type           = No Chromatography
          test_ms_inlet                  = Direct Inlet Probe
          test_detector_type             = Electron Multiplier
          test_resolution_type           = Constant Resolution
          test_scan_function             = Mass Scan
          test_scan_direction            = Up
          test_scan_law                  = Linear
          raw_data_mass_format           = Float
          raw_data_time_format           = Double
          raw_data_intensity_format      = Float
          units                          = Seconds
          global_mass_min                = 9.949999809265137
          global_mass_max                = 1199.75
          actual_run_time_length         = 133.46099853515625
          starting_scan_number           = 1
          actual_delay_time              = 0
          raw_data_uniform_sampling_flag = 0

        VARIABLES:
          error_log                      = ["","","","","","","","","","","","","","","",""," (length: 64)
          scan_index                     = [0,3096,6282,9865,13409,16765,20281,23603,27099,30 (length: 60)
          point_count                    = [3096,3186,3583,3544,3356,3516,3322,3496,3351,3031 (length: 60)
          flag_count                     = [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 (length: 60)
          actual_scan_number             = [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19 (length: 60)
          a_d_coaddition_factor          = [-9999,-9999,-9999,-9999,-9999,-9999,-9999,-9999,- (length: 60)
          a_d_sampling_rate              = [-9999,-9999,-9999,-9999,-9999,-9999,-9999,-9999,- (length: 60)
          inter_scan_time                = [-9999,-9999,-9999,-9999,-9999,-9999,-9999,-9999,- (length: 60)
          mass_range_min                 = [9.949999809265137,9.949999809265137,9.94999980926 (length: 60)
          mass_range_max                 = [164.6999969482422,169.1999969482422,189.050003051 (length: 60)
          scan_acquisition_time          = [0.08100000023841858,2.3420000076293945,4.60300016 (length: 60)
          scan_duration                  = [2.261000007390976,2.261000156402588,2.25999975204 (length: 60)
          resolution                     = [-9999,-9999,-9999,-9999,-9999,-9999,-9999,-9999,- (length: 60)
          time_range_min                 = [-9999,-9999,-9999,-9999,-9999,-9999,-9999,-9999,- (length: 60)
          time_range_max                 = [-9999,-9999,-9999,-9999,-9999,-9999,-9999,-9999,- (length: 60)
          total_intensity                = [4498210816,4468554240,5001547264,5405233152,50000 (length: 60)
          mass_values                    = [9.949999809265137,83.5,83.55000305175781,83.59999 (length: 199393)
          intensity_values               = [0,818716,462148,0,735558,952901,0,165241,421829,0 (length: 199393)
    */

    function advionGCMS(reader) {
      const time = reader.getDataVariable('scan_acquisition_time');
      const tic = reader.getDataVariable('total_intensity');

      // variables to get the mass-intensity values
      let scanIndex = reader.getDataVariable('scan_index');
      const massValues = reader.getDataVariable('mass_values');
      const intensityValues = reader.getDataVariable('intensity_values');
      scanIndex.push(massValues.length);
      let ms = new Array(time.length);
      let index = 0;
      for (let i = 0; i < ms.length; i++) {
        let size = scanIndex[i + 1] - scanIndex[i];
        ms[i] = [new Array(size), new Array(size)];
        for (let j = 0; j < size; j++) {
          ms[i][0][j] = massValues[index];
          ms[i][1][j] = intensityValues[index++];
        }
      }
      return {
        times: time,
        series: [{
          name: 'tic',
          dimension: 1,
          data: tic
        }, {
          name: 'ms',
          dimension: 2,
          data: ms
        }]
      };
    }

    /* reader.toString() provides the following information
        GLOBAL ATTRIBUTES
          dataset_completeness           = C1+C2
          ms_template_revision           = 1.0.1
          netcdf_revision                = 2.3.2
          languages                      = English
          administrative_comments        = 1% CH2Cl2
          dataset_origin                 = Santa Clara, CA
          netcdf_file_date_time_stamp    = 20161012052159+0200
          experiment_title               = P071 Essence super BP
          experiment_date_time_stamp     = 20070923040800+0200
          operator_name                  = SC
          external_file_ref_0            = FIRE_RTL.M
          experiment_type                = Centroided Mass Spectrum
          number_of_times_processed      = 1
          number_of_times_calibrated     = 0
          sample_state                   = Other State
          test_separation_type           = No Chromatography
          test_ms_inlet                  = Capillary Direct
          test_ionization_mode           = Electron Impact
          test_ionization_polarity       = Positive Polarity
          test_detector_type             = Electron Multiplier
          test_resolution_type           = Constant Resolution
          test_scan_function             = Mass Scan
          test_scan_direction            = Up
          test_scan_law                  = Linear
          raw_data_mass_format           = Float
          raw_data_time_format           = Short
          raw_data_intensity_format      = Float

        VARIABLES:
          error_log                      = [" "," "," "," "," "," "," "," "," "," "," "," "," (length: 64)
          a_d_sampling_rate              = [-9999,-9999,-9999,-9999,-9999,-9999,-9999,-9999,- (length: 6401)
          a_d_coaddition_factor          = [-9999,-9999,-9999,-9999,-9999,-9999,-9999,-9999,- (length: 6402)
          scan_acquisition_time          = [5.25,5.84,6.428999999999999,7.019,7.609,8.199,8.7 (length: 6401)
          scan_duration                  = [-9999,-9999,-9999,-9999,-9999,-9999,-9999,-9999,- (length: 6401)
          inter_scan_time                = [-9999,-9999,-9999,-9999,-9999,-9999,-9999,-9999,- (length: 6401)
          resolution                     = [-9999,-9999,-9999,-9999,-9999,-9999,-9999,-9999,- (length: 6401)
          actual_scan_number             = [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19 (length: 6401)
          total_intensity                = [3134,3157,3085,3134,3093,3113,3061,3057,3030,3166 (length: 6401)
          mass_range_min                 = [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 (length: 6401)
          mass_range_max                 = [206.89999389648438,206.89999389648438,207,207.100 (length: 6401)
          time_range_min                 = [-9999,-9999,-9999,-9999,-9999,-9999,-9999,-9999,- (length: 6401)
          time_range_max                 = [-9999,-9999,-9999,-9999,-9999,-9999,-9999,-9999,- (length: 6401)
          scan_index                     = [0,11,22,33,44,55,66,76,88,99,111,122,134,145,156, (length: 6401)
          point_count                    = [11,11,11,11,11,11,10,12,11,12,11,12,11,11,11,11,1 (length: 6401)
          flag_count                     = [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 (length: 6401)
          mass_values                    = [16,17,18.100000381469727,28,32,35,36,38,40,44.099 (length: 157201)
          time_values                    = [9.969209968386869e+36,9.969209968386869e+36,9.969 (length: 157201)
          intensity_values               = [37,293,1243,737,420,45,196,72,22,35,34,28,299,123 (length: 157201)
          instrument_name                = ["G","a","s"," ","C","h","r","o","m","a","t","o"," (length: 32)
          instrument_id                  = [" "," "," "," "," "," "," "," "," "," "," "," "," (length: 32)
          instrument_mfr                 = [" "," "," "," "," "," "," "," "," "," "," "," "," (length: 32)
          instrument_model               = [" "," "," "," "," "," "," "," "," "," "," "," "," (length: 32)
          instrument_serial_no           = [" "," "," "," "," "," "," "," "," "," "," "," "," (length: 32)
          instrument_sw_version          = [" "," "," "," "," "," "," "," "," "," "," "," "," (length: 32)
          instrument_fw_version          = [" "," "," "," "," "," "," "," "," "," "," "," "," (length: 32)
          instrument_os_version          = [" "," "," "," "," "," "," "," "," "," "," "," "," (length: 32)
          instrument_app_version         = [" "," "," "," "," "," "," "," "," "," "," "," "," (length: 32)
          instrument_comments            = [" "," "," "," "," "," "," "," "," "," "," "," "," (length: 32)
    */

    function agilentGCMS(reader) {
      const time = reader.getDataVariable('scan_acquisition_time');
      const tic = reader.getDataVariable('total_intensity');

      // variables to get the mass-intensity values
      const pointCount = reader.getDataVariable('point_count');
      const massValues = reader.getDataVariable('mass_values');
      const intensityValues = reader.getDataVariable('intensity_values');
      let ms = new Array(pointCount.length);
      let index = 0;
      for (let i = 0; i < ms.length; i++) {
        let size = pointCount[i];
        ms[i] = [new Array(size), new Array(size)];
        for (let j = 0; j < size; j++) {
          ms[i][0][j] = massValues[index];
          ms[i][1][j] = intensityValues[index++];
        }
      }
      return {
        times: time,
        series: [{
          name: 'tic',
          dimension: 1,
          data: tic
        }, {
          name: 'ms',
          dimension: 2,
          data: ms
        }]
      };
    }

    /* reader.toString() provides the following information
        GLOBAL ATTRIBUTES
          dataset_completeness           = C1+C2
          aia_template_revision          = 1.0
          netcdf_revision                = 2.3
          languages                      = English only
          injection_date_time_stamp      = 20181030174305+0000
          HP_injection_time              = 30-Oct-18, 17:43:05
          experiment_title               = SequenceLine: 1  Inj: 1
          operator_name                  = SYSTEM
          separation_experiment_type     = liquid chromatography
          source_file_reference          = C:\CHEM32\1\DATA\MINGMING\MW-1-MEO-I IC-90 2018-10-30 17-42-13\MW-2-6-6 IC 90.D
          sample_name                    = MW-2-6-6 IC 90
          sample_id                      =
          detector_unit                  = mAU
          detection_method_name          = POS 3 IC 90-10 31 MIN.M
          detector_name                  = DAD1 A, Sig=254,4 Ref=360,100
          retention_unit                 = seconds

       VARIABLES:
          detector_maximum_value         = [130.9263458251953] (length: 1)
          detector_minimum_value         = [-0.1758841574192047] (length: 1)
          actual_run_time_length         = [1860] (length: 1)
          actual_delay_time              = [0.012000000104308128] (length: 1)
          actual_sampling_interval       = [0.4000000059604645] (length: 1)
          ordinate_values                = [-0.07588416337966919,-0.07525086402893066,-0.0740 (length: 4651)
          peak_retention_time            = [196.0651397705078,332.5663757324219,527.549865722 (length: 8)
          peak_start_time                = [186.81199645996094,239.21200561523438,502.4119873 (length: 8)
          peak_end_time                  = [220.81201171875,471.5176696777344,572.47869873046 (length: 8)
          peak_width                     = [4.974428176879883,62.90694808959961,11.9328641891 (length: 8)
          peak_area                      = [556.7650146484375,419.825439453125,66.56610107421 (length: 8)
          peak_area_percent              = [7.0321502685546875,5.302552223205566,0.8407546877 (length: 8)
          peak_height                    = [100.07515716552734,5.1860527992248535,4.827196121 (length: 8)
          peak_height_percent            = [29.76352310180664,1.5423927307128906,1.4356645345 (length: 8)
          peak_asymmetry                 = [1.4555920362472534,0.8351489901542664,1.707817316 (length: 8)
          baseline_start_time            = [186.81199645996094,239.21200561523438,502.4119873 (length: 8)
          baseline_start_value           = [1.9561424255371094,0.9857341647148132,1.127734780 (length: 8)
          baseline_stop_time             = [220.81201171875,471.5176696777344,572.47869873046 (length: 8)
          baseline_stop_value            = [1.1907591819763184,1.10896897315979,1.18347382545 (length: 8)
          peak_start_detection_code      = ["B","","B","","B","","B","","V","","B","","B","", (length: 16)
          peak_stop_detection_code       = ["B","","B","","B","","V","","B","","B","","B","", (length: 16)
          migration_time                 = [196.0651397705078,332.5663757324219,527.549865722 (length: 8)
          peak_area_square_root          = [23.595869064331055,20.489643096923828,8.158804893 (length: 8)
          manually_reintegrated_peaks    = [0,0,0,0,0,0,0,0] (length: 8)
    */

    function agilentHPLC(reader) {
      const intensities = reader.getDataVariable('ordinate_values');
      const numberPoints = intensities.length;
      const detector = reader.getAttribute('detector_name');
      let channel;
      if (detector.match(/dad/i)) {
        channel = `uv${Number(detector.replace(/.*Sig=(\d+).*/, '$1'))}`;
      } else if (detector.match(/tic/i)) {
        channel = 'tic';
      } else {
        channel = 'unknown';
      }
      const delayTime = reader.getDataVariable('actual_delay_time')[0];
      const runtimeLength = reader.getDataVariable('actual_run_time_length')[0];
      let samplingInterval;
      if (reader.dataVariableExists('actual_sampling_interval')) {
        samplingInterval = reader.getDataVariable('actual_sampling_interval')[0];
        if (Math.abs(delayTime + samplingInterval * numberPoints - runtimeLength) > 3) {
          throw new Error('The expected last time does not correspond to the runtimeLength');
        }
      } else {
        samplingInterval = (runtimeLength - delayTime) / numberPoints;
      }
      let times = [];
      let time = delayTime;
      for (let i = 0; i < numberPoints; i++) {
        times.push(time);
        time += samplingInterval;
      }
      return {
        times,
        series: [{
          name: channel,
          dimension: 1,
          data: intensities
        }]
      };
    }

    function aiaTemplate(reader) {
      let time = [];
      const tic = reader.getDataVariable('ordinate_values');

      // variables to get the time
      const delayTime = Number(reader.getDataVariable('actual_delay_time'));
      const interval = Number(reader.getDataVariable('actual_sampling_interval'));
      let currentTime = delayTime;
      for (let i = 0; i < tic.length; i++) {
        time.push(currentTime);
        currentTime += interval;
      }
      return {
        times: time,
        series: [{
          name: 'tic',
          dimension: 1,
          data: tic
        }]
      };
    }

    /* reader.toString() provides the following information
        GLOBAL ATTRIBUTES
          dataset_completeness           = C1+C2
          ms_template_revision           = 1.0.1
          netcdf_revision                = 2.3.2
          languages                      = English
          netcdf_file_date_time_stamp    = 20170428032023+0000
          experiment_title               = MS51762A16
        11829FC03_3__60_40
          experiment_date_time_stamp     = 20160930202145-0001
          operator_name                  = Begemann/Eikenberg/Roettger
          pre_experiment_program_name    = otofControl 3.4.16.0
          post_experiment_program_name   = Bruker Compass DataAnalysis 4.2
          source_file_reference          = X:\2016\MS5\1700\MS51762A16.d
          source_file_format             = Bruker Daltonics Data File
          experiment_type                = Centroided Mass Spectrum
          sample_state                   = Other State
          test_separation_type           = No Chromatography
          test_ms_inlet                  = Direct Inlet Probe
          test_ionization_mode           = Electrospray Ionization
          test_ionization_polarity       = Positive Polarity
          test_detector_type             = Electron Multiplier
          test_resolution_type           = Proportional Resolution
          test_scan_function             = Mass Scan
          test_scan_direction            = Up
          test_scan_law                  = Linear
          raw_data_mass_format           = Double
          raw_data_time_format           = Float
          raw_data_intensity_format      = Float
          units                          = Seconds
          scale_factor                   = 1

        VARIABLES:
          error_log                      = [" "," "," "," "," "," "," "," "," "," "," "," "," (length: 64)
          a_d_sampling_rate              = [-9999,-9999,-9999,-9999,-9999,-9999,-9999,-9999,- (length: 4513)
          a_d_coaddition_factor          = [-9999,-9999,-9999,-9999,-9999,-9999,-9999,-9999,- (length: 4514)
          scan_acquisition_time          = [0.329,0.73,1.132,1.534,1.936,2.337,2.739,3.14,3.5 (length: 4513)
          scan_duration                  = [-9999,-9999,-9999,-9999,-9999,-9999,-9999,-9999,- (length: 4513)
          inter_scan_time                = [-9999,-9999,-9999,-9999,-9999,-9999,-9999,-9999,- (length: 4513)
          resolution                     = [106.6623112889557,110.7855343519544,104.407495112 (length: 4513)
          actual_scan_number             = [0,2,4,6,8,10,12,14,16,18,20,22,24,26,28,30,32,34, (length: 4513)
          total_intensity                = [5297.4945068359375,6172.123912811279,5934.7557412 (length: 4513)
          mass_range_min                 = [49.99999997418507,49.99999997418507,49.9999999741 (length: 4513)
          mass_range_max                 = [1599.9999564432276,1599.9999564432276,1599.999956 (length: 4513)
          time_range_min                 = [-9999,-9999,-9999,-9999,-9999,-9999,-9999,-9999,- (length: 4513)
          time_range_max                 = [-9999,-9999,-9999,-9999,-9999,-9999,-9999,-9999,- (length: 4513)
          scan_index                     = [0,60,128,195,265,324,399,472,542,596,671,738,803, (length: 4513)
          point_count                    = [60,68,67,70,59,75,73,70,54,75,67,65,64,73,56,69,6 (length: 4513)
          flag_count                     = [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 (length: 4513)
          mass_values                    = [51.53516375878996,95.32974890044508,106.334477231 (length: 1176507)
          intensity_values               = [76.99999237060547,80,90,78.99799346923828,80.9352 (length: 1176507)
          instrument_name                = ["m","i","c","r","O","T","O","F",""," "," "," ","  (length: 32)
          instrument_id                  = [" "," "," "," "," "," "," "," "," "," "," "," "," (length: 32)
          instrument_mfr                 = ["B","r","u","k","e","r"," ","D","a","l","t","o"," (length: 32)
          instrument_model               = [" "," "," "," "," "," "," "," "," "," "," "," "," (length: 32)
          instrument_serial_no           = ["2","1","3","7","5","0",".","1","0","3","5","9"," (length: 32)
          instrument_sw_version          = [" "," "," "," "," "," "," "," "," "," "," "," "," (length: 32)
          instrument_fw_version          = [""," "," "," "," "," "," "," "," "," "," "," ","  (length: 32)
          instrument_os_version          = [" "," "," "," "," "," "," "," "," "," "," "," "," (length: 32)
          instrument_app_version         = [" "," "," "," "," "," "," "," "," "," "," "," "," (length: 32)
          instrument_comments            = [" "," "," "," "," "," "," "," "," "," "," "," "," (length: 32)
    */

    function brukerGCMS(reader) {
      const time = reader.getDataVariable('scan_acquisition_time');
      const tic = reader.getDataVariable('total_intensity');

      // variables to get the mass-intensity values
      let scanIndex = reader.getDataVariable('scan_index');
      const massValues = reader.getDataVariable('mass_values');
      const intensityValues = reader.getDataVariable('intensity_values');
      scanIndex.push(massValues.length);
      let ms = new Array(time.length);
      let index = 0;
      for (let i = 0; i < ms.length; i++) {
        let size = scanIndex[i + 1] - scanIndex[i];
        ms[i] = [new Array(size), new Array(size)];
        for (let j = 0; j < size; j++) {
          ms[i][0][j] = massValues[index];
          ms[i][1][j] = intensityValues[index++];
        }
      }
      return {
        times: time,
        series: [{
          name: 'tic',
          dimension: 1,
          data: tic
        }, {
          name: 'ms',
          dimension: 2,
          data: ms
        }]
      };
    }

    /* reader.toString() provides the following information
        GLOBAL ATTRIBUTES
          dataset_completeness           = C1+C2
          ms_template_revision           = 1.0.1
          administrative_comments        =
          dataset_owner                  =
          experiment_title               =
          experiment_date_time_stamp     = 20150902041002+0100
          netcdf_file_date_time_stamp    = 20151026063419+0000
          experiment_type                = Centroided Mass Spectrum
          netcdf_revision                = 2.3.2
          operator_name                  = DSQ
          source_file_reference          = G:\FCO\FCO_CIO\K2\MP2013\T1 IL database 2013\9. IL Data Entry\12_HU_HIFS\IL database\RAW data\Lukoil-Disel-150901.RAW
          source_file_date_time_stamp    = 20150902041002+0100
          source_file_format             = Finnigan
          languages                      = English
          external_file_ref_0            =
          instrument_number              = 1
          sample_prep_comments           =
          sample_comments                = Lukoil Disel 0,5 % CS2 1 % inkt
          test_separation_type           =
          test_ms_inlet                  =
          test_ionization_mode           =
          test_ionization_polarity       = Positive Polarity
          test_detector_type             = Conversion Dynode Electron Multiplier
          test_scan_function             = Mass Scan
          test_scan_direction            =
          test_scan_law                  = Linear
          number_of_scans                = 11832
          raw_data_mass_format           = Double
          raw_data_intensity_format      = Long
          actual_run_time                = 3519.6410000000005
          actual_delay_time              = 82.328
          global_mass_min                = 0
          global_mass_max                = 450
          calibrated_mass_min            = 0
          calibrated_mass_max            = 0
          mass_axis_label                = M/Z
          intensity_axis_label           = Abundance

        VARIABLES:
          error_log                      = ["","","","","","","","","","","","","","","",""," (length: 64)
          instrument_name                = ["L","C","Q","","","","","","","","","","","",""," (length: 32)
          instrument_id                  = ["","","","","","","","","","","","","","","",""," (length: 32)
          instrument_mfr                 = ["F","i","n","n","i","g","a","n","-","M","A","T"," (length: 32)
          instrument_model               = ["","","","","","","","","","","","","","","",""," (length: 32)
          instrument_sw_version          = ["3",".","1","","","","","","","","","","","",""," (length: 32)
          instrument_os_version          = ["W","i","n","d","o","w","s"," ","V","i","s","t"," (length: 32)
          scan_index                     = [0,34,74,113,145,177,211,239,267,299,341,374,400,4 (length: 11832)
          point_count                    = [34,40,39,32,32,34,28,28,32,42,33,26,29,34,31,28,2 (length: 11832)
          flag_count                     = [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 (length: 11832)
          a_d_sampling_rate              = [1000,1000,1000,1000,1000,1000,1000,1000,1000,1000 (length: 11832)
          scan_acquisition_time          = [82.328,82.625,82.76599999999999,83.063,83.188,83. (length: 11832)
          scan_duration                  = [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 (length: 11832)
          mass_range_min                 = [35,16,35,16,35,16,35,16,35,16,35,16,35,16,35,16,3 (length: 11832)
          mass_range_max                 = [450,150,450,150,450,150,450,150,450,150,450,150,4 (length: 11832)
          scan_type                      = [65537,65537,65537,65537,65537,65537,65537,65537,6 (length: 11832)
          resolution                     = [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 (length: 11832)
          total_intensity                = [375220,1054339,228245,576718,58280,288629,29815,1 (length: 11832)
          mass_values                    = [36.3023681640625,36.98402404785156,38.08326721191 (length: 1366002)
          intensity_values               = [335,287,331,266,2423,448,9009,833,261,661,4003,21 (length: 1366002)

    */

    function finniganGCMS(reader) {
      const time = reader.getDataVariable('scan_acquisition_time');
      const tic = reader.getDataVariable('total_intensity');

      // variables to get the mass-intensity values
      let scanIndex = reader.getDataVariable('scan_index');
      const massValues = reader.getDataVariable('mass_values');
      const intensityValues = reader.getDataVariable('intensity_values');
      scanIndex.push(massValues.length);
      let ms = new Array(time.length);
      let index = 0;
      for (let i = 0; i < ms.length; i++) {
        let size = scanIndex[i + 1] - scanIndex[i];
        ms[i] = [new Array(size), new Array(size)];
        for (let j = 0; j < size; j++) {
          ms[i][0][j] = massValues[index];
          ms[i][1][j] = intensityValues[index++];
        }
      }
      return {
        times: time,
        series: [{
          name: 'tic',
          dimension: 1,
          data: tic
        }, {
          name: 'ms',
          dimension: 2,
          data: ms
        }]
      };
    }

    /* reader.toString() provides the following information
        GLOBAL ATTRIBUTES
          dataset_completeness           = C1+C2
          ms_template_revision           = 1.0.1
          netcdf_revision                = 2.3.2
          languages                      = English
          administrative_comments        =
          netcdf_file_date_time_stamp    = 20180913165502+0000
          experiment_title               =
          experiment_date_time_stamp     = 20180910165319+0000
          operator_name                  = Admin
          source_file_reference          = D:\GCMSsolution\Data\Chromatograms\Cato\bormann_CB000_Test2.qgd
          source_file_format             = Shimadzu GCMSsolution
          source_file_date_time_stamp    = 20180910165319+0000
          experiment_type                = Centroided Mass Spectrum
          sample_internal_id             =
          sample_comments                =
          sample_state                   = Other State
          test_separation_type           = Gas-Solid Chromatography
          test_ms_inlet                  = Other Probe
          test_ionization_mode           = Electron Impact
          test_ionization_polarity       = Positive Polarity
          test_electron_energy           = 70
          test_detector_type             = Electron Multiplier
          test_resolution_type           = Constant Resolution
          test_scan_function             = Mass Scan
          test_scan_direction            = Up
          test_scan_law                  = Quadratic
          test_scan_time                 = 0
          raw_data_mass_format           = Double
          raw_data_time_format           = Long
          raw_data_intensity_format      = Long
          units                          = Seconds
          scale_factor                   = 1
          long_name                      = Seconds
          starting_scan_number           = 0
          actual_run_time_length         = 1289
          actual_delay_time              = 0
          raw_data_uniform_sampling_flag = 1

        VARIABLES:
          error_log                      = [" "," "," "," "," "," "," "," "," "," "," "," "," (length: 64)
          a_d_sampling_rate              = [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 (length: 3820)
          a_d_coaddition_factor          = [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 (length: 3820)
          scan_acquisition_time          = [144,144.3,144.6,144.9,145.2,145.5,145.8,146.1,146 (length: 3820)
          scan_duration                  = [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 (length: 3820)
          inter_scan_time                = [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 (length: 3820)
          resolution                     = [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 (length: 3820)
          actual_scan_number             = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,2 (length: 3820)
          total_intensity                = [63566,61702,61873,59738,58321,59001,59364,59871,6 (length: 3820)
          mass_range_min                 = [35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,3 (length: 3820)
          mass_range_max                 = [500,500,500,500,500,500,500,500,500,500,500,500,5 (length: 3820)
          time_range_min                 = [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 (length: 3820)
          time_range_max                 = [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 (length: 3820)
          scan_index                     = [0,466,932,1398,1863,2329,2795,3260,3726,4192,4658 (length: 3820)
          point_count                    = [466,466,466,465,466,466,465,466,466,466,466,466,4 (length: 3820)
          flag_count                     = [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 (length: 3820)
          mass_values                    = [35,36,37.1,38.1,39.1,40.15,41.1,42.1,43.15,44.1,4 (length: 1779397)
          intensity_values               = [26,111,412,785,3098,485,5772,7391,11213,711,687,1 (length: 1779397)
          instrument_name                = [" "," "," "," "," "," "," "," "," "," "," "," "," (length: 32)
          instrument_id                  = [" "," "," "," "," "," "," "," "," "," "," "," "," (length: 32)
          instrument_mfr                 = ["S","h","i","m","a","d","z","u"," ","C","o","r"," (length: 32)
          instrument_model               = ["G","C","M","S","","","","","","","","","","","", (length: 32)
          instrument_serial_no           = ["","","","","","","","","","","","","","","",""," (length: 32)
          instrument_sw_version          = ["4",".","2","0","","","","","","","","","","","", (length: 32)
          instrument_fw_version          = ["G","C","M","S","-","Q","P","2","0","1","0","1"," (length: 32)
          instrument_os_version          = ["W","i","n","d","o","w","s","","","","","","","", (length: 32)
          instrument_app_version         = ["G","C","M","S","s","o","l","u","t","i","o","n"," (length: 32)
          instrument_comments            = [" "," "," "," "," "," "," "," "," "," "," "," "," (length: 32)
    */

    function shimadzuGCMS(reader) {
      const time = reader.getDataVariable('scan_acquisition_time');
      const tic = reader.getDataVariable('total_intensity');

      // variables to get the mass-intensity values
      let scanIndex = reader.getDataVariable('scan_index');
      const massValues = reader.getDataVariable('mass_values');
      const intensityValues = reader.getDataVariable('intensity_values');
      scanIndex.push(massValues.length);
      let ms = new Array(time.length);
      let index = 0;
      for (let i = 0; i < ms.length; i++) {
        let size = scanIndex[i + 1] - scanIndex[i];
        ms[i] = [new Array(size), new Array(size)];
        for (let j = 0; j < size; j++) {
          ms[i][0][j] = massValues[index];
          ms[i][1][j] = intensityValues[index++];
        }
      }
      return {
        times: time,
        series: [{
          name: 'tic',
          dimension: 1,
          data: tic
        }, {
          name: 'ms',
          dimension: 2,
          data: ms
        }]
      };
    }

    /**
     * Reads a NetCDF file and returns a formatted JSON with the data from it
     * @param {ArrayBuffer} data - ArrayBuffer or any Typed Array (including Node.js' Buffer from v4) with the data
     * @returns {{times, series, meta, variables}} - JSON with the time, TIC and mass spectra values
     */
    function netcdfGcms(data) {
      let reader = new NetCDFReader(data);
      const globalAttributes = reader.globalAttributes;
      const instrument_mfr = reader.dataVariableExists('instrument_mfr') && reader.getDataVariableAsString('instrument_mfr');
      const dataset_origin = reader.attributeExists('dataset_origin');
      const mass_values = reader.dataVariableExists('mass_values');
      const detector_name = reader.getAttribute('detector_name');
      const aia_template_revision = reader.attributeExists('aia_template_revision');
      const source_file_format = reader.getAttribute('source_file_format');
      let result;
      if (mass_values && dataset_origin) {
        result = agilentGCMS(reader);
      } else if (mass_values && instrument_mfr && instrument_mfr.match(/finnigan/i)) {
        result = finniganGCMS(reader);
      } else if (mass_values && instrument_mfr && instrument_mfr.match(/bruker/i)) {
        result = brukerGCMS(reader);
      } else if (mass_values && source_file_format && source_file_format.match(/shimadzu/i)) {
        result = shimadzuGCMS(reader);
      } else if (mass_values && source_file_format && source_file_format.match(/advion/i)) {
        result = advionGCMS(reader);
      } else if (detector_name && detector_name.match(/(dad|tic)/i)) {
        // diode array agilent HPLC
        result = agilentHPLC(reader);
      } else if (aia_template_revision) {
        result = aiaTemplate(reader);
      } else {
        throw new TypeError('Unknown file format');
      }
      result.meta = addMeta(globalAttributes);
      result.variables = addVariables(reader);
      return result;
    }
    function addMeta(globalAttributes) {
      let meta = {};
      for (const item of globalAttributes) {
        meta[item.name] = item.value;
      }
      return meta;
    }
    function addVariables(reader) {
      for (let variable of reader.variables) {
        variable.value = reader.getDataVariable(variable);
      }
      return reader.variables;
    }

    function fromNetCDF(netcdf) {
      return fromJSON(netcdfGcms(netcdf));
    }

    const utf8Decoder$1 = new TextDecoder();
    const decoder$5 = {
      decode: array => {
        return utf8Decoder$1.decode(array);
      }
    };
    const defaultOptions = {
      trimValues: true,
      attributeNamePrefix: '$',
      attributesNodeName: false,
      ignoreAttributes: false,
      ignoreNameSpace: false,
      allowBooleanAttributes: false,
      dynamicTypingAttributeValue: true,
      textNodeName: '#text',
      dynamicTypingNodeValue: true,
      arrayMode: false,
      cdataTagName: false,
      tagValueProcessor: value => {
        return decoder$5.decode(value).replace(/\r/g, '');
      },
      attributeValueProcessor: value => value,
      stopNodes: []
    };

    class XMLNode {
      constructor(tagName, parent, value) {
        this.tagName = tagName;
        this.parent = parent;
        this.children = Object.create({}); //child tags
        this.attributes = Object.create({}); //attributes map
        this.value = value; //text only
        this.startIndex = -1;
      }
      addChild(child) {
        if (Array.isArray(this.children[child.tagName])) {
          //already presents
          this.children[child.tagName].push(child);
        } else {
          this.children[child.tagName] = [child];
        }
      }
    }

    function arrayIndexOf(array, referenceArray, index = 0) {
      let found = 0;
      let foundIndex = -1;
      for (let i = index; i < array.length && found < referenceArray.length; i++) {
        if (array[i] === referenceArray[found]) {
          if (!found) {
            foundIndex = i;
          }
          found++;
        } else {
          if (found > 0) {
            let j = 0;
            for (; j <= found && array[foundIndex + j] === array[foundIndex + found]; j++);
            if (j < found + 1) {
              foundIndex = -1;
              found = 0;
            } else {
              foundIndex++;
            }
          } else {
            found = 0;
            foundIndex = -1;
          }
        }
      }
      if (found !== referenceArray.length) {
        foundIndex = -1;
      }
      return foundIndex;
    }

    function arrayTrim(array) {
      let i = 0;
      let j = array.length - 1;
      for (; i < array.length && array[i] <= 0x20; i++);
      for (; j >= i && array[j] <= 0x20; j--);
      if (i === 0 && j === array.length - 1) return array;
      return array.subarray(i, j + 1);
    }

    function closingIndexForOpeningTag(data, i) {
      let attrBoundary;
      let endIndex = 0;
      for (let index = i; index < data.length; index++) {
        let byte = data[index];
        if (attrBoundary) {
          if (byte === attrBoundary) attrBoundary = 0; //reset
        } else if (byte === 0x22 || byte === 0x27) {
          attrBoundary = byte;
        } else if (byte === 0x3e) {
          return {
            data: decoder$4.decode(data.subarray(i, i + endIndex)),
            index
          };
        } else if (byte === 0x09) {
          byte = 0x20;
        }
        endIndex++;
      }
    }

    function findClosingIndex(xmlData, str, i, errMsg) {
      const closingIndex = arrayIndexOf(xmlData, str, i);
      if (closingIndex === -1) {
        throw new Error(errMsg);
      } else {
        return closingIndex + str.length - 1;
      }
    }

    /**
     * Dynamically type a string
     * @param {string} value String to dynamically type
     * @returns {boolean|string|number}
     */
    function parseString(value) {
      if (value.length === 4 || value.length === 5) {
        let lowercase = value.toLowerCase();
        if (lowercase === 'true') return true;
        if (lowercase === 'false') return false;
      }
      let number = Number(value);
      if (number === 0 && !value.includes('0')) {
        return value;
      }
      if (!Number.isNaN(number)) return number;
      return value;
    }

    function getAllMatches(string, regex) {
      return Array.from(string.matchAll(regex));
    }
    function isEmptyObject(obj) {
      // fastest implementation: https://jsbench.me/qfkqv692c8/1
      // eslint-disable-next-line no-unreachable-loop
      for (const key in obj) {
        return false;
      }
      return true;
    }

    /**
     * Copy all the properties of a into b.
     * @param {object} target
     * @param {object} source
     */
    function merge(target, source, arrayMode) {
      if (!source) return;
      for (const key in source) {
        if (arrayMode === 'strict') {
          target[key] = [source[key]];
        } else {
          target[key] = source[key];
        }
      }
    }

    /**
     * Check if a tag name should be treated as array
     *
     * @param tagName the node tagName
     * @param arrayMode the array mode option
     * @param parentTagName the parent tag name
     * @returns {boolean} true if node should be parsed as array
     */
    function isTagNameInArrayMode(tagName, arrayMode, parentTagName) {
      if (arrayMode === false) {
        return false;
      } else if (arrayMode instanceof RegExp) {
        return arrayMode.test(tagName);
      } else if (typeof arrayMode === 'function') {
        return !!arrayMode(tagName, parentTagName);
      }
      return arrayMode === 'strict';
    }

    const newLocal = '([^\\s=]+)\\s*(=\\s*([\'"])(.*?)\\3)?';
    const attrsRegx = new RegExp(newLocal, 'g');

    //Attributes are strings so no point in using arrayBuffers here
    function parseAttributesString(string, options) {
      if (options.ignoreAttributes) {
        return;
      }
      string = string.replace(/\r?\n/g, ' ');
      const matches = getAllMatches(string, attrsRegx);
      const attributes = {};
      for (let match of matches) {
        const attrName = resolveNameSpace(match[1], options);
        if (attrName.length) {
          if (match[4] !== undefined) {
            if (options.trimValues) {
              match[4] = match[4].trim();
            }
            match[4] = options.attributeValueProcessor(match[4], attrName);
            attributes[attrName] = stringParseValue(match[4], options.dynamicTypingAttributeValue);
          } else if (options.allowBooleanAttributes) {
            attributes[attrName] = true;
          }
        }
      }
      if (isEmptyObject(attributes)) return;
      return attributes;
    }
    function stringParseValue(value, shouldParse) {
      if (shouldParse && typeof value === 'string') {
        return parseString(value);
      } else {
        return value === undefined ? '' : value;
      }
    }
    function resolveNameSpace(tagName, options) {
      if (options.ignoreNameSpace) {
        const tags = tagName.split(':');
        const prefix = tagName.charAt(0) === '/' ? '/' : '';
        if (tags[0] === 'xmlns') {
          return '';
        }
        if (tags.length === 2) {
          tagName = prefix + tags[1];
        }
      }
      return tagName;
    }

    const utf8Decoder = new TextDecoder();
    const decoder$4 = {
      decode: array => {
        return utf8Decoder.decode(array);
      }
    };
    function getTraversable(xmlData, options) {
      const traversable = new XMLNode('!xml');
      let currentNode = traversable;
      let dataSize = 0;
      let dataIndex = 0;
      for (let i = 0; i < xmlData.length; i++) {
        if (xmlData[i] === 0x3c) {
          // <
          const xmlData1 = xmlData[i + 1];
          const xmlData2 = xmlData[i + 2];
          if (xmlData1 === 0x2f) {
            // </ Closing Tag
            const closeIndex = findClosingIndex(xmlData, [0x3e],
            //>
            i, 'Closing Tag is not closed.');
            let tagName = decoder$4.decode(arrayTrim(xmlData.subarray(i + 2, closeIndex)));
            tagName = removeNameSpaceIfNeeded(tagName, options);
            if (currentNode) {
              const value = options.trimValues ? arrayTrim(xmlData.subarray(dataIndex, dataIndex + dataSize)) : xmlData.subarray(dataIndex, dataIndex + dataSize);
              if (currentNode.value === undefined) {
                currentNode.value = value;
              } else {
                currentNode.value = concat(currentNode.value, value);
              }
            }
            if (options.stopNodes.length && options.stopNodes.includes(currentNode.tagName)) {
              currentNode.children = [];
              if (currentNode.attributes === undefined) {
                currentNode.attributes = {};
              }
              currentNode.value = xmlData.subarray(currentNode.startIndex + 1, i);
            }
            currentNode = currentNode.parent;
            i = closeIndex;
            dataSize = 0;
            dataIndex = i + 1;
          } else if (xmlData1 === 0x3f) {
            // <? PI, processing instruction
            i = findClosingIndex(xmlData, [0x3f, 0x3e], i, 'Pi Tag is not closed.');
          } else if (
          //!-- comment
          xmlData1 === 0x21 && xmlData2 === 0x2d && xmlData[i + 3] === 0x2d) {
            i = findClosingIndex(xmlData, [0x2d, 0x2d, 0x3e],
            //-->
            i, 'Comment is not closed.');
            if (currentNode && dataSize !== 0) {
              if (currentNode.tagName !== '!xml') {
                currentNode.value = concat(currentNode.value, options.trimValues ? arrayTrim(xmlData.subarray(dataIndex, dataSize + dataIndex)) : xmlData.subarray(dataIndex, dataSize + dataIndex));
              }
            }
            dataSize = 0;
            dataIndex = i + 1;
            //!D
          } else if (xmlData1 === 0x21 && xmlData2 === 0x44) {
            // <!D
            const closeIndex = findClosingIndex(xmlData, [0x3e],
            //>
            i, 'DOCTYPE is not closed.');
            const tagExp = xmlData.subarray(i, closeIndex);
            if (arrayIndexOf(tagExp, [0x5b]) >= 0) {
              i = arrayIndexOf(xmlData, [0x5d, 0x3e], i) + 1;
            } else {
              i = closeIndex;
            } //![
          } else if (xmlData1 === 0x21 && xmlData2 === 0x5b) {
            // <![CDATA[some stuff]]>
            const closeIndex = findClosingIndex(xmlData, [0x5d, 0x5d, 0x3e],
            //]]>
            i, 'CDATA is not closed.') - 2;
            const tagExp = xmlData.subarray(i + 9, closeIndex);

            //considerations
            //1. CDATA will always have parent node
            //2. A tag with CDATA is not a leaf node so it's value would be string type.
            if (dataSize !== 0) {
              const value = options.trimValues ? arrayTrim(xmlData.subarray(dataIndex, dataIndex + dataSize)) : xmlData.subarray(dataIndex, dataIndex + dataSize);
              currentNode.value = concat(currentNode.value, value);
            }
            if (options.cdataTagName) {
              //add cdata node
              const childNode = new XMLNode(options.cdataTagName, currentNode, tagExp);
              currentNode.addChild(childNode);
              //add rest value to parent node
              if (tagExp) {
                childNode.value = tagExp;
              }
            } else {
              currentNode.value = concat(currentNode.value, tagExp);
            }
            i = closeIndex + 2;
            dataSize = 0;
            dataIndex = i + 1;
          } else {
            //Opening a normal tag
            const parsedOpeningTag = closingIndexForOpeningTag(xmlData, i + 1);
            let tagData = parsedOpeningTag.data.replace(/\r?\n|\t/g, ' ');
            const closeIndex = parsedOpeningTag.index;
            const separatorIndex = tagData.indexOf(' ');
            let shouldBuildAttributesMap = true;
            let tagName = separatorIndex >= 0 ? tagData.substr(0, separatorIndex).replace(/\s+$/, '') : tagData;
            let tagAttributes = separatorIndex >= 0 ? tagData.substr(separatorIndex + 1) : '';
            if (options.ignoreNameSpace) {
              const colonIndex = tagName.indexOf(':');
              if (colonIndex !== -1) {
                tagName = tagName.substr(colonIndex + 1);
                shouldBuildAttributesMap = tagName !== parsedOpeningTag.data.substr(colonIndex + 1);
              }
            }

            //save text to parent node
            if (currentNode && dataSize !== 0) {
              if (currentNode.tagName !== '!xml') {
                currentNode.value = concat(currentNode.value, options.trimValues ? arrayTrim(xmlData.subarray(dataIndex, dataIndex + dataSize)) : xmlData.subarray(dataIndex, dataIndex + dataSize));
              }
            }
            if (tagData.length > 0 && tagData[tagData.length - 1] === '/') {
              //selfClosing tag

              if (tagAttributes) {
                // <abc def="123"/>
                tagAttributes = tagAttributes.substr(0, tagAttributes.length - 1);
              } else {
                // <abc/>
                tagName = tagName.substr(0, tagName.length - 1);
              }
              const childNode = new XMLNode(tagName, currentNode, '');
              if (tagAttributes) {
                childNode.attributes = parseAttributesString(tagAttributes, options);
              }
              currentNode.addChild(childNode);
            } else {
              //opening tag

              const childNode = new XMLNode(tagName, currentNode);
              if (options.stopNodes.length && options.stopNodes.includes(childNode.tagName)) {
                childNode.startIndex = closeIndex;
              }
              if (tagAttributes && shouldBuildAttributesMap) {
                childNode.attributes = parseAttributesString(tagAttributes, options);
              }
              currentNode.addChild(childNode);
              currentNode = childNode;
            }
            i = closeIndex;
            dataSize = 0;
            dataIndex = i + 1;
          }
        } else {
          dataSize++;
        }
      }
      return traversable;
    }
    function concat(a, b) {
      if (a === undefined) {
        a = typeof b === 'string' ? '' : new Uint8Array(0);
      }
      if (b === undefined) {
        b = typeof a === 'string' ? '' : new Uint8Array(0);
      }
      if (typeof a === 'string' && typeof b === 'string') {
        return a + b;
      } else if (ArrayBuffer.isView(a) && ArrayBuffer.isView(b)) {
        const arrayConcat = new Uint8Array(a.length + b.length);
        arrayConcat.set(a);
        arrayConcat.set(b, a.length);
        return arrayConcat;
      } else {
        throw new Error(`Unsuported value type for concatenation: ${typeof a} ${typeof b}`);
      }
    }
    function removeNameSpaceIfNeeded(tagName, options) {
      if (!options.ignoreNameSpace) return tagName;
      const colonIndex = tagName.indexOf(':');
      if (colonIndex !== -1) {
        tagName = tagName.substr(colonIndex + 1);
      }
    }

    /**
     *
     * @param {*} node
     * @param {*} options
     * @param {*} parentTagName
     * @returns
     */
    function traversableToJSON(node, options, parentTagName) {
      const {
        dynamicTypingNodeValue,
        tagValueProcessor,
        arrayMode
      } = options;
      const result = {};
      if (tagValueProcessor) {
        node.value = node.value && tagValueProcessor(node.value, node);
      }
      if (typeof node.value === 'string' && dynamicTypingNodeValue) {
        node.value = parseString(node.value);
      }
      // when no child node or attr is present
      if ((!node.children || isEmptyObject(node.children)) && (!node.attributes || isEmptyObject(node.attributes))) {
        return node.value === undefined ? '' : node.value;
      }

      // otherwise create a textnode if node has some text
      if (node.value !== undefined && node.value.length !== 0) {
        const asArray = isTagNameInArrayMode(node.tagName, arrayMode, parentTagName);
        result[options.textNodeName] = asArray ? [node.value] : node.value;
      }
      if (node.attributes && !isEmptyObject(node.attributes)) {
        let attributes = options.parseAttributesString ? {} : node.attributes;
        if (options.attributeNamePrefix) {
          // need to rename the attributes
          const renamedAttributes = {};
          for (let key in node.attributes) {
            renamedAttributes[options.attributeNamePrefix + key] = node.attributes[key];
          }
          attributes = renamedAttributes;
        }
        if (options.attributesNodeName) {
          let encapsulatedAttributes = {};
          encapsulatedAttributes[options.attributesNodeName] = attributes;
          attributes = encapsulatedAttributes;
        }
        merge(result, attributes, arrayMode);
      }
      const keys = Object.keys(node.children);
      for (let index = 0; index < keys.length; index++) {
        const tagName = keys[index];
        if (node.children[tagName] && node.children[tagName].length > 1) {
          result[tagName] = [];
          for (let tag in node.children[tagName]) {
            if (Object.prototype.hasOwnProperty.call(node.children[tagName], tag)) {
              result[tagName].push(traversableToJSON(node.children[tagName][tag], options, tagName));
            }
          }
        } else {
          const subResult = traversableToJSON(node.children[tagName][0], options, tagName);
          const asArray = arrayMode === true && typeof subResult === 'object' || isTagNameInArrayMode(tagName, arrayMode, parentTagName);
          result[tagName] = asArray ? [subResult] : subResult;
        }
      }
      return result;
    }

    /**
     * Parse an ArrayBuffer or Uint8Array representing an XML
     * @param {ArrayBuffer|Uint8Arra} xmlData
     * @param {object} [options={}]
     * @param {string} [attributeNamePrefix='$']
     * @param {boolean} [attributesNodeName=false]
     * @param {string} [textNodeName='#text']
     * @param {boolean} [trimValues=true] should we remove ascii < 32
     * @param {boolean} [ignoreAttributes=false] skip attributes
     * @param {boolean} [ignoreNameSpace=false]
     * @param {boolean} [dynamicTypingAttributeValue=true] Parse attribute values that looks like number or boolean
     * @param {boolean} [allowBooleanAttributes=false]
     * @param {boolean} [dynamicTypingNodeValue=true] Parse tag values that looks like number or boolean
     * @param {boolean} [arrayMode=false]
     * @param {boolean} [cdataTagName=false]
     * @param {function} [tagValueProcessor=(v, node) => decoder.decode(v)] Tag values can be modified during parsing. By default we decode the tag value (a Uint8Array) using TextDecoder
     * @param {function} [attributeValueProcessor=(v) => v] Attribute values can be modified during parsing
     * @param {boolean} [stopNodes=[]] prevent further parsing
     *
     * @returns {object}
     */
    function parse(xmlData, options = {}) {
      if (typeof xmlData === 'string') {
        const encoder = new TextEncoder();
        xmlData = encoder.encode(xmlData);
      }
      if (!ArrayBuffer.isView(xmlData)) {
        xmlData = new Uint8Array(xmlData);
      }
      options = {
        ...defaultOptions,
        ...options
      };
      const traversable = getTraversable(xmlData, options);
      return traversableToJSON(traversable, options);
    }

    /*! pako 2.1.0 https://github.com/nodeca/pako @license (MIT AND Zlib) */
    // (C) 1995-2013 Jean-loup Gailly and Mark Adler
    // (C) 2014-2017 Vitaly Puzrin and Andrey Tupitsin
    //
    // This software is provided 'as-is', without any express or implied
    // warranty. In no event will the authors be held liable for any damages
    // arising from the use of this software.
    //
    // Permission is granted to anyone to use this software for any purpose,
    // including commercial applications, and to alter it and redistribute it
    // freely, subject to the following restrictions:
    //
    // 1. The origin of this software must not be misrepresented; you must not
    //   claim that you wrote the original software. If you use this software
    //   in a product, an acknowledgment in the product documentation would be
    //   appreciated but is not required.
    // 2. Altered source versions must be plainly marked as such, and must not be
    //   misrepresented as being the original software.
    // 3. This notice may not be removed or altered from any source distribution.

    /* eslint-disable space-unary-ops */

    /* Public constants ==========================================================*/
    /* ===========================================================================*/

    //const Z_FILTERED          = 1;
    //const Z_HUFFMAN_ONLY      = 2;
    //const Z_RLE               = 3;
    const Z_FIXED$1 = 4;
    //const Z_DEFAULT_STRATEGY  = 0;

    /* Possible values of the data_type field (though see inflate()) */
    const Z_BINARY = 0;
    const Z_TEXT = 1;
    //const Z_ASCII             = 1; // = Z_TEXT
    const Z_UNKNOWN$1 = 2;

    /*============================================================================*/

    function zero$1(buf) {
      let len = buf.length;
      while (--len >= 0) {
        buf[len] = 0;
      }
    }

    // From zutil.h

    const STORED_BLOCK = 0;
    const STATIC_TREES = 1;
    const DYN_TREES = 2;
    /* The three kinds of block type */

    const MIN_MATCH$1 = 3;
    const MAX_MATCH$1 = 258;
    /* The minimum and maximum match lengths */

    // From deflate.h
    /* ===========================================================================
     * Internal compression state.
     */

    const LENGTH_CODES$1 = 29;
    /* number of length codes, not counting the special END_BLOCK code */

    const LITERALS$1 = 256;
    /* number of literal bytes 0..255 */

    const L_CODES$1 = LITERALS$1 + 1 + LENGTH_CODES$1;
    /* number of Literal or Length codes, including the END_BLOCK code */

    const D_CODES$1 = 30;
    /* number of distance codes */

    const BL_CODES$1 = 19;
    /* number of codes used to transfer the bit lengths */

    const HEAP_SIZE$1 = 2 * L_CODES$1 + 1;
    /* maximum heap size */

    const MAX_BITS$1 = 15;
    /* All codes must not exceed MAX_BITS bits */

    const Buf_size = 16;
    /* size of bit buffer in bi_buf */

    /* ===========================================================================
     * Constants
     */

    const MAX_BL_BITS = 7;
    /* Bit length codes must not exceed MAX_BL_BITS bits */

    const END_BLOCK = 256;
    /* end of block literal code */

    const REP_3_6 = 16;
    /* repeat previous bit length 3-6 times (2 bits of repeat count) */

    const REPZ_3_10 = 17;
    /* repeat a zero length 3-10 times  (3 bits of repeat count) */

    const REPZ_11_138 = 18;
    /* repeat a zero length 11-138 times  (7 bits of repeat count) */

    /* eslint-disable comma-spacing,array-bracket-spacing */
    const extra_lbits = /* extra bits for each length code */
    new Uint8Array([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]);
    const extra_dbits = /* extra bits for each distance code */
    new Uint8Array([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]);
    const extra_blbits = /* extra bits for each bit length code */
    new Uint8Array([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 3, 7]);
    const bl_order = new Uint8Array([16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15]);
    /* eslint-enable comma-spacing,array-bracket-spacing */

    /* The lengths of the bit length codes are sent in order of decreasing
     * probability, to avoid transmitting the lengths for unused bit length codes.
     */

    /* ===========================================================================
     * Local data. These are initialized only once.
     */

    // We pre-fill arrays with 0 to avoid uninitialized gaps

    const DIST_CODE_LEN = 512; /* see definition of array dist_code below */

    // !!!! Use flat array instead of structure, Freq = i*2, Len = i*2+1
    const static_ltree = new Array((L_CODES$1 + 2) * 2);
    zero$1(static_ltree);
    /* The static literal tree. Since the bit lengths are imposed, there is no
     * need for the L_CODES extra codes used during heap construction. However
     * The codes 286 and 287 are needed to build a canonical tree (see _tr_init
     * below).
     */

    const static_dtree = new Array(D_CODES$1 * 2);
    zero$1(static_dtree);
    /* The static distance tree. (Actually a trivial tree since all codes use
     * 5 bits.)
     */

    const _dist_code = new Array(DIST_CODE_LEN);
    zero$1(_dist_code);
    /* Distance codes. The first 256 values correspond to the distances
     * 3 .. 258, the last 256 values correspond to the top 8 bits of
     * the 15 bit distances.
     */

    const _length_code = new Array(MAX_MATCH$1 - MIN_MATCH$1 + 1);
    zero$1(_length_code);
    /* length code for each normalized match length (0 == MIN_MATCH) */

    const base_length = new Array(LENGTH_CODES$1);
    zero$1(base_length);
    /* First normalized length for each code (0 = MIN_MATCH) */

    const base_dist = new Array(D_CODES$1);
    zero$1(base_dist);
    /* First normalized distance for each code (0 = distance of 1) */

    function StaticTreeDesc(static_tree, extra_bits, extra_base, elems, max_length) {
      this.static_tree = static_tree; /* static tree or NULL */
      this.extra_bits = extra_bits; /* extra bits for each code or NULL */
      this.extra_base = extra_base; /* base index for extra_bits */
      this.elems = elems; /* max number of elements in the tree */
      this.max_length = max_length; /* max bit length for the codes */

      // show if `static_tree` has data or dummy - needed for monomorphic objects
      this.has_stree = static_tree && static_tree.length;
    }
    let static_l_desc;
    let static_d_desc;
    let static_bl_desc;
    function TreeDesc(dyn_tree, stat_desc) {
      this.dyn_tree = dyn_tree; /* the dynamic tree */
      this.max_code = 0; /* largest code with non zero frequency */
      this.stat_desc = stat_desc; /* the corresponding static tree */
    }
    const d_code = dist => {
      return dist < 256 ? _dist_code[dist] : _dist_code[256 + (dist >>> 7)];
    };

    /* ===========================================================================
     * Output a short LSB first on the stream.
     * IN assertion: there is enough room in pendingBuf.
     */
    const put_short = (s, w) => {
      //    put_byte(s, (uch)((w) & 0xff));
      //    put_byte(s, (uch)((ush)(w) >> 8));
      s.pending_buf[s.pending++] = w & 0xff;
      s.pending_buf[s.pending++] = w >>> 8 & 0xff;
    };

    /* ===========================================================================
     * Send a value on a given number of bits.
     * IN assertion: length <= 16 and value fits in length bits.
     */
    const send_bits = (s, value, length) => {
      if (s.bi_valid > Buf_size - length) {
        s.bi_buf |= value << s.bi_valid & 0xffff;
        put_short(s, s.bi_buf);
        s.bi_buf = value >> Buf_size - s.bi_valid;
        s.bi_valid += length - Buf_size;
      } else {
        s.bi_buf |= value << s.bi_valid & 0xffff;
        s.bi_valid += length;
      }
    };
    const send_code = (s, c, tree) => {
      send_bits(s, tree[c * 2] /*.Code*/, tree[c * 2 + 1] /*.Len*/);
    };

    /* ===========================================================================
     * Reverse the first len bits of a code, using straightforward code (a faster
     * method would use a table)
     * IN assertion: 1 <= len <= 15
     */
    const bi_reverse = (code, len) => {
      let res = 0;
      do {
        res |= code & 1;
        code >>>= 1;
        res <<= 1;
      } while (--len > 0);
      return res >>> 1;
    };

    /* ===========================================================================
     * Flush the bit buffer, keeping at most 7 bits in it.
     */
    const bi_flush = s => {
      if (s.bi_valid === 16) {
        put_short(s, s.bi_buf);
        s.bi_buf = 0;
        s.bi_valid = 0;
      } else if (s.bi_valid >= 8) {
        s.pending_buf[s.pending++] = s.bi_buf & 0xff;
        s.bi_buf >>= 8;
        s.bi_valid -= 8;
      }
    };

    /* ===========================================================================
     * Compute the optimal bit lengths for a tree and update the total bit length
     * for the current block.
     * IN assertion: the fields freq and dad are set, heap[heap_max] and
     *    above are the tree nodes sorted by increasing frequency.
     * OUT assertions: the field len is set to the optimal bit length, the
     *     array bl_count contains the frequencies for each bit length.
     *     The length opt_len is updated; static_len is also updated if stree is
     *     not null.
     */
    const gen_bitlen = (s, desc) => {
      //    deflate_state *s;
      //    tree_desc *desc;    /* the tree descriptor */

      const tree = desc.dyn_tree;
      const max_code = desc.max_code;
      const stree = desc.stat_desc.static_tree;
      const has_stree = desc.stat_desc.has_stree;
      const extra = desc.stat_desc.extra_bits;
      const base = desc.stat_desc.extra_base;
      const max_length = desc.stat_desc.max_length;
      let h; /* heap index */
      let n, m; /* iterate over the tree elements */
      let bits; /* bit length */
      let xbits; /* extra bits */
      let f; /* frequency */
      let overflow = 0; /* number of elements with bit length too large */

      for (bits = 0; bits <= MAX_BITS$1; bits++) {
        s.bl_count[bits] = 0;
      }

      /* In a first pass, compute the optimal bit lengths (which may
       * overflow in the case of the bit length tree).
       */
      tree[s.heap[s.heap_max] * 2 + 1] /*.Len*/ = 0; /* root of the heap */

      for (h = s.heap_max + 1; h < HEAP_SIZE$1; h++) {
        n = s.heap[h];
        bits = tree[tree[n * 2 + 1] /*.Dad*/ * 2 + 1] /*.Len*/ + 1;
        if (bits > max_length) {
          bits = max_length;
          overflow++;
        }
        tree[n * 2 + 1] /*.Len*/ = bits;
        /* We overwrite tree[n].Dad which is no longer needed */

        if (n > max_code) {
          continue;
        } /* not a leaf node */

        s.bl_count[bits]++;
        xbits = 0;
        if (n >= base) {
          xbits = extra[n - base];
        }
        f = tree[n * 2] /*.Freq*/;
        s.opt_len += f * (bits + xbits);
        if (has_stree) {
          s.static_len += f * (stree[n * 2 + 1] /*.Len*/ + xbits);
        }
      }
      if (overflow === 0) {
        return;
      }

      // Tracev((stderr,"\nbit length overflow\n"));
      /* This happens for example on obj2 and pic of the Calgary corpus */

      /* Find the first bit length which could increase: */
      do {
        bits = max_length - 1;
        while (s.bl_count[bits] === 0) {
          bits--;
        }
        s.bl_count[bits]--; /* move one leaf down the tree */
        s.bl_count[bits + 1] += 2; /* move one overflow item as its brother */
        s.bl_count[max_length]--;
        /* The brother of the overflow item also moves one step up,
         * but this does not affect bl_count[max_length]
         */
        overflow -= 2;
      } while (overflow > 0);

      /* Now recompute all bit lengths, scanning in increasing frequency.
       * h is still equal to HEAP_SIZE. (It is simpler to reconstruct all
       * lengths instead of fixing only the wrong ones. This idea is taken
       * from 'ar' written by Haruhiko Okumura.)
       */
      for (bits = max_length; bits !== 0; bits--) {
        n = s.bl_count[bits];
        while (n !== 0) {
          m = s.heap[--h];
          if (m > max_code) {
            continue;
          }
          if (tree[m * 2 + 1] /*.Len*/ !== bits) {
            // Tracev((stderr,"code %d bits %d->%d\n", m, tree[m].Len, bits));
            s.opt_len += (bits - tree[m * 2 + 1] /*.Len*/) * tree[m * 2] /*.Freq*/;
            tree[m * 2 + 1] /*.Len*/ = bits;
          }
          n--;
        }
      }
    };

    /* ===========================================================================
     * Generate the codes for a given tree and bit counts (which need not be
     * optimal).
     * IN assertion: the array bl_count contains the bit length statistics for
     * the given tree and the field len is set for all tree elements.
     * OUT assertion: the field code is set for all tree elements of non
     *     zero code length.
     */
    const gen_codes = (tree, max_code, bl_count) => {
      //    ct_data *tree;             /* the tree to decorate */
      //    int max_code;              /* largest code with non zero frequency */
      //    ushf *bl_count;            /* number of codes at each bit length */

      const next_code = new Array(MAX_BITS$1 + 1); /* next code value for each bit length */
      let code = 0; /* running code value */
      let bits; /* bit index */
      let n; /* code index */

      /* The distribution counts are first used to generate the code values
       * without bit reversal.
       */
      for (bits = 1; bits <= MAX_BITS$1; bits++) {
        code = code + bl_count[bits - 1] << 1;
        next_code[bits] = code;
      }
      /* Check that the bit counts in bl_count are consistent. The last code
       * must be all ones.
       */
      //Assert (code + bl_count[MAX_BITS]-1 == (1<<MAX_BITS)-1,
      //        "inconsistent bit counts");
      //Tracev((stderr,"\ngen_codes: max_code %d ", max_code));

      for (n = 0; n <= max_code; n++) {
        let len = tree[n * 2 + 1] /*.Len*/;
        if (len === 0) {
          continue;
        }
        /* Now reverse the bits */
        tree[n * 2] /*.Code*/ = bi_reverse(next_code[len]++, len);

        //Tracecv(tree != static_ltree, (stderr,"\nn %3d %c l %2d c %4x (%x) ",
        //     n, (isgraph(n) ? n : ' '), len, tree[n].Code, next_code[len]-1));
      }
    };

    /* ===========================================================================
     * Initialize the various 'constant' tables.
     */
    const tr_static_init = () => {
      let n; /* iterates over tree elements */
      let bits; /* bit counter */
      let length; /* length value */
      let code; /* code value */
      let dist; /* distance index */
      const bl_count = new Array(MAX_BITS$1 + 1);
      /* number of codes at each bit length for an optimal tree */

      // do check in _tr_init()
      //if (static_init_done) return;

      /* For some embedded targets, global variables are not initialized: */
      /*#ifdef NO_INIT_GLOBAL_POINTERS
        static_l_desc.static_tree = static_ltree;
        static_l_desc.extra_bits = extra_lbits;
        static_d_desc.static_tree = static_dtree;
        static_d_desc.extra_bits = extra_dbits;
        static_bl_desc.extra_bits = extra_blbits;
      #endif*/

      /* Initialize the mapping length (0..255) -> length code (0..28) */
      length = 0;
      for (code = 0; code < LENGTH_CODES$1 - 1; code++) {
        base_length[code] = length;
        for (n = 0; n < 1 << extra_lbits[code]; n++) {
          _length_code[length++] = code;
        }
      }
      //Assert (length == 256, "tr_static_init: length != 256");
      /* Note that the length 255 (match length 258) can be represented
       * in two different ways: code 284 + 5 bits or code 285, so we
       * overwrite length_code[255] to use the best encoding:
       */
      _length_code[length - 1] = code;

      /* Initialize the mapping dist (0..32K) -> dist code (0..29) */
      dist = 0;
      for (code = 0; code < 16; code++) {
        base_dist[code] = dist;
        for (n = 0; n < 1 << extra_dbits[code]; n++) {
          _dist_code[dist++] = code;
        }
      }
      //Assert (dist == 256, "tr_static_init: dist != 256");
      dist >>= 7; /* from now on, all distances are divided by 128 */
      for (; code < D_CODES$1; code++) {
        base_dist[code] = dist << 7;
        for (n = 0; n < 1 << extra_dbits[code] - 7; n++) {
          _dist_code[256 + dist++] = code;
        }
      }
      //Assert (dist == 256, "tr_static_init: 256+dist != 512");

      /* Construct the codes of the static literal tree */
      for (bits = 0; bits <= MAX_BITS$1; bits++) {
        bl_count[bits] = 0;
      }
      n = 0;
      while (n <= 143) {
        static_ltree[n * 2 + 1] /*.Len*/ = 8;
        n++;
        bl_count[8]++;
      }
      while (n <= 255) {
        static_ltree[n * 2 + 1] /*.Len*/ = 9;
        n++;
        bl_count[9]++;
      }
      while (n <= 279) {
        static_ltree[n * 2 + 1] /*.Len*/ = 7;
        n++;
        bl_count[7]++;
      }
      while (n <= 287) {
        static_ltree[n * 2 + 1] /*.Len*/ = 8;
        n++;
        bl_count[8]++;
      }
      /* Codes 286 and 287 do not exist, but we must include them in the
       * tree construction to get a canonical Huffman tree (longest code
       * all ones)
       */
      gen_codes(static_ltree, L_CODES$1 + 1, bl_count);

      /* The static distance tree is trivial: */
      for (n = 0; n < D_CODES$1; n++) {
        static_dtree[n * 2 + 1] /*.Len*/ = 5;
        static_dtree[n * 2] /*.Code*/ = bi_reverse(n, 5);
      }

      // Now data ready and we can init static trees
      static_l_desc = new StaticTreeDesc(static_ltree, extra_lbits, LITERALS$1 + 1, L_CODES$1, MAX_BITS$1);
      static_d_desc = new StaticTreeDesc(static_dtree, extra_dbits, 0, D_CODES$1, MAX_BITS$1);
      static_bl_desc = new StaticTreeDesc(new Array(0), extra_blbits, 0, BL_CODES$1, MAX_BL_BITS);

      //static_init_done = true;
    };

    /* ===========================================================================
     * Initialize a new block.
     */
    const init_block = s => {
      let n; /* iterates over tree elements */

      /* Initialize the trees. */
      for (n = 0; n < L_CODES$1; n++) {
        s.dyn_ltree[n * 2] /*.Freq*/ = 0;
      }
      for (n = 0; n < D_CODES$1; n++) {
        s.dyn_dtree[n * 2] /*.Freq*/ = 0;
      }
      for (n = 0; n < BL_CODES$1; n++) {
        s.bl_tree[n * 2] /*.Freq*/ = 0;
      }
      s.dyn_ltree[END_BLOCK * 2] /*.Freq*/ = 1;
      s.opt_len = s.static_len = 0;
      s.sym_next = s.matches = 0;
    };

    /* ===========================================================================
     * Flush the bit buffer and align the output on a byte boundary
     */
    const bi_windup = s => {
      if (s.bi_valid > 8) {
        put_short(s, s.bi_buf);
      } else if (s.bi_valid > 0) {
        //put_byte(s, (Byte)s->bi_buf);
        s.pending_buf[s.pending++] = s.bi_buf;
      }
      s.bi_buf = 0;
      s.bi_valid = 0;
    };

    /* ===========================================================================
     * Compares to subtrees, using the tree depth as tie breaker when
     * the subtrees have equal frequency. This minimizes the worst case length.
     */
    const smaller = (tree, n, m, depth) => {
      const _n2 = n * 2;
      const _m2 = m * 2;
      return tree[_n2] /*.Freq*/ < tree[_m2] /*.Freq*/ || tree[_n2] /*.Freq*/ === tree[_m2] /*.Freq*/ && depth[n] <= depth[m];
    };

    /* ===========================================================================
     * Restore the heap property by moving down the tree starting at node k,
     * exchanging a node with the smallest of its two sons if necessary, stopping
     * when the heap property is re-established (each father smaller than its
     * two sons).
     */
    const pqdownheap = (s, tree, k) => {
      //    deflate_state *s;
      //    ct_data *tree;  /* the tree to restore */
      //    int k;               /* node to move down */

      const v = s.heap[k];
      let j = k << 1; /* left son of k */
      while (j <= s.heap_len) {
        /* Set j to the smallest of the two sons: */
        if (j < s.heap_len && smaller(tree, s.heap[j + 1], s.heap[j], s.depth)) {
          j++;
        }
        /* Exit if v is smaller than both sons */
        if (smaller(tree, v, s.heap[j], s.depth)) {
          break;
        }

        /* Exchange v with the smallest son */
        s.heap[k] = s.heap[j];
        k = j;

        /* And continue down the tree, setting j to the left son of k */
        j <<= 1;
      }
      s.heap[k] = v;
    };

    // inlined manually
    // const SMALLEST = 1;

    /* ===========================================================================
     * Send the block data compressed using the given Huffman trees
     */
    const compress_block = (s, ltree, dtree) => {
      //    deflate_state *s;
      //    const ct_data *ltree; /* literal tree */
      //    const ct_data *dtree; /* distance tree */

      let dist; /* distance of matched string */
      let lc; /* match length or unmatched char (if dist == 0) */
      let sx = 0; /* running index in sym_buf */
      let code; /* the code to send */
      let extra; /* number of extra bits to send */

      if (s.sym_next !== 0) {
        do {
          dist = s.pending_buf[s.sym_buf + sx++] & 0xff;
          dist += (s.pending_buf[s.sym_buf + sx++] & 0xff) << 8;
          lc = s.pending_buf[s.sym_buf + sx++];
          if (dist === 0) {
            send_code(s, lc, ltree); /* send a literal byte */
            //Tracecv(isgraph(lc), (stderr," '%c' ", lc));
          } else {
            /* Here, lc is the match length - MIN_MATCH */
            code = _length_code[lc];
            send_code(s, code + LITERALS$1 + 1, ltree); /* send the length code */
            extra = extra_lbits[code];
            if (extra !== 0) {
              lc -= base_length[code];
              send_bits(s, lc, extra); /* send the extra length bits */
            }
            dist--; /* dist is now the match distance - 1 */
            code = d_code(dist);
            //Assert (code < D_CODES, "bad d_code");

            send_code(s, code, dtree); /* send the distance code */
            extra = extra_dbits[code];
            if (extra !== 0) {
              dist -= base_dist[code];
              send_bits(s, dist, extra); /* send the extra distance bits */
            }
          } /* literal or match pair ? */

          /* Check that the overlay between pending_buf and sym_buf is ok: */
          //Assert(s->pending < s->lit_bufsize + sx, "pendingBuf overflow");
        } while (sx < s.sym_next);
      }
      send_code(s, END_BLOCK, ltree);
    };

    /* ===========================================================================
     * Construct one Huffman tree and assigns the code bit strings and lengths.
     * Update the total bit length for the current block.
     * IN assertion: the field freq is set for all tree elements.
     * OUT assertions: the fields len and code are set to the optimal bit length
     *     and corresponding code. The length opt_len is updated; static_len is
     *     also updated if stree is not null. The field max_code is set.
     */
    const build_tree = (s, desc) => {
      //    deflate_state *s;
      //    tree_desc *desc; /* the tree descriptor */

      const tree = desc.dyn_tree;
      const stree = desc.stat_desc.static_tree;
      const has_stree = desc.stat_desc.has_stree;
      const elems = desc.stat_desc.elems;
      let n, m; /* iterate over heap elements */
      let max_code = -1; /* largest code with non zero frequency */
      let node; /* new node being created */

      /* Construct the initial heap, with least frequent element in
       * heap[SMALLEST]. The sons of heap[n] are heap[2*n] and heap[2*n+1].
       * heap[0] is not used.
       */
      s.heap_len = 0;
      s.heap_max = HEAP_SIZE$1;
      for (n = 0; n < elems; n++) {
        if (tree[n * 2] /*.Freq*/ !== 0) {
          s.heap[++s.heap_len] = max_code = n;
          s.depth[n] = 0;
        } else {
          tree[n * 2 + 1] /*.Len*/ = 0;
        }
      }

      /* The pkzip format requires that at least one distance code exists,
       * and that at least one bit should be sent even if there is only one
       * possible code. So to avoid special checks later on we force at least
       * two codes of non zero frequency.
       */
      while (s.heap_len < 2) {
        node = s.heap[++s.heap_len] = max_code < 2 ? ++max_code : 0;
        tree[node * 2] /*.Freq*/ = 1;
        s.depth[node] = 0;
        s.opt_len--;
        if (has_stree) {
          s.static_len -= stree[node * 2 + 1] /*.Len*/;
        }
        /* node is 0 or 1 so it does not have extra bits */
      }
      desc.max_code = max_code;

      /* The elements heap[heap_len/2+1 .. heap_len] are leaves of the tree,
       * establish sub-heaps of increasing lengths:
       */
      for (n = s.heap_len >> 1 /*int /2*/; n >= 1; n--) {
        pqdownheap(s, tree, n);
      }

      /* Construct the Huffman tree by repeatedly combining the least two
       * frequent nodes.
       */
      node = elems; /* next internal node of the tree */
      do {
        //pqremove(s, tree, n);  /* n = node of least frequency */
        /*** pqremove ***/
        n = s.heap[1 /*SMALLEST*/];
        s.heap[1 /*SMALLEST*/] = s.heap[s.heap_len--];
        pqdownheap(s, tree, 1 /*SMALLEST*/);
        /***/

        m = s.heap[1 /*SMALLEST*/]; /* m = node of next least frequency */

        s.heap[--s.heap_max] = n; /* keep the nodes sorted by frequency */
        s.heap[--s.heap_max] = m;

        /* Create a new node father of n and m */
        tree[node * 2] /*.Freq*/ = tree[n * 2] /*.Freq*/ + tree[m * 2] /*.Freq*/;
        s.depth[node] = (s.depth[n] >= s.depth[m] ? s.depth[n] : s.depth[m]) + 1;
        tree[n * 2 + 1] /*.Dad*/ = tree[m * 2 + 1] /*.Dad*/ = node;

        /* and insert the new node in the heap */
        s.heap[1 /*SMALLEST*/] = node++;
        pqdownheap(s, tree, 1 /*SMALLEST*/);
      } while (s.heap_len >= 2);
      s.heap[--s.heap_max] = s.heap[1 /*SMALLEST*/];

      /* At this point, the fields freq and dad are set. We can now
       * generate the bit lengths.
       */
      gen_bitlen(s, desc);

      /* The field len is now set, we can generate the bit codes */
      gen_codes(tree, max_code, s.bl_count);
    };

    /* ===========================================================================
     * Scan a literal or distance tree to determine the frequencies of the codes
     * in the bit length tree.
     */
    const scan_tree = (s, tree, max_code) => {
      //    deflate_state *s;
      //    ct_data *tree;   /* the tree to be scanned */
      //    int max_code;    /* and its largest code of non zero frequency */

      let n; /* iterates over all tree elements */
      let prevlen = -1; /* last emitted length */
      let curlen; /* length of current code */

      let nextlen = tree[0 * 2 + 1] /*.Len*/; /* length of next code */

      let count = 0; /* repeat count of the current code */
      let max_count = 7; /* max repeat count */
      let min_count = 4; /* min repeat count */

      if (nextlen === 0) {
        max_count = 138;
        min_count = 3;
      }
      tree[(max_code + 1) * 2 + 1] /*.Len*/ = 0xffff; /* guard */

      for (n = 0; n <= max_code; n++) {
        curlen = nextlen;
        nextlen = tree[(n + 1) * 2 + 1] /*.Len*/;
        if (++count < max_count && curlen === nextlen) {
          continue;
        } else if (count < min_count) {
          s.bl_tree[curlen * 2] /*.Freq*/ += count;
        } else if (curlen !== 0) {
          if (curlen !== prevlen) {
            s.bl_tree[curlen * 2] /*.Freq*/++;
          }
          s.bl_tree[REP_3_6 * 2] /*.Freq*/++;
        } else if (count <= 10) {
          s.bl_tree[REPZ_3_10 * 2] /*.Freq*/++;
        } else {
          s.bl_tree[REPZ_11_138 * 2] /*.Freq*/++;
        }
        count = 0;
        prevlen = curlen;
        if (nextlen === 0) {
          max_count = 138;
          min_count = 3;
        } else if (curlen === nextlen) {
          max_count = 6;
          min_count = 3;
        } else {
          max_count = 7;
          min_count = 4;
        }
      }
    };

    /* ===========================================================================
     * Send a literal or distance tree in compressed form, using the codes in
     * bl_tree.
     */
    const send_tree = (s, tree, max_code) => {
      //    deflate_state *s;
      //    ct_data *tree; /* the tree to be scanned */
      //    int max_code;       /* and its largest code of non zero frequency */

      let n; /* iterates over all tree elements */
      let prevlen = -1; /* last emitted length */
      let curlen; /* length of current code */

      let nextlen = tree[0 * 2 + 1] /*.Len*/; /* length of next code */

      let count = 0; /* repeat count of the current code */
      let max_count = 7; /* max repeat count */
      let min_count = 4; /* min repeat count */

      /* tree[max_code+1].Len = -1; */ /* guard already set */
      if (nextlen === 0) {
        max_count = 138;
        min_count = 3;
      }
      for (n = 0; n <= max_code; n++) {
        curlen = nextlen;
        nextlen = tree[(n + 1) * 2 + 1] /*.Len*/;
        if (++count < max_count && curlen === nextlen) {
          continue;
        } else if (count < min_count) {
          do {
            send_code(s, curlen, s.bl_tree);
          } while (--count !== 0);
        } else if (curlen !== 0) {
          if (curlen !== prevlen) {
            send_code(s, curlen, s.bl_tree);
            count--;
          }
          //Assert(count >= 3 && count <= 6, " 3_6?");
          send_code(s, REP_3_6, s.bl_tree);
          send_bits(s, count - 3, 2);
        } else if (count <= 10) {
          send_code(s, REPZ_3_10, s.bl_tree);
          send_bits(s, count - 3, 3);
        } else {
          send_code(s, REPZ_11_138, s.bl_tree);
          send_bits(s, count - 11, 7);
        }
        count = 0;
        prevlen = curlen;
        if (nextlen === 0) {
          max_count = 138;
          min_count = 3;
        } else if (curlen === nextlen) {
          max_count = 6;
          min_count = 3;
        } else {
          max_count = 7;
          min_count = 4;
        }
      }
    };

    /* ===========================================================================
     * Construct the Huffman tree for the bit lengths and return the index in
     * bl_order of the last bit length code to send.
     */
    const build_bl_tree = s => {
      let max_blindex; /* index of last bit length code of non zero freq */

      /* Determine the bit length frequencies for literal and distance trees */
      scan_tree(s, s.dyn_ltree, s.l_desc.max_code);
      scan_tree(s, s.dyn_dtree, s.d_desc.max_code);

      /* Build the bit length tree: */
      build_tree(s, s.bl_desc);
      /* opt_len now includes the length of the tree representations, except
       * the lengths of the bit lengths codes and the 5+5+4 bits for the counts.
       */

      /* Determine the number of bit length codes to send. The pkzip format
       * requires that at least 4 bit length codes be sent. (appnote.txt says
       * 3 but the actual value used is 4.)
       */
      for (max_blindex = BL_CODES$1 - 1; max_blindex >= 3; max_blindex--) {
        if (s.bl_tree[bl_order[max_blindex] * 2 + 1] /*.Len*/ !== 0) {
          break;
        }
      }
      /* Update opt_len to include the bit length tree and counts */
      s.opt_len += 3 * (max_blindex + 1) + 5 + 5 + 4;
      //Tracev((stderr, "\ndyn trees: dyn %ld, stat %ld",
      //        s->opt_len, s->static_len));

      return max_blindex;
    };

    /* ===========================================================================
     * Send the header for a block using dynamic Huffman trees: the counts, the
     * lengths of the bit length codes, the literal tree and the distance tree.
     * IN assertion: lcodes >= 257, dcodes >= 1, blcodes >= 4.
     */
    const send_all_trees = (s, lcodes, dcodes, blcodes) => {
      //    deflate_state *s;
      //    int lcodes, dcodes, blcodes; /* number of codes for each tree */

      let rank; /* index in bl_order */

      //Assert (lcodes >= 257 && dcodes >= 1 && blcodes >= 4, "not enough codes");
      //Assert (lcodes <= L_CODES && dcodes <= D_CODES && blcodes <= BL_CODES,
      //        "too many codes");
      //Tracev((stderr, "\nbl counts: "));
      send_bits(s, lcodes - 257, 5); /* not +255 as stated in appnote.txt */
      send_bits(s, dcodes - 1, 5);
      send_bits(s, blcodes - 4, 4); /* not -3 as stated in appnote.txt */
      for (rank = 0; rank < blcodes; rank++) {
        //Tracev((stderr, "\nbl code %2d ", bl_order[rank]));
        send_bits(s, s.bl_tree[bl_order[rank] * 2 + 1] /*.Len*/, 3);
      }
      //Tracev((stderr, "\nbl tree: sent %ld", s->bits_sent));

      send_tree(s, s.dyn_ltree, lcodes - 1); /* literal tree */
      //Tracev((stderr, "\nlit tree: sent %ld", s->bits_sent));

      send_tree(s, s.dyn_dtree, dcodes - 1); /* distance tree */
      //Tracev((stderr, "\ndist tree: sent %ld", s->bits_sent));
    };

    /* ===========================================================================
     * Check if the data type is TEXT or BINARY, using the following algorithm:
     * - TEXT if the two conditions below are satisfied:
     *    a) There are no non-portable control characters belonging to the
     *       "block list" (0..6, 14..25, 28..31).
     *    b) There is at least one printable character belonging to the
     *       "allow list" (9 {TAB}, 10 {LF}, 13 {CR}, 32..255).
     * - BINARY otherwise.
     * - The following partially-portable control characters form a
     *   "gray list" that is ignored in this detection algorithm:
     *   (7 {BEL}, 8 {BS}, 11 {VT}, 12 {FF}, 26 {SUB}, 27 {ESC}).
     * IN assertion: the fields Freq of dyn_ltree are set.
     */
    const detect_data_type = s => {
      /* block_mask is the bit mask of block-listed bytes
       * set bits 0..6, 14..25, and 28..31
       * 0xf3ffc07f = binary 11110011111111111100000001111111
       */
      let block_mask = 0xf3ffc07f;
      let n;

      /* Check for non-textual ("block-listed") bytes. */
      for (n = 0; n <= 31; n++, block_mask >>>= 1) {
        if (block_mask & 1 && s.dyn_ltree[n * 2] /*.Freq*/ !== 0) {
          return Z_BINARY;
        }
      }

      /* Check for textual ("allow-listed") bytes. */
      if (s.dyn_ltree[9 * 2] /*.Freq*/ !== 0 || s.dyn_ltree[10 * 2] /*.Freq*/ !== 0 || s.dyn_ltree[13 * 2] /*.Freq*/ !== 0) {
        return Z_TEXT;
      }
      for (n = 32; n < LITERALS$1; n++) {
        if (s.dyn_ltree[n * 2] /*.Freq*/ !== 0) {
          return Z_TEXT;
        }
      }

      /* There are no "block-listed" or "allow-listed" bytes:
       * this stream either is empty or has tolerated ("gray-listed") bytes only.
       */
      return Z_BINARY;
    };
    let static_init_done = false;

    /* ===========================================================================
     * Initialize the tree data structures for a new zlib stream.
     */
    const _tr_init$1 = s => {
      if (!static_init_done) {
        tr_static_init();
        static_init_done = true;
      }
      s.l_desc = new TreeDesc(s.dyn_ltree, static_l_desc);
      s.d_desc = new TreeDesc(s.dyn_dtree, static_d_desc);
      s.bl_desc = new TreeDesc(s.bl_tree, static_bl_desc);
      s.bi_buf = 0;
      s.bi_valid = 0;

      /* Initialize the first block of the first file: */
      init_block(s);
    };

    /* ===========================================================================
     * Send a stored block
     */
    const _tr_stored_block$1 = (s, buf, stored_len, last) => {
      //DeflateState *s;
      //charf *buf;       /* input block */
      //ulg stored_len;   /* length of input block */
      //int last;         /* one if this is the last block for a file */

      send_bits(s, (STORED_BLOCK << 1) + (last ? 1 : 0), 3); /* send block type */
      bi_windup(s); /* align on byte boundary */
      put_short(s, stored_len);
      put_short(s, ~stored_len);
      if (stored_len) {
        s.pending_buf.set(s.window.subarray(buf, buf + stored_len), s.pending);
      }
      s.pending += stored_len;
    };

    /* ===========================================================================
     * Send one empty static block to give enough lookahead for inflate.
     * This takes 10 bits, of which 7 may remain in the bit buffer.
     */
    const _tr_align$1 = s => {
      send_bits(s, STATIC_TREES << 1, 3);
      send_code(s, END_BLOCK, static_ltree);
      bi_flush(s);
    };

    /* ===========================================================================
     * Determine the best encoding for the current block: dynamic trees, static
     * trees or store, and write out the encoded block.
     */
    const _tr_flush_block$1 = (s, buf, stored_len, last) => {
      //DeflateState *s;
      //charf *buf;       /* input block, or NULL if too old */
      //ulg stored_len;   /* length of input block */
      //int last;         /* one if this is the last block for a file */

      let opt_lenb, static_lenb; /* opt_len and static_len in bytes */
      let max_blindex = 0; /* index of last bit length code of non zero freq */

      /* Build the Huffman trees unless a stored block is forced */
      if (s.level > 0) {
        /* Check if the file is binary or text */
        if (s.strm.data_type === Z_UNKNOWN$1) {
          s.strm.data_type = detect_data_type(s);
        }

        /* Construct the literal and distance trees */
        build_tree(s, s.l_desc);
        // Tracev((stderr, "\nlit data: dyn %ld, stat %ld", s->opt_len,
        //        s->static_len));

        build_tree(s, s.d_desc);
        // Tracev((stderr, "\ndist data: dyn %ld, stat %ld", s->opt_len,
        //        s->static_len));
        /* At this point, opt_len and static_len are the total bit lengths of
         * the compressed block data, excluding the tree representations.
         */

        /* Build the bit length tree for the above two trees, and get the index
         * in bl_order of the last bit length code to send.
         */
        max_blindex = build_bl_tree(s);

        /* Determine the best encoding. Compute the block lengths in bytes. */
        opt_lenb = s.opt_len + 3 + 7 >>> 3;
        static_lenb = s.static_len + 3 + 7 >>> 3;

        // Tracev((stderr, "\nopt %lu(%lu) stat %lu(%lu) stored %lu lit %u ",
        //        opt_lenb, s->opt_len, static_lenb, s->static_len, stored_len,
        //        s->sym_next / 3));

        if (static_lenb <= opt_lenb) {
          opt_lenb = static_lenb;
        }
      } else {
        // Assert(buf != (char*)0, "lost buf");
        opt_lenb = static_lenb = stored_len + 5; /* force a stored block */
      }
      if (stored_len + 4 <= opt_lenb && buf !== -1) {
        /* 4: two words for the lengths */

        /* The test buf != NULL is only necessary if LIT_BUFSIZE > WSIZE.
         * Otherwise we can't have processed more than WSIZE input bytes since
         * the last block flush, because compression would have been
         * successful. If LIT_BUFSIZE <= WSIZE, it is never too late to
         * transform a block into a stored block.
         */
        _tr_stored_block$1(s, buf, stored_len, last);
      } else if (s.strategy === Z_FIXED$1 || static_lenb === opt_lenb) {
        send_bits(s, (STATIC_TREES << 1) + (last ? 1 : 0), 3);
        compress_block(s, static_ltree, static_dtree);
      } else {
        send_bits(s, (DYN_TREES << 1) + (last ? 1 : 0), 3);
        send_all_trees(s, s.l_desc.max_code + 1, s.d_desc.max_code + 1, max_blindex + 1);
        compress_block(s, s.dyn_ltree, s.dyn_dtree);
      }
      // Assert (s->compressed_len == s->bits_sent, "bad compressed size");
      /* The above check is made mod 2^32, for files larger than 512 MB
       * and uLong implemented on 32 bits.
       */
      init_block(s);
      if (last) {
        bi_windup(s);
      }
      // Tracev((stderr,"\ncomprlen %lu(%lu) ", s->compressed_len>>3,
      //       s->compressed_len-7*last));
    };

    /* ===========================================================================
     * Save the match info and tally the frequency counts. Return true if
     * the current block must be flushed.
     */
    const _tr_tally$1 = (s, dist, lc) => {
      //    deflate_state *s;
      //    unsigned dist;  /* distance of matched string */
      //    unsigned lc;    /* match length-MIN_MATCH or unmatched char (if dist==0) */

      s.pending_buf[s.sym_buf + s.sym_next++] = dist;
      s.pending_buf[s.sym_buf + s.sym_next++] = dist >> 8;
      s.pending_buf[s.sym_buf + s.sym_next++] = lc;
      if (dist === 0) {
        /* lc is the unmatched char */
        s.dyn_ltree[lc * 2] /*.Freq*/++;
      } else {
        s.matches++;
        /* Here, lc is the match length - MIN_MATCH */
        dist--; /* dist = match distance - 1 */
        //Assert((ush)dist < (ush)MAX_DIST(s) &&
        //       (ush)lc <= (ush)(MAX_MATCH-MIN_MATCH) &&
        //       (ush)d_code(dist) < (ush)D_CODES,  "_tr_tally: bad match");

        s.dyn_ltree[(_length_code[lc] + LITERALS$1 + 1) * 2] /*.Freq*/++;
        s.dyn_dtree[d_code(dist) * 2] /*.Freq*/++;
      }
      return s.sym_next === s.sym_end;
    };
    var _tr_init_1 = _tr_init$1;
    var _tr_stored_block_1 = _tr_stored_block$1;
    var _tr_flush_block_1 = _tr_flush_block$1;
    var _tr_tally_1 = _tr_tally$1;
    var _tr_align_1 = _tr_align$1;
    var trees = {
      _tr_init: _tr_init_1,
      _tr_stored_block: _tr_stored_block_1,
      _tr_flush_block: _tr_flush_block_1,
      _tr_tally: _tr_tally_1,
      _tr_align: _tr_align_1
    };

    // Note: adler32 takes 12% for level 0 and 2% for level 6.
    // It isn't worth it to make additional optimizations as in original.
    // Small size is preferable.

    // (C) 1995-2013 Jean-loup Gailly and Mark Adler
    // (C) 2014-2017 Vitaly Puzrin and Andrey Tupitsin
    //
    // This software is provided 'as-is', without any express or implied
    // warranty. In no event will the authors be held liable for any damages
    // arising from the use of this software.
    //
    // Permission is granted to anyone to use this software for any purpose,
    // including commercial applications, and to alter it and redistribute it
    // freely, subject to the following restrictions:
    //
    // 1. The origin of this software must not be misrepresented; you must not
    //   claim that you wrote the original software. If you use this software
    //   in a product, an acknowledgment in the product documentation would be
    //   appreciated but is not required.
    // 2. Altered source versions must be plainly marked as such, and must not be
    //   misrepresented as being the original software.
    // 3. This notice may not be removed or altered from any source distribution.

    const adler32 = (adler, buf, len, pos) => {
      let s1 = adler & 0xffff | 0,
        s2 = adler >>> 16 & 0xffff | 0,
        n = 0;
      while (len !== 0) {
        // Set limit ~ twice less than 5552, to keep
        // s2 in 31-bits, because we force signed ints.
        // in other case %= will fail.
        n = len > 2000 ? 2000 : len;
        len -= n;
        do {
          s1 = s1 + buf[pos++] | 0;
          s2 = s2 + s1 | 0;
        } while (--n);
        s1 %= 65521;
        s2 %= 65521;
      }
      return s1 | s2 << 16 | 0;
    };
    var adler32_1 = adler32;

    // Note: we can't get significant speed boost here.
    // So write code to minimize size - no pregenerated tables
    // and array tools dependencies.

    // (C) 1995-2013 Jean-loup Gailly and Mark Adler
    // (C) 2014-2017 Vitaly Puzrin and Andrey Tupitsin
    //
    // This software is provided 'as-is', without any express or implied
    // warranty. In no event will the authors be held liable for any damages
    // arising from the use of this software.
    //
    // Permission is granted to anyone to use this software for any purpose,
    // including commercial applications, and to alter it and redistribute it
    // freely, subject to the following restrictions:
    //
    // 1. The origin of this software must not be misrepresented; you must not
    //   claim that you wrote the original software. If you use this software
    //   in a product, an acknowledgment in the product documentation would be
    //   appreciated but is not required.
    // 2. Altered source versions must be plainly marked as such, and must not be
    //   misrepresented as being the original software.
    // 3. This notice may not be removed or altered from any source distribution.

    // Use ordinary array, since untyped makes no boost here
    const makeTable = () => {
      let c,
        table = [];
      for (var n = 0; n < 256; n++) {
        c = n;
        for (var k = 0; k < 8; k++) {
          c = c & 1 ? 0xEDB88320 ^ c >>> 1 : c >>> 1;
        }
        table[n] = c;
      }
      return table;
    };

    // Create table on load. Just 255 signed longs. Not a problem.
    const crcTable = new Uint32Array(makeTable());
    const crc32 = (crc, buf, len, pos) => {
      const t = crcTable;
      const end = pos + len;
      crc ^= -1;
      for (let i = pos; i < end; i++) {
        crc = crc >>> 8 ^ t[(crc ^ buf[i]) & 0xFF];
      }
      return crc ^ -1; // >>> 0;
    };
    var crc32_1 = crc32;

    // (C) 1995-2013 Jean-loup Gailly and Mark Adler
    // (C) 2014-2017 Vitaly Puzrin and Andrey Tupitsin
    //
    // This software is provided 'as-is', without any express or implied
    // warranty. In no event will the authors be held liable for any damages
    // arising from the use of this software.
    //
    // Permission is granted to anyone to use this software for any purpose,
    // including commercial applications, and to alter it and redistribute it
    // freely, subject to the following restrictions:
    //
    // 1. The origin of this software must not be misrepresented; you must not
    //   claim that you wrote the original software. If you use this software
    //   in a product, an acknowledgment in the product documentation would be
    //   appreciated but is not required.
    // 2. Altered source versions must be plainly marked as such, and must not be
    //   misrepresented as being the original software.
    // 3. This notice may not be removed or altered from any source distribution.

    var messages = {
      2: 'need dictionary',
      /* Z_NEED_DICT       2  */
      1: 'stream end',
      /* Z_STREAM_END      1  */
      0: '',
      /* Z_OK              0  */
      '-1': 'file error',
      /* Z_ERRNO         (-1) */
      '-2': 'stream error',
      /* Z_STREAM_ERROR  (-2) */
      '-3': 'data error',
      /* Z_DATA_ERROR    (-3) */
      '-4': 'insufficient memory',
      /* Z_MEM_ERROR     (-4) */
      '-5': 'buffer error',
      /* Z_BUF_ERROR     (-5) */
      '-6': 'incompatible version' /* Z_VERSION_ERROR (-6) */
    };

    // (C) 1995-2013 Jean-loup Gailly and Mark Adler
    // (C) 2014-2017 Vitaly Puzrin and Andrey Tupitsin
    //
    // This software is provided 'as-is', without any express or implied
    // warranty. In no event will the authors be held liable for any damages
    // arising from the use of this software.
    //
    // Permission is granted to anyone to use this software for any purpose,
    // including commercial applications, and to alter it and redistribute it
    // freely, subject to the following restrictions:
    //
    // 1. The origin of this software must not be misrepresented; you must not
    //   claim that you wrote the original software. If you use this software
    //   in a product, an acknowledgment in the product documentation would be
    //   appreciated but is not required.
    // 2. Altered source versions must be plainly marked as such, and must not be
    //   misrepresented as being the original software.
    // 3. This notice may not be removed or altered from any source distribution.

    var constants$2 = {
      /* Allowed flush values; see deflate() and inflate() below for details */
      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,
      /* Return codes for the compression/decompression functions. Negative values
      * are errors, positive values are used for special but normal events.
      */
      Z_OK: 0,
      Z_STREAM_END: 1,
      Z_NEED_DICT: 2,
      Z_ERRNO: -1,
      Z_STREAM_ERROR: -2,
      Z_DATA_ERROR: -3,
      Z_MEM_ERROR: -4,
      Z_BUF_ERROR: -5,
      //Z_VERSION_ERROR: -6,

      /* compression levels */
      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,
      /* Possible values of the data_type field (though see inflate()) */
      Z_BINARY: 0,
      Z_TEXT: 1,
      //Z_ASCII:                1, // = Z_TEXT (deprecated)
      Z_UNKNOWN: 2,
      /* The deflate compression method */
      Z_DEFLATED: 8
      //Z_NULL:                 null // Use -1 or null inline, depending on var type
    };

    // (C) 1995-2013 Jean-loup Gailly and Mark Adler
    // (C) 2014-2017 Vitaly Puzrin and Andrey Tupitsin
    //
    // This software is provided 'as-is', without any express or implied
    // warranty. In no event will the authors be held liable for any damages
    // arising from the use of this software.
    //
    // Permission is granted to anyone to use this software for any purpose,
    // including commercial applications, and to alter it and redistribute it
    // freely, subject to the following restrictions:
    //
    // 1. The origin of this software must not be misrepresented; you must not
    //   claim that you wrote the original software. If you use this software
    //   in a product, an acknowledgment in the product documentation would be
    //   appreciated but is not required.
    // 2. Altered source versions must be plainly marked as such, and must not be
    //   misrepresented as being the original software.
    // 3. This notice may not be removed or altered from any source distribution.

    const {
      _tr_init,
      _tr_stored_block,
      _tr_flush_block,
      _tr_tally,
      _tr_align
    } = trees;

    /* Public constants ==========================================================*/
    /* ===========================================================================*/

    const {
      Z_NO_FLUSH: Z_NO_FLUSH$2,
      Z_PARTIAL_FLUSH,
      Z_FULL_FLUSH: Z_FULL_FLUSH$1,
      Z_FINISH: Z_FINISH$3,
      Z_BLOCK: Z_BLOCK$1,
      Z_OK: Z_OK$3,
      Z_STREAM_END: Z_STREAM_END$3,
      Z_STREAM_ERROR: Z_STREAM_ERROR$2,
      Z_DATA_ERROR: Z_DATA_ERROR$2,
      Z_BUF_ERROR: Z_BUF_ERROR$1,
      Z_DEFAULT_COMPRESSION: Z_DEFAULT_COMPRESSION$1,
      Z_FILTERED,
      Z_HUFFMAN_ONLY,
      Z_RLE,
      Z_FIXED,
      Z_DEFAULT_STRATEGY: Z_DEFAULT_STRATEGY$1,
      Z_UNKNOWN,
      Z_DEFLATED: Z_DEFLATED$2
    } = constants$2;

    /*============================================================================*/

    const MAX_MEM_LEVEL = 9;
    /* Maximum value for memLevel in deflateInit2 */
    const MAX_WBITS$1 = 15;
    /* 32K LZ77 window */
    const DEF_MEM_LEVEL = 8;
    const LENGTH_CODES = 29;
    /* number of length codes, not counting the special END_BLOCK code */
    const LITERALS = 256;
    /* number of literal bytes 0..255 */
    const L_CODES = LITERALS + 1 + LENGTH_CODES;
    /* number of Literal or Length codes, including the END_BLOCK code */
    const D_CODES = 30;
    /* number of distance codes */
    const BL_CODES = 19;
    /* number of codes used to transfer the bit lengths */
    const HEAP_SIZE = 2 * L_CODES + 1;
    /* maximum heap size */
    const MAX_BITS = 15;
    /* All codes must not exceed MAX_BITS bits */

    const MIN_MATCH = 3;
    const MAX_MATCH = 258;
    const MIN_LOOKAHEAD = MAX_MATCH + MIN_MATCH + 1;
    const PRESET_DICT = 0x20;
    const INIT_STATE = 42; /* zlib header -> BUSY_STATE */
    //#ifdef GZIP
    const GZIP_STATE = 57; /* gzip header -> BUSY_STATE | EXTRA_STATE */
    //#endif
    const EXTRA_STATE = 69; /* gzip extra block -> NAME_STATE */
    const NAME_STATE = 73; /* gzip file name -> COMMENT_STATE */
    const COMMENT_STATE = 91; /* gzip comment -> HCRC_STATE */
    const HCRC_STATE = 103; /* gzip header CRC -> BUSY_STATE */
    const BUSY_STATE = 113; /* deflate -> FINISH_STATE */
    const FINISH_STATE = 666; /* stream complete */

    const BS_NEED_MORE = 1; /* block not completed, need more input or more output */
    const BS_BLOCK_DONE = 2; /* block flush performed */
    const BS_FINISH_STARTED = 3; /* finish started, need only more output at next deflate */
    const BS_FINISH_DONE = 4; /* finish done, accept no more input or output */

    const OS_CODE = 0x03; // Unix :) . Don't detect, use this default.

    const err = (strm, errorCode) => {
      strm.msg = messages[errorCode];
      return errorCode;
    };
    const rank = f => {
      return f * 2 - (f > 4 ? 9 : 0);
    };
    const zero = buf => {
      let len = buf.length;
      while (--len >= 0) {
        buf[len] = 0;
      }
    };

    /* ===========================================================================
     * Slide the hash table when sliding the window down (could be avoided with 32
     * bit values at the expense of memory usage). We slide even when level == 0 to
     * keep the hash table consistent if we switch back to level > 0 later.
     */
    const slide_hash = s => {
      let n, m;
      let p;
      let wsize = s.w_size;
      n = s.hash_size;
      p = n;
      do {
        m = s.head[--p];
        s.head[p] = m >= wsize ? m - wsize : 0;
      } while (--n);
      n = wsize;
      //#ifndef FASTEST
      p = n;
      do {
        m = s.prev[--p];
        s.prev[p] = m >= wsize ? m - wsize : 0;
        /* If n is not on any hash chain, prev[n] is garbage but
         * its value will never be used.
         */
      } while (--n);
      //#endif
    };

    /* eslint-disable new-cap */
    let HASH_ZLIB = (s, prev, data) => (prev << s.hash_shift ^ data) & s.hash_mask;
    // This hash causes less collisions, https://github.com/nodeca/pako/issues/135
    // But breaks binary compatibility
    //let HASH_FAST = (s, prev, data) => ((prev << 8) + (prev >> 8) + (data << 4)) & s.hash_mask;
    let HASH = HASH_ZLIB;

    /* =========================================================================
     * Flush as much pending output as possible. All deflate() output, except for
     * some deflate_stored() output, goes through this function so some
     * applications may wish to modify it to avoid allocating a large
     * strm->next_out buffer and copying into it. (See also read_buf()).
     */
    const flush_pending = strm => {
      const s = strm.state;

      //_tr_flush_bits(s);
      let len = s.pending;
      if (len > strm.avail_out) {
        len = strm.avail_out;
      }
      if (len === 0) {
        return;
      }
      strm.output.set(s.pending_buf.subarray(s.pending_out, s.pending_out + len), strm.next_out);
      strm.next_out += len;
      s.pending_out += len;
      strm.total_out += len;
      strm.avail_out -= len;
      s.pending -= len;
      if (s.pending === 0) {
        s.pending_out = 0;
      }
    };
    const flush_block_only = (s, last) => {
      _tr_flush_block(s, s.block_start >= 0 ? s.block_start : -1, s.strstart - s.block_start, last);
      s.block_start = s.strstart;
      flush_pending(s.strm);
    };
    const put_byte = (s, b) => {
      s.pending_buf[s.pending++] = b;
    };

    /* =========================================================================
     * Put a short in the pending buffer. The 16-bit value is put in MSB order.
     * IN assertion: the stream state is correct and there is enough room in
     * pending_buf.
     */
    const putShortMSB = (s, b) => {
      //  put_byte(s, (Byte)(b >> 8));
      //  put_byte(s, (Byte)(b & 0xff));
      s.pending_buf[s.pending++] = b >>> 8 & 0xff;
      s.pending_buf[s.pending++] = b & 0xff;
    };

    /* ===========================================================================
     * Read a new buffer from the current input stream, update the adler32
     * and total number of bytes read.  All deflate() input goes through
     * this function so some applications may wish to modify it to avoid
     * allocating a large strm->input buffer and copying from it.
     * (See also flush_pending()).
     */
    const read_buf = (strm, buf, start, size) => {
      let len = strm.avail_in;
      if (len > size) {
        len = size;
      }
      if (len === 0) {
        return 0;
      }
      strm.avail_in -= len;

      // zmemcpy(buf, strm->next_in, len);
      buf.set(strm.input.subarray(strm.next_in, strm.next_in + len), start);
      if (strm.state.wrap === 1) {
        strm.adler = adler32_1(strm.adler, buf, len, start);
      } else if (strm.state.wrap === 2) {
        strm.adler = crc32_1(strm.adler, buf, len, start);
      }
      strm.next_in += len;
      strm.total_in += len;
      return len;
    };

    /* ===========================================================================
     * Set match_start to the longest match starting at the given string and
     * return its length. Matches shorter or equal to prev_length are discarded,
     * in which case the result is equal to prev_length and match_start is
     * garbage.
     * IN assertions: cur_match is the head of the hash chain for the current
     *   string (strstart) and its distance is <= MAX_DIST, and prev_length >= 1
     * OUT assertion: the match length is not greater than s->lookahead.
     */
    const longest_match = (s, cur_match) => {
      let chain_length = s.max_chain_length; /* max hash chain length */
      let scan = s.strstart; /* current string */
      let match; /* matched string */
      let len; /* length of current match */
      let best_len = s.prev_length; /* best match length so far */
      let nice_match = s.nice_match; /* stop if match long enough */
      const limit = s.strstart > s.w_size - MIN_LOOKAHEAD ? s.strstart - (s.w_size - MIN_LOOKAHEAD) : 0 /*NIL*/;
      const _win = s.window; // shortcut

      const wmask = s.w_mask;
      const prev = s.prev;

      /* Stop when cur_match becomes <= limit. To simplify the code,
       * we prevent matches with the string of window index 0.
       */

      const strend = s.strstart + MAX_MATCH;
      let scan_end1 = _win[scan + best_len - 1];
      let scan_end = _win[scan + best_len];

      /* The code is optimized for HASH_BITS >= 8 and MAX_MATCH-2 multiple of 16.
       * It is easy to get rid of this optimization if necessary.
       */
      // Assert(s->hash_bits >= 8 && MAX_MATCH == 258, "Code too clever");

      /* Do not waste too much time if we already have a good match: */
      if (s.prev_length >= s.good_match) {
        chain_length >>= 2;
      }
      /* Do not look for matches beyond the end of the input. This is necessary
       * to make deflate deterministic.
       */
      if (nice_match > s.lookahead) {
        nice_match = s.lookahead;
      }

      // Assert((ulg)s->strstart <= s->window_size-MIN_LOOKAHEAD, "need lookahead");

      do {
        // Assert(cur_match < s->strstart, "no future");
        match = cur_match;

        /* Skip to next match if the match length cannot increase
         * or if the match length is less than 2.  Note that the checks below
         * for insufficient lookahead only occur occasionally for performance
         * reasons.  Therefore uninitialized memory will be accessed, and
         * conditional jumps will be made that depend on those values.
         * However the length of the match is limited to the lookahead, so
         * the output of deflate is not affected by the uninitialized values.
         */

        if (_win[match + best_len] !== scan_end || _win[match + best_len - 1] !== scan_end1 || _win[match] !== _win[scan] || _win[++match] !== _win[scan + 1]) {
          continue;
        }

        /* The check at best_len-1 can be removed because it will be made
         * again later. (This heuristic is not always a win.)
         * It is not necessary to compare scan[2] and match[2] since they
         * are always equal when the other bytes match, given that
         * the hash keys are equal and that HASH_BITS >= 8.
         */
        scan += 2;
        match++;
        // Assert(*scan == *match, "match[2]?");

        /* We check for insufficient lookahead only every 8th comparison;
         * the 256th check will be made at strstart+258.
         */
        do {
          /*jshint noempty:false*/
        } while (_win[++scan] === _win[++match] && _win[++scan] === _win[++match] && _win[++scan] === _win[++match] && _win[++scan] === _win[++match] && _win[++scan] === _win[++match] && _win[++scan] === _win[++match] && _win[++scan] === _win[++match] && _win[++scan] === _win[++match] && scan < strend);

        // Assert(scan <= s->window+(unsigned)(s->window_size-1), "wild scan");

        len = MAX_MATCH - (strend - scan);
        scan = strend - MAX_MATCH;
        if (len > best_len) {
          s.match_start = cur_match;
          best_len = len;
          if (len >= nice_match) {
            break;
          }
          scan_end1 = _win[scan + best_len - 1];
          scan_end = _win[scan + best_len];
        }
      } while ((cur_match = prev[cur_match & wmask]) > limit && --chain_length !== 0);
      if (best_len <= s.lookahead) {
        return best_len;
      }
      return s.lookahead;
    };

    /* ===========================================================================
     * Fill the window when the lookahead becomes insufficient.
     * Updates strstart and lookahead.
     *
     * IN assertion: lookahead < MIN_LOOKAHEAD
     * OUT assertions: strstart <= window_size-MIN_LOOKAHEAD
     *    At least one byte has been read, or avail_in == 0; reads are
     *    performed for at least two bytes (required for the zip translate_eol
     *    option -- not supported here).
     */
    const fill_window = s => {
      const _w_size = s.w_size;
      let n, more, str;

      //Assert(s->lookahead < MIN_LOOKAHEAD, "already enough lookahead");

      do {
        more = s.window_size - s.lookahead - s.strstart;

        // JS ints have 32 bit, block below not needed
        /* Deal with !@#$% 64K limit: */
        //if (sizeof(int) <= 2) {
        //    if (more == 0 && s->strstart == 0 && s->lookahead == 0) {
        //        more = wsize;
        //
        //  } else if (more == (unsigned)(-1)) {
        //        /* Very unlikely, but possible on 16 bit machine if
        //         * strstart == 0 && lookahead == 1 (input done a byte at time)
        //         */
        //        more--;
        //    }
        //}

        /* If the window is almost full and there is insufficient lookahead,
         * move the upper half to the lower one to make room in the upper half.
         */
        if (s.strstart >= _w_size + (_w_size - MIN_LOOKAHEAD)) {
          s.window.set(s.window.subarray(_w_size, _w_size + _w_size - more), 0);
          s.match_start -= _w_size;
          s.strstart -= _w_size;
          /* we now have strstart >= MAX_DIST */
          s.block_start -= _w_size;
          if (s.insert > s.strstart) {
            s.insert = s.strstart;
          }
          slide_hash(s);
          more += _w_size;
        }
        if (s.strm.avail_in === 0) {
          break;
        }

        /* If there was no sliding:
         *    strstart <= WSIZE+MAX_DIST-1 && lookahead <= MIN_LOOKAHEAD - 1 &&
         *    more == window_size - lookahead - strstart
         * => more >= window_size - (MIN_LOOKAHEAD-1 + WSIZE + MAX_DIST-1)
         * => more >= window_size - 2*WSIZE + 2
         * In the BIG_MEM or MMAP case (not yet supported),
         *   window_size == input_size + MIN_LOOKAHEAD  &&
         *   strstart + s->lookahead <= input_size => more >= MIN_LOOKAHEAD.
         * Otherwise, window_size == 2*WSIZE so more >= 2.
         * If there was sliding, more >= WSIZE. So in all cases, more >= 2.
         */
        //Assert(more >= 2, "more < 2");
        n = read_buf(s.strm, s.window, s.strstart + s.lookahead, more);
        s.lookahead += n;

        /* Initialize the hash value now that we have some input: */
        if (s.lookahead + s.insert >= MIN_MATCH) {
          str = s.strstart - s.insert;
          s.ins_h = s.window[str];

          /* UPDATE_HASH(s, s->ins_h, s->window[str + 1]); */
          s.ins_h = HASH(s, s.ins_h, s.window[str + 1]);
          //#if MIN_MATCH != 3
          //        Call update_hash() MIN_MATCH-3 more times
          //#endif
          while (s.insert) {
            /* UPDATE_HASH(s, s->ins_h, s->window[str + MIN_MATCH-1]); */
            s.ins_h = HASH(s, s.ins_h, s.window[str + MIN_MATCH - 1]);
            s.prev[str & s.w_mask] = s.head[s.ins_h];
            s.head[s.ins_h] = str;
            str++;
            s.insert--;
            if (s.lookahead + s.insert < MIN_MATCH) {
              break;
            }
          }
        }
        /* If the whole input has less than MIN_MATCH bytes, ins_h is garbage,
         * but this is not important since only literal bytes will be emitted.
         */
      } while (s.lookahead < MIN_LOOKAHEAD && s.strm.avail_in !== 0);

      /* If the WIN_INIT bytes after the end of the current data have never been
       * written, then zero those bytes in order to avoid memory check reports of
       * the use of uninitialized (or uninitialised as Julian writes) bytes by
       * the longest match routines.  Update the high water mark for the next
       * time through here.  WIN_INIT is set to MAX_MATCH since the longest match
       * routines allow scanning to strstart + MAX_MATCH, ignoring lookahead.
       */
      //  if (s.high_water < s.window_size) {
      //    const curr = s.strstart + s.lookahead;
      //    let init = 0;
      //
      //    if (s.high_water < curr) {
      //      /* Previous high water mark below current data -- zero WIN_INIT
      //       * bytes or up to end of window, whichever is less.
      //       */
      //      init = s.window_size - curr;
      //      if (init > WIN_INIT)
      //        init = WIN_INIT;
      //      zmemzero(s->window + curr, (unsigned)init);
      //      s->high_water = curr + init;
      //    }
      //    else if (s->high_water < (ulg)curr + WIN_INIT) {
      //      /* High water mark at or above current data, but below current data
      //       * plus WIN_INIT -- zero out to current data plus WIN_INIT, or up
      //       * to end of window, whichever is less.
      //       */
      //      init = (ulg)curr + WIN_INIT - s->high_water;
      //      if (init > s->window_size - s->high_water)
      //        init = s->window_size - s->high_water;
      //      zmemzero(s->window + s->high_water, (unsigned)init);
      //      s->high_water += init;
      //    }
      //  }
      //
      //  Assert((ulg)s->strstart <= s->window_size - MIN_LOOKAHEAD,
      //    "not enough room for search");
    };

    /* ===========================================================================
     * Copy without compression as much as possible from the input stream, return
     * the current block state.
     *
     * In case deflateParams() is used to later switch to a non-zero compression
     * level, s->matches (otherwise unused when storing) keeps track of the number
     * of hash table slides to perform. If s->matches is 1, then one hash table
     * slide will be done when switching. If s->matches is 2, the maximum value
     * allowed here, then the hash table will be cleared, since two or more slides
     * is the same as a clear.
     *
     * deflate_stored() is written to minimize the number of times an input byte is
     * copied. It is most efficient with large input and output buffers, which
     * maximizes the opportunites to have a single copy from next_in to next_out.
     */
    const deflate_stored = (s, flush) => {
      /* Smallest worthy block size when not flushing or finishing. By default
       * this is 32K. This can be as small as 507 bytes for memLevel == 1. For
       * large input and output buffers, the stored block size will be larger.
       */
      let min_block = s.pending_buf_size - 5 > s.w_size ? s.w_size : s.pending_buf_size - 5;

      /* Copy as many min_block or larger stored blocks directly to next_out as
       * possible. If flushing, copy the remaining available input to next_out as
       * stored blocks, if there is enough space.
       */
      let len,
        left,
        have,
        last = 0;
      let used = s.strm.avail_in;
      do {
        /* Set len to the maximum size block that we can copy directly with the
         * available input data and output space. Set left to how much of that
         * would be copied from what's left in the window.
         */
        len = 65535 /* MAX_STORED */; /* maximum deflate stored block length */
        have = s.bi_valid + 42 >> 3; /* number of header bytes */
        if (s.strm.avail_out < have) {
          /* need room for header */
          break;
        }
        /* maximum stored block length that will fit in avail_out: */
        have = s.strm.avail_out - have;
        left = s.strstart - s.block_start; /* bytes left in window */
        if (len > left + s.strm.avail_in) {
          len = left + s.strm.avail_in; /* limit len to the input */
        }
        if (len > have) {
          len = have; /* limit len to the output */
        }

        /* If the stored block would be less than min_block in length, or if
         * unable to copy all of the available input when flushing, then try
         * copying to the window and the pending buffer instead. Also don't
         * write an empty block when flushing -- deflate() does that.
         */
        if (len < min_block && (len === 0 && flush !== Z_FINISH$3 || flush === Z_NO_FLUSH$2 || len !== left + s.strm.avail_in)) {
          break;
        }

        /* Make a dummy stored block in pending to get the header bytes,
         * including any pending bits. This also updates the debugging counts.
         */
        last = flush === Z_FINISH$3 && len === left + s.strm.avail_in ? 1 : 0;
        _tr_stored_block(s, 0, 0, last);

        /* Replace the lengths in the dummy stored block with len. */
        s.pending_buf[s.pending - 4] = len;
        s.pending_buf[s.pending - 3] = len >> 8;
        s.pending_buf[s.pending - 2] = ~len;
        s.pending_buf[s.pending - 1] = ~len >> 8;

        /* Write the stored block header bytes. */
        flush_pending(s.strm);

        //#ifdef ZLIB_DEBUG
        //    /* Update debugging counts for the data about to be copied. */
        //    s->compressed_len += len << 3;
        //    s->bits_sent += len << 3;
        //#endif

        /* Copy uncompressed bytes from the window to next_out. */
        if (left) {
          if (left > len) {
            left = len;
          }
          //zmemcpy(s->strm->next_out, s->window + s->block_start, left);
          s.strm.output.set(s.window.subarray(s.block_start, s.block_start + left), s.strm.next_out);
          s.strm.next_out += left;
          s.strm.avail_out -= left;
          s.strm.total_out += left;
          s.block_start += left;
          len -= left;
        }

        /* Copy uncompressed bytes directly from next_in to next_out, updating
         * the check value.
         */
        if (len) {
          read_buf(s.strm, s.strm.output, s.strm.next_out, len);
          s.strm.next_out += len;
          s.strm.avail_out -= len;
          s.strm.total_out += len;
        }
      } while (last === 0);

      /* Update the sliding window with the last s->w_size bytes of the copied
       * data, or append all of the copied data to the existing window if less
       * than s->w_size bytes were copied. Also update the number of bytes to
       * insert in the hash tables, in the event that deflateParams() switches to
       * a non-zero compression level.
       */
      used -= s.strm.avail_in; /* number of input bytes directly copied */
      if (used) {
        /* If any input was used, then no unused input remains in the window,
         * therefore s->block_start == s->strstart.
         */
        if (used >= s.w_size) {
          /* supplant the previous history */
          s.matches = 2; /* clear hash */
          //zmemcpy(s->window, s->strm->next_in - s->w_size, s->w_size);
          s.window.set(s.strm.input.subarray(s.strm.next_in - s.w_size, s.strm.next_in), 0);
          s.strstart = s.w_size;
          s.insert = s.strstart;
        } else {
          if (s.window_size - s.strstart <= used) {
            /* Slide the window down. */
            s.strstart -= s.w_size;
            //zmemcpy(s->window, s->window + s->w_size, s->strstart);
            s.window.set(s.window.subarray(s.w_size, s.w_size + s.strstart), 0);
            if (s.matches < 2) {
              s.matches++; /* add a pending slide_hash() */
            }
            if (s.insert > s.strstart) {
              s.insert = s.strstart;
            }
          }
          //zmemcpy(s->window + s->strstart, s->strm->next_in - used, used);
          s.window.set(s.strm.input.subarray(s.strm.next_in - used, s.strm.next_in), s.strstart);
          s.strstart += used;
          s.insert += used > s.w_size - s.insert ? s.w_size - s.insert : used;
        }
        s.block_start = s.strstart;
      }
      if (s.high_water < s.strstart) {
        s.high_water = s.strstart;
      }

      /* If the last block was written to next_out, then done. */
      if (last) {
        return BS_FINISH_DONE;
      }

      /* If flushing and all input has been consumed, then done. */
      if (flush !== Z_NO_FLUSH$2 && flush !== Z_FINISH$3 && s.strm.avail_in === 0 && s.strstart === s.block_start) {
        return BS_BLOCK_DONE;
      }

      /* Fill the window with any remaining input. */
      have = s.window_size - s.strstart;
      if (s.strm.avail_in > have && s.block_start >= s.w_size) {
        /* Slide the window down. */
        s.block_start -= s.w_size;
        s.strstart -= s.w_size;
        //zmemcpy(s->window, s->window + s->w_size, s->strstart);
        s.window.set(s.window.subarray(s.w_size, s.w_size + s.strstart), 0);
        if (s.matches < 2) {
          s.matches++; /* add a pending slide_hash() */
        }
        have += s.w_size; /* more space now */
        if (s.insert > s.strstart) {
          s.insert = s.strstart;
        }
      }
      if (have > s.strm.avail_in) {
        have = s.strm.avail_in;
      }
      if (have) {
        read_buf(s.strm, s.window, s.strstart, have);
        s.strstart += have;
        s.insert += have > s.w_size - s.insert ? s.w_size - s.insert : have;
      }
      if (s.high_water < s.strstart) {
        s.high_water = s.strstart;
      }

      /* There was not enough avail_out to write a complete worthy or flushed
       * stored block to next_out. Write a stored block to pending instead, if we
       * have enough input for a worthy block, or if flushing and there is enough
       * room for the remaining input as a stored block in the pending buffer.
       */
      have = s.bi_valid + 42 >> 3; /* number of header bytes */
      /* maximum stored block length that will fit in pending: */
      have = s.pending_buf_size - have > 65535 /* MAX_STORED */ ? 65535 /* MAX_STORED */ : s.pending_buf_size - have;
      min_block = have > s.w_size ? s.w_size : have;
      left = s.strstart - s.block_start;
      if (left >= min_block || (left || flush === Z_FINISH$3) && flush !== Z_NO_FLUSH$2 && s.strm.avail_in === 0 && left <= have) {
        len = left > have ? have : left;
        last = flush === Z_FINISH$3 && s.strm.avail_in === 0 && len === left ? 1 : 0;
        _tr_stored_block(s, s.block_start, len, last);
        s.block_start += len;
        flush_pending(s.strm);
      }

      /* We've done all we can with the available input and output. */
      return last ? BS_FINISH_STARTED : BS_NEED_MORE;
    };

    /* ===========================================================================
     * Compress as much as possible from the input stream, return the current
     * block state.
     * This function does not perform lazy evaluation of matches and inserts
     * new strings in the dictionary only for unmatched strings or for short
     * matches. It is used only for the fast compression options.
     */
    const deflate_fast = (s, flush) => {
      let hash_head; /* head of the hash chain */
      let bflush; /* set if current block must be flushed */

      for (;;) {
        /* Make sure that we always have enough lookahead, except
         * at the end of the input file. We need MAX_MATCH bytes
         * for the next match, plus MIN_MATCH bytes to insert the
         * string following the next match.
         */
        if (s.lookahead < MIN_LOOKAHEAD) {
          fill_window(s);
          if (s.lookahead < MIN_LOOKAHEAD && flush === Z_NO_FLUSH$2) {
            return BS_NEED_MORE;
          }
          if (s.lookahead === 0) {
            break; /* flush the current block */
          }
        }

        /* Insert the string window[strstart .. strstart+2] in the
         * dictionary, and set hash_head to the head of the hash chain:
         */
        hash_head = 0 /*NIL*/;
        if (s.lookahead >= MIN_MATCH) {
          /*** INSERT_STRING(s, s.strstart, hash_head); ***/
          s.ins_h = HASH(s, s.ins_h, s.window[s.strstart + MIN_MATCH - 1]);
          hash_head = s.prev[s.strstart & s.w_mask] = s.head[s.ins_h];
          s.head[s.ins_h] = s.strstart;
          /***/
        }

        /* Find the longest match, discarding those <= prev_length.
         * At this point we have always match_length < MIN_MATCH
         */
        if (hash_head !== 0 /*NIL*/ && s.strstart - hash_head <= s.w_size - MIN_LOOKAHEAD) {
          /* To simplify the code, we prevent matches with the string
           * of window index 0 (in particular we have to avoid a match
           * of the string with itself at the start of the input file).
           */
          s.match_length = longest_match(s, hash_head);
          /* longest_match() sets match_start */
        }
        if (s.match_length >= MIN_MATCH) {
          // check_match(s, s.strstart, s.match_start, s.match_length); // for debug only

          /*** _tr_tally_dist(s, s.strstart - s.match_start,
                         s.match_length - MIN_MATCH, bflush); ***/
          bflush = _tr_tally(s, s.strstart - s.match_start, s.match_length - MIN_MATCH);
          s.lookahead -= s.match_length;

          /* Insert new strings in the hash table only if the match length
           * is not too large. This saves time but degrades compression.
           */
          if (s.match_length <= s.max_lazy_match /*max_insert_length*/ && s.lookahead >= MIN_MATCH) {
            s.match_length--; /* string at strstart already in table */
            do {
              s.strstart++;
              /*** INSERT_STRING(s, s.strstart, hash_head); ***/
              s.ins_h = HASH(s, s.ins_h, s.window[s.strstart + MIN_MATCH - 1]);
              hash_head = s.prev[s.strstart & s.w_mask] = s.head[s.ins_h];
              s.head[s.ins_h] = s.strstart;
              /***/
              /* strstart never exceeds WSIZE-MAX_MATCH, so there are
               * always MIN_MATCH bytes ahead.
               */
            } while (--s.match_length !== 0);
            s.strstart++;
          } else {
            s.strstart += s.match_length;
            s.match_length = 0;
            s.ins_h = s.window[s.strstart];
            /* UPDATE_HASH(s, s.ins_h, s.window[s.strstart+1]); */
            s.ins_h = HASH(s, s.ins_h, s.window[s.strstart + 1]);

            //#if MIN_MATCH != 3
            //                Call UPDATE_HASH() MIN_MATCH-3 more times
            //#endif
            /* If lookahead < MIN_MATCH, ins_h is garbage, but it does not
             * matter since it will be recomputed at next deflate call.
             */
          }
        } else {
          /* No match, output a literal byte */
          //Tracevv((stderr,"%c", s.window[s.strstart]));
          /*** _tr_tally_lit(s, s.window[s.strstart], bflush); ***/
          bflush = _tr_tally(s, 0, s.window[s.strstart]);
          s.lookahead--;
          s.strstart++;
        }
        if (bflush) {
          /*** FLUSH_BLOCK(s, 0); ***/
          flush_block_only(s, false);
          if (s.strm.avail_out === 0) {
            return BS_NEED_MORE;
          }
          /***/
        }
      }
      s.insert = s.strstart < MIN_MATCH - 1 ? s.strstart : MIN_MATCH - 1;
      if (flush === Z_FINISH$3) {
        /*** FLUSH_BLOCK(s, 1); ***/
        flush_block_only(s, true);
        if (s.strm.avail_out === 0) {
          return BS_FINISH_STARTED;
        }
        /***/
        return BS_FINISH_DONE;
      }
      if (s.sym_next) {
        /*** FLUSH_BLOCK(s, 0); ***/
        flush_block_only(s, false);
        if (s.strm.avail_out === 0) {
          return BS_NEED_MORE;
        }
        /***/
      }
      return BS_BLOCK_DONE;
    };

    /* ===========================================================================
     * Same as above, but achieves better compression. We use a lazy
     * evaluation for matches: a match is finally adopted only if there is
     * no better match at the next window position.
     */
    const deflate_slow = (s, flush) => {
      let hash_head; /* head of hash chain */
      let bflush; /* set if current block must be flushed */

      let max_insert;

      /* Process the input block. */
      for (;;) {
        /* Make sure that we always have enough lookahead, except
         * at the end of the input file. We need MAX_MATCH bytes
         * for the next match, plus MIN_MATCH bytes to insert the
         * string following the next match.
         */
        if (s.lookahead < MIN_LOOKAHEAD) {
          fill_window(s);
          if (s.lookahead < MIN_LOOKAHEAD && flush === Z_NO_FLUSH$2) {
            return BS_NEED_MORE;
          }
          if (s.lookahead === 0) {
            break;
          } /* flush the current block */
        }

        /* Insert the string window[strstart .. strstart+2] in the
         * dictionary, and set hash_head to the head of the hash chain:
         */
        hash_head = 0 /*NIL*/;
        if (s.lookahead >= MIN_MATCH) {
          /*** INSERT_STRING(s, s.strstart, hash_head); ***/
          s.ins_h = HASH(s, s.ins_h, s.window[s.strstart + MIN_MATCH - 1]);
          hash_head = s.prev[s.strstart & s.w_mask] = s.head[s.ins_h];
          s.head[s.ins_h] = s.strstart;
          /***/
        }

        /* Find the longest match, discarding those <= prev_length.
         */
        s.prev_length = s.match_length;
        s.prev_match = s.match_start;
        s.match_length = MIN_MATCH - 1;
        if (hash_head !== 0 /*NIL*/ && s.prev_length < s.max_lazy_match && s.strstart - hash_head <= s.w_size - MIN_LOOKAHEAD /*MAX_DIST(s)*/) {
          /* To simplify the code, we prevent matches with the string
           * of window index 0 (in particular we have to avoid a match
           * of the string with itself at the start of the input file).
           */
          s.match_length = longest_match(s, hash_head);
          /* longest_match() sets match_start */

          if (s.match_length <= 5 && (s.strategy === Z_FILTERED || s.match_length === MIN_MATCH && s.strstart - s.match_start > 4096 /*TOO_FAR*/)) {
            /* If prev_match is also MIN_MATCH, match_start is garbage
             * but we will ignore the current match anyway.
             */
            s.match_length = MIN_MATCH - 1;
          }
        }
        /* If there was a match at the previous step and the current
         * match is not better, output the previous match:
         */
        if (s.prev_length >= MIN_MATCH && s.match_length <= s.prev_length) {
          max_insert = s.strstart + s.lookahead - MIN_MATCH;
          /* Do not insert strings in hash table beyond this. */

          //check_match(s, s.strstart-1, s.prev_match, s.prev_length);

          /***_tr_tally_dist(s, s.strstart - 1 - s.prev_match,
                         s.prev_length - MIN_MATCH, bflush);***/
          bflush = _tr_tally(s, s.strstart - 1 - s.prev_match, s.prev_length - MIN_MATCH);
          /* Insert in hash table all strings up to the end of the match.
           * strstart-1 and strstart are already inserted. If there is not
           * enough lookahead, the last two strings are not inserted in
           * the hash table.
           */
          s.lookahead -= s.prev_length - 1;
          s.prev_length -= 2;
          do {
            if (++s.strstart <= max_insert) {
              /*** INSERT_STRING(s, s.strstart, hash_head); ***/
              s.ins_h = HASH(s, s.ins_h, s.window[s.strstart + MIN_MATCH - 1]);
              hash_head = s.prev[s.strstart & s.w_mask] = s.head[s.ins_h];
              s.head[s.ins_h] = s.strstart;
              /***/
            }
          } while (--s.prev_length !== 0);
          s.match_available = 0;
          s.match_length = MIN_MATCH - 1;
          s.strstart++;
          if (bflush) {
            /*** FLUSH_BLOCK(s, 0); ***/
            flush_block_only(s, false);
            if (s.strm.avail_out === 0) {
              return BS_NEED_MORE;
            }
            /***/
          }
        } else if (s.match_available) {
          /* If there was no match at the previous position, output a
           * single literal. If there was a match but the current match
           * is longer, truncate the previous match to a single literal.
           */
          //Tracevv((stderr,"%c", s->window[s->strstart-1]));
          /*** _tr_tally_lit(s, s.window[s.strstart-1], bflush); ***/
          bflush = _tr_tally(s, 0, s.window[s.strstart - 1]);
          if (bflush) {
            /*** FLUSH_BLOCK_ONLY(s, 0) ***/
            flush_block_only(s, false);
            /***/
          }
          s.strstart++;
          s.lookahead--;
          if (s.strm.avail_out === 0) {
            return BS_NEED_MORE;
          }
        } else {
          /* There is no previous match to compare with, wait for
           * the next step to decide.
           */
          s.match_available = 1;
          s.strstart++;
          s.lookahead--;
        }
      }
      //Assert (flush != Z_NO_FLUSH, "no flush?");
      if (s.match_available) {
        //Tracevv((stderr,"%c", s->window[s->strstart-1]));
        /*** _tr_tally_lit(s, s.window[s.strstart-1], bflush); ***/
        bflush = _tr_tally(s, 0, s.window[s.strstart - 1]);
        s.match_available = 0;
      }
      s.insert = s.strstart < MIN_MATCH - 1 ? s.strstart : MIN_MATCH - 1;
      if (flush === Z_FINISH$3) {
        /*** FLUSH_BLOCK(s, 1); ***/
        flush_block_only(s, true);
        if (s.strm.avail_out === 0) {
          return BS_FINISH_STARTED;
        }
        /***/
        return BS_FINISH_DONE;
      }
      if (s.sym_next) {
        /*** FLUSH_BLOCK(s, 0); ***/
        flush_block_only(s, false);
        if (s.strm.avail_out === 0) {
          return BS_NEED_MORE;
        }
        /***/
      }
      return BS_BLOCK_DONE;
    };

    /* ===========================================================================
     * For Z_RLE, simply look for runs of bytes, generate matches only of distance
     * one.  Do not maintain a hash table.  (It will be regenerated if this run of
     * deflate switches away from Z_RLE.)
     */
    const deflate_rle = (s, flush) => {
      let bflush; /* set if current block must be flushed */
      let prev; /* byte at distance one to match */
      let scan, strend; /* scan goes up to strend for length of run */

      const _win = s.window;
      for (;;) {
        /* Make sure that we always have enough lookahead, except
         * at the end of the input file. We need MAX_MATCH bytes
         * for the longest run, plus one for the unrolled loop.
         */
        if (s.lookahead <= MAX_MATCH) {
          fill_window(s);
          if (s.lookahead <= MAX_MATCH && flush === Z_NO_FLUSH$2) {
            return BS_NEED_MORE;
          }
          if (s.lookahead === 0) {
            break;
          } /* flush the current block */
        }

        /* See how many times the previous byte repeats */
        s.match_length = 0;
        if (s.lookahead >= MIN_MATCH && s.strstart > 0) {
          scan = s.strstart - 1;
          prev = _win[scan];
          if (prev === _win[++scan] && prev === _win[++scan] && prev === _win[++scan]) {
            strend = s.strstart + MAX_MATCH;
            do {
              /*jshint noempty:false*/
            } while (prev === _win[++scan] && prev === _win[++scan] && prev === _win[++scan] && prev === _win[++scan] && prev === _win[++scan] && prev === _win[++scan] && prev === _win[++scan] && prev === _win[++scan] && scan < strend);
            s.match_length = MAX_MATCH - (strend - scan);
            if (s.match_length > s.lookahead) {
              s.match_length = s.lookahead;
            }
          }
          //Assert(scan <= s->window+(uInt)(s->window_size-1), "wild scan");
        }

        /* Emit match if have run of MIN_MATCH or longer, else emit literal */
        if (s.match_length >= MIN_MATCH) {
          //check_match(s, s.strstart, s.strstart - 1, s.match_length);

          /*** _tr_tally_dist(s, 1, s.match_length - MIN_MATCH, bflush); ***/
          bflush = _tr_tally(s, 1, s.match_length - MIN_MATCH);
          s.lookahead -= s.match_length;
          s.strstart += s.match_length;
          s.match_length = 0;
        } else {
          /* No match, output a literal byte */
          //Tracevv((stderr,"%c", s->window[s->strstart]));
          /*** _tr_tally_lit(s, s.window[s.strstart], bflush); ***/
          bflush = _tr_tally(s, 0, s.window[s.strstart]);
          s.lookahead--;
          s.strstart++;
        }
        if (bflush) {
          /*** FLUSH_BLOCK(s, 0); ***/
          flush_block_only(s, false);
          if (s.strm.avail_out === 0) {
            return BS_NEED_MORE;
          }
          /***/
        }
      }
      s.insert = 0;
      if (flush === Z_FINISH$3) {
        /*** FLUSH_BLOCK(s, 1); ***/
        flush_block_only(s, true);
        if (s.strm.avail_out === 0) {
          return BS_FINISH_STARTED;
        }
        /***/
        return BS_FINISH_DONE;
      }
      if (s.sym_next) {
        /*** FLUSH_BLOCK(s, 0); ***/
        flush_block_only(s, false);
        if (s.strm.avail_out === 0) {
          return BS_NEED_MORE;
        }
        /***/
      }
      return BS_BLOCK_DONE;
    };

    /* ===========================================================================
     * For Z_HUFFMAN_ONLY, do not look for matches.  Do not maintain a hash table.
     * (It will be regenerated if this run of deflate switches away from Huffman.)
     */
    const deflate_huff = (s, flush) => {
      let bflush; /* set if current block must be flushed */

      for (;;) {
        /* Make sure that we have a literal to write. */
        if (s.lookahead === 0) {
          fill_window(s);
          if (s.lookahead === 0) {
            if (flush === Z_NO_FLUSH$2) {
              return BS_NEED_MORE;
            }
            break; /* flush the current block */
          }
        }

        /* Output a literal byte */
        s.match_length = 0;
        //Tracevv((stderr,"%c", s->window[s->strstart]));
        /*** _tr_tally_lit(s, s.window[s.strstart], bflush); ***/
        bflush = _tr_tally(s, 0, s.window[s.strstart]);
        s.lookahead--;
        s.strstart++;
        if (bflush) {
          /*** FLUSH_BLOCK(s, 0); ***/
          flush_block_only(s, false);
          if (s.strm.avail_out === 0) {
            return BS_NEED_MORE;
          }
          /***/
        }
      }
      s.insert = 0;
      if (flush === Z_FINISH$3) {
        /*** FLUSH_BLOCK(s, 1); ***/
        flush_block_only(s, true);
        if (s.strm.avail_out === 0) {
          return BS_FINISH_STARTED;
        }
        /***/
        return BS_FINISH_DONE;
      }
      if (s.sym_next) {
        /*** FLUSH_BLOCK(s, 0); ***/
        flush_block_only(s, false);
        if (s.strm.avail_out === 0) {
          return BS_NEED_MORE;
        }
        /***/
      }
      return BS_BLOCK_DONE;
    };

    /* Values for max_lazy_match, good_match and max_chain_length, depending on
     * the desired pack level (0..9). The values given below have been tuned to
     * exclude worst case performance for pathological files. Better values may be
     * found for specific files.
     */
    function Config(good_length, max_lazy, nice_length, max_chain, func) {
      this.good_length = good_length;
      this.max_lazy = max_lazy;
      this.nice_length = nice_length;
      this.max_chain = max_chain;
      this.func = func;
    }
    const configuration_table = [/*      good lazy nice chain */
    new Config(0, 0, 0, 0, deflate_stored), /* 0 store only */
    new Config(4, 4, 8, 4, deflate_fast), /* 1 max speed, no lazy matches */
    new Config(4, 5, 16, 8, deflate_fast), /* 2 */
    new Config(4, 6, 32, 32, deflate_fast), /* 3 */

    new Config(4, 4, 16, 16, deflate_slow), /* 4 lazy matches */
    new Config(8, 16, 32, 32, deflate_slow), /* 5 */
    new Config(8, 16, 128, 128, deflate_slow), /* 6 */
    new Config(8, 32, 128, 256, deflate_slow), /* 7 */
    new Config(32, 128, 258, 1024, deflate_slow), /* 8 */
    new Config(32, 258, 258, 4096, deflate_slow) /* 9 max compression */];

    /* ===========================================================================
     * Initialize the "longest match" routines for a new zlib stream
     */
    const lm_init = s => {
      s.window_size = 2 * s.w_size;

      /*** CLEAR_HASH(s); ***/
      zero(s.head); // Fill with NIL (= 0);

      /* Set the default configuration parameters:
       */
      s.max_lazy_match = configuration_table[s.level].max_lazy;
      s.good_match = configuration_table[s.level].good_length;
      s.nice_match = configuration_table[s.level].nice_length;
      s.max_chain_length = configuration_table[s.level].max_chain;
      s.strstart = 0;
      s.block_start = 0;
      s.lookahead = 0;
      s.insert = 0;
      s.match_length = s.prev_length = MIN_MATCH - 1;
      s.match_available = 0;
      s.ins_h = 0;
    };
    function DeflateState() {
      this.strm = null; /* pointer back to this zlib stream */
      this.status = 0; /* as the name implies */
      this.pending_buf = null; /* output still pending */
      this.pending_buf_size = 0; /* size of pending_buf */
      this.pending_out = 0; /* next pending byte to output to the stream */
      this.pending = 0; /* nb of bytes in the pending buffer */
      this.wrap = 0; /* bit 0 true for zlib, bit 1 true for gzip */
      this.gzhead = null; /* gzip header information to write */
      this.gzindex = 0; /* where in extra, name, or comment */
      this.method = Z_DEFLATED$2; /* can only be DEFLATED */
      this.last_flush = -1; /* value of flush param for previous deflate call */

      this.w_size = 0; /* LZ77 window size (32K by default) */
      this.w_bits = 0; /* log2(w_size)  (8..16) */
      this.w_mask = 0; /* w_size - 1 */

      this.window = null;
      /* Sliding window. Input bytes are read into the second half of the window,
       * and move to the first half later to keep a dictionary of at least wSize
       * bytes. With this organization, matches are limited to a distance of
       * wSize-MAX_MATCH bytes, but this ensures that IO is always
       * performed with a length multiple of the block size.
       */

      this.window_size = 0;
      /* Actual size of window: 2*wSize, except when the user input buffer
       * is directly used as sliding window.
       */

      this.prev = null;
      /* Link to older string with same hash index. To limit the size of this
       * array to 64K, this link is maintained only for the last 32K strings.
       * An index in this array is thus a window index modulo 32K.
       */

      this.head = null; /* Heads of the hash chains or NIL. */

      this.ins_h = 0; /* hash index of string to be inserted */
      this.hash_size = 0; /* number of elements in hash table */
      this.hash_bits = 0; /* log2(hash_size) */
      this.hash_mask = 0; /* hash_size-1 */

      this.hash_shift = 0;
      /* Number of bits by which ins_h must be shifted at each input
       * step. It must be such that after MIN_MATCH steps, the oldest
       * byte no longer takes part in the hash key, that is:
       *   hash_shift * MIN_MATCH >= hash_bits
       */

      this.block_start = 0;
      /* Window position at the beginning of the current output block. Gets
       * negative when the window is moved backwards.
       */

      this.match_length = 0; /* length of best match */
      this.prev_match = 0; /* previous match */
      this.match_available = 0; /* set if previous match exists */
      this.strstart = 0; /* start of string to insert */
      this.match_start = 0; /* start of matching string */
      this.lookahead = 0; /* number of valid bytes ahead in window */

      this.prev_length = 0;
      /* Length of the best match at previous step. Matches not greater than this
       * are discarded. This is used in the lazy match evaluation.
       */

      this.max_chain_length = 0;
      /* To speed up deflation, hash chains are never searched beyond this
       * length.  A higher limit improves compression ratio but degrades the
       * speed.
       */

      this.max_lazy_match = 0;
      /* Attempt to find a better match only when the current match is strictly
       * smaller than this value. This mechanism is used only for compression
       * levels >= 4.
       */
      // That's alias to max_lazy_match, don't use directly
      //this.max_insert_length = 0;
      /* Insert new strings in the hash table only if the match length is not
       * greater than this length. This saves time but degrades compression.
       * max_insert_length is used only for compression levels <= 3.
       */

      this.level = 0; /* compression level (1..9) */
      this.strategy = 0; /* favor or force Huffman coding*/

      this.good_match = 0;
      /* Use a faster search when the previous match is longer than this */

      this.nice_match = 0; /* Stop searching when current match exceeds this */

      /* used by trees.c: */

      /* Didn't use ct_data typedef below to suppress compiler warning */

      // struct ct_data_s dyn_ltree[HEAP_SIZE];   /* literal and length tree */
      // struct ct_data_s dyn_dtree[2*D_CODES+1]; /* distance tree */
      // struct ct_data_s bl_tree[2*BL_CODES+1];  /* Huffman tree for bit lengths */

      // Use flat array of DOUBLE size, with interleaved fata,
      // because JS does not support effective
      this.dyn_ltree = new Uint16Array(HEAP_SIZE * 2);
      this.dyn_dtree = new Uint16Array((2 * D_CODES + 1) * 2);
      this.bl_tree = new Uint16Array((2 * BL_CODES + 1) * 2);
      zero(this.dyn_ltree);
      zero(this.dyn_dtree);
      zero(this.bl_tree);
      this.l_desc = null; /* desc. for literal tree */
      this.d_desc = null; /* desc. for distance tree */
      this.bl_desc = null; /* desc. for bit length tree */

      //ush bl_count[MAX_BITS+1];
      this.bl_count = new Uint16Array(MAX_BITS + 1);
      /* number of codes at each bit length for an optimal tree */

      //int heap[2*L_CODES+1];      /* heap used to build the Huffman trees */
      this.heap = new Uint16Array(2 * L_CODES + 1); /* heap used to build the Huffman trees */
      zero(this.heap);
      this.heap_len = 0; /* number of elements in the heap */
      this.heap_max = 0; /* element of largest frequency */
      /* The sons of heap[n] are heap[2*n] and heap[2*n+1]. heap[0] is not used.
       * The same heap array is used to build all trees.
       */

      this.depth = new Uint16Array(2 * L_CODES + 1); //uch depth[2*L_CODES+1];
      zero(this.depth);
      /* Depth of each subtree used as tie breaker for trees of equal frequency
       */

      this.sym_buf = 0; /* buffer for distances and literals/lengths */

      this.lit_bufsize = 0;
      /* Size of match buffer for literals/lengths.  There are 4 reasons for
       * limiting lit_bufsize to 64K:
       *   - frequencies can be kept in 16 bit counters
       *   - if compression is not successful for the first block, all input
       *     data is still in the window so we can still emit a stored block even
       *     when input comes from standard input.  (This can also be done for
       *     all blocks if lit_bufsize is not greater than 32K.)
       *   - if compression is not successful for a file smaller than 64K, we can
       *     even emit a stored file instead of a stored block (saving 5 bytes).
       *     This is applicable only for zip (not gzip or zlib).
       *   - creating new Huffman trees less frequently may not provide fast
       *     adaptation to changes in the input data statistics. (Take for
       *     example a binary file with poorly compressible code followed by
       *     a highly compressible string table.) Smaller buffer sizes give
       *     fast adaptation but have of course the overhead of transmitting
       *     trees more frequently.
       *   - I can't count above 4
       */

      this.sym_next = 0; /* running index in sym_buf */
      this.sym_end = 0; /* symbol table full when sym_next reaches this */

      this.opt_len = 0; /* bit length of current block with optimal trees */
      this.static_len = 0; /* bit length of current block with static trees */
      this.matches = 0; /* number of string matches in current block */
      this.insert = 0; /* bytes at end of window left to insert */

      this.bi_buf = 0;
      /* Output buffer. bits are inserted starting at the bottom (least
       * significant bits).
       */
      this.bi_valid = 0;
      /* Number of valid bits in bi_buf.  All bits above the last valid bit
       * are always zero.
       */

      // Used for window memory init. We safely ignore it for JS. That makes
      // sense only for pointers and memory check tools.
      //this.high_water = 0;
      /* High water mark offset in window for initialized bytes -- bytes above
       * this are set to zero in order to avoid memory check warnings when
       * longest match routines access bytes past the input.  This is then
       * updated to the new high water mark.
       */
    }

    /* =========================================================================
     * Check for a valid deflate stream state. Return 0 if ok, 1 if not.
     */
    const deflateStateCheck = strm => {
      if (!strm) {
        return 1;
      }
      const s = strm.state;
      if (!s || s.strm !== strm || s.status !== INIT_STATE &&
      //#ifdef GZIP
      s.status !== GZIP_STATE &&
      //#endif
      s.status !== EXTRA_STATE && s.status !== NAME_STATE && s.status !== COMMENT_STATE && s.status !== HCRC_STATE && s.status !== BUSY_STATE && s.status !== FINISH_STATE) {
        return 1;
      }
      return 0;
    };
    const deflateResetKeep = strm => {
      if (deflateStateCheck(strm)) {
        return err(strm, Z_STREAM_ERROR$2);
      }
      strm.total_in = strm.total_out = 0;
      strm.data_type = Z_UNKNOWN;
      const s = strm.state;
      s.pending = 0;
      s.pending_out = 0;
      if (s.wrap < 0) {
        s.wrap = -s.wrap;
        /* was made negative by deflate(..., Z_FINISH); */
      }
      s.status =
      //#ifdef GZIP
      s.wrap === 2 ? GZIP_STATE :
      //#endif
      s.wrap ? INIT_STATE : BUSY_STATE;
      strm.adler = s.wrap === 2 ? 0 // crc32(0, Z_NULL, 0)
      : 1; // adler32(0, Z_NULL, 0)
      s.last_flush = -2;
      _tr_init(s);
      return Z_OK$3;
    };
    const deflateReset = strm => {
      const ret = deflateResetKeep(strm);
      if (ret === Z_OK$3) {
        lm_init(strm.state);
      }
      return ret;
    };
    const deflateSetHeader = (strm, head) => {
      if (deflateStateCheck(strm) || strm.state.wrap !== 2) {
        return Z_STREAM_ERROR$2;
      }
      strm.state.gzhead = head;
      return Z_OK$3;
    };
    const deflateInit2 = (strm, level, method, windowBits, memLevel, strategy) => {
      if (!strm) {
        // === Z_NULL
        return Z_STREAM_ERROR$2;
      }
      let wrap = 1;
      if (level === Z_DEFAULT_COMPRESSION$1) {
        level = 6;
      }
      if (windowBits < 0) {
        /* suppress zlib wrapper */
        wrap = 0;
        windowBits = -windowBits;
      } else if (windowBits > 15) {
        wrap = 2; /* write gzip wrapper instead */
        windowBits -= 16;
      }
      if (memLevel < 1 || memLevel > MAX_MEM_LEVEL || method !== Z_DEFLATED$2 || windowBits < 8 || windowBits > 15 || level < 0 || level > 9 || strategy < 0 || strategy > Z_FIXED || windowBits === 8 && wrap !== 1) {
        return err(strm, Z_STREAM_ERROR$2);
      }
      if (windowBits === 8) {
        windowBits = 9;
      }
      /* until 256-byte window bug fixed */

      const s = new DeflateState();
      strm.state = s;
      s.strm = strm;
      s.status = INIT_STATE; /* to pass state test in deflateReset() */

      s.wrap = wrap;
      s.gzhead = null;
      s.w_bits = windowBits;
      s.w_size = 1 << s.w_bits;
      s.w_mask = s.w_size - 1;
      s.hash_bits = memLevel + 7;
      s.hash_size = 1 << s.hash_bits;
      s.hash_mask = s.hash_size - 1;
      s.hash_shift = ~~((s.hash_bits + MIN_MATCH - 1) / MIN_MATCH);
      s.window = new Uint8Array(s.w_size * 2);
      s.head = new Uint16Array(s.hash_size);
      s.prev = new Uint16Array(s.w_size);

      // Don't need mem init magic for JS.
      //s.high_water = 0;  /* nothing written to s->window yet */

      s.lit_bufsize = 1 << memLevel + 6; /* 16K elements by default */

      /* We overlay pending_buf and sym_buf. This works since the average size
       * for length/distance pairs over any compressed block is assured to be 31
       * bits or less.
       *
       * Analysis: The longest fixed codes are a length code of 8 bits plus 5
       * extra bits, for lengths 131 to 257. The longest fixed distance codes are
       * 5 bits plus 13 extra bits, for distances 16385 to 32768. The longest
       * possible fixed-codes length/distance pair is then 31 bits total.
       *
       * sym_buf starts one-fourth of the way into pending_buf. So there are
       * three bytes in sym_buf for every four bytes in pending_buf. Each symbol
       * in sym_buf is three bytes -- two for the distance and one for the
       * literal/length. As each symbol is consumed, the pointer to the next
       * sym_buf value to read moves forward three bytes. From that symbol, up to
       * 31 bits are written to pending_buf. The closest the written pending_buf
       * bits gets to the next sym_buf symbol to read is just before the last
       * code is written. At that time, 31*(n-2) bits have been written, just
       * after 24*(n-2) bits have been consumed from sym_buf. sym_buf starts at
       * 8*n bits into pending_buf. (Note that the symbol buffer fills when n-1
       * symbols are written.) The closest the writing gets to what is unread is
       * then n+14 bits. Here n is lit_bufsize, which is 16384 by default, and
       * can range from 128 to 32768.
       *
       * Therefore, at a minimum, there are 142 bits of space between what is
       * written and what is read in the overlain buffers, so the symbols cannot
       * be overwritten by the compressed data. That space is actually 139 bits,
       * due to the three-bit fixed-code block header.
       *
       * That covers the case where either Z_FIXED is specified, forcing fixed
       * codes, or when the use of fixed codes is chosen, because that choice
       * results in a smaller compressed block than dynamic codes. That latter
       * condition then assures that the above analysis also covers all dynamic
       * blocks. A dynamic-code block will only be chosen to be emitted if it has
       * fewer bits than a fixed-code block would for the same set of symbols.
       * Therefore its average symbol length is assured to be less than 31. So
       * the compressed data for a dynamic block also cannot overwrite the
       * symbols from which it is being constructed.
       */

      s.pending_buf_size = s.lit_bufsize * 4;
      s.pending_buf = new Uint8Array(s.pending_buf_size);

      // It is offset from `s.pending_buf` (size is `s.lit_bufsize * 2`)
      //s->sym_buf = s->pending_buf + s->lit_bufsize;
      s.sym_buf = s.lit_bufsize;

      //s->sym_end = (s->lit_bufsize - 1) * 3;
      s.sym_end = (s.lit_bufsize - 1) * 3;
      /* We avoid equality with lit_bufsize*3 because of wraparound at 64K
       * on 16 bit machines and because stored blocks are restricted to
       * 64K-1 bytes.
       */

      s.level = level;
      s.strategy = strategy;
      s.method = method;
      return deflateReset(strm);
    };
    const deflateInit = (strm, level) => {
      return deflateInit2(strm, level, Z_DEFLATED$2, MAX_WBITS$1, DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY$1);
    };

    /* ========================================================================= */
    const deflate$2 = (strm, flush) => {
      if (deflateStateCheck(strm) || flush > Z_BLOCK$1 || flush < 0) {
        return strm ? err(strm, Z_STREAM_ERROR$2) : Z_STREAM_ERROR$2;
      }
      const s = strm.state;
      if (!strm.output || strm.avail_in !== 0 && !strm.input || s.status === FINISH_STATE && flush !== Z_FINISH$3) {
        return err(strm, strm.avail_out === 0 ? Z_BUF_ERROR$1 : Z_STREAM_ERROR$2);
      }
      const old_flush = s.last_flush;
      s.last_flush = flush;

      /* Flush as much pending output as possible */
      if (s.pending !== 0) {
        flush_pending(strm);
        if (strm.avail_out === 0) {
          /* Since avail_out is 0, deflate will be called again with
           * more output space, but possibly with both pending and
           * avail_in equal to zero. There won't be anything to do,
           * but this is not an error situation so make sure we
           * return OK instead of BUF_ERROR at next call of deflate:
           */
          s.last_flush = -1;
          return Z_OK$3;
        }

        /* Make sure there is something to do and avoid duplicate consecutive
         * flushes. For repeated and useless calls with Z_FINISH, we keep
         * returning Z_STREAM_END instead of Z_BUF_ERROR.
         */
      } else if (strm.avail_in === 0 && rank(flush) <= rank(old_flush) && flush !== Z_FINISH$3) {
        return err(strm, Z_BUF_ERROR$1);
      }

      /* User must not provide more input after the first FINISH: */
      if (s.status === FINISH_STATE && strm.avail_in !== 0) {
        return err(strm, Z_BUF_ERROR$1);
      }

      /* Write the header */
      if (s.status === INIT_STATE && s.wrap === 0) {
        s.status = BUSY_STATE;
      }
      if (s.status === INIT_STATE) {
        /* zlib header */
        let header = Z_DEFLATED$2 + (s.w_bits - 8 << 4) << 8;
        let level_flags = -1;
        if (s.strategy >= Z_HUFFMAN_ONLY || s.level < 2) {
          level_flags = 0;
        } else if (s.level < 6) {
          level_flags = 1;
        } else if (s.level === 6) {
          level_flags = 2;
        } else {
          level_flags = 3;
        }
        header |= level_flags << 6;
        if (s.strstart !== 0) {
          header |= PRESET_DICT;
        }
        header += 31 - header % 31;
        putShortMSB(s, header);

        /* Save the adler32 of the preset dictionary: */
        if (s.strstart !== 0) {
          putShortMSB(s, strm.adler >>> 16);
          putShortMSB(s, strm.adler & 0xffff);
        }
        strm.adler = 1; // adler32(0L, Z_NULL, 0);
        s.status = BUSY_STATE;

        /* Compression must start with an empty pending buffer */
        flush_pending(strm);
        if (s.pending !== 0) {
          s.last_flush = -1;
          return Z_OK$3;
        }
      }
      //#ifdef GZIP
      if (s.status === GZIP_STATE) {
        /* gzip header */
        strm.adler = 0; //crc32(0L, Z_NULL, 0);
        put_byte(s, 31);
        put_byte(s, 139);
        put_byte(s, 8);
        if (!s.gzhead) {
          // s->gzhead == Z_NULL
          put_byte(s, 0);
          put_byte(s, 0);
          put_byte(s, 0);
          put_byte(s, 0);
          put_byte(s, 0);
          put_byte(s, s.level === 9 ? 2 : s.strategy >= Z_HUFFMAN_ONLY || s.level < 2 ? 4 : 0);
          put_byte(s, OS_CODE);
          s.status = BUSY_STATE;

          /* Compression must start with an empty pending buffer */
          flush_pending(strm);
          if (s.pending !== 0) {
            s.last_flush = -1;
            return Z_OK$3;
          }
        } else {
          put_byte(s, (s.gzhead.text ? 1 : 0) + (s.gzhead.hcrc ? 2 : 0) + (!s.gzhead.extra ? 0 : 4) + (!s.gzhead.name ? 0 : 8) + (!s.gzhead.comment ? 0 : 16));
          put_byte(s, s.gzhead.time & 0xff);
          put_byte(s, s.gzhead.time >> 8 & 0xff);
          put_byte(s, s.gzhead.time >> 16 & 0xff);
          put_byte(s, s.gzhead.time >> 24 & 0xff);
          put_byte(s, s.level === 9 ? 2 : s.strategy >= Z_HUFFMAN_ONLY || s.level < 2 ? 4 : 0);
          put_byte(s, s.gzhead.os & 0xff);
          if (s.gzhead.extra && s.gzhead.extra.length) {
            put_byte(s, s.gzhead.extra.length & 0xff);
            put_byte(s, s.gzhead.extra.length >> 8 & 0xff);
          }
          if (s.gzhead.hcrc) {
            strm.adler = crc32_1(strm.adler, s.pending_buf, s.pending, 0);
          }
          s.gzindex = 0;
          s.status = EXTRA_STATE;
        }
      }
      if (s.status === EXTRA_STATE) {
        if (s.gzhead.extra /* != Z_NULL*/) {
          let beg = s.pending; /* start of bytes to update crc */
          let left = (s.gzhead.extra.length & 0xffff) - s.gzindex;
          while (s.pending + left > s.pending_buf_size) {
            let copy = s.pending_buf_size - s.pending;
            // zmemcpy(s.pending_buf + s.pending,
            //    s.gzhead.extra + s.gzindex, copy);
            s.pending_buf.set(s.gzhead.extra.subarray(s.gzindex, s.gzindex + copy), s.pending);
            s.pending = s.pending_buf_size;
            //--- HCRC_UPDATE(beg) ---//
            if (s.gzhead.hcrc && s.pending > beg) {
              strm.adler = crc32_1(strm.adler, s.pending_buf, s.pending - beg, beg);
            }
            //---//
            s.gzindex += copy;
            flush_pending(strm);
            if (s.pending !== 0) {
              s.last_flush = -1;
              return Z_OK$3;
            }
            beg = 0;
            left -= copy;
          }
          // JS specific: s.gzhead.extra may be TypedArray or Array for backward compatibility
          //              TypedArray.slice and TypedArray.from don't exist in IE10-IE11
          let gzhead_extra = new Uint8Array(s.gzhead.extra);
          // zmemcpy(s->pending_buf + s->pending,
          //     s->gzhead->extra + s->gzindex, left);
          s.pending_buf.set(gzhead_extra.subarray(s.gzindex, s.gzindex + left), s.pending);
          s.pending += left;
          //--- HCRC_UPDATE(beg) ---//
          if (s.gzhead.hcrc && s.pending > beg) {
            strm.adler = crc32_1(strm.adler, s.pending_buf, s.pending - beg, beg);
          }
          //---//
          s.gzindex = 0;
        }
        s.status = NAME_STATE;
      }
      if (s.status === NAME_STATE) {
        if (s.gzhead.name /* != Z_NULL*/) {
          let beg = s.pending; /* start of bytes to update crc */
          let val;
          do {
            if (s.pending === s.pending_buf_size) {
              //--- HCRC_UPDATE(beg) ---//
              if (s.gzhead.hcrc && s.pending > beg) {
                strm.adler = crc32_1(strm.adler, s.pending_buf, s.pending - beg, beg);
              }
              //---//
              flush_pending(strm);
              if (s.pending !== 0) {
                s.last_flush = -1;
                return Z_OK$3;
              }
              beg = 0;
            }
            // JS specific: little magic to add zero terminator to end of string
            if (s.gzindex < s.gzhead.name.length) {
              val = s.gzhead.name.charCodeAt(s.gzindex++) & 0xff;
            } else {
              val = 0;
            }
            put_byte(s, val);
          } while (val !== 0);
          //--- HCRC_UPDATE(beg) ---//
          if (s.gzhead.hcrc && s.pending > beg) {
            strm.adler = crc32_1(strm.adler, s.pending_buf, s.pending - beg, beg);
          }
          //---//
          s.gzindex = 0;
        }
        s.status = COMMENT_STATE;
      }
      if (s.status === COMMENT_STATE) {
        if (s.gzhead.comment /* != Z_NULL*/) {
          let beg = s.pending; /* start of bytes to update crc */
          let val;
          do {
            if (s.pending === s.pending_buf_size) {
              //--- HCRC_UPDATE(beg) ---//
              if (s.gzhead.hcrc && s.pending > beg) {
                strm.adler = crc32_1(strm.adler, s.pending_buf, s.pending - beg, beg);
              }
              //---//
              flush_pending(strm);
              if (s.pending !== 0) {
                s.last_flush = -1;
                return Z_OK$3;
              }
              beg = 0;
            }
            // JS specific: little magic to add zero terminator to end of string
            if (s.gzindex < s.gzhead.comment.length) {
              val = s.gzhead.comment.charCodeAt(s.gzindex++) & 0xff;
            } else {
              val = 0;
            }
            put_byte(s, val);
          } while (val !== 0);
          //--- HCRC_UPDATE(beg) ---//
          if (s.gzhead.hcrc && s.pending > beg) {
            strm.adler = crc32_1(strm.adler, s.pending_buf, s.pending - beg, beg);
          }
          //---//
        }
        s.status = HCRC_STATE;
      }
      if (s.status === HCRC_STATE) {
        if (s.gzhead.hcrc) {
          if (s.pending + 2 > s.pending_buf_size) {
            flush_pending(strm);
            if (s.pending !== 0) {
              s.last_flush = -1;
              return Z_OK$3;
            }
          }
          put_byte(s, strm.adler & 0xff);
          put_byte(s, strm.adler >> 8 & 0xff);
          strm.adler = 0; //crc32(0L, Z_NULL, 0);
        }
        s.status = BUSY_STATE;

        /* Compression must start with an empty pending buffer */
        flush_pending(strm);
        if (s.pending !== 0) {
          s.last_flush = -1;
          return Z_OK$3;
        }
      }
      //#endif

      /* Start a new block or continue the current one.
       */
      if (strm.avail_in !== 0 || s.lookahead !== 0 || flush !== Z_NO_FLUSH$2 && s.status !== FINISH_STATE) {
        let bstate = s.level === 0 ? deflate_stored(s, flush) : s.strategy === Z_HUFFMAN_ONLY ? deflate_huff(s, flush) : s.strategy === Z_RLE ? deflate_rle(s, flush) : configuration_table[s.level].func(s, flush);
        if (bstate === BS_FINISH_STARTED || bstate === BS_FINISH_DONE) {
          s.status = FINISH_STATE;
        }
        if (bstate === BS_NEED_MORE || bstate === BS_FINISH_STARTED) {
          if (strm.avail_out === 0) {
            s.last_flush = -1;
            /* avoid BUF_ERROR next call, see above */
          }
          return Z_OK$3;
          /* If flush != Z_NO_FLUSH && avail_out == 0, the next call
           * of deflate should use the same flush parameter to make sure
           * that the flush is complete. So we don't have to output an
           * empty block here, this will be done at next call. This also
           * ensures that for a very small output buffer, we emit at most
           * one empty block.
           */
        }
        if (bstate === BS_BLOCK_DONE) {
          if (flush === Z_PARTIAL_FLUSH) {
            _tr_align(s);
          } else if (flush !== Z_BLOCK$1) {
            /* FULL_FLUSH or SYNC_FLUSH */

            _tr_stored_block(s, 0, 0, false);
            /* For a full flush, this empty block will be recognized
             * as a special marker by inflate_sync().
             */
            if (flush === Z_FULL_FLUSH$1) {
              /*** CLEAR_HASH(s); ***/ /* forget history */
              zero(s.head); // Fill with NIL (= 0);

              if (s.lookahead === 0) {
                s.strstart = 0;
                s.block_start = 0;
                s.insert = 0;
              }
            }
          }
          flush_pending(strm);
          if (strm.avail_out === 0) {
            s.last_flush = -1; /* avoid BUF_ERROR at next call, see above */
            return Z_OK$3;
          }
        }
      }
      if (flush !== Z_FINISH$3) {
        return Z_OK$3;
      }
      if (s.wrap <= 0) {
        return Z_STREAM_END$3;
      }

      /* Write the trailer */
      if (s.wrap === 2) {
        put_byte(s, strm.adler & 0xff);
        put_byte(s, strm.adler >> 8 & 0xff);
        put_byte(s, strm.adler >> 16 & 0xff);
        put_byte(s, strm.adler >> 24 & 0xff);
        put_byte(s, strm.total_in & 0xff);
        put_byte(s, strm.total_in >> 8 & 0xff);
        put_byte(s, strm.total_in >> 16 & 0xff);
        put_byte(s, strm.total_in >> 24 & 0xff);
      } else {
        putShortMSB(s, strm.adler >>> 16);
        putShortMSB(s, strm.adler & 0xffff);
      }
      flush_pending(strm);
      /* If avail_out is zero, the application will call deflate again
       * to flush the rest.
       */
      if (s.wrap > 0) {
        s.wrap = -s.wrap;
      }
      /* write the trailer only once! */
      return s.pending !== 0 ? Z_OK$3 : Z_STREAM_END$3;
    };
    const deflateEnd = strm => {
      if (deflateStateCheck(strm)) {
        return Z_STREAM_ERROR$2;
      }
      const status = strm.state.status;
      strm.state = null;
      return status === BUSY_STATE ? err(strm, Z_DATA_ERROR$2) : Z_OK$3;
    };

    /* =========================================================================
     * Initializes the compression dictionary from the given byte
     * sequence without producing any compressed output.
     */
    const deflateSetDictionary = (strm, dictionary) => {
      let dictLength = dictionary.length;
      if (deflateStateCheck(strm)) {
        return Z_STREAM_ERROR$2;
      }
      const s = strm.state;
      const wrap = s.wrap;
      if (wrap === 2 || wrap === 1 && s.status !== INIT_STATE || s.lookahead) {
        return Z_STREAM_ERROR$2;
      }

      /* when using zlib wrappers, compute Adler-32 for provided dictionary */
      if (wrap === 1) {
        /* adler32(strm->adler, dictionary, dictLength); */
        strm.adler = adler32_1(strm.adler, dictionary, dictLength, 0);
      }
      s.wrap = 0; /* avoid computing Adler-32 in read_buf */

      /* if dictionary would fill window, just replace the history */
      if (dictLength >= s.w_size) {
        if (wrap === 0) {
          /* already empty otherwise */
          /*** CLEAR_HASH(s); ***/
          zero(s.head); // Fill with NIL (= 0);
          s.strstart = 0;
          s.block_start = 0;
          s.insert = 0;
        }
        /* use the tail */
        // dictionary = dictionary.slice(dictLength - s.w_size);
        let tmpDict = new Uint8Array(s.w_size);
        tmpDict.set(dictionary.subarray(dictLength - s.w_size, dictLength), 0);
        dictionary = tmpDict;
        dictLength = s.w_size;
      }
      /* insert dictionary into window and hash */
      const avail = strm.avail_in;
      const next = strm.next_in;
      const input = strm.input;
      strm.avail_in = dictLength;
      strm.next_in = 0;
      strm.input = dictionary;
      fill_window(s);
      while (s.lookahead >= MIN_MATCH) {
        let str = s.strstart;
        let n = s.lookahead - (MIN_MATCH - 1);
        do {
          /* UPDATE_HASH(s, s->ins_h, s->window[str + MIN_MATCH-1]); */
          s.ins_h = HASH(s, s.ins_h, s.window[str + MIN_MATCH - 1]);
          s.prev[str & s.w_mask] = s.head[s.ins_h];
          s.head[s.ins_h] = str;
          str++;
        } while (--n);
        s.strstart = str;
        s.lookahead = MIN_MATCH - 1;
        fill_window(s);
      }
      s.strstart += s.lookahead;
      s.block_start = s.strstart;
      s.insert = s.lookahead;
      s.lookahead = 0;
      s.match_length = s.prev_length = MIN_MATCH - 1;
      s.match_available = 0;
      strm.next_in = next;
      strm.input = input;
      strm.avail_in = avail;
      s.wrap = wrap;
      return Z_OK$3;
    };
    var deflateInit_1 = deflateInit;
    var deflateInit2_1 = deflateInit2;
    var deflateReset_1 = deflateReset;
    var deflateResetKeep_1 = deflateResetKeep;
    var deflateSetHeader_1 = deflateSetHeader;
    var deflate_2$1 = deflate$2;
    var deflateEnd_1 = deflateEnd;
    var deflateSetDictionary_1 = deflateSetDictionary;
    var deflateInfo = 'pako deflate (from Nodeca project)';

    /* Not implemented
    module.exports.deflateBound = deflateBound;
    module.exports.deflateCopy = deflateCopy;
    module.exports.deflateGetDictionary = deflateGetDictionary;
    module.exports.deflateParams = deflateParams;
    module.exports.deflatePending = deflatePending;
    module.exports.deflatePrime = deflatePrime;
    module.exports.deflateTune = deflateTune;
    */

    var deflate_1$2 = {
      deflateInit: deflateInit_1,
      deflateInit2: deflateInit2_1,
      deflateReset: deflateReset_1,
      deflateResetKeep: deflateResetKeep_1,
      deflateSetHeader: deflateSetHeader_1,
      deflate: deflate_2$1,
      deflateEnd: deflateEnd_1,
      deflateSetDictionary: deflateSetDictionary_1,
      deflateInfo: deflateInfo
    };
    const _has = (obj, key) => {
      return Object.prototype.hasOwnProperty.call(obj, key);
    };
    var assign = function (obj /*from1, from2, from3, ...*/) {
      const sources = Array.prototype.slice.call(arguments, 1);
      while (sources.length) {
        const source = sources.shift();
        if (!source) {
          continue;
        }
        if (typeof source !== 'object') {
          throw new TypeError(source + 'must be non-object');
        }
        for (const p in source) {
          if (_has(source, p)) {
            obj[p] = source[p];
          }
        }
      }
      return obj;
    };

    // Join array of chunks to single array.
    var flattenChunks = chunks => {
      // calculate data length
      let len = 0;
      for (let i = 0, l = chunks.length; i < l; i++) {
        len += chunks[i].length;
      }

      // join chunks
      const result = new Uint8Array(len);
      for (let i = 0, pos = 0, l = chunks.length; i < l; i++) {
        let chunk = chunks[i];
        result.set(chunk, pos);
        pos += chunk.length;
      }
      return result;
    };
    var common = {
      assign: assign,
      flattenChunks: flattenChunks
    };

    // String encode/decode helpers

    // Quick check if we can use fast array to bin string conversion
    //
    // - apply(Array) can fail on Android 2.2
    // - apply(Uint8Array) can fail on iOS 5.1 Safari
    //
    let STR_APPLY_UIA_OK = true;
    try {
      String.fromCharCode.apply(null, new Uint8Array(1));
    } catch (__) {
      STR_APPLY_UIA_OK = false;
    }

    // Table with utf8 lengths (calculated by first byte of sequence)
    // Note, that 5 & 6-byte values and some 4-byte values can not be represented in JS,
    // because max possible codepoint is 0x10ffff
    const _utf8len = new Uint8Array(256);
    for (let q = 0; q < 256; q++) {
      _utf8len[q] = q >= 252 ? 6 : q >= 248 ? 5 : q >= 240 ? 4 : q >= 224 ? 3 : q >= 192 ? 2 : 1;
    }
    _utf8len[254] = _utf8len[254] = 1; // Invalid sequence start

    // convert string to array (typed, when possible)
    var string2buf = str => {
      if (typeof TextEncoder === 'function' && TextEncoder.prototype.encode) {
        return new TextEncoder().encode(str);
      }
      let buf,
        c,
        c2,
        m_pos,
        i,
        str_len = str.length,
        buf_len = 0;

      // count binary size
      for (m_pos = 0; m_pos < str_len; m_pos++) {
        c = str.charCodeAt(m_pos);
        if ((c & 0xfc00) === 0xd800 && m_pos + 1 < str_len) {
          c2 = str.charCodeAt(m_pos + 1);
          if ((c2 & 0xfc00) === 0xdc00) {
            c = 0x10000 + (c - 0xd800 << 10) + (c2 - 0xdc00);
            m_pos++;
          }
        }
        buf_len += c < 0x80 ? 1 : c < 0x800 ? 2 : c < 0x10000 ? 3 : 4;
      }

      // allocate buffer
      buf = new Uint8Array(buf_len);

      // convert
      for (i = 0, m_pos = 0; i < buf_len; m_pos++) {
        c = str.charCodeAt(m_pos);
        if ((c & 0xfc00) === 0xd800 && m_pos + 1 < str_len) {
          c2 = str.charCodeAt(m_pos + 1);
          if ((c2 & 0xfc00) === 0xdc00) {
            c = 0x10000 + (c - 0xd800 << 10) + (c2 - 0xdc00);
            m_pos++;
          }
        }
        if (c < 0x80) {
          /* one byte */
          buf[i++] = c;
        } else if (c < 0x800) {
          /* two bytes */
          buf[i++] = 0xC0 | c >>> 6;
          buf[i++] = 0x80 | c & 0x3f;
        } else if (c < 0x10000) {
          /* three bytes */
          buf[i++] = 0xE0 | c >>> 12;
          buf[i++] = 0x80 | c >>> 6 & 0x3f;
          buf[i++] = 0x80 | c & 0x3f;
        } else {
          /* four bytes */
          buf[i++] = 0xf0 | c >>> 18;
          buf[i++] = 0x80 | c >>> 12 & 0x3f;
          buf[i++] = 0x80 | c >>> 6 & 0x3f;
          buf[i++] = 0x80 | c & 0x3f;
        }
      }
      return buf;
    };

    // Helper
    const buf2binstring = (buf, len) => {
      // On Chrome, the arguments in a function call that are allowed is `65534`.
      // If the length of the buffer is smaller than that, we can use this optimization,
      // otherwise we will take a slower path.
      if (len < 65534) {
        if (buf.subarray && STR_APPLY_UIA_OK) {
          return String.fromCharCode.apply(null, buf.length === len ? buf : buf.subarray(0, len));
        }
      }
      let result = '';
      for (let i = 0; i < len; i++) {
        result += String.fromCharCode(buf[i]);
      }
      return result;
    };

    // convert array to string
    var buf2string = (buf, max) => {
      const len = max || buf.length;
      if (typeof TextDecoder === 'function' && TextDecoder.prototype.decode) {
        return new TextDecoder().decode(buf.subarray(0, max));
      }
      let i, out;

      // Reserve max possible length (2 words per char)
      // NB: by unknown reasons, Array is significantly faster for
      //     String.fromCharCode.apply than Uint16Array.
      const utf16buf = new Array(len * 2);
      for (out = 0, i = 0; i < len;) {
        let c = buf[i++];
        // quick process ascii
        if (c < 0x80) {
          utf16buf[out++] = c;
          continue;
        }
        let c_len = _utf8len[c];
        // skip 5 & 6 byte codes
        if (c_len > 4) {
          utf16buf[out++] = 0xfffd;
          i += c_len - 1;
          continue;
        }

        // apply mask on first byte
        c &= c_len === 2 ? 0x1f : c_len === 3 ? 0x0f : 0x07;
        // join the rest
        while (c_len > 1 && i < len) {
          c = c << 6 | buf[i++] & 0x3f;
          c_len--;
        }

        // terminated by end of string?
        if (c_len > 1) {
          utf16buf[out++] = 0xfffd;
          continue;
        }
        if (c < 0x10000) {
          utf16buf[out++] = c;
        } else {
          c -= 0x10000;
          utf16buf[out++] = 0xd800 | c >> 10 & 0x3ff;
          utf16buf[out++] = 0xdc00 | c & 0x3ff;
        }
      }
      return buf2binstring(utf16buf, out);
    };

    // Calculate max possible position in utf8 buffer,
    // that will not break sequence. If that's not possible
    // - (very small limits) return max size as is.
    //
    // buf[] - utf8 bytes array
    // max   - length limit (mandatory);
    var utf8border = (buf, max) => {
      max = max || buf.length;
      if (max > buf.length) {
        max = buf.length;
      }

      // go back from last position, until start of sequence found
      let pos = max - 1;
      while (pos >= 0 && (buf[pos] & 0xC0) === 0x80) {
        pos--;
      }

      // Very small and broken sequence,
      // return max, because we should return something anyway.
      if (pos < 0) {
        return max;
      }

      // If we came to start of buffer - that means buffer is too small,
      // return max too.
      if (pos === 0) {
        return max;
      }
      return pos + _utf8len[buf[pos]] > max ? pos : max;
    };
    var strings = {
      string2buf: string2buf,
      buf2string: buf2string,
      utf8border: utf8border
    };

    // (C) 1995-2013 Jean-loup Gailly and Mark Adler
    // (C) 2014-2017 Vitaly Puzrin and Andrey Tupitsin
    //
    // This software is provided 'as-is', without any express or implied
    // warranty. In no event will the authors be held liable for any damages
    // arising from the use of this software.
    //
    // Permission is granted to anyone to use this software for any purpose,
    // including commercial applications, and to alter it and redistribute it
    // freely, subject to the following restrictions:
    //
    // 1. The origin of this software must not be misrepresented; you must not
    //   claim that you wrote the original software. If you use this software
    //   in a product, an acknowledgment in the product documentation would be
    //   appreciated but is not required.
    // 2. Altered source versions must be plainly marked as such, and must not be
    //   misrepresented as being the original software.
    // 3. This notice may not be removed or altered from any source distribution.

    function ZStream() {
      /* next input byte */
      this.input = null; // JS specific, because we have no pointers
      this.next_in = 0;
      /* number of bytes available at input */
      this.avail_in = 0;
      /* total number of input bytes read so far */
      this.total_in = 0;
      /* next output byte should be put there */
      this.output = null; // JS specific, because we have no pointers
      this.next_out = 0;
      /* remaining free space at output */
      this.avail_out = 0;
      /* total number of bytes output so far */
      this.total_out = 0;
      /* last error message, NULL if no error */
      this.msg = '' /*Z_NULL*/;
      /* not visible by applications */
      this.state = null;
      /* best guess about the data type: binary or text */
      this.data_type = 2 /*Z_UNKNOWN*/;
      /* adler32 value of the uncompressed data */
      this.adler = 0;
    }
    var zstream = ZStream;
    const toString$1 = Object.prototype.toString;

    /* Public constants ==========================================================*/
    /* ===========================================================================*/

    const {
      Z_NO_FLUSH: Z_NO_FLUSH$1,
      Z_SYNC_FLUSH,
      Z_FULL_FLUSH,
      Z_FINISH: Z_FINISH$2,
      Z_OK: Z_OK$2,
      Z_STREAM_END: Z_STREAM_END$2,
      Z_DEFAULT_COMPRESSION,
      Z_DEFAULT_STRATEGY,
      Z_DEFLATED: Z_DEFLATED$1
    } = constants$2;

    /* ===========================================================================*/

    /**
     * class Deflate
     *
     * Generic JS-style wrapper for zlib calls. If you don't need
     * streaming behaviour - use more simple functions: [[deflate]],
     * [[deflateRaw]] and [[gzip]].
     **/

    /* internal
     * Deflate.chunks -> Array
     *
     * Chunks of output data, if [[Deflate#onData]] not overridden.
     **/

    /**
     * Deflate.result -> Uint8Array
     *
     * Compressed result, generated by default [[Deflate#onData]]
     * and [[Deflate#onEnd]] handlers. Filled after you push last chunk
     * (call [[Deflate#push]] with `Z_FINISH` / `true` param).
     **/

    /**
     * Deflate.err -> Number
     *
     * Error code after deflate finished. 0 (Z_OK) on success.
     * You will not need it in real life, because deflate errors
     * are possible only on wrong options or bad `onData` / `onEnd`
     * custom handlers.
     **/

    /**
     * Deflate.msg -> String
     *
     * Error message, if [[Deflate.err]] != 0
     **/

    /**
     * new Deflate(options)
     * - options (Object): zlib deflate options.
     *
     * Creates new deflator instance with specified params. Throws exception
     * on bad params. Supported options:
     *
     * - `level`
     * - `windowBits`
     * - `memLevel`
     * - `strategy`
     * - `dictionary`
     *
     * [http://zlib.net/manual.html#Advanced](http://zlib.net/manual.html#Advanced)
     * for more information on these.
     *
     * Additional options, for internal needs:
     *
     * - `chunkSize` - size of generated data chunks (16K by default)
     * - `raw` (Boolean) - do raw deflate
     * - `gzip` (Boolean) - create gzip wrapper
     * - `header` (Object) - custom header for gzip
     *   - `text` (Boolean) - true if compressed data believed to be text
     *   - `time` (Number) - modification time, unix timestamp
     *   - `os` (Number) - operation system code
     *   - `extra` (Array) - array of bytes with extra data (max 65536)
     *   - `name` (String) - file name (binary string)
     *   - `comment` (String) - comment (binary string)
     *   - `hcrc` (Boolean) - true if header crc should be added
     *
     * ##### Example:
     *
     * ```javascript
     * const pako = require('pako')
     *   , chunk1 = new Uint8Array([1,2,3,4,5,6,7,8,9])
     *   , chunk2 = new Uint8Array([10,11,12,13,14,15,16,17,18,19]);
     *
     * const deflate = new pako.Deflate({ level: 3});
     *
     * deflate.push(chunk1, false);
     * deflate.push(chunk2, true);  // true -> last chunk
     *
     * if (deflate.err) { throw new Error(deflate.err); }
     *
     * console.log(deflate.result);
     * ```
     **/
    function Deflate$1(options) {
      this.options = common.assign({
        level: Z_DEFAULT_COMPRESSION,
        method: Z_DEFLATED$1,
        chunkSize: 16384,
        windowBits: 15,
        memLevel: 8,
        strategy: Z_DEFAULT_STRATEGY
      }, options || {});
      let opt = this.options;
      if (opt.raw && opt.windowBits > 0) {
        opt.windowBits = -opt.windowBits;
      } else if (opt.gzip && opt.windowBits > 0 && opt.windowBits < 16) {
        opt.windowBits += 16;
      }
      this.err = 0; // error code, if happens (0 = Z_OK)
      this.msg = ''; // error message
      this.ended = false; // used to avoid multiple onEnd() calls
      this.chunks = []; // chunks of compressed data

      this.strm = new zstream();
      this.strm.avail_out = 0;
      let status = deflate_1$2.deflateInit2(this.strm, opt.level, opt.method, opt.windowBits, opt.memLevel, opt.strategy);
      if (status !== Z_OK$2) {
        throw new Error(messages[status]);
      }
      if (opt.header) {
        deflate_1$2.deflateSetHeader(this.strm, opt.header);
      }
      if (opt.dictionary) {
        let dict;
        // Convert data if needed
        if (typeof opt.dictionary === 'string') {
          // If we need to compress text, change encoding to utf8.
          dict = strings.string2buf(opt.dictionary);
        } else if (toString$1.call(opt.dictionary) === '[object ArrayBuffer]') {
          dict = new Uint8Array(opt.dictionary);
        } else {
          dict = opt.dictionary;
        }
        status = deflate_1$2.deflateSetDictionary(this.strm, dict);
        if (status !== Z_OK$2) {
          throw new Error(messages[status]);
        }
        this._dict_set = true;
      }
    }

    /**
     * Deflate#push(data[, flush_mode]) -> Boolean
     * - data (Uint8Array|ArrayBuffer|String): input data. Strings will be
     *   converted to utf8 byte sequence.
     * - flush_mode (Number|Boolean): 0..6 for corresponding Z_NO_FLUSH..Z_TREE modes.
     *   See constants. Skipped or `false` means Z_NO_FLUSH, `true` means Z_FINISH.
     *
     * Sends input data to deflate pipe, generating [[Deflate#onData]] calls with
     * new compressed chunks. Returns `true` on success. The last data block must
     * have `flush_mode` Z_FINISH (or `true`). That will flush internal pending
     * buffers and call [[Deflate#onEnd]].
     *
     * On fail call [[Deflate#onEnd]] with error code and return false.
     *
     * ##### Example
     *
     * ```javascript
     * push(chunk, false); // push one of data chunks
     * ...
     * push(chunk, true);  // push last chunk
     * ```
     **/
    Deflate$1.prototype.push = function (data, flush_mode) {
      const strm = this.strm;
      const chunkSize = this.options.chunkSize;
      let status, _flush_mode;
      if (this.ended) {
        return false;
      }
      if (flush_mode === ~~flush_mode) _flush_mode = flush_mode;else _flush_mode = flush_mode === true ? Z_FINISH$2 : Z_NO_FLUSH$1;

      // Convert data if needed
      if (typeof data === 'string') {
        // If we need to compress text, change encoding to utf8.
        strm.input = strings.string2buf(data);
      } else if (toString$1.call(data) === '[object ArrayBuffer]') {
        strm.input = new Uint8Array(data);
      } else {
        strm.input = data;
      }
      strm.next_in = 0;
      strm.avail_in = strm.input.length;
      for (;;) {
        if (strm.avail_out === 0) {
          strm.output = new Uint8Array(chunkSize);
          strm.next_out = 0;
          strm.avail_out = chunkSize;
        }

        // Make sure avail_out > 6 to avoid repeating markers
        if ((_flush_mode === Z_SYNC_FLUSH || _flush_mode === Z_FULL_FLUSH) && strm.avail_out <= 6) {
          this.onData(strm.output.subarray(0, strm.next_out));
          strm.avail_out = 0;
          continue;
        }
        status = deflate_1$2.deflate(strm, _flush_mode);

        // Ended => flush and finish
        if (status === Z_STREAM_END$2) {
          if (strm.next_out > 0) {
            this.onData(strm.output.subarray(0, strm.next_out));
          }
          status = deflate_1$2.deflateEnd(this.strm);
          this.onEnd(status);
          this.ended = true;
          return status === Z_OK$2;
        }

        // Flush if out buffer full
        if (strm.avail_out === 0) {
          this.onData(strm.output);
          continue;
        }

        // Flush if requested and has data
        if (_flush_mode > 0 && strm.next_out > 0) {
          this.onData(strm.output.subarray(0, strm.next_out));
          strm.avail_out = 0;
          continue;
        }
        if (strm.avail_in === 0) break;
      }
      return true;
    };

    /**
     * Deflate#onData(chunk) -> Void
     * - chunk (Uint8Array): output data.
     *
     * By default, stores data blocks in `chunks[]` property and glue
     * those in `onEnd`. Override this handler, if you need another behaviour.
     **/
    Deflate$1.prototype.onData = function (chunk) {
      this.chunks.push(chunk);
    };

    /**
     * Deflate#onEnd(status) -> Void
     * - status (Number): deflate status. 0 (Z_OK) on success,
     *   other if not.
     *
     * Called once after you tell deflate that the input stream is
     * complete (Z_FINISH). By default - join collected chunks,
     * free memory and fill `results` / `err` properties.
     **/
    Deflate$1.prototype.onEnd = function (status) {
      // On success - join
      if (status === Z_OK$2) {
        this.result = common.flattenChunks(this.chunks);
      }
      this.chunks = [];
      this.err = status;
      this.msg = this.strm.msg;
    };

    /**
     * deflate(data[, options]) -> Uint8Array
     * - data (Uint8Array|ArrayBuffer|String): input data to compress.
     * - options (Object): zlib deflate options.
     *
     * Compress `data` with deflate algorithm and `options`.
     *
     * Supported options are:
     *
     * - level
     * - windowBits
     * - memLevel
     * - strategy
     * - dictionary
     *
     * [http://zlib.net/manual.html#Advanced](http://zlib.net/manual.html#Advanced)
     * for more information on these.
     *
     * Sugar (options):
     *
     * - `raw` (Boolean) - say that we work with raw stream, if you don't wish to specify
     *   negative windowBits implicitly.
     *
     * ##### Example:
     *
     * ```javascript
     * const pako = require('pako')
     * const data = new Uint8Array([1,2,3,4,5,6,7,8,9]);
     *
     * console.log(pako.deflate(data));
     * ```
     **/
    function deflate$1(input, options) {
      const deflator = new Deflate$1(options);
      deflator.push(input, true);

      // That will never happens, if you don't cheat with options :)
      if (deflator.err) {
        throw deflator.msg || messages[deflator.err];
      }
      return deflator.result;
    }

    /**
     * deflateRaw(data[, options]) -> Uint8Array
     * - data (Uint8Array|ArrayBuffer|String): input data to compress.
     * - options (Object): zlib deflate options.
     *
     * The same as [[deflate]], but creates raw data, without wrapper
     * (header and adler32 crc).
     **/
    function deflateRaw$1(input, options) {
      options = options || {};
      options.raw = true;
      return deflate$1(input, options);
    }

    /**
     * gzip(data[, options]) -> Uint8Array
     * - data (Uint8Array|ArrayBuffer|String): input data to compress.
     * - options (Object): zlib deflate options.
     *
     * The same as [[deflate]], but create gzip wrapper instead of
     * deflate one.
     **/
    function gzip$1(input, options) {
      options = options || {};
      options.gzip = true;
      return deflate$1(input, options);
    }
    var Deflate_1$1 = Deflate$1;
    var deflate_2 = deflate$1;
    var deflateRaw_1$1 = deflateRaw$1;
    var gzip_1$1 = gzip$1;
    var constants$1 = constants$2;
    var deflate_1$1 = {
      Deflate: Deflate_1$1,
      deflate: deflate_2,
      deflateRaw: deflateRaw_1$1,
      gzip: gzip_1$1,
      constants: constants$1
    };

    // (C) 1995-2013 Jean-loup Gailly and Mark Adler
    // (C) 2014-2017 Vitaly Puzrin and Andrey Tupitsin
    //
    // This software is provided 'as-is', without any express or implied
    // warranty. In no event will the authors be held liable for any damages
    // arising from the use of this software.
    //
    // Permission is granted to anyone to use this software for any purpose,
    // including commercial applications, and to alter it and redistribute it
    // freely, subject to the following restrictions:
    //
    // 1. The origin of this software must not be misrepresented; you must not
    //   claim that you wrote the original software. If you use this software
    //   in a product, an acknowledgment in the product documentation would be
    //   appreciated but is not required.
    // 2. Altered source versions must be plainly marked as such, and must not be
    //   misrepresented as being the original software.
    // 3. This notice may not be removed or altered from any source distribution.

    // See state defs from inflate.js
    const BAD$1 = 16209; /* got a data error -- remain here until reset */
    const TYPE$1 = 16191; /* i: waiting for type bits, including last-flag bit */

    /*
       Decode literal, length, and distance codes and write out the resulting
       literal and match bytes until either not enough input or output is
       available, an end-of-block is encountered, or a data error is encountered.
       When large enough input and output buffers are supplied to inflate(), for
       example, a 16K input buffer and a 64K output buffer, more than 95% of the
       inflate execution time is spent in this routine.

       Entry assumptions:

            state.mode === LEN
            strm.avail_in >= 6
            strm.avail_out >= 258
            start >= strm.avail_out
            state.bits < 8

       On return, state.mode is one of:

            LEN -- ran out of enough output space or enough available input
            TYPE -- reached end of block code, inflate() to interpret next block
            BAD -- error in block data

       Notes:

        - The maximum input bits used by a length/distance pair is 15 bits for the
          length code, 5 bits for the length extra, 15 bits for the distance code,
          and 13 bits for the distance extra.  This totals 48 bits, or six bytes.
          Therefore if strm.avail_in >= 6, then there is enough input to avoid
          checking for available input while decoding.

        - The maximum bytes that a single length/distance pair can output is 258
          bytes, which is the maximum length that can be coded.  inflate_fast()
          requires strm.avail_out >= 258 for each loop to avoid checking for
          output space.
     */
    var inffast = function inflate_fast(strm, start) {
      let _in; /* local strm.input */
      let last; /* have enough input while in < last */
      let _out; /* local strm.output */
      let beg; /* inflate()'s initial strm.output */
      let end; /* while out < end, enough space available */
      //#ifdef INFLATE_STRICT
      let dmax; /* maximum distance from zlib header */
      //#endif
      let wsize; /* window size or zero if not using window */
      let whave; /* valid bytes in the window */
      let wnext; /* window write index */
      // Use `s_window` instead `window`, avoid conflict with instrumentation tools
      let s_window; /* allocated sliding window, if wsize != 0 */
      let hold; /* local strm.hold */
      let bits; /* local strm.bits */
      let lcode; /* local strm.lencode */
      let dcode; /* local strm.distcode */
      let lmask; /* mask for first level of length codes */
      let dmask; /* mask for first level of distance codes */
      let here; /* retrieved table entry */
      let op; /* code bits, operation, extra bits, or */
      /*  window position, window bytes to copy */
      let len; /* match length, unused bytes */
      let dist; /* match distance */
      let from; /* where to copy match from */
      let from_source;
      let input, output; // JS specific, because we have no pointers

      /* copy state to local variables */
      const state = strm.state;
      //here = state.here;
      _in = strm.next_in;
      input = strm.input;
      last = _in + (strm.avail_in - 5);
      _out = strm.next_out;
      output = strm.output;
      beg = _out - (start - strm.avail_out);
      end = _out + (strm.avail_out - 257);
      //#ifdef INFLATE_STRICT
      dmax = state.dmax;
      //#endif
      wsize = state.wsize;
      whave = state.whave;
      wnext = state.wnext;
      s_window = state.window;
      hold = state.hold;
      bits = state.bits;
      lcode = state.lencode;
      dcode = state.distcode;
      lmask = (1 << state.lenbits) - 1;
      dmask = (1 << state.distbits) - 1;

      /* decode literals and length/distances until end-of-block or not enough
         input data or output space */

      top: do {
        if (bits < 15) {
          hold += input[_in++] << bits;
          bits += 8;
          hold += input[_in++] << bits;
          bits += 8;
        }
        here = lcode[hold & lmask];
        dolen: for (;;) {
          // Goto emulation
          op = here >>> 24 /*here.bits*/;
          hold >>>= op;
          bits -= op;
          op = here >>> 16 & 0xff /*here.op*/;
          if (op === 0) {
            /* literal */
            //Tracevv((stderr, here.val >= 0x20 && here.val < 0x7f ?
            //        "inflate:         literal '%c'\n" :
            //        "inflate:         literal 0x%02x\n", here.val));
            output[_out++] = here & 0xffff /*here.val*/;
          } else if (op & 16) {
            /* length base */
            len = here & 0xffff /*here.val*/;
            op &= 15; /* number of extra bits */
            if (op) {
              if (bits < op) {
                hold += input[_in++] << bits;
                bits += 8;
              }
              len += hold & (1 << op) - 1;
              hold >>>= op;
              bits -= op;
            }
            //Tracevv((stderr, "inflate:         length %u\n", len));
            if (bits < 15) {
              hold += input[_in++] << bits;
              bits += 8;
              hold += input[_in++] << bits;
              bits += 8;
            }
            here = dcode[hold & dmask];
            dodist: for (;;) {
              // goto emulation
              op = here >>> 24 /*here.bits*/;
              hold >>>= op;
              bits -= op;
              op = here >>> 16 & 0xff /*here.op*/;
              if (op & 16) {
                /* distance base */
                dist = here & 0xffff /*here.val*/;
                op &= 15; /* number of extra bits */
                if (bits < op) {
                  hold += input[_in++] << bits;
                  bits += 8;
                  if (bits < op) {
                    hold += input[_in++] << bits;
                    bits += 8;
                  }
                }
                dist += hold & (1 << op) - 1;
                //#ifdef INFLATE_STRICT
                if (dist > dmax) {
                  strm.msg = 'invalid distance too far back';
                  state.mode = BAD$1;
                  break top;
                }
                //#endif
                hold >>>= op;
                bits -= op;
                //Tracevv((stderr, "inflate:         distance %u\n", dist));
                op = _out - beg; /* max distance in output */
                if (dist > op) {
                  /* see if copy from window */
                  op = dist - op; /* distance back in window */
                  if (op > whave) {
                    if (state.sane) {
                      strm.msg = 'invalid distance too far back';
                      state.mode = BAD$1;
                      break top;
                    }

                    // (!) This block is disabled in zlib defaults,
                    // don't enable it for binary compatibility
                    //#ifdef INFLATE_ALLOW_INVALID_DISTANCE_TOOFAR_ARRR
                    //                if (len <= op - whave) {
                    //                  do {
                    //                    output[_out++] = 0;
                    //                  } while (--len);
                    //                  continue top;
                    //                }
                    //                len -= op - whave;
                    //                do {
                    //                  output[_out++] = 0;
                    //                } while (--op > whave);
                    //                if (op === 0) {
                    //                  from = _out - dist;
                    //                  do {
                    //                    output[_out++] = output[from++];
                    //                  } while (--len);
                    //                  continue top;
                    //                }
                    //#endif
                  }
                  from = 0; // window index
                  from_source = s_window;
                  if (wnext === 0) {
                    /* very common case */
                    from += wsize - op;
                    if (op < len) {
                      /* some from window */
                      len -= op;
                      do {
                        output[_out++] = s_window[from++];
                      } while (--op);
                      from = _out - dist; /* rest from output */
                      from_source = output;
                    }
                  } else if (wnext < op) {
                    /* wrap around window */
                    from += wsize + wnext - op;
                    op -= wnext;
                    if (op < len) {
                      /* some from end of window */
                      len -= op;
                      do {
                        output[_out++] = s_window[from++];
                      } while (--op);
                      from = 0;
                      if (wnext < len) {
                        /* some from start of window */
                        op = wnext;
                        len -= op;
                        do {
                          output[_out++] = s_window[from++];
                        } while (--op);
                        from = _out - dist; /* rest from output */
                        from_source = output;
                      }
                    }
                  } else {
                    /* contiguous in window */
                    from += wnext - op;
                    if (op < len) {
                      /* some from window */
                      len -= op;
                      do {
                        output[_out++] = s_window[from++];
                      } while (--op);
                      from = _out - dist; /* rest from output */
                      from_source = output;
                    }
                  }
                  while (len > 2) {
                    output[_out++] = from_source[from++];
                    output[_out++] = from_source[from++];
                    output[_out++] = from_source[from++];
                    len -= 3;
                  }
                  if (len) {
                    output[_out++] = from_source[from++];
                    if (len > 1) {
                      output[_out++] = from_source[from++];
                    }
                  }
                } else {
                  from = _out - dist; /* copy direct from output */
                  do {
                    /* minimum length is three */
                    output[_out++] = output[from++];
                    output[_out++] = output[from++];
                    output[_out++] = output[from++];
                    len -= 3;
                  } while (len > 2);
                  if (len) {
                    output[_out++] = output[from++];
                    if (len > 1) {
                      output[_out++] = output[from++];
                    }
                  }
                }
              } else if ((op & 64) === 0) {
                /* 2nd level distance code */
                here = dcode[(here & 0xffff /*here.val*/) + (hold & (1 << op) - 1)];
                continue dodist;
              } else {
                strm.msg = 'invalid distance code';
                state.mode = BAD$1;
                break top;
              }
              break; // need to emulate goto via "continue"
            }
          } else if ((op & 64) === 0) {
            /* 2nd level length code */
            here = lcode[(here & 0xffff /*here.val*/) + (hold & (1 << op) - 1)];
            continue dolen;
          } else if (op & 32) {
            /* end-of-block */
            //Tracevv((stderr, "inflate:         end of block\n"));
            state.mode = TYPE$1;
            break top;
          } else {
            strm.msg = 'invalid literal/length code';
            state.mode = BAD$1;
            break top;
          }
          break; // need to emulate goto via "continue"
        }
      } while (_in < last && _out < end);

      /* return unused bytes (on entry, bits < 8, so in won't go too far back) */
      len = bits >> 3;
      _in -= len;
      bits -= len << 3;
      hold &= (1 << bits) - 1;

      /* update state and return */
      strm.next_in = _in;
      strm.next_out = _out;
      strm.avail_in = _in < last ? 5 + (last - _in) : 5 - (_in - last);
      strm.avail_out = _out < end ? 257 + (end - _out) : 257 - (_out - end);
      state.hold = hold;
      state.bits = bits;
      return;
    };

    // (C) 1995-2013 Jean-loup Gailly and Mark Adler
    // (C) 2014-2017 Vitaly Puzrin and Andrey Tupitsin
    //
    // This software is provided 'as-is', without any express or implied
    // warranty. In no event will the authors be held liable for any damages
    // arising from the use of this software.
    //
    // Permission is granted to anyone to use this software for any purpose,
    // including commercial applications, and to alter it and redistribute it
    // freely, subject to the following restrictions:
    //
    // 1. The origin of this software must not be misrepresented; you must not
    //   claim that you wrote the original software. If you use this software
    //   in a product, an acknowledgment in the product documentation would be
    //   appreciated but is not required.
    // 2. Altered source versions must be plainly marked as such, and must not be
    //   misrepresented as being the original software.
    // 3. This notice may not be removed or altered from any source distribution.

    const MAXBITS = 15;
    const ENOUGH_LENS$1 = 852;
    const ENOUGH_DISTS$1 = 592;
    //const ENOUGH = (ENOUGH_LENS+ENOUGH_DISTS);

    const CODES$1 = 0;
    const LENS$1 = 1;
    const DISTS$1 = 2;
    const lbase = new Uint16Array([/* Length codes 257..285 base */
    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]);
    const lext = new Uint8Array([/* Length codes 257..285 extra */
    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]);
    const dbase = new Uint16Array([/* Distance codes 0..29 base */
    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]);
    const dext = new Uint8Array([/* Distance codes 0..29 extra */
    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]);
    const inflate_table = (type, lens, lens_index, codes, table, table_index, work, opts) => {
      const bits = opts.bits;
      //here = opts.here; /* table entry for duplication */

      let len = 0; /* a code's length in bits */
      let sym = 0; /* index of code symbols */
      let min = 0,
        max = 0; /* minimum and maximum code lengths */
      let root = 0; /* number of index bits for root table */
      let curr = 0; /* number of index bits for current table */
      let drop = 0; /* code bits to drop for sub-table */
      let left = 0; /* number of prefix codes available */
      let used = 0; /* code entries in table used */
      let huff = 0; /* Huffman code */
      let incr; /* for incrementing code, index */
      let fill; /* index for replicating entries */
      let low; /* low bits for current root entry */
      let mask; /* mask for low root bits */
      let next; /* next available space in table */
      let base = null; /* base value table to use */
      //  let shoextra;    /* extra bits table to use */
      let match; /* use base and extra for symbol >= match */
      const count = new Uint16Array(MAXBITS + 1); //[MAXBITS+1];    /* number of codes of each length */
      const offs = new Uint16Array(MAXBITS + 1); //[MAXBITS+1];     /* offsets in table for each length */
      let extra = null;
      let here_bits, here_op, here_val;

      /*
       Process a set of code lengths to create a canonical Huffman code.  The
       code lengths are lens[0..codes-1].  Each length corresponds to the
       symbols 0..codes-1.  The Huffman code is generated by first sorting the
       symbols by length from short to long, and retaining the symbol order
       for codes with equal lengths.  Then the code starts with all zero bits
       for the first code of the shortest length, and the codes are integer
       increments for the same length, and zeros are appended as the length
       increases.  For the deflate format, these bits are stored backwards
       from their more natural integer increment ordering, and so when the
       decoding tables are built in the large loop below, the integer codes
       are incremented backwards.
        This routine assumes, but does not check, that all of the entries in
       lens[] are in the range 0..MAXBITS.  The caller must assure this.
       1..MAXBITS is interpreted as that code length.  zero means that that
       symbol does not occur in this code.
        The codes are sorted by computing a count of codes for each length,
       creating from that a table of starting indices for each length in the
       sorted table, and then entering the symbols in order in the sorted
       table.  The sorted table is work[], with that space being provided by
       the caller.
        The length counts are used for other purposes as well, i.e. finding
       the minimum and maximum length codes, determining if there are any
       codes at all, checking for a valid set of lengths, and looking ahead
       at length counts to determine sub-table sizes when building the
       decoding tables.
       */

      /* accumulate lengths for codes (assumes lens[] all in 0..MAXBITS) */
      for (len = 0; len <= MAXBITS; len++) {
        count[len] = 0;
      }
      for (sym = 0; sym < codes; sym++) {
        count[lens[lens_index + sym]]++;
      }

      /* bound code lengths, force root to be within code lengths */
      root = bits;
      for (max = MAXBITS; max >= 1; max--) {
        if (count[max] !== 0) {
          break;
        }
      }
      if (root > max) {
        root = max;
      }
      if (max === 0) {
        /* no symbols to code at all */
        //table.op[opts.table_index] = 64;  //here.op = (var char)64;    /* invalid code marker */
        //table.bits[opts.table_index] = 1;   //here.bits = (var char)1;
        //table.val[opts.table_index++] = 0;   //here.val = (var short)0;
        table[table_index++] = 1 << 24 | 64 << 16 | 0;

        //table.op[opts.table_index] = 64;
        //table.bits[opts.table_index] = 1;
        //table.val[opts.table_index++] = 0;
        table[table_index++] = 1 << 24 | 64 << 16 | 0;
        opts.bits = 1;
        return 0; /* no symbols, but wait for decoding to report error */
      }
      for (min = 1; min < max; min++) {
        if (count[min] !== 0) {
          break;
        }
      }
      if (root < min) {
        root = min;
      }

      /* check for an over-subscribed or incomplete set of lengths */
      left = 1;
      for (len = 1; len <= MAXBITS; len++) {
        left <<= 1;
        left -= count[len];
        if (left < 0) {
          return -1;
        } /* over-subscribed */
      }
      if (left > 0 && (type === CODES$1 || max !== 1)) {
        return -1; /* incomplete set */
      }

      /* generate offsets into symbol table for each length for sorting */
      offs[1] = 0;
      for (len = 1; len < MAXBITS; len++) {
        offs[len + 1] = offs[len] + count[len];
      }

      /* sort symbols by length, by symbol order within each length */
      for (sym = 0; sym < codes; sym++) {
        if (lens[lens_index + sym] !== 0) {
          work[offs[lens[lens_index + sym]]++] = sym;
        }
      }

      /*
       Create and fill in decoding tables.  In this loop, the table being
       filled is at next and has curr index bits.  The code being used is huff
       with length len.  That code is converted to an index by dropping drop
       bits off of the bottom.  For codes where len is less than drop + curr,
       those top drop + curr - len bits are incremented through all values to
       fill the table with replicated entries.
        root is the number of index bits for the root table.  When len exceeds
       root, sub-tables are created pointed to by the root entry with an index
       of the low root bits of huff.  This is saved in low to check for when a
       new sub-table should be started.  drop is zero when the root table is
       being filled, and drop is root when sub-tables are being filled.
        When a new sub-table is needed, it is necessary to look ahead in the
       code lengths to determine what size sub-table is needed.  The length
       counts are used for this, and so count[] is decremented as codes are
       entered in the tables.
        used keeps track of how many table entries have been allocated from the
       provided *table space.  It is checked for LENS and DIST tables against
       the constants ENOUGH_LENS and ENOUGH_DISTS to guard against changes in
       the initial root table size constants.  See the comments in inftrees.h
       for more information.
        sym increments through all symbols, and the loop terminates when
       all codes of length max, i.e. all codes, have been processed.  This
       routine permits incomplete codes, so another loop after this one fills
       in the rest of the decoding tables with invalid code markers.
       */

      /* set up for code type */
      // poor man optimization - use if-else instead of switch,
      // to avoid deopts in old v8
      if (type === CODES$1) {
        base = extra = work; /* dummy value--not used */
        match = 20;
      } else if (type === LENS$1) {
        base = lbase;
        extra = lext;
        match = 257;
      } else {
        /* DISTS */
        base = dbase;
        extra = dext;
        match = 0;
      }

      /* initialize opts for loop */
      huff = 0; /* starting code */
      sym = 0; /* starting code symbol */
      len = min; /* starting code length */
      next = table_index; /* current table to fill in */
      curr = root; /* current table index bits */
      drop = 0; /* current bits to drop from code for index */
      low = -1; /* trigger new sub-table when len > root */
      used = 1 << root; /* use root table entries */
      mask = used - 1; /* mask for comparing low */

      /* check available table space */
      if (type === LENS$1 && used > ENOUGH_LENS$1 || type === DISTS$1 && used > ENOUGH_DISTS$1) {
        return 1;
      }

      /* process all codes and make table entries */
      for (;;) {
        /* create table entry */
        here_bits = len - drop;
        if (work[sym] + 1 < match) {
          here_op = 0;
          here_val = work[sym];
        } else if (work[sym] >= match) {
          here_op = extra[work[sym] - match];
          here_val = base[work[sym] - match];
        } else {
          here_op = 32 + 64; /* end of block */
          here_val = 0;
        }

        /* replicate for those indices with low len bits equal to huff */
        incr = 1 << len - drop;
        fill = 1 << curr;
        min = fill; /* save offset to next table */
        do {
          fill -= incr;
          table[next + (huff >> drop) + fill] = here_bits << 24 | here_op << 16 | here_val | 0;
        } while (fill !== 0);

        /* backwards increment the len-bit code huff */
        incr = 1 << len - 1;
        while (huff & incr) {
          incr >>= 1;
        }
        if (incr !== 0) {
          huff &= incr - 1;
          huff += incr;
        } else {
          huff = 0;
        }

        /* go to next symbol, update count, len */
        sym++;
        if (--count[len] === 0) {
          if (len === max) {
            break;
          }
          len = lens[lens_index + work[sym]];
        }

        /* create new sub-table if needed */
        if (len > root && (huff & mask) !== low) {
          /* if first time, transition to sub-tables */
          if (drop === 0) {
            drop = root;
          }

          /* increment past last table */
          next += min; /* here min is 1 << curr */

          /* determine length of next table */
          curr = len - drop;
          left = 1 << curr;
          while (curr + drop < max) {
            left -= count[curr + drop];
            if (left <= 0) {
              break;
            }
            curr++;
            left <<= 1;
          }

          /* check for enough space */
          used += 1 << curr;
          if (type === LENS$1 && used > ENOUGH_LENS$1 || type === DISTS$1 && used > ENOUGH_DISTS$1) {
            return 1;
          }

          /* point entry in root table to sub-table */
          low = huff & mask;
          /*table.op[low] = curr;
          table.bits[low] = root;
          table.val[low] = next - opts.table_index;*/
          table[low] = root << 24 | curr << 16 | next - table_index | 0;
        }
      }

      /* fill in remaining table entry if code is incomplete (guaranteed to have
       at most one remaining entry, since if the code is incomplete, the
       maximum code length that was allowed to get this far is one bit) */
      if (huff !== 0) {
        //table.op[next + huff] = 64;            /* invalid code marker */
        //table.bits[next + huff] = len - drop;
        //table.val[next + huff] = 0;
        table[next + huff] = len - drop << 24 | 64 << 16 | 0;
      }

      /* set return parameters */
      //opts.table_index += used;
      opts.bits = root;
      return 0;
    };
    var inftrees = inflate_table;

    // (C) 1995-2013 Jean-loup Gailly and Mark Adler
    // (C) 2014-2017 Vitaly Puzrin and Andrey Tupitsin
    //
    // This software is provided 'as-is', without any express or implied
    // warranty. In no event will the authors be held liable for any damages
    // arising from the use of this software.
    //
    // Permission is granted to anyone to use this software for any purpose,
    // including commercial applications, and to alter it and redistribute it
    // freely, subject to the following restrictions:
    //
    // 1. The origin of this software must not be misrepresented; you must not
    //   claim that you wrote the original software. If you use this software
    //   in a product, an acknowledgment in the product documentation would be
    //   appreciated but is not required.
    // 2. Altered source versions must be plainly marked as such, and must not be
    //   misrepresented as being the original software.
    // 3. This notice may not be removed or altered from any source distribution.

    const CODES = 0;
    const LENS = 1;
    const DISTS = 2;

    /* Public constants ==========================================================*/
    /* ===========================================================================*/

    const {
      Z_FINISH: Z_FINISH$1,
      Z_BLOCK,
      Z_TREES,
      Z_OK: Z_OK$1,
      Z_STREAM_END: Z_STREAM_END$1,
      Z_NEED_DICT: Z_NEED_DICT$1,
      Z_STREAM_ERROR: Z_STREAM_ERROR$1,
      Z_DATA_ERROR: Z_DATA_ERROR$1,
      Z_MEM_ERROR: Z_MEM_ERROR$1,
      Z_BUF_ERROR,
      Z_DEFLATED
    } = constants$2;

    /* STATES ====================================================================*/
    /* ===========================================================================*/

    const HEAD = 16180; /* i: waiting for magic header */
    const FLAGS = 16181; /* i: waiting for method and flags (gzip) */
    const TIME = 16182; /* i: waiting for modification time (gzip) */
    const OS = 16183; /* i: waiting for extra flags and operating system (gzip) */
    const EXLEN = 16184; /* i: waiting for extra length (gzip) */
    const EXTRA = 16185; /* i: waiting for extra bytes (gzip) */
    const NAME = 16186; /* i: waiting for end of file name (gzip) */
    const COMMENT = 16187; /* i: waiting for end of comment (gzip) */
    const HCRC = 16188; /* i: waiting for header crc (gzip) */
    const DICTID = 16189; /* i: waiting for dictionary check value */
    const DICT = 16190; /* waiting for inflateSetDictionary() call */
    const TYPE = 16191; /* i: waiting for type bits, including last-flag bit */
    const TYPEDO = 16192; /* i: same, but skip check to exit inflate on new block */
    const STORED = 16193; /* i: waiting for stored size (length and complement) */
    const COPY_ = 16194; /* i/o: same as COPY below, but only first time in */
    const COPY = 16195; /* i/o: waiting for input or output to copy stored block */
    const TABLE = 16196; /* i: waiting for dynamic block table lengths */
    const LENLENS = 16197; /* i: waiting for code length code lengths */
    const CODELENS = 16198; /* i: waiting for length/lit and distance code lengths */
    const LEN_ = 16199; /* i: same as LEN below, but only first time in */
    const LEN = 16200; /* i: waiting for length/lit/eob code */
    const LENEXT = 16201; /* i: waiting for length extra bits */
    const DIST = 16202; /* i: waiting for distance code */
    const DISTEXT = 16203; /* i: waiting for distance extra bits */
    const MATCH = 16204; /* o: waiting for output space to copy string */
    const LIT = 16205; /* o: waiting for output space to write literal */
    const CHECK = 16206; /* i: waiting for 32-bit check value */
    const LENGTH = 16207; /* i: waiting for 32-bit length (gzip) */
    const DONE = 16208; /* finished check, done -- remain here until reset */
    const BAD = 16209; /* got a data error -- remain here until reset */
    const MEM = 16210; /* got an inflate() memory error -- remain here until reset */
    const SYNC = 16211; /* looking for synchronization bytes to restart inflate() */

    /* ===========================================================================*/

    const ENOUGH_LENS = 852;
    const ENOUGH_DISTS = 592;
    //const ENOUGH =  (ENOUGH_LENS+ENOUGH_DISTS);

    const MAX_WBITS = 15;
    /* 32K LZ77 window */
    const DEF_WBITS = MAX_WBITS;
    const zswap32 = q => {
      return (q >>> 24 & 0xff) + (q >>> 8 & 0xff00) + ((q & 0xff00) << 8) + ((q & 0xff) << 24);
    };
    function InflateState() {
      this.strm = null; /* pointer back to this zlib stream */
      this.mode = 0; /* current inflate mode */
      this.last = false; /* true if processing last block */
      this.wrap = 0; /* bit 0 true for zlib, bit 1 true for gzip,
                        bit 2 true to validate check value */
      this.havedict = false; /* true if dictionary provided */
      this.flags = 0; /* gzip header method and flags (0 if zlib), or
                         -1 if raw or no header yet */
      this.dmax = 0; /* zlib header max distance (INFLATE_STRICT) */
      this.check = 0; /* protected copy of check value */
      this.total = 0; /* protected copy of output count */
      // TODO: may be {}
      this.head = null; /* where to save gzip header information */

      /* sliding window */
      this.wbits = 0; /* log base 2 of requested window size */
      this.wsize = 0; /* window size or zero if not using window */
      this.whave = 0; /* valid bytes in the window */
      this.wnext = 0; /* window write index */
      this.window = null; /* allocated sliding window, if needed */

      /* bit accumulator */
      this.hold = 0; /* input bit accumulator */
      this.bits = 0; /* number of bits in "in" */

      /* for string and stored block copying */
      this.length = 0; /* literal or length of data to copy */
      this.offset = 0; /* distance back to copy string from */

      /* for table and code decoding */
      this.extra = 0; /* extra bits needed */

      /* fixed and dynamic code tables */
      this.lencode = null; /* starting table for length/literal codes */
      this.distcode = null; /* starting table for distance codes */
      this.lenbits = 0; /* index bits for lencode */
      this.distbits = 0; /* index bits for distcode */

      /* dynamic table building */
      this.ncode = 0; /* number of code length code lengths */
      this.nlen = 0; /* number of length code lengths */
      this.ndist = 0; /* number of distance code lengths */
      this.have = 0; /* number of code lengths in lens[] */
      this.next = null; /* next available space in codes[] */

      this.lens = new Uint16Array(320); /* temporary storage for code lengths */
      this.work = new Uint16Array(288); /* work area for code table building */

      /*
       because we don't have pointers in js, we use lencode and distcode directly
       as buffers so we don't need codes
      */
      //this.codes = new Int32Array(ENOUGH);       /* space for code tables */
      this.lendyn = null; /* dynamic table for length/literal codes (JS specific) */
      this.distdyn = null; /* dynamic table for distance codes (JS specific) */
      this.sane = 0; /* if false, allow invalid distance too far */
      this.back = 0; /* bits back of last unprocessed length/lit */
      this.was = 0; /* initial length of match */
    }
    const inflateStateCheck = strm => {
      if (!strm) {
        return 1;
      }
      const state = strm.state;
      if (!state || state.strm !== strm || state.mode < HEAD || state.mode > SYNC) {
        return 1;
      }
      return 0;
    };
    const inflateResetKeep = strm => {
      if (inflateStateCheck(strm)) {
        return Z_STREAM_ERROR$1;
      }
      const state = strm.state;
      strm.total_in = strm.total_out = state.total = 0;
      strm.msg = ''; /*Z_NULL*/
      if (state.wrap) {
        /* to support ill-conceived Java test suite */
        strm.adler = state.wrap & 1;
      }
      state.mode = HEAD;
      state.last = 0;
      state.havedict = 0;
      state.flags = -1;
      state.dmax = 32768;
      state.head = null /*Z_NULL*/;
      state.hold = 0;
      state.bits = 0;
      //state.lencode = state.distcode = state.next = state.codes;
      state.lencode = state.lendyn = new Int32Array(ENOUGH_LENS);
      state.distcode = state.distdyn = new Int32Array(ENOUGH_DISTS);
      state.sane = 1;
      state.back = -1;
      //Tracev((stderr, "inflate: reset\n"));
      return Z_OK$1;
    };
    const inflateReset = strm => {
      if (inflateStateCheck(strm)) {
        return Z_STREAM_ERROR$1;
      }
      const state = strm.state;
      state.wsize = 0;
      state.whave = 0;
      state.wnext = 0;
      return inflateResetKeep(strm);
    };
    const inflateReset2 = (strm, windowBits) => {
      let wrap;

      /* get the state */
      if (inflateStateCheck(strm)) {
        return Z_STREAM_ERROR$1;
      }
      const state = strm.state;

      /* extract wrap request from windowBits parameter */
      if (windowBits < 0) {
        wrap = 0;
        windowBits = -windowBits;
      } else {
        wrap = (windowBits >> 4) + 5;
        if (windowBits < 48) {
          windowBits &= 15;
        }
      }

      /* set number of window bits, free window if different */
      if (windowBits && (windowBits < 8 || windowBits > 15)) {
        return Z_STREAM_ERROR$1;
      }
      if (state.window !== null && state.wbits !== windowBits) {
        state.window = null;
      }

      /* update state and reset the rest of it */
      state.wrap = wrap;
      state.wbits = windowBits;
      return inflateReset(strm);
    };
    const inflateInit2 = (strm, windowBits) => {
      if (!strm) {
        return Z_STREAM_ERROR$1;
      }
      //strm.msg = Z_NULL;                 /* in case we return an error */

      const state = new InflateState();

      //if (state === Z_NULL) return Z_MEM_ERROR;
      //Tracev((stderr, "inflate: allocated\n"));
      strm.state = state;
      state.strm = strm;
      state.window = null /*Z_NULL*/;
      state.mode = HEAD; /* to pass state test in inflateReset2() */
      const ret = inflateReset2(strm, windowBits);
      if (ret !== Z_OK$1) {
        strm.state = null /*Z_NULL*/;
      }
      return ret;
    };
    const inflateInit = strm => {
      return inflateInit2(strm, DEF_WBITS);
    };

    /*
     Return state with length and distance decoding tables and index sizes set to
     fixed code decoding.  Normally this returns fixed tables from inffixed.h.
     If BUILDFIXED is defined, then instead this routine builds the tables the
     first time it's called, and returns those tables the first time and
     thereafter.  This reduces the size of the code by about 2K bytes, in
     exchange for a little execution time.  However, BUILDFIXED should not be
     used for threaded applications, since the rewriting of the tables and virgin
     may not be thread-safe.
     */
    let virgin = true;
    let lenfix, distfix; // We have no pointers in JS, so keep tables separate

    const fixedtables = state => {
      /* build fixed huffman tables if first call (may not be thread safe) */
      if (virgin) {
        lenfix = new Int32Array(512);
        distfix = new Int32Array(32);

        /* literal/length table */
        let sym = 0;
        while (sym < 144) {
          state.lens[sym++] = 8;
        }
        while (sym < 256) {
          state.lens[sym++] = 9;
        }
        while (sym < 280) {
          state.lens[sym++] = 7;
        }
        while (sym < 288) {
          state.lens[sym++] = 8;
        }
        inftrees(LENS, state.lens, 0, 288, lenfix, 0, state.work, {
          bits: 9
        });

        /* distance table */
        sym = 0;
        while (sym < 32) {
          state.lens[sym++] = 5;
        }
        inftrees(DISTS, state.lens, 0, 32, distfix, 0, state.work, {
          bits: 5
        });

        /* do this just once */
        virgin = false;
      }
      state.lencode = lenfix;
      state.lenbits = 9;
      state.distcode = distfix;
      state.distbits = 5;
    };

    /*
     Update the window with the last wsize (normally 32K) bytes written before
     returning.  If window does not exist yet, create it.  This is only called
     when a window is already in use, or when output has been written during this
     inflate call, but the end of the deflate stream has not been reached yet.
     It is also called to create a window for dictionary data when a dictionary
     is loaded.

     Providing output buffers larger than 32K to inflate() should provide a speed
     advantage, since only the last 32K of output is copied to the sliding window
     upon return from inflate(), and since all distances after the first 32K of
     output will fall in the output data, making match copies simpler and faster.
     The advantage may be dependent on the size of the processor's data caches.
     */
    const updatewindow = (strm, src, end, copy) => {
      let dist;
      const state = strm.state;

      /* if it hasn't been done already, allocate space for the window */
      if (state.window === null) {
        state.wsize = 1 << state.wbits;
        state.wnext = 0;
        state.whave = 0;
        state.window = new Uint8Array(state.wsize);
      }

      /* copy state->wsize or less output bytes into the circular window */
      if (copy >= state.wsize) {
        state.window.set(src.subarray(end - state.wsize, end), 0);
        state.wnext = 0;
        state.whave = state.wsize;
      } else {
        dist = state.wsize - state.wnext;
        if (dist > copy) {
          dist = copy;
        }
        //zmemcpy(state->window + state->wnext, end - copy, dist);
        state.window.set(src.subarray(end - copy, end - copy + dist), state.wnext);
        copy -= dist;
        if (copy) {
          //zmemcpy(state->window, end - copy, copy);
          state.window.set(src.subarray(end - copy, end), 0);
          state.wnext = copy;
          state.whave = state.wsize;
        } else {
          state.wnext += dist;
          if (state.wnext === state.wsize) {
            state.wnext = 0;
          }
          if (state.whave < state.wsize) {
            state.whave += dist;
          }
        }
      }
      return 0;
    };
    const inflate$2 = (strm, flush) => {
      let state;
      let input, output; // input/output buffers
      let next; /* next input INDEX */
      let put; /* next output INDEX */
      let have, left; /* available input and output */
      let hold; /* bit buffer */
      let bits; /* bits in bit buffer */
      let _in, _out; /* save starting available input and output */
      let copy; /* number of stored or match bytes to copy */
      let from; /* where to copy match bytes from */
      let from_source;
      let here = 0; /* current decoding table entry */
      let here_bits, here_op, here_val; // paked "here" denormalized (JS specific)
      //let last;                   /* parent table entry */
      let last_bits, last_op, last_val; // paked "last" denormalized (JS specific)
      let len; /* length to copy for repeats, bits to drop */
      let ret; /* return code */
      const hbuf = new Uint8Array(4); /* buffer for gzip header crc calculation */
      let opts;
      let n; // temporary variable for NEED_BITS

      const order = /* permutation of code lengths */
      new Uint8Array([16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15]);
      if (inflateStateCheck(strm) || !strm.output || !strm.input && strm.avail_in !== 0) {
        return Z_STREAM_ERROR$1;
      }
      state = strm.state;
      if (state.mode === TYPE) {
        state.mode = TYPEDO;
      } /* skip check */

      //--- LOAD() ---
      put = strm.next_out;
      output = strm.output;
      left = strm.avail_out;
      next = strm.next_in;
      input = strm.input;
      have = strm.avail_in;
      hold = state.hold;
      bits = state.bits;
      //---

      _in = have;
      _out = left;
      ret = Z_OK$1;
      inf_leave:
      // goto emulation
      for (;;) {
        switch (state.mode) {
          case HEAD:
            if (state.wrap === 0) {
              state.mode = TYPEDO;
              break;
            }
            //=== NEEDBITS(16);
            while (bits < 16) {
              if (have === 0) {
                break inf_leave;
              }
              have--;
              hold += input[next++] << bits;
              bits += 8;
            }
            //===//
            if (state.wrap & 2 && hold === 0x8b1f) {
              /* gzip header */
              if (state.wbits === 0) {
                state.wbits = 15;
              }
              state.check = 0 /*crc32(0L, Z_NULL, 0)*/;
              //=== CRC2(state.check, hold);
              hbuf[0] = hold & 0xff;
              hbuf[1] = hold >>> 8 & 0xff;
              state.check = crc32_1(state.check, hbuf, 2, 0);
              //===//

              //=== INITBITS();
              hold = 0;
              bits = 0;
              //===//
              state.mode = FLAGS;
              break;
            }
            if (state.head) {
              state.head.done = false;
            }
            if (!(state.wrap & 1) || /* check if zlib header allowed */
            (((hold & 0xff /*BITS(8)*/) << 8) + (hold >> 8)) % 31) {
              strm.msg = 'incorrect header check';
              state.mode = BAD;
              break;
            }
            if ((hold & 0x0f /*BITS(4)*/) !== Z_DEFLATED) {
              strm.msg = 'unknown compression method';
              state.mode = BAD;
              break;
            }
            //--- DROPBITS(4) ---//
            hold >>>= 4;
            bits -= 4;
            //---//
            len = (hold & 0x0f /*BITS(4)*/) + 8;
            if (state.wbits === 0) {
              state.wbits = len;
            }
            if (len > 15 || len > state.wbits) {
              strm.msg = 'invalid window size';
              state.mode = BAD;
              break;
            }

            // !!! pako patch. Force use `options.windowBits` if passed.
            // Required to always use max window size by default.
            state.dmax = 1 << state.wbits;
            //state.dmax = 1 << len;

            state.flags = 0; /* indicate zlib header */
            //Tracev((stderr, "inflate:   zlib header ok\n"));
            strm.adler = state.check = 1 /*adler32(0L, Z_NULL, 0)*/;
            state.mode = hold & 0x200 ? DICTID : TYPE;
            //=== INITBITS();
            hold = 0;
            bits = 0;
            //===//
            break;
          case FLAGS:
            //=== NEEDBITS(16); */
            while (bits < 16) {
              if (have === 0) {
                break inf_leave;
              }
              have--;
              hold += input[next++] << bits;
              bits += 8;
            }
            //===//
            state.flags = hold;
            if ((state.flags & 0xff) !== Z_DEFLATED) {
              strm.msg = 'unknown compression method';
              state.mode = BAD;
              break;
            }
            if (state.flags & 0xe000) {
              strm.msg = 'unknown header flags set';
              state.mode = BAD;
              break;
            }
            if (state.head) {
              state.head.text = hold >> 8 & 1;
            }
            if (state.flags & 0x0200 && state.wrap & 4) {
              //=== CRC2(state.check, hold);
              hbuf[0] = hold & 0xff;
              hbuf[1] = hold >>> 8 & 0xff;
              state.check = crc32_1(state.check, hbuf, 2, 0);
              //===//
            }
            //=== INITBITS();
            hold = 0;
            bits = 0;
            //===//
            state.mode = TIME;
          /* falls through */
          case TIME:
            //=== NEEDBITS(32); */
            while (bits < 32) {
              if (have === 0) {
                break inf_leave;
              }
              have--;
              hold += input[next++] << bits;
              bits += 8;
            }
            //===//
            if (state.head) {
              state.head.time = hold;
            }
            if (state.flags & 0x0200 && state.wrap & 4) {
              //=== CRC4(state.check, hold)
              hbuf[0] = hold & 0xff;
              hbuf[1] = hold >>> 8 & 0xff;
              hbuf[2] = hold >>> 16 & 0xff;
              hbuf[3] = hold >>> 24 & 0xff;
              state.check = crc32_1(state.check, hbuf, 4, 0);
              //===
            }
            //=== INITBITS();
            hold = 0;
            bits = 0;
            //===//
            state.mode = OS;
          /* falls through */
          case OS:
            //=== NEEDBITS(16); */
            while (bits < 16) {
              if (have === 0) {
                break inf_leave;
              }
              have--;
              hold += input[next++] << bits;
              bits += 8;
            }
            //===//
            if (state.head) {
              state.head.xflags = hold & 0xff;
              state.head.os = hold >> 8;
            }
            if (state.flags & 0x0200 && state.wrap & 4) {
              //=== CRC2(state.check, hold);
              hbuf[0] = hold & 0xff;
              hbuf[1] = hold >>> 8 & 0xff;
              state.check = crc32_1(state.check, hbuf, 2, 0);
              //===//
            }
            //=== INITBITS();
            hold = 0;
            bits = 0;
            //===//
            state.mode = EXLEN;
          /* falls through */
          case EXLEN:
            if (state.flags & 0x0400) {
              //=== NEEDBITS(16); */
              while (bits < 16) {
                if (have === 0) {
                  break inf_leave;
                }
                have--;
                hold += input[next++] << bits;
                bits += 8;
              }
              //===//
              state.length = hold;
              if (state.head) {
                state.head.extra_len = hold;
              }
              if (state.flags & 0x0200 && state.wrap & 4) {
                //=== CRC2(state.check, hold);
                hbuf[0] = hold & 0xff;
                hbuf[1] = hold >>> 8 & 0xff;
                state.check = crc32_1(state.check, hbuf, 2, 0);
                //===//
              }
              //=== INITBITS();
              hold = 0;
              bits = 0;
              //===//
            } else if (state.head) {
              state.head.extra = null /*Z_NULL*/;
            }
            state.mode = EXTRA;
          /* falls through */
          case EXTRA:
            if (state.flags & 0x0400) {
              copy = state.length;
              if (copy > have) {
                copy = have;
              }
              if (copy) {
                if (state.head) {
                  len = state.head.extra_len - state.length;
                  if (!state.head.extra) {
                    // Use untyped array for more convenient processing later
                    state.head.extra = new Uint8Array(state.head.extra_len);
                  }
                  state.head.extra.set(input.subarray(next,
                  // extra field is limited to 65536 bytes
                  // - no need for additional size check
                  next + copy), /*len + copy > state.head.extra_max - len ? state.head.extra_max : copy,*/
                  len);
                  //zmemcpy(state.head.extra + len, next,
                  //        len + copy > state.head.extra_max ?
                  //        state.head.extra_max - len : copy);
                }
                if (state.flags & 0x0200 && state.wrap & 4) {
                  state.check = crc32_1(state.check, input, copy, next);
                }
                have -= copy;
                next += copy;
                state.length -= copy;
              }
              if (state.length) {
                break inf_leave;
              }
            }
            state.length = 0;
            state.mode = NAME;
          /* falls through */
          case NAME:
            if (state.flags & 0x0800) {
              if (have === 0) {
                break inf_leave;
              }
              copy = 0;
              do {
                // TODO: 2 or 1 bytes?
                len = input[next + copy++];
                /* use constant limit because in js we should not preallocate memory */
                if (state.head && len && state.length < 65536 /*state.head.name_max*/) {
                  state.head.name += String.fromCharCode(len);
                }
              } while (len && copy < have);
              if (state.flags & 0x0200 && state.wrap & 4) {
                state.check = crc32_1(state.check, input, copy, next);
              }
              have -= copy;
              next += copy;
              if (len) {
                break inf_leave;
              }
            } else if (state.head) {
              state.head.name = null;
            }
            state.length = 0;
            state.mode = COMMENT;
          /* falls through */
          case COMMENT:
            if (state.flags & 0x1000) {
              if (have === 0) {
                break inf_leave;
              }
              copy = 0;
              do {
                len = input[next + copy++];
                /* use constant limit because in js we should not preallocate memory */
                if (state.head && len && state.length < 65536 /*state.head.comm_max*/) {
                  state.head.comment += String.fromCharCode(len);
                }
              } while (len && copy < have);
              if (state.flags & 0x0200 && state.wrap & 4) {
                state.check = crc32_1(state.check, input, copy, next);
              }
              have -= copy;
              next += copy;
              if (len) {
                break inf_leave;
              }
            } else if (state.head) {
              state.head.comment = null;
            }
            state.mode = HCRC;
          /* falls through */
          case HCRC:
            if (state.flags & 0x0200) {
              //=== NEEDBITS(16); */
              while (bits < 16) {
                if (have === 0) {
                  break inf_leave;
                }
                have--;
                hold += input[next++] << bits;
                bits += 8;
              }
              //===//
              if (state.wrap & 4 && hold !== (state.check & 0xffff)) {
                strm.msg = 'header crc mismatch';
                state.mode = BAD;
                break;
              }
              //=== INITBITS();
              hold = 0;
              bits = 0;
              //===//
            }
            if (state.head) {
              state.head.hcrc = state.flags >> 9 & 1;
              state.head.done = true;
            }
            strm.adler = state.check = 0;
            state.mode = TYPE;
            break;
          case DICTID:
            //=== NEEDBITS(32); */
            while (bits < 32) {
              if (have === 0) {
                break inf_leave;
              }
              have--;
              hold += input[next++] << bits;
              bits += 8;
            }
            //===//
            strm.adler = state.check = zswap32(hold);
            //=== INITBITS();
            hold = 0;
            bits = 0;
            //===//
            state.mode = DICT;
          /* falls through */
          case DICT:
            if (state.havedict === 0) {
              //--- RESTORE() ---
              strm.next_out = put;
              strm.avail_out = left;
              strm.next_in = next;
              strm.avail_in = have;
              state.hold = hold;
              state.bits = bits;
              //---
              return Z_NEED_DICT$1;
            }
            strm.adler = state.check = 1 /*adler32(0L, Z_NULL, 0)*/;
            state.mode = TYPE;
          /* falls through */
          case TYPE:
            if (flush === Z_BLOCK || flush === Z_TREES) {
              break inf_leave;
            }
          /* falls through */
          case TYPEDO:
            if (state.last) {
              //--- BYTEBITS() ---//
              hold >>>= bits & 7;
              bits -= bits & 7;
              //---//
              state.mode = CHECK;
              break;
            }
            //=== NEEDBITS(3); */
            while (bits < 3) {
              if (have === 0) {
                break inf_leave;
              }
              have--;
              hold += input[next++] << bits;
              bits += 8;
            }
            //===//
            state.last = hold & 0x01 /*BITS(1)*/;
            //--- DROPBITS(1) ---//
            hold >>>= 1;
            bits -= 1;
            //---//

            switch (hold & 0x03 /*BITS(2)*/) {
              case 0:
                /* stored block */
                //Tracev((stderr, "inflate:     stored block%s\n",
                //        state.last ? " (last)" : ""));
                state.mode = STORED;
                break;
              case 1:
                /* fixed block */
                fixedtables(state);
                //Tracev((stderr, "inflate:     fixed codes block%s\n",
                //        state.last ? " (last)" : ""));
                state.mode = LEN_; /* decode codes */
                if (flush === Z_TREES) {
                  //--- DROPBITS(2) ---//
                  hold >>>= 2;
                  bits -= 2;
                  //---//
                  break inf_leave;
                }
                break;
              case 2:
                /* dynamic block */
                //Tracev((stderr, "inflate:     dynamic codes block%s\n",
                //        state.last ? " (last)" : ""));
                state.mode = TABLE;
                break;
              case 3:
                strm.msg = 'invalid block type';
                state.mode = BAD;
            }
            //--- DROPBITS(2) ---//
            hold >>>= 2;
            bits -= 2;
            //---//
            break;
          case STORED:
            //--- BYTEBITS() ---// /* go to byte boundary */
            hold >>>= bits & 7;
            bits -= bits & 7;
            //---//
            //=== NEEDBITS(32); */
            while (bits < 32) {
              if (have === 0) {
                break inf_leave;
              }
              have--;
              hold += input[next++] << bits;
              bits += 8;
            }
            //===//
            if ((hold & 0xffff) !== (hold >>> 16 ^ 0xffff)) {
              strm.msg = 'invalid stored block lengths';
              state.mode = BAD;
              break;
            }
            state.length = hold & 0xffff;
            //Tracev((stderr, "inflate:       stored length %u\n",
            //        state.length));
            //=== INITBITS();
            hold = 0;
            bits = 0;
            //===//
            state.mode = COPY_;
            if (flush === Z_TREES) {
              break inf_leave;
            }
          /* falls through */
          case COPY_:
            state.mode = COPY;
          /* falls through */
          case COPY:
            copy = state.length;
            if (copy) {
              if (copy > have) {
                copy = have;
              }
              if (copy > left) {
                copy = left;
              }
              if (copy === 0) {
                break inf_leave;
              }
              //--- zmemcpy(put, next, copy); ---
              output.set(input.subarray(next, next + copy), put);
              //---//
              have -= copy;
              next += copy;
              left -= copy;
              put += copy;
              state.length -= copy;
              break;
            }
            //Tracev((stderr, "inflate:       stored end\n"));
            state.mode = TYPE;
            break;
          case TABLE:
            //=== NEEDBITS(14); */
            while (bits < 14) {
              if (have === 0) {
                break inf_leave;
              }
              have--;
              hold += input[next++] << bits;
              bits += 8;
            }
            //===//
            state.nlen = (hold & 0x1f /*BITS(5)*/) + 257;
            //--- DROPBITS(5) ---//
            hold >>>= 5;
            bits -= 5;
            //---//
            state.ndist = (hold & 0x1f /*BITS(5)*/) + 1;
            //--- DROPBITS(5) ---//
            hold >>>= 5;
            bits -= 5;
            //---//
            state.ncode = (hold & 0x0f /*BITS(4)*/) + 4;
            //--- DROPBITS(4) ---//
            hold >>>= 4;
            bits -= 4;
            //---//
            //#ifndef PKZIP_BUG_WORKAROUND
            if (state.nlen > 286 || state.ndist > 30) {
              strm.msg = 'too many length or distance symbols';
              state.mode = BAD;
              break;
            }
            //#endif
            //Tracev((stderr, "inflate:       table sizes ok\n"));
            state.have = 0;
            state.mode = LENLENS;
          /* falls through */
          case LENLENS:
            while (state.have < state.ncode) {
              //=== NEEDBITS(3);
              while (bits < 3) {
                if (have === 0) {
                  break inf_leave;
                }
                have--;
                hold += input[next++] << bits;
                bits += 8;
              }
              //===//
              state.lens[order[state.have++]] = hold & 0x07; //BITS(3);
              //--- DROPBITS(3) ---//
              hold >>>= 3;
              bits -= 3;
              //---//
            }
            while (state.have < 19) {
              state.lens[order[state.have++]] = 0;
            }
            // We have separate tables & no pointers. 2 commented lines below not needed.
            //state.next = state.codes;
            //state.lencode = state.next;
            // Switch to use dynamic table
            state.lencode = state.lendyn;
            state.lenbits = 7;
            opts = {
              bits: state.lenbits
            };
            ret = inftrees(CODES, state.lens, 0, 19, state.lencode, 0, state.work, opts);
            state.lenbits = opts.bits;
            if (ret) {
              strm.msg = 'invalid code lengths set';
              state.mode = BAD;
              break;
            }
            //Tracev((stderr, "inflate:       code lengths ok\n"));
            state.have = 0;
            state.mode = CODELENS;
          /* falls through */
          case CODELENS:
            while (state.have < state.nlen + state.ndist) {
              for (;;) {
                here = state.lencode[hold & (1 << state.lenbits) - 1]; /*BITS(state.lenbits)*/
                here_bits = here >>> 24;
                here_op = here >>> 16 & 0xff;
                here_val = here & 0xffff;
                if (here_bits <= bits) {
                  break;
                }
                //--- PULLBYTE() ---//
                if (have === 0) {
                  break inf_leave;
                }
                have--;
                hold += input[next++] << bits;
                bits += 8;
                //---//
              }
              if (here_val < 16) {
                //--- DROPBITS(here.bits) ---//
                hold >>>= here_bits;
                bits -= here_bits;
                //---//
                state.lens[state.have++] = here_val;
              } else {
                if (here_val === 16) {
                  //=== NEEDBITS(here.bits + 2);
                  n = here_bits + 2;
                  while (bits < n) {
                    if (have === 0) {
                      break inf_leave;
                    }
                    have--;
                    hold += input[next++] << bits;
                    bits += 8;
                  }
                  //===//
                  //--- DROPBITS(here.bits) ---//
                  hold >>>= here_bits;
                  bits -= here_bits;
                  //---//
                  if (state.have === 0) {
                    strm.msg = 'invalid bit length repeat';
                    state.mode = BAD;
                    break;
                  }
                  len = state.lens[state.have - 1];
                  copy = 3 + (hold & 0x03); //BITS(2);
                  //--- DROPBITS(2) ---//
                  hold >>>= 2;
                  bits -= 2;
                  //---//
                } else if (here_val === 17) {
                  //=== NEEDBITS(here.bits + 3);
                  n = here_bits + 3;
                  while (bits < n) {
                    if (have === 0) {
                      break inf_leave;
                    }
                    have--;
                    hold += input[next++] << bits;
                    bits += 8;
                  }
                  //===//
                  //--- DROPBITS(here.bits) ---//
                  hold >>>= here_bits;
                  bits -= here_bits;
                  //---//
                  len = 0;
                  copy = 3 + (hold & 0x07); //BITS(3);
                  //--- DROPBITS(3) ---//
                  hold >>>= 3;
                  bits -= 3;
                  //---//
                } else {
                  //=== NEEDBITS(here.bits + 7);
                  n = here_bits + 7;
                  while (bits < n) {
                    if (have === 0) {
                      break inf_leave;
                    }
                    have--;
                    hold += input[next++] << bits;
                    bits += 8;
                  }
                  //===//
                  //--- DROPBITS(here.bits) ---//
                  hold >>>= here_bits;
                  bits -= here_bits;
                  //---//
                  len = 0;
                  copy = 11 + (hold & 0x7f); //BITS(7);
                  //--- DROPBITS(7) ---//
                  hold >>>= 7;
                  bits -= 7;
                  //---//
                }
                if (state.have + copy > state.nlen + state.ndist) {
                  strm.msg = 'invalid bit length repeat';
                  state.mode = BAD;
                  break;
                }
                while (copy--) {
                  state.lens[state.have++] = len;
                }
              }
            }

            /* handle error breaks in while */
            if (state.mode === BAD) {
              break;
            }

            /* check for end-of-block code (better have one) */
            if (state.lens[256] === 0) {
              strm.msg = 'invalid code -- missing end-of-block';
              state.mode = BAD;
              break;
            }

            /* build code tables -- note: do not change the lenbits or distbits
               values here (9 and 6) without reading the comments in inftrees.h
               concerning the ENOUGH constants, which depend on those values */
            state.lenbits = 9;
            opts = {
              bits: state.lenbits
            };
            ret = inftrees(LENS, state.lens, 0, state.nlen, state.lencode, 0, state.work, opts);
            // We have separate tables & no pointers. 2 commented lines below not needed.
            // state.next_index = opts.table_index;
            state.lenbits = opts.bits;
            // state.lencode = state.next;

            if (ret) {
              strm.msg = 'invalid literal/lengths set';
              state.mode = BAD;
              break;
            }
            state.distbits = 6;
            //state.distcode.copy(state.codes);
            // Switch to use dynamic table
            state.distcode = state.distdyn;
            opts = {
              bits: state.distbits
            };
            ret = inftrees(DISTS, state.lens, state.nlen, state.ndist, state.distcode, 0, state.work, opts);
            // We have separate tables & no pointers. 2 commented lines below not needed.
            // state.next_index = opts.table_index;
            state.distbits = opts.bits;
            // state.distcode = state.next;

            if (ret) {
              strm.msg = 'invalid distances set';
              state.mode = BAD;
              break;
            }
            //Tracev((stderr, 'inflate:       codes ok\n'));
            state.mode = LEN_;
            if (flush === Z_TREES) {
              break inf_leave;
            }
          /* falls through */
          case LEN_:
            state.mode = LEN;
          /* falls through */
          case LEN:
            if (have >= 6 && left >= 258) {
              //--- RESTORE() ---
              strm.next_out = put;
              strm.avail_out = left;
              strm.next_in = next;
              strm.avail_in = have;
              state.hold = hold;
              state.bits = bits;
              //---
              inffast(strm, _out);
              //--- LOAD() ---
              put = strm.next_out;
              output = strm.output;
              left = strm.avail_out;
              next = strm.next_in;
              input = strm.input;
              have = strm.avail_in;
              hold = state.hold;
              bits = state.bits;
              //---

              if (state.mode === TYPE) {
                state.back = -1;
              }
              break;
            }
            state.back = 0;
            for (;;) {
              here = state.lencode[hold & (1 << state.lenbits) - 1]; /*BITS(state.lenbits)*/
              here_bits = here >>> 24;
              here_op = here >>> 16 & 0xff;
              here_val = here & 0xffff;
              if (here_bits <= bits) {
                break;
              }
              //--- PULLBYTE() ---//
              if (have === 0) {
                break inf_leave;
              }
              have--;
              hold += input[next++] << bits;
              bits += 8;
              //---//
            }
            if (here_op && (here_op & 0xf0) === 0) {
              last_bits = here_bits;
              last_op = here_op;
              last_val = here_val;
              for (;;) {
                here = state.lencode[last_val + ((hold & (1 << last_bits + last_op) - 1 /*BITS(last.bits + last.op)*/) >> last_bits)];
                here_bits = here >>> 24;
                here_op = here >>> 16 & 0xff;
                here_val = here & 0xffff;
                if (last_bits + here_bits <= bits) {
                  break;
                }
                //--- PULLBYTE() ---//
                if (have === 0) {
                  break inf_leave;
                }
                have--;
                hold += input[next++] << bits;
                bits += 8;
                //---//
              }
              //--- DROPBITS(last.bits) ---//
              hold >>>= last_bits;
              bits -= last_bits;
              //---//
              state.back += last_bits;
            }
            //--- DROPBITS(here.bits) ---//
            hold >>>= here_bits;
            bits -= here_bits;
            //---//
            state.back += here_bits;
            state.length = here_val;
            if (here_op === 0) {
              //Tracevv((stderr, here.val >= 0x20 && here.val < 0x7f ?
              //        "inflate:         literal '%c'\n" :
              //        "inflate:         literal 0x%02x\n", here.val));
              state.mode = LIT;
              break;
            }
            if (here_op & 32) {
              //Tracevv((stderr, "inflate:         end of block\n"));
              state.back = -1;
              state.mode = TYPE;
              break;
            }
            if (here_op & 64) {
              strm.msg = 'invalid literal/length code';
              state.mode = BAD;
              break;
            }
            state.extra = here_op & 15;
            state.mode = LENEXT;
          /* falls through */
          case LENEXT:
            if (state.extra) {
              //=== NEEDBITS(state.extra);
              n = state.extra;
              while (bits < n) {
                if (have === 0) {
                  break inf_leave;
                }
                have--;
                hold += input[next++] << bits;
                bits += 8;
              }
              //===//
              state.length += hold & (1 << state.extra) - 1 /*BITS(state.extra)*/;
              //--- DROPBITS(state.extra) ---//
              hold >>>= state.extra;
              bits -= state.extra;
              //---//
              state.back += state.extra;
            }
            //Tracevv((stderr, "inflate:         length %u\n", state.length));
            state.was = state.length;
            state.mode = DIST;
          /* falls through */
          case DIST:
            for (;;) {
              here = state.distcode[hold & (1 << state.distbits) - 1]; /*BITS(state.distbits)*/
              here_bits = here >>> 24;
              here_op = here >>> 16 & 0xff;
              here_val = here & 0xffff;
              if (here_bits <= bits) {
                break;
              }
              //--- PULLBYTE() ---//
              if (have === 0) {
                break inf_leave;
              }
              have--;
              hold += input[next++] << bits;
              bits += 8;
              //---//
            }
            if ((here_op & 0xf0) === 0) {
              last_bits = here_bits;
              last_op = here_op;
              last_val = here_val;
              for (;;) {
                here = state.distcode[last_val + ((hold & (1 << last_bits + last_op) - 1 /*BITS(last.bits + last.op)*/) >> last_bits)];
                here_bits = here >>> 24;
                here_op = here >>> 16 & 0xff;
                here_val = here & 0xffff;
                if (last_bits + here_bits <= bits) {
                  break;
                }
                //--- PULLBYTE() ---//
                if (have === 0) {
                  break inf_leave;
                }
                have--;
                hold += input[next++] << bits;
                bits += 8;
                //---//
              }
              //--- DROPBITS(last.bits) ---//
              hold >>>= last_bits;
              bits -= last_bits;
              //---//
              state.back += last_bits;
            }
            //--- DROPBITS(here.bits) ---//
            hold >>>= here_bits;
            bits -= here_bits;
            //---//
            state.back += here_bits;
            if (here_op & 64) {
              strm.msg = 'invalid distance code';
              state.mode = BAD;
              break;
            }
            state.offset = here_val;
            state.extra = here_op & 15;
            state.mode = DISTEXT;
          /* falls through */
          case DISTEXT:
            if (state.extra) {
              //=== NEEDBITS(state.extra);
              n = state.extra;
              while (bits < n) {
                if (have === 0) {
                  break inf_leave;
                }
                have--;
                hold += input[next++] << bits;
                bits += 8;
              }
              //===//
              state.offset += hold & (1 << state.extra) - 1 /*BITS(state.extra)*/;
              //--- DROPBITS(state.extra) ---//
              hold >>>= state.extra;
              bits -= state.extra;
              //---//
              state.back += state.extra;
            }
            //#ifdef INFLATE_STRICT
            if (state.offset > state.dmax) {
              strm.msg = 'invalid distance too far back';
              state.mode = BAD;
              break;
            }
            //#endif
            //Tracevv((stderr, "inflate:         distance %u\n", state.offset));
            state.mode = MATCH;
          /* falls through */
          case MATCH:
            if (left === 0) {
              break inf_leave;
            }
            copy = _out - left;
            if (state.offset > copy) {
              /* copy from window */
              copy = state.offset - copy;
              if (copy > state.whave) {
                if (state.sane) {
                  strm.msg = 'invalid distance too far back';
                  state.mode = BAD;
                  break;
                }
                // (!) This block is disabled in zlib defaults,
                // don't enable it for binary compatibility
                //#ifdef INFLATE_ALLOW_INVALID_DISTANCE_TOOFAR_ARRR
                //          Trace((stderr, "inflate.c too far\n"));
                //          copy -= state.whave;
                //          if (copy > state.length) { copy = state.length; }
                //          if (copy > left) { copy = left; }
                //          left -= copy;
                //          state.length -= copy;
                //          do {
                //            output[put++] = 0;
                //          } while (--copy);
                //          if (state.length === 0) { state.mode = LEN; }
                //          break;
                //#endif
              }
              if (copy > state.wnext) {
                copy -= state.wnext;
                from = state.wsize - copy;
              } else {
                from = state.wnext - copy;
              }
              if (copy > state.length) {
                copy = state.length;
              }
              from_source = state.window;
            } else {
              /* copy from output */
              from_source = output;
              from = put - state.offset;
              copy = state.length;
            }
            if (copy > left) {
              copy = left;
            }
            left -= copy;
            state.length -= copy;
            do {
              output[put++] = from_source[from++];
            } while (--copy);
            if (state.length === 0) {
              state.mode = LEN;
            }
            break;
          case LIT:
            if (left === 0) {
              break inf_leave;
            }
            output[put++] = state.length;
            left--;
            state.mode = LEN;
            break;
          case CHECK:
            if (state.wrap) {
              //=== NEEDBITS(32);
              while (bits < 32) {
                if (have === 0) {
                  break inf_leave;
                }
                have--;
                // Use '|' instead of '+' to make sure that result is signed
                hold |= input[next++] << bits;
                bits += 8;
              }
              //===//
              _out -= left;
              strm.total_out += _out;
              state.total += _out;
              if (state.wrap & 4 && _out) {
                strm.adler = state.check = /*UPDATE_CHECK(state.check, put - _out, _out);*/
                state.flags ? crc32_1(state.check, output, _out, put - _out) : adler32_1(state.check, output, _out, put - _out);
              }
              _out = left;
              // NB: crc32 stored as signed 32-bit int, zswap32 returns signed too
              if (state.wrap & 4 && (state.flags ? hold : zswap32(hold)) !== state.check) {
                strm.msg = 'incorrect data check';
                state.mode = BAD;
                break;
              }
              //=== INITBITS();
              hold = 0;
              bits = 0;
              //===//
              //Tracev((stderr, "inflate:   check matches trailer\n"));
            }
            state.mode = LENGTH;
          /* falls through */
          case LENGTH:
            if (state.wrap && state.flags) {
              //=== NEEDBITS(32);
              while (bits < 32) {
                if (have === 0) {
                  break inf_leave;
                }
                have--;
                hold += input[next++] << bits;
                bits += 8;
              }
              //===//
              if (state.wrap & 4 && hold !== (state.total & 0xffffffff)) {
                strm.msg = 'incorrect length check';
                state.mode = BAD;
                break;
              }
              //=== INITBITS();
              hold = 0;
              bits = 0;
              //===//
              //Tracev((stderr, "inflate:   length matches trailer\n"));
            }
            state.mode = DONE;
          /* falls through */
          case DONE:
            ret = Z_STREAM_END$1;
            break inf_leave;
          case BAD:
            ret = Z_DATA_ERROR$1;
            break inf_leave;
          case MEM:
            return Z_MEM_ERROR$1;
          case SYNC:
          /* falls through */
          default:
            return Z_STREAM_ERROR$1;
        }
      }

      // inf_leave <- here is real place for "goto inf_leave", emulated via "break inf_leave"

      /*
         Return from inflate(), updating the total counts and the check value.
         If there was no progress during the inflate() call, return a buffer
         error.  Call updatewindow() to create and/or update the window state.
         Note: a memory error from inflate() is non-recoverable.
       */

      //--- RESTORE() ---
      strm.next_out = put;
      strm.avail_out = left;
      strm.next_in = next;
      strm.avail_in = have;
      state.hold = hold;
      state.bits = bits;
      //---

      if (state.wsize || _out !== strm.avail_out && state.mode < BAD && (state.mode < CHECK || flush !== Z_FINISH$1)) {
        if (updatewindow(strm, strm.output, strm.next_out, _out - strm.avail_out)) ;
      }
      _in -= strm.avail_in;
      _out -= strm.avail_out;
      strm.total_in += _in;
      strm.total_out += _out;
      state.total += _out;
      if (state.wrap & 4 && _out) {
        strm.adler = state.check = /*UPDATE_CHECK(state.check, strm.next_out - _out, _out);*/
        state.flags ? crc32_1(state.check, output, _out, strm.next_out - _out) : adler32_1(state.check, output, _out, strm.next_out - _out);
      }
      strm.data_type = state.bits + (state.last ? 64 : 0) + (state.mode === TYPE ? 128 : 0) + (state.mode === LEN_ || state.mode === COPY_ ? 256 : 0);
      if ((_in === 0 && _out === 0 || flush === Z_FINISH$1) && ret === Z_OK$1) {
        ret = Z_BUF_ERROR;
      }
      return ret;
    };
    const inflateEnd = strm => {
      if (inflateStateCheck(strm)) {
        return Z_STREAM_ERROR$1;
      }
      let state = strm.state;
      if (state.window) {
        state.window = null;
      }
      strm.state = null;
      return Z_OK$1;
    };
    const inflateGetHeader = (strm, head) => {
      /* check state */
      if (inflateStateCheck(strm)) {
        return Z_STREAM_ERROR$1;
      }
      const state = strm.state;
      if ((state.wrap & 2) === 0) {
        return Z_STREAM_ERROR$1;
      }

      /* save header structure */
      state.head = head;
      head.done = false;
      return Z_OK$1;
    };
    const inflateSetDictionary = (strm, dictionary) => {
      const dictLength = dictionary.length;
      let state;
      let dictid;
      let ret;

      /* check state */
      if (inflateStateCheck(strm)) {
        return Z_STREAM_ERROR$1;
      }
      state = strm.state;
      if (state.wrap !== 0 && state.mode !== DICT) {
        return Z_STREAM_ERROR$1;
      }

      /* check for correct dictionary identifier */
      if (state.mode === DICT) {
        dictid = 1; /* adler32(0, null, 0)*/
        /* dictid = adler32(dictid, dictionary, dictLength); */
        dictid = adler32_1(dictid, dictionary, dictLength, 0);
        if (dictid !== state.check) {
          return Z_DATA_ERROR$1;
        }
      }
      /* copy dictionary to window using updatewindow(), which will amend the
       existing dictionary if appropriate */
      ret = updatewindow(strm, dictionary, dictLength, dictLength);
      if (ret) {
        state.mode = MEM;
        return Z_MEM_ERROR$1;
      }
      state.havedict = 1;
      // Tracev((stderr, "inflate:   dictionary set\n"));
      return Z_OK$1;
    };
    var inflateReset_1 = inflateReset;
    var inflateReset2_1 = inflateReset2;
    var inflateResetKeep_1 = inflateResetKeep;
    var inflateInit_1 = inflateInit;
    var inflateInit2_1 = inflateInit2;
    var inflate_2$1 = inflate$2;
    var inflateEnd_1 = inflateEnd;
    var inflateGetHeader_1 = inflateGetHeader;
    var inflateSetDictionary_1 = inflateSetDictionary;
    var inflateInfo = 'pako inflate (from Nodeca project)';

    /* Not implemented
    module.exports.inflateCodesUsed = inflateCodesUsed;
    module.exports.inflateCopy = inflateCopy;
    module.exports.inflateGetDictionary = inflateGetDictionary;
    module.exports.inflateMark = inflateMark;
    module.exports.inflatePrime = inflatePrime;
    module.exports.inflateSync = inflateSync;
    module.exports.inflateSyncPoint = inflateSyncPoint;
    module.exports.inflateUndermine = inflateUndermine;
    module.exports.inflateValidate = inflateValidate;
    */

    var inflate_1$2 = {
      inflateReset: inflateReset_1,
      inflateReset2: inflateReset2_1,
      inflateResetKeep: inflateResetKeep_1,
      inflateInit: inflateInit_1,
      inflateInit2: inflateInit2_1,
      inflate: inflate_2$1,
      inflateEnd: inflateEnd_1,
      inflateGetHeader: inflateGetHeader_1,
      inflateSetDictionary: inflateSetDictionary_1,
      inflateInfo: inflateInfo
    };

    // (C) 1995-2013 Jean-loup Gailly and Mark Adler
    // (C) 2014-2017 Vitaly Puzrin and Andrey Tupitsin
    //
    // This software is provided 'as-is', without any express or implied
    // warranty. In no event will the authors be held liable for any damages
    // arising from the use of this software.
    //
    // Permission is granted to anyone to use this software for any purpose,
    // including commercial applications, and to alter it and redistribute it
    // freely, subject to the following restrictions:
    //
    // 1. The origin of this software must not be misrepresented; you must not
    //   claim that you wrote the original software. If you use this software
    //   in a product, an acknowledgment in the product documentation would be
    //   appreciated but is not required.
    // 2. Altered source versions must be plainly marked as such, and must not be
    //   misrepresented as being the original software.
    // 3. This notice may not be removed or altered from any source distribution.

    function GZheader() {
      /* true if compressed data believed to be text */
      this.text = 0;
      /* modification time */
      this.time = 0;
      /* extra flags (not used when writing a gzip file) */
      this.xflags = 0;
      /* operating system */
      this.os = 0;
      /* pointer to extra field or Z_NULL if none */
      this.extra = null;
      /* extra field length (valid if extra != Z_NULL) */
      this.extra_len = 0; // Actually, we don't need it in JS,
      // but leave for few code modifications

      //
      // Setup limits is not necessary because in js we should not preallocate memory
      // for inflate use constant limit in 65536 bytes
      //

      /* space at extra (only when reading header) */
      // this.extra_max  = 0;
      /* pointer to zero-terminated file name or Z_NULL */
      this.name = '';
      /* space at name (only when reading header) */
      // this.name_max   = 0;
      /* pointer to zero-terminated comment or Z_NULL */
      this.comment = '';
      /* space at comment (only when reading header) */
      // this.comm_max   = 0;
      /* true if there was or will be a header crc */
      this.hcrc = 0;
      /* true when done reading gzip header (not used when writing a gzip file) */
      this.done = false;
    }
    var gzheader = GZheader;
    const toString = Object.prototype.toString;

    /* Public constants ==========================================================*/
    /* ===========================================================================*/

    const {
      Z_NO_FLUSH,
      Z_FINISH,
      Z_OK,
      Z_STREAM_END,
      Z_NEED_DICT,
      Z_STREAM_ERROR,
      Z_DATA_ERROR,
      Z_MEM_ERROR
    } = constants$2;

    /* ===========================================================================*/

    /**
     * class Inflate
     *
     * Generic JS-style wrapper for zlib calls. If you don't need
     * streaming behaviour - use more simple functions: [[inflate]]
     * and [[inflateRaw]].
     **/

    /* internal
     * inflate.chunks -> Array
     *
     * Chunks of output data, if [[Inflate#onData]] not overridden.
     **/

    /**
     * Inflate.result -> Uint8Array|String
     *
     * Uncompressed result, generated by default [[Inflate#onData]]
     * and [[Inflate#onEnd]] handlers. Filled after you push last chunk
     * (call [[Inflate#push]] with `Z_FINISH` / `true` param).
     **/

    /**
     * Inflate.err -> Number
     *
     * Error code after inflate finished. 0 (Z_OK) on success.
     * Should be checked if broken data possible.
     **/

    /**
     * Inflate.msg -> String
     *
     * Error message, if [[Inflate.err]] != 0
     **/

    /**
     * new Inflate(options)
     * - options (Object): zlib inflate options.
     *
     * Creates new inflator instance with specified params. Throws exception
     * on bad params. Supported options:
     *
     * - `windowBits`
     * - `dictionary`
     *
     * [http://zlib.net/manual.html#Advanced](http://zlib.net/manual.html#Advanced)
     * for more information on these.
     *
     * Additional options, for internal needs:
     *
     * - `chunkSize` - size of generated data chunks (16K by default)
     * - `raw` (Boolean) - do raw inflate
     * - `to` (String) - if equal to 'string', then result will be converted
     *   from utf8 to utf16 (javascript) string. When string output requested,
     *   chunk length can differ from `chunkSize`, depending on content.
     *
     * By default, when no options set, autodetect deflate/gzip data format via
     * wrapper header.
     *
     * ##### Example:
     *
     * ```javascript
     * const pako = require('pako')
     * const chunk1 = new Uint8Array([1,2,3,4,5,6,7,8,9])
     * const chunk2 = new Uint8Array([10,11,12,13,14,15,16,17,18,19]);
     *
     * const inflate = new pako.Inflate({ level: 3});
     *
     * inflate.push(chunk1, false);
     * inflate.push(chunk2, true);  // true -> last chunk
     *
     * if (inflate.err) { throw new Error(inflate.err); }
     *
     * console.log(inflate.result);
     * ```
     **/
    function Inflate$1(options) {
      this.options = common.assign({
        chunkSize: 1024 * 64,
        windowBits: 15,
        to: ''
      }, options || {});
      const opt = this.options;

      // Force window size for `raw` data, if not set directly,
      // because we have no header for autodetect.
      if (opt.raw && opt.windowBits >= 0 && opt.windowBits < 16) {
        opt.windowBits = -opt.windowBits;
        if (opt.windowBits === 0) {
          opt.windowBits = -15;
        }
      }

      // If `windowBits` not defined (and mode not raw) - set autodetect flag for gzip/deflate
      if (opt.windowBits >= 0 && opt.windowBits < 16 && !(options && options.windowBits)) {
        opt.windowBits += 32;
      }

      // Gzip header has no info about windows size, we can do autodetect only
      // for deflate. So, if window size not set, force it to max when gzip possible
      if (opt.windowBits > 15 && opt.windowBits < 48) {
        // bit 3 (16) -> gzipped data
        // bit 4 (32) -> autodetect gzip/deflate
        if ((opt.windowBits & 15) === 0) {
          opt.windowBits |= 15;
        }
      }
      this.err = 0; // error code, if happens (0 = Z_OK)
      this.msg = ''; // error message
      this.ended = false; // used to avoid multiple onEnd() calls
      this.chunks = []; // chunks of compressed data

      this.strm = new zstream();
      this.strm.avail_out = 0;
      let status = inflate_1$2.inflateInit2(this.strm, opt.windowBits);
      if (status !== Z_OK) {
        throw new Error(messages[status]);
      }
      this.header = new gzheader();
      inflate_1$2.inflateGetHeader(this.strm, this.header);

      // Setup dictionary
      if (opt.dictionary) {
        // Convert data if needed
        if (typeof opt.dictionary === 'string') {
          opt.dictionary = strings.string2buf(opt.dictionary);
        } else if (toString.call(opt.dictionary) === '[object ArrayBuffer]') {
          opt.dictionary = new Uint8Array(opt.dictionary);
        }
        if (opt.raw) {
          //In raw mode we need to set the dictionary early
          status = inflate_1$2.inflateSetDictionary(this.strm, opt.dictionary);
          if (status !== Z_OK) {
            throw new Error(messages[status]);
          }
        }
      }
    }

    /**
     * Inflate#push(data[, flush_mode]) -> Boolean
     * - data (Uint8Array|ArrayBuffer): input data
     * - flush_mode (Number|Boolean): 0..6 for corresponding Z_NO_FLUSH..Z_TREE
     *   flush modes. See constants. Skipped or `false` means Z_NO_FLUSH,
     *   `true` means Z_FINISH.
     *
     * Sends input data to inflate pipe, generating [[Inflate#onData]] calls with
     * new output chunks. Returns `true` on success. If end of stream detected,
     * [[Inflate#onEnd]] will be called.
     *
     * `flush_mode` is not needed for normal operation, because end of stream
     * detected automatically. You may try to use it for advanced things, but
     * this functionality was not tested.
     *
     * On fail call [[Inflate#onEnd]] with error code and return false.
     *
     * ##### Example
     *
     * ```javascript
     * push(chunk, false); // push one of data chunks
     * ...
     * push(chunk, true);  // push last chunk
     * ```
     **/
    Inflate$1.prototype.push = function (data, flush_mode) {
      const strm = this.strm;
      const chunkSize = this.options.chunkSize;
      const dictionary = this.options.dictionary;
      let status, _flush_mode, last_avail_out;
      if (this.ended) return false;
      if (flush_mode === ~~flush_mode) _flush_mode = flush_mode;else _flush_mode = flush_mode === true ? Z_FINISH : Z_NO_FLUSH;

      // Convert data if needed
      if (toString.call(data) === '[object ArrayBuffer]') {
        strm.input = new Uint8Array(data);
      } else {
        strm.input = data;
      }
      strm.next_in = 0;
      strm.avail_in = strm.input.length;
      for (;;) {
        if (strm.avail_out === 0) {
          strm.output = new Uint8Array(chunkSize);
          strm.next_out = 0;
          strm.avail_out = chunkSize;
        }
        status = inflate_1$2.inflate(strm, _flush_mode);
        if (status === Z_NEED_DICT && dictionary) {
          status = inflate_1$2.inflateSetDictionary(strm, dictionary);
          if (status === Z_OK) {
            status = inflate_1$2.inflate(strm, _flush_mode);
          } else if (status === Z_DATA_ERROR) {
            // Replace code with more verbose
            status = Z_NEED_DICT;
          }
        }

        // Skip snyc markers if more data follows and not raw mode
        while (strm.avail_in > 0 && status === Z_STREAM_END && strm.state.wrap > 0 && data[strm.next_in] !== 0) {
          inflate_1$2.inflateReset(strm);
          status = inflate_1$2.inflate(strm, _flush_mode);
        }
        switch (status) {
          case Z_STREAM_ERROR:
          case Z_DATA_ERROR:
          case Z_NEED_DICT:
          case Z_MEM_ERROR:
            this.onEnd(status);
            this.ended = true;
            return false;
        }

        // Remember real `avail_out` value, because we may patch out buffer content
        // to align utf8 strings boundaries.
        last_avail_out = strm.avail_out;
        if (strm.next_out) {
          if (strm.avail_out === 0 || status === Z_STREAM_END) {
            if (this.options.to === 'string') {
              let next_out_utf8 = strings.utf8border(strm.output, strm.next_out);
              let tail = strm.next_out - next_out_utf8;
              let utf8str = strings.buf2string(strm.output, next_out_utf8);

              // move tail & realign counters
              strm.next_out = tail;
              strm.avail_out = chunkSize - tail;
              if (tail) strm.output.set(strm.output.subarray(next_out_utf8, next_out_utf8 + tail), 0);
              this.onData(utf8str);
            } else {
              this.onData(strm.output.length === strm.next_out ? strm.output : strm.output.subarray(0, strm.next_out));
            }
          }
        }

        // Must repeat iteration if out buffer is full
        if (status === Z_OK && last_avail_out === 0) continue;

        // Finalize if end of stream reached.
        if (status === Z_STREAM_END) {
          status = inflate_1$2.inflateEnd(this.strm);
          this.onEnd(status);
          this.ended = true;
          return true;
        }
        if (strm.avail_in === 0) break;
      }
      return true;
    };

    /**
     * Inflate#onData(chunk) -> Void
     * - chunk (Uint8Array|String): output data. When string output requested,
     *   each chunk will be string.
     *
     * By default, stores data blocks in `chunks[]` property and glue
     * those in `onEnd`. Override this handler, if you need another behaviour.
     **/
    Inflate$1.prototype.onData = function (chunk) {
      this.chunks.push(chunk);
    };

    /**
     * Inflate#onEnd(status) -> Void
     * - status (Number): inflate status. 0 (Z_OK) on success,
     *   other if not.
     *
     * Called either after you tell inflate that the input stream is
     * complete (Z_FINISH). By default - join collected chunks,
     * free memory and fill `results` / `err` properties.
     **/
    Inflate$1.prototype.onEnd = function (status) {
      // On success - join
      if (status === Z_OK) {
        if (this.options.to === 'string') {
          this.result = this.chunks.join('');
        } else {
          this.result = common.flattenChunks(this.chunks);
        }
      }
      this.chunks = [];
      this.err = status;
      this.msg = this.strm.msg;
    };

    /**
     * inflate(data[, options]) -> Uint8Array|String
     * - data (Uint8Array|ArrayBuffer): input data to decompress.
     * - options (Object): zlib inflate options.
     *
     * Decompress `data` with inflate/ungzip and `options`. Autodetect
     * format via wrapper header by default. That's why we don't provide
     * separate `ungzip` method.
     *
     * Supported options are:
     *
     * - windowBits
     *
     * [http://zlib.net/manual.html#Advanced](http://zlib.net/manual.html#Advanced)
     * for more information.
     *
     * Sugar (options):
     *
     * - `raw` (Boolean) - say that we work with raw stream, if you don't wish to specify
     *   negative windowBits implicitly.
     * - `to` (String) - if equal to 'string', then result will be converted
     *   from utf8 to utf16 (javascript) string. When string output requested,
     *   chunk length can differ from `chunkSize`, depending on content.
     *
     *
     * ##### Example:
     *
     * ```javascript
     * const pako = require('pako');
     * const input = pako.deflate(new Uint8Array([1,2,3,4,5,6,7,8,9]));
     * let output;
     *
     * try {
     *   output = pako.inflate(input);
     * } catch (err) {
     *   console.log(err);
     * }
     * ```
     **/
    function inflate$1(input, options) {
      const inflator = new Inflate$1(options);
      inflator.push(input);

      // That will never happens, if you don't cheat with options :)
      if (inflator.err) throw inflator.msg || messages[inflator.err];
      return inflator.result;
    }

    /**
     * inflateRaw(data[, options]) -> Uint8Array|String
     * - data (Uint8Array|ArrayBuffer): input data to decompress.
     * - options (Object): zlib inflate options.
     *
     * The same as [[inflate]], but creates raw data, without wrapper
     * (header and adler32 crc).
     **/
    function inflateRaw$1(input, options) {
      options = options || {};
      options.raw = true;
      return inflate$1(input, options);
    }

    /**
     * ungzip(data[, options]) -> Uint8Array|String
     * - data (Uint8Array|ArrayBuffer): input data to decompress.
     * - options (Object): zlib inflate options.
     *
     * Just shortcut to [[inflate]], because it autodetects format
     * by header.content. Done for convenience.
     **/

    var Inflate_1$1 = Inflate$1;
    var inflate_2 = inflate$1;
    var inflateRaw_1$1 = inflateRaw$1;
    var ungzip$1 = inflate$1;
    var constants = constants$2;
    var inflate_1$1 = {
      Inflate: Inflate_1$1,
      inflate: inflate_2,
      inflateRaw: inflateRaw_1$1,
      ungzip: ungzip$1,
      constants: constants
    };
    const {
      Deflate,
      deflate,
      deflateRaw,
      gzip
    } = deflate_1$1;
    const {
      Inflate,
      inflate,
      inflateRaw,
      ungzip
    } = inflate_1$1;
    var Deflate_1 = Deflate;
    var deflate_1 = deflate;
    var deflateRaw_1 = deflateRaw;
    var gzip_1 = gzip;
    var Inflate_1 = Inflate;
    var inflate_1 = inflate;
    var inflateRaw_1 = inflateRaw;
    var ungzip_1 = ungzip;
    var constants_1 = constants$2;
    var pako = {
      Deflate: Deflate_1,
      deflate: deflate_1,
      deflateRaw: deflateRaw_1,
      gzip: gzip_1,
      Inflate: Inflate_1,
      inflate: inflate_1,
      inflateRaw: inflateRaw_1,
      ungzip: ungzip_1,
      constants: constants_1
    };

    const base64codes = Uint8Array.from([255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 62, 255, 255, 255, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 255, 255, 255, 0, 255, 255, 255, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 255, 255, 255, 255, 255, 255, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51]);
    /**
     * Convert a Uint8Array containing a base64 encoded bytes to a Uint8Array containing decoded values
     * @returns a Uint8Array containing the decoded bytes
     */
    function decode(input) {
      if (!ArrayBuffer.isView(input)) {
        input = new Uint8Array(input);
      }
      if (input.length % 4 !== 0) {
        throw new Error('Unable to parse base64 string.');
      }
      let output = new Uint8Array(3 * (input.length / 4));
      if (input.length === 0) return output;
      const missingOctets = input[input.length - 2] === 61 ? 2 : input[input.length - 1] === 61 ? 1 : 0;
      for (let i = 0, j = 0; i < input.length; i += 4, j += 3) {
        const buffer = base64codes[input[i]] << 18 | base64codes[input[i + 1]] << 12 | base64codes[input[i + 2]] << 6 | base64codes[input[i + 3]];
        output[j] = buffer >> 16;
        output[j + 1] = buffer >> 8 & 0xff;
        output[j + 2] = buffer & 0xff;
      }
      return output.subarray(0, output.length - missingOctets);
    }

    function decodeBase64(base64, options = {}) {
      let {
        endian = 'little',
        precision,
        float = true,
        compression = '',
        ontologies
      } = options;
      if (ontologies) {
        if (ontologies.includes('MS:1000519')) {
          precision = 32;
          float = false;
        }
        if (ontologies.includes('MS:1000520')) precision = 16;
        if (ontologies.includes('MS:1000521')) precision = 32;
        if (ontologies.includes('MS:1000522')) {
          float = false;
          precision = 64;
        }
        if (ontologies.includes('MS:1000523')) precision = 64;
        if (ontologies.includes('MS:1000574')) compression = 'zlib';
      }
      let uint8Array = decode(base64);
      switch (compression.toLowerCase()) {
        case 'zlib':
          uint8Array = pako.inflate(uint8Array);
          break;
        case '':
        case 'none':
          break;
        default:
          throw new Error(`Unknow compression algorithm: ${compression}`);
      }
      switch (endian.toLowerCase()) {
        case 'little':
          break;
        case 'network':
        case 'big':
          {
            // we will invert in place the data
            let step;
            switch (precision) {
              case 32:
                step = 4;
                break;
              case 64:
                step = 8;
                break;
              default:
                throw new Error('Can not process bigendian file');
            }
            for (let i = 0; i < uint8Array.length - uint8Array.length % step; i += step) {
              for (let j = 0; j < step / 2; j++) {
                const temp = uint8Array[i + j];
                uint8Array[i + j] = uint8Array[i + step - 1 - j];
                uint8Array[i + step - 1 - j] = temp;
              }
            }
          }
          break;
        default:
          throw new TypeError(`Attributes endian not correct: ${endian}`);
      }

      /*
           We should take care that the length of the Uint8Array is correct but the buffer
           may be a little bit bigger because when decoding base 64 it may end with = or ==
           and we plan the size in the buffer.
          */
      if (float) {
        switch (precision) {
          case 32:
            return new Float32Array(uint8Array.buffer, 0, uint8Array.length / 4);
          case 64:
            return new Float64Array(uint8Array.buffer, 0, uint8Array.length / 8);
          default:
            throw new TypeError(`Incorrect precision: ${precision}`);
        }
      } else {
        switch (precision) {
          case 32:
            return new Int32Array(uint8Array.buffer, 0, uint8Array.length / 4);
          case 64:
            // eslint-disable-next-line no-undef
            return new BigInt64Array(uint8Array.buffer, 0, uint8Array.length / 8);
          default:
            throw new TypeError(`Incorrect precision: ${precision}`);
        }
      }
    }

    function parseCvParam$1(cvParam) {
      let result = {};
      if (!cvParam) return result;
      let cvParams;
      if (Array.isArray(cvParam)) {
        cvParams = cvParam;
      } else {
        cvParams = [cvParam];
      }
      for (let param of cvParams) {
        let attributes = param.attributes;
        if (attributes.name) {
          result[attributes.name.toLowerCase()] = {
            accession: attributes.accession,
            cvLabel: attributes.cvLabel,
            value: attributes.value,
            name: attributes.name
          };
        }
      }
      return result;
    }

    function processMetadata(parsed, metadata) {
      if (!parsed || !parsed.description) return;
      let description = parsed.description;
      if (description.dataProcessing) {
        let dataProcessing = description.dataProcessing;
        if (dataProcessing.software && dataProcessing.software.name) {
          metadata.software = dataProcessing.software.name;
        }
      }
      if (description.instrument) {
        let instrument = description.instrument;
        if (instrument.analyzerList && instrument.analyzerList.analyzer) {
          let analyzer = instrument.analyzerList.analyzer;
          let cvParam = parseCvParam$1(analyzer.cvParam);
          if (cvParam.analyzertype) {
            metadata.analyzer = cvParam.analyzertype.value;
          }
        }
        if (instrument.detector) {
          let detector = instrument.detector;
          let cvParam = parseCvParam$1(detector.cvParam);
          if (cvParam.detectortype) {
            metadata.detector = cvParam.detectortype.value;
          }
        }
      }
    }

    function processSpectrumList$2(parsed, times, msData) {
      if (!parsed || !parsed.spectrumList || !parsed.spectrumList.spectrum) return;
      let spectrumList = parsed.spectrumList.spectrum;
      for (let spectrum of spectrumList) {
        let info = parseCvParam$1(spectrum.spectrumDesc.spectrumSettings.spectrumInstrument.cvParam);
        times.push(info.timeinminutes.value);
        let mzArray = spectrum.mzArrayBinary.data['#text'] || [];
        let intensity = spectrum.intenArrayBinary.data['#text'] || [];
        msData.push([mzArray, intensity]);
      }
    }

    const decoder$3 = new TextDecoder();
    function parseMzData(arrayBuffer) {
      const result = {
        metadata: {},
        times: [],
        series: {
          ms: {
            data: []
          }
        }
      };
      let parsed = parse(arrayBuffer, {
        attributeNamePrefix: '',
        attributesNodeName: 'attributes',
        tagValueProcessor: (value, node) => {
          if (node.tagName !== 'data') return decoder$3.decode(value);
          return decodeBase64(node.value, node.attributes);
        }
      });
      processMetadata(parsed.mzData, result.metadata);
      processSpectrumList$2(parsed.mzData, result.times, result.series.ms.data);
      return result;
    }

    var camelcase = {exports: {}};

    const UPPERCASE = /[\p{Lu}]/u;
    const LOWERCASE = /[\p{Ll}]/u;
    const LEADING_CAPITAL = /^[\p{Lu}](?![\p{Lu}])/gu;
    const IDENTIFIER = /([\p{Alpha}\p{N}_]|$)/u;
    const SEPARATORS = /[_.\- ]+/;
    const LEADING_SEPARATORS = new RegExp('^' + SEPARATORS.source);
    const SEPARATORS_AND_IDENTIFIER = new RegExp(SEPARATORS.source + IDENTIFIER.source, 'gu');
    const NUMBERS_AND_IDENTIFIER = new RegExp('\\d+' + IDENTIFIER.source, 'gu');
    const preserveCamelCase = (string, toLowerCase, toUpperCase) => {
      let isLastCharLower = false;
      let isLastCharUpper = false;
      let isLastLastCharUpper = false;
      for (let i = 0; i < string.length; i++) {
        const character = string[i];
        if (isLastCharLower && UPPERCASE.test(character)) {
          string = string.slice(0, i) + '-' + string.slice(i);
          isLastCharLower = false;
          isLastLastCharUpper = isLastCharUpper;
          isLastCharUpper = true;
          i++;
        } else if (isLastCharUpper && isLastLastCharUpper && LOWERCASE.test(character)) {
          string = string.slice(0, i - 1) + '-' + string.slice(i - 1);
          isLastLastCharUpper = isLastCharUpper;
          isLastCharUpper = false;
          isLastCharLower = true;
        } else {
          isLastCharLower = toLowerCase(character) === character && toUpperCase(character) !== character;
          isLastLastCharUpper = isLastCharUpper;
          isLastCharUpper = toUpperCase(character) === character && toLowerCase(character) !== character;
        }
      }
      return string;
    };
    const preserveConsecutiveUppercase = (input, toLowerCase) => {
      LEADING_CAPITAL.lastIndex = 0;
      return input.replace(LEADING_CAPITAL, m1 => toLowerCase(m1));
    };
    const postProcess = (input, toUpperCase) => {
      SEPARATORS_AND_IDENTIFIER.lastIndex = 0;
      NUMBERS_AND_IDENTIFIER.lastIndex = 0;
      return input.replace(SEPARATORS_AND_IDENTIFIER, (_, identifier) => toUpperCase(identifier)).replace(NUMBERS_AND_IDENTIFIER, m => toUpperCase(m));
    };
    const camelCase = (input, options) => {
      if (!(typeof input === 'string' || Array.isArray(input))) {
        throw new TypeError('Expected the input to be `string | string[]`');
      }
      options = {
        pascalCase: false,
        preserveConsecutiveUppercase: false,
        ...options
      };
      if (Array.isArray(input)) {
        input = input.map(x => x.trim()).filter(x => x.length).join('-');
      } else {
        input = input.trim();
      }
      if (input.length === 0) {
        return '';
      }
      const toLowerCase = options.locale === false ? string => string.toLowerCase() : string => string.toLocaleLowerCase(options.locale);
      const toUpperCase = options.locale === false ? string => string.toUpperCase() : string => string.toLocaleUpperCase(options.locale);
      if (input.length === 1) {
        return options.pascalCase ? toUpperCase(input) : toLowerCase(input);
      }
      const hasUpperCase = input !== toLowerCase(input);
      if (hasUpperCase) {
        input = preserveCamelCase(input, toLowerCase, toUpperCase);
      }
      input = input.replace(LEADING_SEPARATORS, '');
      if (options.preserveConsecutiveUppercase) {
        input = preserveConsecutiveUppercase(input, toLowerCase);
      } else {
        input = toLowerCase(input);
      }
      if (options.pascalCase) {
        input = toUpperCase(input.charAt(0)) + input.slice(1);
      }
      return postProcess(input, toUpperCase);
    };
    camelcase.exports = camelCase;
    // TODO: Remove this for the next major release
    camelcase.exports.default = camelCase;
    var camelCase$1 = camelcase.exports;

    function parseCvParam(cvParam) {
      let result = {};
      if (!cvParam) return result;
      let cvParams;
      if (Array.isArray(cvParam)) {
        cvParams = cvParam;
      } else {
        cvParams = [cvParam];
      }
      for (let parameter of cvParams) {
        let attribute = parameter.attributes;
        if (attribute.name) {
          result[camelCase$1(attribute.name.toLowerCase().replace(/[^ a-z0-9]/g, ''))] = {
            accession: attribute.accession,
            cvLabel: attribute.cvLabel,
            value: attribute.value,
            name: attribute.name
          };
        }
      }
      return result;
    }

    function processSpectrumList$1(parsed, times, msData) {
      if (!parsed || !parsed.run || !parsed.run.spectrumList || !parsed.run.spectrumList.spectrum) {
        return;
      }
      let spectrumList = parsed.run.spectrumList.spectrum;
      for (let spectrum of spectrumList) {
        if (!spectrum.binaryDataArrayList) continue;
        let scanList = spectrum.scanList;
        if (Array.isArray(scanList)) throw new Error('Unsupported scanList');
        let scan = scanList.scan;
        if (typeof scan !== 'object') continue;
        if (Array.isArray(scan)) {
          throw new Error('processSpectrumList: scan may not be an array');
        }
        const cvParam = parseCvParam(scan.cvParam);
        times.push(cvParam.scanStartTime.value);
        const dataArrayList = spectrum.binaryDataArrayList.binaryDataArray;
        if (dataArrayList.length !== 2) {
          throw new Error('Can not decodeData because length !== 2');
        }
        const first = dataArrayList[0];
        const firstCVParams = parseCvParam(first.cvParam);
        const second = dataArrayList[1];
        const secondCVParams = parseCvParam(second.cvParam);
        if (firstCVParams.mzArray && secondCVParams.intensityArray) {
          msData.push([first.binary, second.binary]);
        }
        if (firstCVParams.intensityArray && secondCVParams.mzArray) {
          msData.push([second.binary, first.binary]);
        }
      }
    }

    const decoder$2 = new TextDecoder();
    function parseMzML(arrayBuffer) {
      const result = {
        metadata: {},
        times: [],
        series: {
          ms: {
            data: []
          }
        }
      };
      let parsed = parse(arrayBuffer, {
        attributeNamePrefix: '',
        attributesNodeName: 'attributes',
        tagValueProcessor: (value, node) => {
          if (node.tagName !== 'binary') return decoder$2.decode(value);
          const ontologies = node.parent.children.cvParam.map(entry => entry.attributes.accession);
          return decodeBase64(node.value, {
            ontologies
          });
        }
      });
      const mzML = parsed.mzML || parsed.indexedmzML.mzML;
      processSpectrumList$1(mzML, result.times, result.series.ms.data);
      return result;
    }

    function processSpectrumList(parsed, times, msData) {
      if (!parsed.msRun.scan) return;
      let scanList = parsed.msRun.scan;
      if (Array.isArray(scanList) === false) scanList = [scanList];
      if (scanList[0].attributes) msData.info = [];
      for (let scan of scanList) {
        if (typeof scan !== 'object') continue;
        if (Array.isArray(scan)) {
          throw new Error('processSpectrumList: scan may not be an array');
        }
        const dataArray = scan.peaks['#text'];
        let length = dataArray.length / 2;
        let first = new Float64Array(length);
        let second = new Float64Array(length);
        for (let i = 0; i < length; i++) {
          first[i] = dataArray[i * 2];
          second[i] = dataArray[i * 2 + 1];
        }
        msData.data.push([first, second]);
        msData.info.push(scan.attributes);
        times.push(parseFloat(scan.attributes.retentionTime.replace(/(?:P*)(?:T*)(?:S*)/gi, '')));
      }
    }

    const decoder$1 = new TextDecoder();
    function parseMzXML(arrayBuffer) {
      const result = {
        metadata: {},
        times: [],
        series: {
          ms: {
            data: []
          }
        }
      };
      let parsed = parse(arrayBuffer, {
        attributeNamePrefix: '',
        attributesNodeName: 'attributes',
        tagValueProcessor: (value, node) => {
          if (node.tagName !== 'peaks') return decoder$1.decode(value);
          return decodeBase64(node.value, {
            precision: node.attributes.precision,
            endian: node.attributes.byteOrder,
            compression: node.attributes.compressionType
          });
        }
      });
      processSpectrumList(parsed.mzXML, result.times, result.series.ms);
      return result;
    }

    const decoder = new TextDecoder();

    /**
     * Reads a mzData v1.05 file
     * @param {ArrayBuffer|string} xml - ArrayBuffer or String or any Typed Array (including Node.js' Buffer from v4) with the data
     * @return {{times: Array<number>, series: { ms: { data:Array<Array<number>>}}}}
     */
    function parseMZ(xml) {
      if (typeof xml === 'string') {
        const encoder = new TextEncoder();
        xml = encoder.encode(xml);
      }
      if (!ArrayBuffer.isView(xml)) {
        xml = new Uint8Array(xml);
      }
      const header = xml.subarray ? decoder.decode(xml.subarray(0, 200)) : xml.substring(0, 200);
      if (header.includes('mzData')) {
        return parseMzData(xml);
      } else if (header.includes('mzML')) {
        return parseMzML(xml);
      } else if (header.includes('mzXML')) {
        return parseMzXML(xml);
      } else {
        throw new Error(`MZ parser: unknown format`);
      }
    }

    function fromXML(xml) {
      return fromJSON(parseMZ(xml));
    }

    exports.Chromatogram = Chromatogram;
    exports.appendKovats = appendKovats;
    exports.appendMass = appendMass;
    exports.cosineSimilarity = cosineSimilarity;
    exports.fromJSON = fromJSON;
    exports.fromJcamp = fromJcamp;
    exports.fromNetCDF = fromNetCDF;
    exports.fromText = fromText;
    exports.fromXML = fromXML;
    exports.getKovatsConversionFunction = getKovatsConversionFunction;
    exports.kovats = kovats;
    exports.massFilter = massFilter;
    exports.scaleAlignment = scaleAlignment;
    exports.spectraComparison = spectraComparison;
    exports.vectorify = vectorify;

    Object.defineProperty(exports, '__esModule', { value: true });

}));
//# sourceMappingURL=chromatography.js.map
