/**
 * nmr-processing - Pure functions allowing to process NMR spectra.
 * @version v9.8.0
 * @link https://github.com/cheminfo/nmr-processing#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.nmrProcessing = {}));
})(this, (function (exports) { 'use strict';

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

    /* eslint-disable camelcase */
    const impuritiesContent = {
      cdcl3: {
        tms: [{
          proton: 'X',
          coupling: 0,
          multiplicity: '',
          shift: 0
        }],
        solvent: [{
          proton: 'X',
          coupling: 0,
          multiplicity: 'ds',
          shift: 7.26
        }],
        h2o: [{
          proton: 'H2O',
          coupling: 0,
          multiplicity: 'bs',
          shift: 1.56
        }],
        acetic_acid: [{
          proton: 'CH3',
          coupling: 0,
          multiplicity: 's',
          shift: 2.1
        }],
        acetone: [{
          proton: 'CH3',
          coupling: 0,
          multiplicity: 's',
          shift: 2.17
        }],
        acetonitrile: [{
          proton: 'CH3',
          coupling: 0,
          multiplicity: 's',
          shift: 2.1
        }],
        benzene: [{
          proton: 'CH',
          coupling: 0,
          multiplicity: 's',
          shift: 7.36
        }],
        'tert-butyl_alcohol': [{
          proton: 'CH3',
          coupling: 0,
          multiplicity: 's',
          shift: 1.28
        }],
        'tert-butyl_methyl_ether': [{
          proton: 'CCH3',
          coupling: 0,
          multiplicity: 's',
          shift: 1.19
        }, {
          proton: 'OCH3',
          coupling: 0,
          multiplicity: 's',
          shift: 3.22
        }],
        bhtb: [{
          proton: 'ArH',
          coupling: 0,
          multiplicity: 's',
          shift: 6.98
        }, {
          proton: 'OHc',
          coupling: 0,
          multiplicity: 's',
          shift: 5.01
        }, {
          proton: 'ArCH3',
          coupling: 0,
          multiplicity: 's',
          shift: 2.27
        }, {
          proton: 'ArC(CH3)3',
          coupling: 0,
          multiplicity: 's',
          shift: 1.43
        }],
        chloroform: [{
          proton: 'CH',
          coupling: 0,
          multiplicity: 's',
          shift: 7.26
        }],
        cyclohexane: [{
          proton: 'CH2',
          coupling: 0,
          multiplicity: 's',
          shift: 1.43
        }],
        '1,2-dichloroethane': [{
          proton: 'CH2',
          coupling: 0,
          multiplicity: 's',
          shift: 3.73
        }],
        dichloromethane: [{
          proton: 'CH2',
          coupling: 0,
          multiplicity: 's',
          shift: 5.3
        }],
        diethyl_ether: [{
          proton: 'CH3',
          coupling: 7,
          multiplicity: 't',
          shift: 1.21
        }, {
          proton: 'CH2',
          coupling: 7,
          multiplicity: 'q',
          shift: 3.48
        }],
        diglyme: [{
          proton: 'CH2',
          coupling: 0,
          multiplicity: 'm',
          shift: 3.65
        }, {
          proton: 'CH2',
          coupling: 0,
          multiplicity: 'm',
          shift: 3.57
        }, {
          proton: 'OCH3',
          coupling: 0,
          multiplicity: 's',
          shift: 3.39
        }],
        '1,2-dimethoxyethane': [{
          proton: 'CH3',
          coupling: 0,
          multiplicity: 's',
          shift: 3.4
        }, {
          proton: 'CH2',
          coupling: 0,
          multiplicity: 's',
          shift: 3.55
        }],
        dimethylacetamide: [{
          proton: 'CH3CO',
          coupling: 0,
          multiplicity: 's',
          shift: 2.09
        }, {
          proton: 'NCH3',
          coupling: 0,
          multiplicity: 's',
          shift: 3.02
        }, {
          proton: 'NCH3',
          coupling: 0,
          multiplicity: 's',
          shift: 2.94
        }],
        dimethylformamide: [{
          proton: 'CH',
          coupling: 0,
          multiplicity: 's',
          shift: 8.02
        }, {
          proton: 'CH3',
          coupling: 0,
          multiplicity: 's',
          shift: 2.96
        }, {
          proton: 'CH3',
          coupling: 0,
          multiplicity: 's',
          shift: 2.88
        }],
        dimethyl_sulfoxide: [{
          proton: 'CH3',
          coupling: 0,
          multiplicity: 's',
          shift: 2.62
        }],
        dioxane: [{
          proton: 'CH2',
          coupling: 0,
          multiplicity: 's',
          shift: 3.71
        }],
        ethanol: [{
          proton: 'CH3',
          coupling: 7,
          multiplicity: 't',
          shift: 1.25
        }, {
          proton: 'CH2',
          coupling: 7,
          multiplicity: 'q',
          shift: 3.72
        }, {
          proton: 'OH',
          coupling: 5,
          multiplicity: 's,t',
          shift: 1.32
        }],
        ethyl_acetate: [{
          proton: 'CH3CO',
          coupling: 0,
          multiplicity: 's',
          shift: 2.05
        }, {
          proton: 'CH2CH3',
          coupling: 7,
          multiplicity: 'q',
          shift: 4.12
        }, {
          proton: 'CH2CH3',
          coupling: 7,
          multiplicity: 't',
          shift: 1.26
        }],
        ethyl_methyl_ketone: [{
          proton: 'CH3CO',
          coupling: 0,
          multiplicity: 's',
          shift: 2.14
        }, {
          proton: 'CH2CH3',
          coupling: 7,
          multiplicity: 'q',
          shift: 2.46
        }, {
          proton: 'CH2CH3',
          coupling: 7,
          multiplicity: 't',
          shift: 1.06
        }],
        ethylene_glycol: [{
          proton: 'CH',
          coupling: 0,
          multiplicity: 's',
          shift: 3.76
        }],
        'grease^f': [{
          proton: 'CH3',
          coupling: 0,
          multiplicity: 'm',
          shift: 0.86
        }, {
          proton: 'CH2',
          coupling: 0,
          multiplicity: 'br_s',
          shift: 1.26
        }],
        'n-hexane': [{
          proton: 'CH3',
          coupling: 0,
          multiplicity: 't',
          shift: 0.88
        }, {
          proton: 'CH2',
          coupling: 0,
          multiplicity: 'm',
          shift: 1.26
        }],
        hmpag: [{
          proton: 'CH3',
          coupling: 9.5,
          multiplicity: 'd',
          shift: 2.65
        }],
        methanol: [{
          proton: 'CH3',
          coupling: 0,
          multiplicity: 's',
          shift: 3.49
        }, {
          proton: 'OH',
          coupling: 0,
          multiplicity: 's',
          shift: 1.09
        }],
        nitromethane: [{
          proton: 'CH3',
          coupling: 0,
          multiplicity: 's',
          shift: 4.33
        }],
        'n-pentane': [{
          proton: 'CH3',
          coupling: 7,
          multiplicity: 't',
          shift: 7
        }, {
          proton: 'CH2',
          coupling: 0,
          multiplicity: 'm',
          shift: 1.27
        }],
        '2-propanol': [{
          proton: 'CH3',
          coupling: 6,
          multiplicity: 'd',
          shift: 1.22
        }, {
          proton: 'CH',
          coupling: 6,
          multiplicity: 'sep',
          shift: 4.04
        }],
        pyridine: [{
          proton: 'CH(2)',
          coupling: 0,
          multiplicity: 'm',
          shift: 8.62
        }, {
          proton: 'CH(3)',
          coupling: 0,
          multiplicity: 'm',
          shift: 7.29
        }, {
          proton: 'CH(4)',
          coupling: 0,
          multiplicity: 'm',
          shift: 7.68
        }],
        silicone_greasei: [{
          proton: 'CH3',
          coupling: 0,
          multiplicity: 's',
          shift: 0.07
        }],
        tetrahydrofuran: [{
          proton: 'CH2',
          coupling: 0,
          multiplicity: 'm',
          shift: 1.85
        }, {
          proton: 'CH2O',
          coupling: 0,
          multiplicity: 'm',
          shift: 3.76
        }],
        toluene: [{
          proton: 'CH3',
          coupling: 0,
          multiplicity: 's',
          shift: 2.36
        }, {
          proton: 'CH(o/p)',
          coupling: 0,
          multiplicity: 'm',
          shift: 7.17
        }, {
          proton: 'CH(m)',
          coupling: 0,
          multiplicity: 'm',
          shift: 7.25
        }],
        triethylamine: [{
          proton: 'CH3',
          coupling: 7,
          multiplicity: 't',
          shift: 1.03
        }, {
          proton: 'CH2',
          coupling: 7,
          multiplicity: 'q',
          shift: 2.53
        }]
      },
      '(cd3)2co': {
        tms: [{
          proton: 'X',
          coupling: 0,
          multiplicity: '',
          shift: 0
        }],
        solvent: [{
          proton: 'X',
          coupling: 0,
          multiplicity: '',
          shift: 2.05
        }],
        h2o: [{
          proton: 'H2O',
          coupling: 0,
          multiplicity: 's',
          shift: 2.84
        }],
        acetic_acid: [{
          proton: 'CH3',
          coupling: 0,
          multiplicity: 's',
          shift: 1.96
        }],
        acetone: [{
          proton: 'CH3',
          coupling: 0,
          multiplicity: 's',
          shift: 2.09
        }],
        acetonitrile: [{
          proton: 'CH3',
          coupling: 0,
          multiplicity: 's',
          shift: 2.05
        }],
        benzene: [{
          proton: 'CH',
          coupling: 0,
          multiplicity: 's',
          shift: 7.36
        }],
        'tert-butyl_alcohol': [{
          proton: 'CH3',
          coupling: 0,
          multiplicity: 's',
          shift: 1.18
        }],
        'tert-butyl_methyl_ether': [{
          proton: 'CCH3',
          coupling: 0,
          multiplicity: 's',
          shift: 1.13
        }, {
          proton: 'OCH3',
          coupling: 0,
          multiplicity: 's',
          shift: 3.13
        }],
        bhtb: [{
          proton: 'ArH',
          coupling: 0,
          multiplicity: 's',
          shift: 6.96
        }, {
          proton: 'ArCH3',
          coupling: 0,
          multiplicity: 's',
          shift: 2.22
        }, {
          proton: 'ArC(CH3)3',
          coupling: 0,
          multiplicity: 's',
          shift: 1.41
        }],
        chloroform: [{
          proton: 'CH',
          coupling: 0,
          multiplicity: 's',
          shift: 8.02
        }],
        cyclohexane: [{
          proton: 'CH2',
          coupling: 0,
          multiplicity: 's',
          shift: 1.43
        }],
        '1,2-dichloroethane': [{
          proton: 'CH2',
          coupling: 0,
          multiplicity: 's',
          shift: 3.87
        }],
        dichloromethane: [{
          proton: 'CH2',
          coupling: 0,
          multiplicity: 's',
          shift: 5.63
        }],
        diethyl_ether: [{
          proton: 'CH3',
          coupling: 7,
          multiplicity: 't',
          shift: 1.11
        }, {
          proton: 'CH2',
          coupling: 7,
          multiplicity: 'q',
          shift: 3.41
        }],
        diglyme: [{
          proton: 'CH2',
          coupling: 0,
          multiplicity: 'm',
          shift: 3.56
        }, {
          proton: 'CH2',
          coupling: 0,
          multiplicity: 'm',
          shift: 3.47
        }, {
          proton: 'OCH3',
          coupling: 0,
          multiplicity: 's',
          shift: 3.28
        }],
        '1,2-dimethoxyethane': [{
          proton: 'CH3',
          coupling: 0,
          multiplicity: 's',
          shift: 3.28
        }, {
          proton: 'CH2',
          coupling: 0,
          multiplicity: 's',
          shift: 3.46
        }],
        dimethylacetamide: [{
          proton: 'CH3CO',
          coupling: 0,
          multiplicity: 's',
          shift: 1.97
        }, {
          proton: 'NCH3',
          coupling: 0,
          multiplicity: 's',
          shift: 3
        }, {
          proton: 'NCH3',
          coupling: 0,
          multiplicity: 's',
          shift: 2.83
        }],
        dimethylformamide: [{
          proton: 'CH',
          coupling: 0,
          multiplicity: 's',
          shift: 7.96
        }, {
          proton: 'CH3',
          coupling: 0,
          multiplicity: 's',
          shift: 2.94
        }, {
          proton: 'CH3',
          coupling: 0,
          multiplicity: 's',
          shift: 2.78
        }],
        dimethyl_sulfoxide: [{
          proton: 'CH3',
          coupling: 0,
          multiplicity: 's',
          shift: 2.52
        }],
        dioxane: [{
          proton: 'CH2',
          coupling: 0,
          multiplicity: 's',
          shift: 3.59
        }],
        ethanol: [{
          proton: 'CH3',
          coupling: 7,
          multiplicity: 't',
          shift: 1.12
        }, {
          proton: 'CH2',
          coupling: 7,
          multiplicity: 'q',
          shift: 3.57
        }, {
          proton: 'OH',
          coupling: 5,
          multiplicity: 's,t',
          shift: 3.39
        }],
        ethyl_acetate: [{
          proton: 'CH3CO',
          coupling: 0,
          multiplicity: 's',
          shift: 1.97
        }, {
          proton: 'CH2CH3',
          coupling: 7,
          multiplicity: 'q',
          shift: 4.05
        }, {
          proton: 'CH2CH3',
          coupling: 7,
          multiplicity: 't',
          shift: 1.2
        }],
        ethyl_methyl_ketone: [{
          proton: 'CH3CO',
          coupling: 0,
          multiplicity: 's',
          shift: 2.07
        }, {
          proton: 'CH2CH3',
          coupling: 7,
          multiplicity: 'q',
          shift: 2.45
        }, {
          proton: 'CH2CH3',
          coupling: 7,
          multiplicity: 't',
          shift: 0.96
        }],
        ethylene_glycol: [{
          proton: 'CH',
          coupling: 0,
          multiplicity: 's',
          shift: 3.28
        }],
        'grease^f': [{
          proton: 'CH3',
          coupling: 0,
          multiplicity: 'm',
          shift: 0.87
        }, {
          proton: 'CH2',
          coupling: 0,
          multiplicity: 'br_s',
          shift: 1.29
        }],
        'n-hexane': [{
          proton: 'CH3',
          coupling: 0,
          multiplicity: 't',
          shift: 0.88
        }, {
          proton: 'CH2',
          coupling: 0,
          multiplicity: 'm',
          shift: 1.28
        }],
        hmpag: [{
          proton: 'CH3',
          coupling: 9.5,
          multiplicity: 'd',
          shift: 2.59
        }],
        methanol: [{
          proton: 'CH3',
          coupling: 0,
          multiplicity: 's',
          shift: 3.31
        }, {
          proton: 'OH',
          coupling: 0,
          multiplicity: 's',
          shift: 3.12
        }],
        nitromethane: [{
          proton: 'CH3',
          coupling: 0,
          multiplicity: 's',
          shift: 4.43
        }],
        'n-pentane': [{
          proton: 'CH3',
          coupling: 7,
          multiplicity: 't',
          shift: 0.88
        }, {
          proton: 'CH2',
          coupling: 0,
          multiplicity: 'm',
          shift: 1.27
        }],
        '2-propanol': [{
          proton: 'CH3',
          coupling: 6,
          multiplicity: 'd',
          shift: 1.1
        }, {
          proton: 'CH',
          coupling: 6,
          multiplicity: 'sep',
          shift: 3.9
        }],
        pyridine: [{
          proton: 'CH(2)',
          coupling: 0,
          multiplicity: 'm',
          shift: 8.58
        }, {
          proton: 'CH(3)',
          coupling: 0,
          multiplicity: 'm',
          shift: 7.35
        }, {
          proton: 'CH(4)',
          coupling: 0,
          multiplicity: 'm',
          shift: 7.76
        }],
        silicone_greasei: [{
          proton: 'CH3',
          coupling: 0,
          multiplicity: 's',
          shift: 0.13
        }],
        tetrahydrofuran: [{
          proton: 'CH2',
          coupling: 0,
          multiplicity: 'm',
          shift: 1.79
        }, {
          proton: 'CH2O',
          coupling: 0,
          multiplicity: 'm',
          shift: 3.63
        }],
        toluene: [{
          proton: 'CH3',
          coupling: 0,
          multiplicity: 's',
          shift: 2.32
        }, {
          proton: 'CH(o/p)',
          coupling: 0,
          multiplicity: 'm',
          shift: 7.5
        }, {
          proton: 'CH(m)',
          coupling: 0,
          multiplicity: 'm',
          shift: 7.5
        }],
        triethylamine: [{
          proton: 'CH3',
          coupling: 7,
          multiplicity: 't',
          shift: 0.96
        }, {
          proton: 'CH2',
          coupling: 7,
          multiplicity: 'q',
          shift: 2.45
        }]
      },
      dmso: {
        tms: [{
          proton: 'X',
          coupling: 0,
          multiplicity: '',
          shift: 0
        }],
        solvent: [{
          proton: 'X',
          coupling: 0,
          multiplicity: 'quint',
          shift: 2.5
        }],
        h2o: [{
          proton: 'H2O',
          coupling: 0,
          multiplicity: 's',
          shift: 3.33
        }],
        acetic_acid: [{
          proton: 'CH3',
          coupling: 0,
          multiplicity: 's',
          shift: 1.91
        }],
        acetone: [{
          proton: 'CH3',
          coupling: 0,
          multiplicity: 's',
          shift: 2.09
        }],
        acetonitrile: [{
          proton: 'CH3',
          coupling: 0,
          multiplicity: 's',
          shift: 2.07
        }],
        benzene: [{
          proton: 'CH',
          coupling: 0,
          multiplicity: 's',
          shift: 7.37
        }],
        'tert-butyl_alcohol': [{
          proton: 'CH3',
          coupling: 0,
          multiplicity: 's',
          shift: 1.11
        }, {
          proton: 'OHc',
          coupling: 0,
          multiplicity: 's',
          shift: 4.19
        }],
        'tert-butyl_methyl_ether': [{
          proton: 'CCH3',
          coupling: 0,
          multiplicity: 's',
          shift: 1.11
        }, {
          proton: 'OCH3',
          coupling: 0,
          multiplicity: 's',
          shift: 3.08
        }],
        bhtb: [{
          proton: 'ArH',
          coupling: 0,
          multiplicity: 's',
          shift: 6.87
        }, {
          proton: 'OHc',
          coupling: 0,
          multiplicity: 's',
          shift: 6.65
        }, {
          proton: 'ArCH3',
          coupling: 0,
          multiplicity: 's',
          shift: 2.18
        }, {
          proton: 'ArC(CH3)3',
          coupling: 0,
          multiplicity: 's',
          shift: 1.36
        }],
        chloroform: [{
          proton: 'CH',
          coupling: 0,
          multiplicity: 's',
          shift: 8.32
        }],
        cyclohexane: [{
          proton: 'CH2',
          coupling: 0,
          multiplicity: 's',
          shift: 1.4
        }],
        '1,2-dichloroethane': [{
          proton: 'CH2',
          coupling: 0,
          multiplicity: 's',
          shift: 3.9
        }],
        dichloromethane: [{
          proton: 'CH2',
          coupling: 0,
          multiplicity: 's',
          shift: 5.76
        }],
        diethyl_ether: [{
          proton: 'CH3',
          coupling: 7,
          multiplicity: 't',
          shift: 1.09
        }, {
          proton: 'CH2',
          coupling: 7,
          multiplicity: 'q',
          shift: 3.38
        }],
        diglyme: [{
          proton: 'CH2',
          coupling: 0,
          multiplicity: 'm',
          shift: 3.51
        }, {
          proton: 'CH2',
          coupling: 0,
          multiplicity: 'm',
          shift: 3.38
        }, {
          proton: 'OCH3',
          coupling: 0,
          multiplicity: 's',
          shift: 3.24
        }],
        '1,2-dimethoxyethane': [{
          proton: 'CH3',
          coupling: 0,
          multiplicity: 's',
          shift: 3.24
        }, {
          proton: 'CH2',
          coupling: 0,
          multiplicity: 's',
          shift: 3.43
        }],
        dimethylacetamide: [{
          proton: 'CH3CO',
          coupling: 0,
          multiplicity: 's',
          shift: 1.96
        }, {
          proton: 'NCH3',
          coupling: 0,
          multiplicity: 's',
          shift: 2.94
        }, {
          proton: 'NCH3',
          coupling: 0,
          multiplicity: 's',
          shift: 2.78
        }],
        dimethylformamide: [{
          proton: 'CH',
          coupling: 0,
          multiplicity: 's',
          shift: 7.95
        }, {
          proton: 'CH3',
          coupling: 0,
          multiplicity: 's',
          shift: 2.89
        }, {
          proton: 'CH3',
          coupling: 0,
          multiplicity: 's',
          shift: 2.73
        }],
        dimethyl_sulfoxide: [{
          proton: 'CH3',
          coupling: 0,
          multiplicity: 's',
          shift: 2.54
        }],
        dioxane: [{
          proton: 'CH2',
          coupling: 0,
          multiplicity: 's',
          shift: 3.57
        }],
        ethanol: [{
          proton: 'CH3',
          coupling: 7,
          multiplicity: 't',
          shift: 1.06
        }, {
          proton: 'CH2',
          coupling: 7,
          multiplicity: 'q',
          shift: 3.44
        }, {
          proton: 'OH',
          coupling: 5,
          multiplicity: 's,t',
          shift: 4.63
        }],
        ethyl_acetate: [{
          proton: 'CH3CO',
          coupling: 0,
          multiplicity: 's',
          shift: 1.99
        }, {
          proton: 'CH2CH3',
          coupling: 7,
          multiplicity: 'q',
          shift: 4.03
        }, {
          proton: 'CH2CH3',
          coupling: 7,
          multiplicity: 't',
          shift: 1.17
        }],
        ethyl_methyl_ketone: [{
          proton: 'CH3CO',
          coupling: 0,
          multiplicity: 's',
          shift: 2.07
        }, {
          proton: 'CH2CH3',
          coupling: 7,
          multiplicity: 'q',
          shift: 2.43
        }, {
          proton: 'CH2CH3',
          coupling: 7,
          multiplicity: 't',
          shift: 0.91
        }],
        ethylene_glycol: [{
          proton: 'CH',
          coupling: 0,
          multiplicity: 's',
          shift: 3.34
        }],
        'grease^f': [],
        'n-hexane': [{
          proton: 'CH3',
          coupling: 0,
          multiplicity: 't',
          shift: 0.86
        }, {
          proton: 'CH2',
          coupling: 0,
          multiplicity: 'm',
          shift: 1.25
        }],
        hmpag: [{
          proton: 'CH3',
          coupling: 9.5,
          multiplicity: 'd',
          shift: 2.53
        }],
        methanol: [{
          proton: 'CH3',
          coupling: 0,
          multiplicity: 's',
          shift: 3.16
        }, {
          proton: 'OH',
          coupling: 0,
          multiplicity: 's',
          shift: 4.01
        }],
        nitromethane: [{
          proton: 'CH3',
          coupling: 0,
          multiplicity: 's',
          shift: 4.42
        }],
        'n-pentane': [{
          proton: 'CH3',
          coupling: 7,
          multiplicity: 't',
          shift: 0.88
        }, {
          proton: 'CH2',
          coupling: 0,
          multiplicity: 'm',
          shift: 1.27
        }],
        '2-propanol': [{
          proton: 'CH3',
          coupling: 6,
          multiplicity: 'd',
          shift: 1.04
        }, {
          proton: 'CH',
          coupling: 6,
          multiplicity: 'sep',
          shift: 3.78
        }],
        pyridine: [{
          proton: 'CH(2)',
          coupling: 0,
          multiplicity: 'm',
          shift: 8.58
        }, {
          proton: 'CH(3)',
          coupling: 0,
          multiplicity: 'm',
          shift: 7.39
        }, {
          proton: 'CH(4)',
          coupling: 0,
          multiplicity: 'm',
          shift: 7.79
        }],
        silicone_greasei: [],
        tetrahydrofuran: [{
          proton: 'CH2',
          coupling: 0,
          multiplicity: 'm',
          shift: 1.76
        }, {
          proton: 'CH2O',
          coupling: 0,
          multiplicity: 'm',
          shift: 3.6
        }],
        toluene: [{
          proton: 'CH3',
          coupling: 0,
          multiplicity: 's',
          shift: 2.3
        }, {
          proton: 'CH(o/p)',
          coupling: 0,
          multiplicity: 'm',
          shift: 7.18
        }, {
          proton: 'CH(m)',
          coupling: 0,
          multiplicity: 'm',
          shift: 7.25
        }],
        triethylamine: [{
          proton: 'CH3',
          coupling: 7,
          multiplicity: 't',
          shift: 0.93
        }, {
          proton: 'CH2',
          coupling: 7,
          multiplicity: 'q',
          shift: 2.43
        }]
      },
      c6d6: {
        tms: [{
          proton: 'X',
          coupling: 0,
          multiplicity: '',
          shift: 0
        }],
        solvent: [{
          proton: 'X',
          coupling: 0,
          multiplicity: '',
          shift: 7.16
        }],
        h2o: [{
          proton: 'H2O',
          coupling: 0,
          multiplicity: 's',
          shift: 0.4
        }],
        acetic_acid: [{
          proton: 'CH3',
          coupling: 0,
          multiplicity: 's',
          shift: 1.55
        }],
        acetone: [{
          proton: 'CH3',
          coupling: 0,
          multiplicity: 's',
          shift: 1.55
        }],
        acetonitrile: [{
          proton: 'CH3',
          coupling: 0,
          multiplicity: 's',
          shift: 1.55
        }],
        benzene: [{
          proton: 'CH',
          coupling: 0,
          multiplicity: 's',
          shift: 7.15
        }],
        'tert-butyl_alcohol': [{
          proton: 'CH3',
          coupling: 0,
          multiplicity: 's',
          shift: 1.05
        }, {
          proton: 'OHc',
          coupling: 0,
          multiplicity: 's',
          shift: 1.55
        }],
        'tert-butyl_methyl_ether': [{
          proton: 'CCH3',
          coupling: 0,
          multiplicity: 's',
          shift: 1.07
        }, {
          proton: 'OCH3',
          coupling: 0,
          multiplicity: 's',
          shift: 3.04
        }],
        bhtb: [{
          proton: 'ArH',
          coupling: 0,
          multiplicity: 's',
          shift: 7.05
        }, {
          proton: 'OHc',
          coupling: 0,
          multiplicity: 's',
          shift: 4.79
        }, {
          proton: 'ArCH3',
          coupling: 0,
          multiplicity: 's',
          shift: 2.24
        }, {
          proton: 'ArC(CH3)3',
          coupling: 0,
          multiplicity: 's',
          shift: 1.38
        }],
        chloroform: [{
          proton: 'CH',
          coupling: 0,
          multiplicity: 's',
          shift: 6.15
        }],
        cyclohexane: [{
          proton: 'CH2',
          coupling: 0,
          multiplicity: 's',
          shift: 1.4
        }],
        '1,2-dichloroethane': [{
          proton: 'CH2',
          coupling: 0,
          multiplicity: 's',
          shift: 2.9
        }],
        dichloromethane: [{
          proton: 'CH2',
          coupling: 0,
          multiplicity: 's',
          shift: 4.27
        }],
        diethyl_ether: [{
          proton: 'CH3',
          coupling: 7,
          multiplicity: 't',
          shift: 1.11
        }, {
          proton: 'CH2',
          coupling: 7,
          multiplicity: 'q',
          shift: 3.26
        }],
        diglyme: [{
          proton: 'CH2',
          coupling: 0,
          multiplicity: 'm',
          shift: 3.46
        }, {
          proton: 'CH2',
          coupling: 0,
          multiplicity: 'm',
          shift: 3.34
        }, {
          proton: 'OCH3',
          coupling: 0,
          multiplicity: 's',
          shift: 3.11
        }],
        '1,2-dimethoxyethane': [{
          proton: 'CH3',
          coupling: 0,
          multiplicity: 's',
          shift: 3.12
        }, {
          proton: 'CH2',
          coupling: 0,
          multiplicity: 's',
          shift: 3.33
        }],
        dimethylacetamide: [{
          proton: 'CH3CO',
          coupling: 0,
          multiplicity: 's',
          shift: 1.6
        }, {
          proton: 'NCH3',
          coupling: 0,
          multiplicity: 's',
          shift: 2.57
        }, {
          proton: 'NCH3',
          coupling: 0,
          multiplicity: 's',
          shift: 2.05
        }],
        dimethylformamide: [{
          proton: 'CH',
          coupling: 0,
          multiplicity: 's',
          shift: 7.63
        }, {
          proton: 'CH3',
          coupling: 0,
          multiplicity: 's',
          shift: 2.36
        }, {
          proton: 'CH3',
          coupling: 0,
          multiplicity: 's',
          shift: 1.86
        }],
        dimethyl_sulfoxide: [{
          proton: 'CH3',
          coupling: 0,
          multiplicity: 's',
          shift: 1.68
        }],
        dioxane: [{
          proton: 'CH2',
          coupling: 0,
          multiplicity: 's',
          shift: 3.35
        }],
        ethanol: [{
          proton: 'CH3',
          coupling: 7,
          multiplicity: 't',
          shift: 0.96
        }, {
          proton: 'CH2',
          coupling: 7,
          multiplicity: 'q',
          shift: 3.34
        }],
        ethyl_acetate: [{
          proton: 'CH3CO',
          coupling: 0,
          multiplicity: 's',
          shift: 1.65
        }, {
          proton: 'CH2CH3',
          coupling: 7,
          multiplicity: 'q',
          shift: 3.89
        }, {
          proton: 'CH2CH3',
          coupling: 7,
          multiplicity: 't',
          shift: 0.92
        }],
        ethyl_methyl_ketone: [{
          proton: 'CH3CO',
          coupling: 0,
          multiplicity: 's',
          shift: 1.58
        }, {
          proton: 'CH2CH3',
          coupling: 7,
          multiplicity: 'q',
          shift: 1.81
        }, {
          proton: 'CH2CH3',
          coupling: 7,
          multiplicity: 't',
          shift: 0.85
        }],
        ethylene_glycol: [{
          proton: 'CH',
          coupling: 0,
          multiplicity: 's',
          shift: 3.41
        }],
        'grease^f': [{
          proton: 'CH3',
          coupling: 0,
          multiplicity: 'm',
          shift: 0.92
        }, {
          proton: 'CH2',
          coupling: 0,
          multiplicity: 'br_s',
          shift: 1.36
        }],
        'n-hexane': [{
          proton: 'CH3',
          coupling: 0,
          multiplicity: 't',
          shift: 0.89
        }, {
          proton: 'CH2',
          coupling: 0,
          multiplicity: 'm',
          shift: 1.24
        }],
        hmpag: [{
          proton: 'CH3',
          coupling: 9.5,
          multiplicity: 'd',
          shift: 2.4
        }],
        methanol: [{
          proton: 'CH3',
          coupling: 0,
          multiplicity: 's',
          shift: 3.07
        }],
        nitromethane: [{
          proton: 'CH3',
          coupling: 0,
          multiplicity: 's',
          shift: 2.94
        }],
        'n-pentane': [{
          proton: 'CH3',
          coupling: 7,
          multiplicity: 't',
          shift: 0.86
        }, {
          proton: 'CH2',
          coupling: 0,
          multiplicity: 'm',
          shift: 1.23
        }],
        '2-propanol': [{
          proton: 'CH3',
          coupling: 6,
          multiplicity: 'd',
          shift: 0.95
        }, {
          proton: 'CH',
          coupling: 6,
          multiplicity: 'sep',
          shift: 3.67
        }],
        pyridine: [{
          proton: 'CH(2)',
          coupling: 0,
          multiplicity: 'm',
          shift: 8.53
        }, {
          proton: 'CH(3)',
          coupling: 0,
          multiplicity: 'm',
          shift: 6.66
        }, {
          proton: 'CH(4)',
          coupling: 0,
          multiplicity: 'm',
          shift: 6.98
        }],
        silicone_greasei: [{
          proton: 'CH3',
          coupling: 0,
          multiplicity: 's',
          shift: 0.29
        }],
        tetrahydrofuran: [{
          proton: 'CH2',
          coupling: 0,
          multiplicity: 'm',
          shift: 1.4
        }, {
          proton: 'CH2O',
          coupling: 0,
          multiplicity: 'm',
          shift: 3.57
        }],
        toluene: [{
          proton: 'CH3',
          coupling: 0,
          multiplicity: 's',
          shift: 2.11
        }, {
          proton: 'CH(o/p)',
          coupling: 0,
          multiplicity: 'm',
          shift: 7.02
        }, {
          proton: 'CH(m)',
          coupling: 0,
          multiplicity: 'm',
          shift: 7.13
        }],
        triethylamine: [{
          proton: 'CH3',
          coupling: 7,
          multiplicity: 't',
          shift: 0.96
        }, {
          proton: 'CH2',
          coupling: 7,
          multiplicity: 'q',
          shift: 2.4
        }]
      },
      cd3cn: {
        tms: [{
          proton: 'X',
          coupling: 0,
          multiplicity: '',
          shift: 0
        }],
        solvent: [{
          proton: 'X',
          coupling: 0,
          multiplicity: '',
          shift: 1.94
        }],
        h2o: [{
          proton: 'H2O',
          coupling: 0,
          multiplicity: 's',
          shift: 2.13
        }],
        acetic_acid: [{
          proton: 'CH3',
          coupling: 0,
          multiplicity: 's',
          shift: 1.96
        }],
        acetone: [{
          proton: 'CH3',
          coupling: 0,
          multiplicity: 's',
          shift: 2.08
        }],
        acetonitrile: [{
          proton: 'CH3',
          coupling: 0,
          multiplicity: 's',
          shift: 1.96
        }],
        benzene: [{
          proton: 'CH',
          coupling: 0,
          multiplicity: 's',
          shift: 7.37
        }],
        'tert-butyl_alcohol': [{
          proton: 'CH3',
          coupling: 0,
          multiplicity: 's',
          shift: 1.16
        }, {
          proton: 'OHc',
          coupling: 0,
          multiplicity: 's',
          shift: 2.18
        }],
        'tert-butyl_methyl_ether': [{
          proton: 'CCH3',
          coupling: 0,
          multiplicity: 's',
          shift: 1.14
        }, {
          proton: 'OCH3',
          coupling: 0,
          multiplicity: 's',
          shift: 3.13
        }],
        bhtb: [{
          proton: 'ArH',
          coupling: 0,
          multiplicity: 's',
          shift: 6.97
        }, {
          proton: 'OHc',
          coupling: 0,
          multiplicity: 's',
          shift: 5.2
        }, {
          proton: 'ArCH3',
          coupling: 0,
          multiplicity: 's',
          shift: 2.22
        }, {
          proton: 'ArC(CH3)3',
          coupling: 0,
          multiplicity: 's',
          shift: 1.39
        }],
        chloroform: [{
          proton: 'CH',
          coupling: 0,
          multiplicity: 's',
          shift: 7.58
        }],
        cyclohexane: [{
          proton: 'CH2',
          coupling: 0,
          multiplicity: 's',
          shift: 1.44
        }],
        '1,2-dichloroethane': [{
          proton: 'CH2',
          coupling: 0,
          multiplicity: 's',
          shift: 3.81
        }],
        dichloromethane: [{
          proton: 'CH2',
          coupling: 0,
          multiplicity: 's',
          shift: 5.44
        }],
        diethyl_ether: [{
          proton: 'CH3',
          coupling: 7,
          multiplicity: 't',
          shift: 1.12
        }, {
          proton: 'CH2',
          coupling: 7,
          multiplicity: 'q',
          shift: 3.42
        }],
        diglyme: [{
          proton: 'CH2',
          coupling: 0,
          multiplicity: 'm',
          shift: 3.53
        }, {
          proton: 'CH2',
          coupling: 0,
          multiplicity: 'm',
          shift: 3.45
        }, {
          proton: 'OCH3',
          coupling: 0,
          multiplicity: 's',
          shift: 3.29
        }],
        '1,2-dimethoxyethane': [{
          proton: 'CH3',
          coupling: 0,
          multiplicity: 's',
          shift: 3.28
        }, {
          proton: 'CH2',
          coupling: 0,
          multiplicity: 's',
          shift: 3.45
        }],
        dimethylacetamide: [{
          proton: 'CH3CO',
          coupling: 0,
          multiplicity: 's',
          shift: 1.97
        }, {
          proton: 'NCH3',
          coupling: 0,
          multiplicity: 's',
          shift: 2.96
        }, {
          proton: 'NCH3',
          coupling: 0,
          multiplicity: 's',
          shift: 2.83
        }],
        dimethylformamide: [{
          proton: 'CH',
          coupling: 0,
          multiplicity: 's',
          shift: 7.92
        }, {
          proton: 'CH3',
          coupling: 0,
          multiplicity: 's',
          shift: 2.89
        }, {
          proton: 'CH3',
          coupling: 0,
          multiplicity: 's',
          shift: 2.77
        }],
        dimethyl_sulfoxide: [{
          proton: 'CH3',
          coupling: 0,
          multiplicity: 's',
          shift: 2.5
        }],
        dioxane: [{
          proton: 'CH2',
          coupling: 0,
          multiplicity: 's',
          shift: 3.6
        }],
        ethanol: [{
          proton: 'CH3',
          coupling: 7,
          multiplicity: 't',
          shift: 1.12
        }, {
          proton: 'CH2',
          coupling: 7,
          multiplicity: 'q',
          shift: 3.54
        }, {
          proton: 'OH',
          coupling: 5,
          multiplicity: 's,t',
          shift: 2.47
        }],
        ethyl_acetate: [{
          proton: 'CH3CO',
          coupling: 0,
          multiplicity: 's',
          shift: 1.97
        }, {
          proton: 'CH2CH3',
          coupling: 7,
          multiplicity: 'q',
          shift: 4.06
        }, {
          proton: 'CH2CH3',
          coupling: 7,
          multiplicity: 't',
          shift: 1.2
        }],
        ethyl_methyl_ketone: [{
          proton: 'CH3CO',
          coupling: 0,
          multiplicity: 's',
          shift: 2.06
        }, {
          proton: 'CH2CH3',
          coupling: 7,
          multiplicity: 'q',
          shift: 2.43
        }, {
          proton: 'CH2CH3',
          coupling: 7,
          multiplicity: 't',
          shift: 0.96
        }],
        ethylene_glycol: [{
          proton: 'CH',
          coupling: 0,
          multiplicity: 's',
          shift: 3.51
        }],
        'grease^f': [{
          proton: 'CH3',
          coupling: 0,
          multiplicity: 'm',
          shift: 0.86
        }, {
          proton: 'CH2',
          coupling: 0,
          multiplicity: 'br_s',
          shift: 1.27
        }],
        'n-hexane': [{
          proton: 'CH3',
          coupling: 0,
          multiplicity: 't',
          shift: 0.89
        }, {
          proton: 'CH2',
          coupling: 0,
          multiplicity: 'm',
          shift: 1.28
        }],
        hmpag: [{
          proton: 'CH3',
          coupling: 9.5,
          multiplicity: 'd',
          shift: 2.57
        }],
        methanol: [{
          proton: 'CH3',
          coupling: 0,
          multiplicity: 's',
          shift: 3.28
        }, {
          proton: 'OH',
          coupling: 0,
          multiplicity: 's',
          shift: 2.16
        }],
        nitromethane: [{
          proton: 'CH3',
          coupling: 0,
          multiplicity: 's',
          shift: 4.31
        }],
        'n-pentane': [{
          proton: 'CH3',
          coupling: 7,
          multiplicity: 't',
          shift: 0.87
        }, {
          proton: 'CH2',
          coupling: 0,
          multiplicity: 'm',
          shift: 1.29
        }],
        '2-propanol': [{
          proton: 'CH3',
          coupling: 6,
          multiplicity: 'd',
          shift: 1.09
        }, {
          proton: 'CH',
          coupling: 6,
          multiplicity: 'sep',
          shift: 3.87
        }],
        pyridine: [{
          proton: 'CH(2)',
          coupling: 0,
          multiplicity: 'm',
          shift: 8.57
        }, {
          proton: 'CH(3)',
          coupling: 0,
          multiplicity: 'm',
          shift: 7.33
        }, {
          proton: 'CH(4)',
          coupling: 0,
          multiplicity: 'm',
          shift: 7.73
        }],
        silicone_greasei: [{
          proton: 'CH3',
          coupling: 0,
          multiplicity: 's',
          shift: 0.08
        }],
        tetrahydrofuran: [{
          proton: 'CH2',
          coupling: 0,
          multiplicity: 'm',
          shift: 1.8
        }, {
          proton: 'CH2O',
          coupling: 0,
          multiplicity: 'm',
          shift: 3.64
        }],
        toluene: [{
          proton: 'CH3',
          coupling: 0,
          multiplicity: 's',
          shift: 2.33
        }, {
          proton: 'CH(o/p)',
          coupling: 0,
          multiplicity: 'm',
          shift: 7.2
        }, {
          proton: 'CH(m)',
          coupling: 0,
          multiplicity: 'm',
          shift: 7.2
        }],
        triethylamine: [{
          proton: 'CH3',
          coupling: 7,
          multiplicity: 't',
          shift: 0.96
        }, {
          proton: 'CH2',
          coupling: 7,
          multiplicity: 'q',
          shift: 2.45
        }]
      },
      cd3od: {
        tms: [{
          proton: 'X',
          coupling: 0,
          multiplicity: '',
          shift: 0
        }],
        solvent: [{
          proton: 'X',
          coupling: 0,
          multiplicity: '',
          shift: 3.31
        }],
        h2o: [{
          proton: 'H2O',
          coupling: 0,
          multiplicity: 's',
          shift: 4.87
        }],
        acetic_acid: [{
          proton: 'CH3',
          coupling: 0,
          multiplicity: 's',
          shift: 1.99
        }],
        acetone: [{
          proton: 'CH3',
          coupling: 0,
          multiplicity: 's',
          shift: 2.15
        }],
        acetonitrile: [{
          proton: 'CH3',
          coupling: 0,
          multiplicity: 's',
          shift: 2.03
        }],
        benzene: [{
          proton: 'CH',
          coupling: 0,
          multiplicity: 's',
          shift: 7.33
        }],
        'tert-butyl_alcohol': [{
          proton: 'CH3',
          coupling: 0,
          multiplicity: 's',
          shift: 1.4
        }],
        'tert-butyl_methyl_ether': [{
          proton: 'CCH3',
          coupling: 0,
          multiplicity: 's',
          shift: 1.15
        }, {
          proton: 'OCH3',
          coupling: 0,
          multiplicity: 's',
          shift: 3.2
        }],
        bhtb: [{
          proton: 'ArH',
          coupling: 0,
          multiplicity: 's',
          shift: 6.92
        }, {
          proton: 'ArCH3',
          coupling: 0,
          multiplicity: 's',
          shift: 2.21
        }, {
          proton: 'ArC(CH3)3',
          coupling: 0,
          multiplicity: 's',
          shift: 1.4
        }],
        chloroform: [{
          proton: 'CH',
          coupling: 0,
          multiplicity: 's',
          shift: 7.9
        }],
        cyclohexane: [{
          proton: 'CH2',
          coupling: 0,
          multiplicity: 's',
          shift: 1.45
        }],
        '1,2-dichloroethane': [{
          proton: 'CH2',
          coupling: 0,
          multiplicity: 's',
          shift: 3.78
        }],
        dichloromethane: [{
          proton: 'CH2',
          coupling: 0,
          multiplicity: 's',
          shift: 5.49
        }],
        diethyl_ether: [{
          proton: 'CH3',
          coupling: 7,
          multiplicity: 't',
          shift: 1.18
        }, {
          proton: 'CH2',
          coupling: 7,
          multiplicity: 'q',
          shift: 3.49
        }],
        diglyme: [{
          proton: 'CH2',
          coupling: 0,
          multiplicity: 'm',
          shift: 3.61
        }, {
          proton: 'CH2',
          coupling: 0,
          multiplicity: 'm',
          shift: 3.58
        }, {
          proton: 'OCH3',
          coupling: 0,
          multiplicity: 's',
          shift: 3.35
        }],
        '1,2-dimethoxyethane': [{
          proton: 'CH3',
          coupling: 0,
          multiplicity: 's',
          shift: 3.35
        }, {
          proton: 'CH2',
          coupling: 0,
          multiplicity: 's',
          shift: 3.52
        }],
        dimethylacetamide: [{
          proton: 'CH3CO',
          coupling: 0,
          multiplicity: 's',
          shift: 2.07
        }, {
          proton: 'NCH3',
          coupling: 0,
          multiplicity: 's',
          shift: 3.31
        }, {
          proton: 'NCH3',
          coupling: 0,
          multiplicity: 's',
          shift: 2.92
        }],
        dimethylformamide: [{
          proton: 'CH',
          coupling: 0,
          multiplicity: 's',
          shift: 7.97
        }, {
          proton: 'CH3',
          coupling: 0,
          multiplicity: 's',
          shift: 2.99
        }, {
          proton: 'CH3',
          coupling: 0,
          multiplicity: 's',
          shift: 2.86
        }],
        dimethyl_sulfoxide: [{
          proton: 'CH3',
          coupling: 0,
          multiplicity: 's',
          shift: 2.65
        }],
        dioxane: [{
          proton: 'CH2',
          coupling: 0,
          multiplicity: 's',
          shift: 3.66
        }],
        ethanol: [{
          proton: 'CH3',
          coupling: 7,
          multiplicity: 't',
          shift: 1.19
        }, {
          proton: 'CH2',
          coupling: 7,
          multiplicity: 'q',
          shift: 3.6
        }],
        ethyl_acetate: [{
          proton: 'CH3CO',
          coupling: 0,
          multiplicity: 's',
          shift: 2.01
        }, {
          proton: 'CH2CH3',
          coupling: 7,
          multiplicity: 'q',
          shift: 4.09
        }, {
          proton: 'CH2CH3',
          coupling: 7,
          multiplicity: 't',
          shift: 1.24
        }],
        ethyl_methyl_ketone: [{
          proton: 'CH3CO',
          coupling: 0,
          multiplicity: 's',
          shift: 2.12
        }, {
          proton: 'CH2CH3',
          coupling: 7,
          multiplicity: 'q',
          shift: 2.5
        }, {
          proton: 'CH2CH3',
          coupling: 7,
          multiplicity: 't',
          shift: 1.01
        }],
        ethylene_glycol: [{
          proton: 'CH',
          coupling: 0,
          multiplicity: 's',
          shift: 3.59
        }],
        'grease^f': [{
          proton: 'CH3',
          coupling: 0,
          multiplicity: 'm',
          shift: 0.88
        }, {
          proton: 'CH2',
          coupling: 0,
          multiplicity: 'br_s',
          shift: 1.29
        }],
        'n-hexane': [{
          proton: 'CH3',
          coupling: 0,
          multiplicity: 't',
          shift: 0.9
        }, {
          proton: 'CH2',
          coupling: 0,
          multiplicity: 'm',
          shift: 1.29
        }],
        hmpag: [{
          proton: 'CH3',
          coupling: 9.5,
          multiplicity: 'd',
          shift: 2.64
        }],
        methanol: [{
          proton: 'CH3',
          coupling: 0,
          multiplicity: 's',
          shift: 3.34
        }],
        nitromethane: [{
          proton: 'CH3',
          coupling: 0,
          multiplicity: 's',
          shift: 4.34
        }],
        'n-pentane': [{
          proton: 'CH3',
          coupling: 7,
          multiplicity: 't',
          shift: 0.89
        }, {
          proton: 'CH2',
          coupling: 0,
          multiplicity: 'm',
          shift: 1.29
        }],
        '2-propanol': [{
          proton: 'CH3',
          coupling: 6,
          multiplicity: 'd',
          shift: 1.5
        }, {
          proton: 'CH',
          coupling: 6,
          multiplicity: 'sep',
          shift: 3.92
        }],
        pyridine: [{
          proton: 'CH(2)',
          coupling: 0,
          multiplicity: 'm',
          shift: 8.53
        }, {
          proton: 'CH(3)',
          coupling: 0,
          multiplicity: 'm',
          shift: 7.44
        }, {
          proton: 'CH(4)',
          coupling: 0,
          multiplicity: 'm',
          shift: 7.85
        }],
        silicone_greasei: [{
          proton: 'CH3',
          coupling: 0,
          multiplicity: 's',
          shift: 0.1
        }],
        tetrahydrofuran: [{
          proton: 'CH2',
          coupling: 0,
          multiplicity: 'm',
          shift: 1.87
        }, {
          proton: 'CH2O',
          coupling: 0,
          multiplicity: 'm',
          shift: 3.71
        }],
        toluene: [{
          proton: 'CH3',
          coupling: 0,
          multiplicity: 's',
          shift: 2.32
        }, {
          proton: 'CH(o/p)',
          coupling: 0,
          multiplicity: 'm',
          shift: 7.16
        }, {
          proton: 'CH(m)',
          coupling: 0,
          multiplicity: 'm',
          shift: 7.16
        }],
        triethylamine: [{
          proton: 'CH3',
          coupling: 7,
          multiplicity: 't',
          shift: 1.05
        }, {
          proton: 'CH2',
          coupling: 7,
          multiplicity: 'q',
          shift: 2.58
        }]
      },
      d2o: {
        tms: [{
          proton: 'X',
          coupling: 0,
          multiplicity: '',
          shift: 0
        }],
        solvent: [{
          proton: 'X',
          coupling: 0,
          multiplicity: '',
          shift: 4.79
        }],
        h2o: [],
        acetic_acid: [{
          proton: 'CH3',
          coupling: 0,
          multiplicity: 's',
          shift: 2.08
        }],
        acetone: [{
          proton: 'CH3',
          coupling: 0,
          multiplicity: 's',
          shift: 2.22
        }],
        acetonitrile: [{
          proton: 'CH3',
          coupling: 0,
          multiplicity: 's',
          shift: 2.06
        }],
        benzene: [],
        'tert-butyl_alcohol': [{
          proton: 'CH3',
          coupling: 0,
          multiplicity: 's',
          shift: 1.24
        }],
        'tert-butyl_methyl_ether': [{
          proton: 'CCH3',
          coupling: 0,
          multiplicity: 's',
          shift: 1.21
        }, {
          proton: 'OCH3',
          coupling: 0,
          multiplicity: 's',
          shift: 3.22
        }],
        bhtb: [],
        chloroform: [],
        cyclohexane: [],
        '1,2-dichloroethane': [],
        dichloromethane: [],
        diethyl_ether: [{
          proton: 'CH3',
          coupling: 7,
          multiplicity: 't',
          shift: 1.17
        }, {
          proton: 'CH2',
          coupling: 7,
          multiplicity: 'q',
          shift: 3.56
        }],
        diglyme: [{
          proton: 'CH2',
          coupling: 0,
          multiplicity: 'm',
          shift: 3.67
        }, {
          proton: 'CH2',
          coupling: 0,
          multiplicity: 'm',
          shift: 3.61
        }, {
          proton: 'OCH3',
          coupling: 0,
          multiplicity: 's',
          shift: 3.37
        }],
        '1,2-dimethoxyethane': [{
          proton: 'CH3',
          coupling: 0,
          multiplicity: 's',
          shift: 3.37
        }, {
          proton: 'CH2',
          coupling: 0,
          multiplicity: 's',
          shift: 3.6
        }],
        dimethylacetamide: [{
          proton: 'CH3CO',
          coupling: 0,
          multiplicity: 's',
          shift: 2.08
        }, {
          proton: 'NCH3',
          coupling: 0,
          multiplicity: 's',
          shift: 3.06
        }, {
          proton: 'NCH3',
          coupling: 0,
          multiplicity: 's',
          shift: 2.9
        }],
        dimethylformamide: [{
          proton: 'CH',
          coupling: 0,
          multiplicity: 's',
          shift: 7.92
        }, {
          proton: 'CH3',
          coupling: 0,
          multiplicity: 's',
          shift: 3.01
        }, {
          proton: 'CH3',
          coupling: 0,
          multiplicity: 's',
          shift: 2.85
        }],
        dimethyl_sulfoxide: [{
          proton: 'CH3',
          coupling: 0,
          multiplicity: 's',
          shift: 2.71
        }],
        dioxane: [{
          proton: 'CH2',
          coupling: 0,
          multiplicity: 's',
          shift: 3.75
        }],
        ethanol: [{
          proton: 'CH3',
          coupling: 7,
          multiplicity: 't',
          shift: 1.17
        }, {
          proton: 'CH2',
          coupling: 7,
          multiplicity: 'q',
          shift: 3.65
        }],
        ethyl_acetate: [{
          proton: 'CH3CO',
          coupling: 0,
          multiplicity: 's',
          shift: 2.07
        }, {
          proton: 'CH2CH3',
          coupling: 7,
          multiplicity: 'q',
          shift: 4.14
        }, {
          proton: 'CH2CH3',
          coupling: 7,
          multiplicity: 't',
          shift: 1.24
        }],
        ethyl_methyl_ketone: [{
          proton: 'CH3CO',
          coupling: 0,
          multiplicity: 's',
          shift: 2.19
        }, {
          proton: 'CH2CH3',
          coupling: 7,
          multiplicity: 'q',
          shift: 3.18
        }, {
          proton: 'CH2CH3',
          coupling: 7,
          multiplicity: 't',
          shift: 1.26
        }],
        ethylene_glycol: [{
          proton: 'CH',
          coupling: 0,
          multiplicity: 's',
          shift: 3.65
        }],
        'grease^f': [],
        'n-hexane': [],
        hmpag: [{
          proton: 'CH3',
          coupling: 9.5,
          multiplicity: 'd',
          shift: 2.61
        }],
        methanol: [{
          proton: 'CH3',
          coupling: 0,
          multiplicity: 's',
          shift: 3.34
        }],
        nitromethane: [{
          proton: 'CH3',
          coupling: 0,
          multiplicity: 's',
          shift: 4.4
        }],
        'n-pentane': [{
          proton: 'CH3',
          coupling: 7,
          multiplicity: 't',
          shift: 0.9
        }],
        '2-propanol': [{
          proton: 'CH3',
          coupling: 6,
          multiplicity: 'd',
          shift: 1.17
        }, {
          proton: 'CH',
          coupling: 6,
          multiplicity: 'sep',
          shift: 4.02
        }],
        pyridine: [{
          proton: 'CH(2)',
          coupling: 0,
          multiplicity: 'm',
          shift: 8.52
        }, {
          proton: 'CH(3)',
          coupling: 0,
          multiplicity: 'm',
          shift: 7.45
        }, {
          proton: 'CH(4)',
          coupling: 0,
          multiplicity: 'm',
          shift: 7.87
        }],
        silicone_greasei: [],
        tetrahydrofuran: [{
          proton: 'CH2',
          coupling: 0,
          multiplicity: 'm',
          shift: 1.88
        }, {
          proton: 'CH2O',
          coupling: 0,
          multiplicity: 'm',
          shift: 3.74
        }],
        toluene: [],
        triethylamine: [{
          proton: 'CH3',
          coupling: 7,
          multiplicity: 't',
          shift: 0.99
        }, {
          proton: 'CH2',
          coupling: 7,
          multiplicity: 'q',
          shift: 2.57
        }]
      }
    };
    const impurities = impuritiesContent;

    const SignalKinds = [{
      label: 'Signal',
      value: 'signal'
    }, {
      label: 'Reference',
      value: 'reference'
    }, {
      label: 'Solvent',
      value: 'solvent'
    }, {
      label: 'Impurity',
      value: 'impurity'
    }, {
      label: 'Standard',
      value: 'standard'
    }, {
      label: 'P1',
      value: 'p1'
    }, {
      label: 'P2',
      value: 'p2'
    }, {
      label: 'P3',
      value: 'p3'
    }];
    const SignalKindsToInclude = ['signal'];
    const DatumKind = {
      signal: 'signal',
      mixed: 'mixed'
    };

    const MultiplicityPatterns = [{
      label: 'singlet',
      value: 's',
      multiplicity: 1,
      names: ['br s', 'br. s']
    }, {
      label: 'triplet',
      value: 't',
      multiplicity: 3,
      names: []
    }, {
      label: 'doublet',
      value: 'd',
      multiplicity: 2,
      names: []
    }, {
      label: 'quartet',
      value: 'q',
      multiplicity: 4,
      names: []
    }, {
      label: 'quintet',
      value: 'i',
      acs: 'quint',
      multiplicity: 5,
      names: ['quint', 'qui', 'qnt', 'pentet', 'pnt', 'pent']
    }, {
      label: 'sextet',
      value: 'h',
      multiplicity: 6,
      names: ['x', 'sxt', 'sext', 'hexuplet']
    }, {
      label: 'septet',
      value: 'p',
      acs: 'sept',
      multiplicity: 7,
      names: ['sept', 'spt', 'heptet', 'hpt', 'hept']
    }, {
      label: 'octet',
      value: 'o',
      multiplicity: 8,
      names: ['oct']
    }, {
      label: 'nonet',
      value: 'n',
      multiplicity: 9,
      names: ['non']
    }, {
      label: 'massive',
      value: 'm',
      multiplicity: null,
      names: []
    }];

    MultiplicityPatterns.sort((a, b) => a.multiplicity !== null && b.multiplicity !== null ? a.multiplicity - b.multiplicity : Number.MAX_SAFE_INTEGER);
    const couplingPatterns = MultiplicityPatterns.map(m => m.value);
    const couplingACSPatterns = MultiplicityPatterns.map(m => m.acs || m.value);

    const toCheck = ['solvent', 'h2o', 'tms'];
    /**
     * Try to remove peaks of impurities.
     */
    function peaksFilterImpurities(peakList, options = {}) {
      const {
        error = 0.025,
        remove = false
      } = options;
      if (options.solvent) {
        let {
          solvent
        } = options;
        if (solvent === '(cd3)2so') solvent = 'dmso';
        if (solvent === 'meod') solvent = 'cd3od';
        const solventImpurities = impurities[solvent];
        for (const impurity of toCheck) {
          const impurityShifts = solventImpurities[impurity];
          checkImpurity(peakList, impurityShifts, {
            error,
            remove,
            name: impurity
          });
        }
      }
      return peakList;
    }
    function checkImpurity(peakList, impurity, options) {
      const {
        name,
        error,
        remove
      } = options;
      let j, tolerance, difference;
      let i = impurity.length;
      while (i--) {
        j = peakList.length;
        while (j--) {
          tolerance = error + peakList[j].width;
          difference = Math.abs(impurity[i].shift - peakList[j].x);
          if (difference < tolerance) {
            // && (impurity[i].multiplicity === '' || (impurity[i].multiplicity.indexOf(peakList[j].multiplicity)) { // some impurities has multiplicities like 'bs' but at presents it is unsupported
            if (remove) {
              peakList.splice(j, 1);
            } else {
              peakList[j].kind = name;
            }
          }
        }
      }
    }

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

    class Gaussian2D {
      constructor(options = {}) {
        let {
          fwhm = 20,
          sd
        } = options;
        fwhm = ensureFWHM2D(fwhm, sd);
        this.fwhmX = fwhm.x;
        this.fwhmY = fwhm.y;
      }
      fct(x, y) {
        return gaussian2DFct(x, y, this.fwhmX, this.fwhmY);
      }
      getData(options = {}) {
        return getGaussian2DData({
          fwhm: {
            x: this.fwhmX,
            y: this.fwhmY
          }
        }, options);
      }
      getFactor(volume = 1) {
        return getGaussianFactor(volume);
      }
      getVolume(height = calculateGaussian2DHeight({
        fwhm: {
          x: this.fwhmX,
          y: this.fwhmY
        },
        volume: 1
      })) {
        return getGaussian2DVolume({
          fwhm: {
            x: this.fwhmX,
            y: this.fwhmY
          },
          height
        });
      }
      widthToFWHM(width) {
        return gaussianWidthToFWHM(width);
      }
      fwhmToWidth(fwhm) {
        return gaussianFwhmToWidth(fwhm);
      }
      calculateHeight(volume = 1) {
        return calculateGaussian2DHeight({
          volume,
          fwhm: {
            x: this.fwhmX,
            y: this.fwhmY
          }
        });
      }
      set fwhm(fwhm) {
        fwhm = ensureXYNumber$2(fwhm);
        this.fwhmX = fwhm.x;
        this.fwhmY = fwhm.y;
      }
    }
    const gaussian2DFct = (x, y, xFWHM, yFWHM) => {
      return Math.exp(GAUSSIAN_EXP_FACTOR * (Math.pow(x / xFWHM, 2) + Math.pow(y / yFWHM, 2)));
    };
    const getGaussian2DData = (shape, options = {}) => {
      let {
        fwhm = 50,
        sd
      } = shape;
      fwhm = ensureFWHM2D(fwhm, sd);
      let {
        factor = getGaussianFactor(),
        length = {
          x: 0,
          y: 0
        },
        height = calculateGaussian2DHeight({
          fwhm,
          volume: 1
        })
      } = options;
      factor = ensureXYNumber$2(factor);
      length = ensureXYNumber$2(length);
      for (const axis of ['x', 'y']) {
        if (!length[axis]) {
          length[axis] = Math.min(Math.ceil(fwhm[axis] * factor[axis]), Math.pow(2, 25) - 1);
          if (length[axis] % 2 === 0) length[axis]++;
        }
      }
      const xCenter = (length.x - 1) / 2;
      const yCenter = (length.y - 1) / 2;
      const data = new Array(length.x);
      for (let i = 0; i < length.x; i++) {
        data[i] = new Float64Array(length.y);
      }
      for (let i = 0; i < length.x; i++) {
        for (let j = 0; j < length.y; j++) {
          data[i][j] = gaussian2DFct(i - xCenter, j - yCenter, fwhm.x, fwhm.y) * height;
        }
      }
      return data;
    };
    const calculateGaussian2DHeight = (options = {}) => {
      let {
        volume = 1,
        fwhm = 50,
        sd
      } = options;
      fwhm = ensureFWHM2D(fwhm, sd);
      return volume * Math.LN2 * 4 / (Math.PI * fwhm.y * fwhm.x);
    };
    const getGaussian2DVolume = (options = {}) => {
      let {
        fwhm = 50,
        height = 1,
        sd
      } = options;
      fwhm = ensureFWHM2D(fwhm, sd);
      return height * Math.PI * fwhm.y * fwhm.x / Math.LN2 / 4;
    };
    function ensureXYNumber$2(input) {
      return typeof input !== 'object' ? {
        x: input,
        y: input
      } : {
        ...input
      };
    }
    function ensureFWHM2D(fwhm, sd) {
      if (sd !== undefined) {
        let sdObject = ensureXYNumber$2(sd);
        return {
          x: gaussianWidthToFWHM(2 * sdObject.x),
          y: gaussianWidthToFWHM(2 * sdObject.y)
        };
      } else if (fwhm !== undefined) {
        return ensureXYNumber$2(fwhm);
      } else {
        throw new Error('ensureFWHM2D must have either fwhm or sd defined');
      }
    }

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

    /**
     * Generate a instance of a specific kind of shape.
     */
    function getShape2D(shape) {
      const {
        kind
      } = shape;
      switch (kind) {
        case 'gaussian':
          return new Gaussian2D(shape);
        default:
          {
            const unHandled = kind;
            // eslint-disable-next-line @typescript-eslint/restrict-template-expressions
            throw Error(`Unknown distribution ${unHandled}`);
          }
      }
    }

    function addBaseline(data, baselineFct) {
      if (!baselineFct) return data;
      let xs = data.x;
      let ys = data.y;
      for (let i = 0; i < xs.length; i++) {
        ys[i] += baselineFct(xs[i]);
      }
      return data;
    }

    /**
     * Calculates reimAbsolute value of a complex spectrum
     *
     * @param data - complex spectrum
     * @returns - reimAbsolute value
     */
    function reimAbsolute(data) {
      const length = data.re.length;
      const re = data.re;
      const im = data.im;
      const newArray = new Float64Array(length);
      for (let i = 0; i < length; i++) {
        newArray[i] = Math.sqrt(re[i] ** 2 + im[i] ** 2);
      }
      return newArray;
    }

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

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

    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;

    /**
     * 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 = {}) {
      let {
        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;
      }
      let 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') {
        let base = (to / from) ** (1 / div);
        let 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('Please choose for the distribution either uniform or log. By default the distribution chosen is uniform.');
      }
      return array;
    }

    /* 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(-Math.pow(x, 2) / (2 * Math.pow(sigma, 2)));
    }

    /**
     * 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) {
        let medianIndex = Math.floor(input.length / 2);
        let median = 0.5 * (input[medianIndex] + input[medianIndex + 1]);
        for (let i = 0; i < input.length; i++) {
          input[i] -= median;
        }
      }
      let 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;
        }
      }
      let signPositive = input.slice(0, lastPositiveValueIndex + 1);
      let signNegative = input.slice(firstNegativeValueIndex);
      let cutOffDist = cutOff || determineCutOff(signPositive, {
        magnitudeMode
      });
      let pIndex = Math.floor(signPositive.length * cutOffDist);
      let initialNoiseLevelPositive = signPositive[pIndex];
      let skyPoint = signPositive[0];
      let initialNoiseLevelNegative;
      if (signNegative.length > 0) {
        let 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))];
        }
      }
      let correctionFactor = -simpleNormInv(cutOffDist / 2, {
        magnitudeMode
      });
      initialNoiseLevelPositive = initialNoiseLevelPositive / correctionFactor;
      initialNoiseLevelNegative = initialNoiseLevelNegative / correctionFactor;
      let effectiveCutOffDist, refinedCorrectionFactor;
      if (refine && cutOffSignalsIndexPlus > -1) {
        effectiveCutOffDist = (cutOffDist * cloneSignPositive.length + cutOffSignalsIndexPlus) / (cloneSignPositive.length + cutOffSignalsIndexPlus);
        refinedCorrectionFactor = -1 * simpleNormInv(effectiveCutOffDist / 2, {
          magnitudeMode
        });
        noiseLevelPositive /= refinedCorrectionFactor;
        if (cutOffSignalsIndexNeg > -1) {
          effectiveCutOffDist = (cutOffDist * cloneSignNegative.length + cutOffSignalsIndexNeg) / (cloneSignNegative.length + cutOffSignalsIndexNeg);
          refinedCorrectionFactor = -1 * simpleNormInv(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 = {}) {
      let {
        magnitudeMode = false,
        considerList = {
          from: 0.5,
          step: 0.1,
          to: 0.9
        }
      } = options;
      //generate a list of values for
      let cutOff = [];
      let indexMax = signPositive.length - 1;
      for (let i = 0.01; i <= 0.99; i += 0.01) {
        let index = Math.round(indexMax * i);
        let value = -signPositive[index] / simpleNormInv([i / 2], {
          magnitudeMode
        });
        cutOff.push([i, value]);
      }
      let minKi = Number.MAX_SAFE_INTEGER;
      let {
        from,
        to,
        step
      } = considerList;
      let delta = step / 2;
      let whereToCutStat = 0.5;
      for (let i = from; i <= to; i += step) {
        let floor = i - delta;
        let top = i + delta;
        let elementsOfCutOff = cutOff.filter(e => e[0] < top && e[0] > floor);
        let averageValue = elementsOfCutOff.reduce((a, b) => a + Math.abs(b[1]), 0);
        let kiSqrt = 0;
        for (const element of elementsOfCutOff) {
          kiSqrt += Math.pow(element[1] - averageValue, 2);
        }
        if (kiSqrt < minKi) {
          minKi = kiSqrt;
          whereToCutStat = i;
        }
      }
      return whereToCutStat;
    }
    /**
     * SimpleNormInvs.
     *
     * @param data - Data 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 Result.
     */
    function simpleNormInv(data, options = {}) {
      const {
        magnitudeMode = false
      } = options;
      if (!Array.isArray(data)) data = [data];
      let from = 0;
      let to = 2;
      let step = 0.01;
      let xTraining = Array.from(createArray(from, to, step));
      let result = new Float64Array(data.length);
      let yTraining = new Float64Array(xTraining.length);
      if (magnitudeMode) {
        let factor = 1;
        for (let i = 0; i < yTraining.length; i++) {
          let finalInput = xTraining[i] * factor;
          yTraining[i] = 1 - rayleighCdf(finalInput);
        }
        let interp = new SplineInterpolator$1(xTraining, yTraining);
        for (let i = 0; i < result.length; i++) {
          let 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.length === 1 ? result[0] : result;
    }
    /**
     * CreateArray.
     *
     * @param from - From.
     * @param to - To.
     * @param step - Step.
     * @returns Array of results.
     */
    function createArray(from, to, step) {
      // Changed Array to Float64Array
      let result = new Float64Array(Math.abs((from - to) / step + 1));
      for (let i = 0; i < result.length; i++) {
        result[i] = 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;
      let sanplot = {};
      for (let key in fromTo) {
        let {
          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
      };
    }

    /**
     * Phase correction filter
     *
     * @param data - complex spectrum
     * @param phi0 - Angle in radians for zero order phase correction
     * @param phi1 - Angle in radians for first order phase correction
     * @returns - returns a new object {re:[], im:[]}
     */
    function reimPhaseCorrection(data, phi0 = 0, phi1 = 0, options = {}) {
      const {
        reverse = false
      } = options;
      phi0 = Number.isFinite(phi0) ? phi0 : 0;
      phi1 = Number.isFinite(phi1) ? phi1 : 0;
      const re = data.re;
      const im = data.im;
      const length = data.re.length;
      let firstAngle = phi0;
      let delta = phi1 / length;
      if (reverse) {
        delta *= -1;
        firstAngle += phi1;
      }
      const alpha = 2 * Math.pow(Math.sin(delta / 2), 2);
      const beta = Math.sin(delta);
      let cosTheta = Math.cos(firstAngle);
      let sinTheta = Math.sin(firstAngle);
      const newRe = new Float64Array(length);
      const newIm = new Float64Array(length);
      for (let i = 0; i < length; i++) {
        newRe[i] = re[i] * cosTheta - im[i] * sinTheta;
        newIm[i] = im[i] * cosTheta + re[i] * sinTheta;
        // calculate angles i+1 from i
        let newCosTheta = cosTheta - (alpha * cosTheta + beta * sinTheta);
        let newSinTheta = sinTheta - (alpha * sinTheta - beta * cosTheta);
        cosTheta = newCosTheta;
        sinTheta = newSinTheta;
      }
      return {
        re: newRe,
        im: newIm
      };
    }

    /**
     * Implementation of the algorithm for automatic phase correction: A robust, general automatic phase
     * correction algorithm for high-resolution NMR data. 10.1002/mrc.4586
     *
     * @param data - complex spectrum
     * @param options - options
     */
    function reimAutoPhaseCorrection(data, options = {}) {
      const {
        re,
        im
      } = data;
      const length = re.length;
      const {
        magnitudeMode = true,
        minRegSize = 30,
        factorNoise = 3,
        maxDistanceToJoin = 256,
        reverse = false
      } = options;
      let magnitudeData = magnitudeMode ? reimAbsolute(data) : re;
      let ds = holoborodko(magnitudeData);
      let peaksDs = robustBaseLineRegionsDetection(ds, {
        maxDistanceToJoin,
        magnitudeMode,
        factorNoise
      });
      let peaksSp = robustBaseLineRegionsDetection(magnitudeData, {
        maxDistanceToJoin,
        magnitudeMode,
        factorNoise
      });
      let finalPeaks = new Uint8Array(length);
      for (let i = 0; i < length; i++) {
        finalPeaks[i] = peaksSp[i] && peaksDs[i];
      }
      // Once the regions are detected, we auto phase each of them separately.
      // This part can be put inside a function
      const indexMask = reverse ? i => length - i : i => i;
      let i = -1;
      let x0 = 0;
      let res = [];
      while (i < length) {
        //phase first region
        let reTmp = [];
        let imTmp = [];
        //Look for the first 1 in the array
        while (!finalPeaks[++i] && i < length) {
          //Add some extra points(0.1 ppm) at rigth and left sides of the region.
          x0 = indexMask(i);
        }
        for (; finalPeaks[i] && i < length; i++) {
          reTmp.push(re[i]);
          imTmp.push(im[i]);
          i++;
        }
        if (reTmp.length > minRegSize) {
          res.push(autoPhaseRegion(reTmp, imTmp, x0));
        }
      }
      // Still some corrections needed. In the paper they remove the outlayers interatively
      // until they can perform a regression witout bad points. Can someone help here?
      let [ph1, ph0] = weightedLinearRegression(res.map(r => r.x0 / length), res.map(r => r.ph0), res.map(r => r.area / 1e11));
      let phased = reimPhaseCorrection({
        re,
        im
      }, ph0 * Math.PI / 180, ph1 * Math.PI / 180, {
        reverse
      });
      return {
        data: phased,
        ph0,
        ph1
      };
    }
    /**
     * AutoPhaseRegion.
     *
     * @param re - Array of Number.
     * @param im - Array of Number.
     * @param x0 - Number.
     * @returns Region.
     */
    function autoPhaseRegion(re, im, x0) {
      let start = -180;
      let stop = 180;
      let nSteps = 6;
      let maxSteps = 5;
      let bestAng = 0;
      let minArea = Number.MAX_SAFE_INTEGER;
      while (maxSteps > 0) {
        let dAng = (stop - start) / (nSteps + 1);
        for (let i = start; i <= stop; i += dAng) {
          let phased = reimPhaseCorrection({
            re,
            im
          }, toRadians(i), 0);
          let negArea = getNegArea(phased.re);
          if (negArea < minArea) {
            [minArea, bestAng] = [negArea, i];
          }
        }
        start = bestAng - dAng;
        stop = bestAng + dAng;
        maxSteps--;
      }
      // Calculate the area for the best angle
      let phased = reimPhaseCorrection({
        re,
        im
      }, toRadians(bestAng), 0);
      let area = 0;
      let sumX = 0;
      for (let j = 0; j < re.length; j++) {
        area += phased.re[j];
        sumX += phased.re[j] * (j + x0);
      }
      return {
        ph0: bestAng,
        area,
        x0: sumX / area
      };
    }
    /**
     * Holoborodko.
     *
     * @param s - Array of float.
     * @returns Array of float.
     */
    function holoborodko(s) {
      let dk = new Float64Array(s.length);
      for (let i = 5; i < s.length - 5; i++) {
        dk[i] = (42 * (s[i + 1] - s[i - 1]) + 48 * (s[i + 2] - s[i - 2]) + 27 * (s[i + 3] + s[i - 3]) + 8 * (s[i + 4] - s[i - 4]) + s[i + 5] - s[i - 5]) / 512;
      }
      //Fill the borders
      for (let i = 0; i < 5; i++) {
        dk[i] = dk[5];
        dk[s.length - i - 1] = dk[s.length - 6];
      }
      return dk;
    }
    /**
     * RobustBaseLineRegionsDetection.
     *
     * @param s
     * @param options
     * @param options.magnitudeMode
     * @param options.maxDistanceToJoin
     * @param options.factorNoise
     */
    function robustBaseLineRegionsDetection(s, options) {
      const {
        maxDistanceToJoin,
        magnitudeMode,
        factorNoise
      } = options;
      let mask = new Uint8Array(s.length);
      for (let i = 0; i < s.length; i++) {
        mask[i] = 0;
      }
      let change = true;
      while (change) {
        let noiseLevel = xNoiseSanPlot(s, {
          magnitudeMode
        });
        let cutOff = factorNoise * noiseLevel.positive;
        change = false;
        for (let i = 0; i < s.length; i++) {
          if (Math.abs(s[i]) > cutOff && !mask[i]) {
            change = true;
            mask[i] = 1;
          }
        }
      }
      // Clean up mask by merging peaks blocks, separated by just a few points(4??).
      let count = 0;
      let prev = 0;
      for (let i = 0; i < s.length; i++) {
        if (!mask[i]) {
          count++;
        } else {
          if (count < maxDistanceToJoin) {
            for (let j = 0; j <= count; j++) {
              mask[prev + j] = 1;
            }
          }
          while (mask[++i] && i < s.length);
          prev = i;
          count = 0;
        }
      }
      return mask;
    }
    /**
     * WeightedLinearRegression.
     *
     * @param x
     * @param y
     * @param w
     */
    function weightedLinearRegression(x, y, w) {
      let sxtw = 0;
      let swx = 0;
      let sw = 0;
      let sxtwy = 0;
      let swy = 0;
      for (let i = 0; i < x.length; i++) {
        sxtw += x[i] * x[i] * w[i];
        swx += x[i] * w[i];
        sw += w[i];
        sxtwy += x[i] * w[i] * y[i];
        swy += w[i] * y[i];
      }
      /* Just to know what is the matrix system that we solve
       let Mx=[[sxtw, swx], [swx, sw]];
       let My=[[sxtwy], [swy]];
      */
      //Mx inverse
      let detMx = sxtw * sw - swx * swx;
      let inMx = [[sw / detMx, -swx / detMx], [-swx / detMx, sxtw / detMx]];
      return [inMx[0][0] * sxtwy + inMx[0][1] * swy, inMx[1][0] * sxtwy + inMx[1][1] * swy];
    }
    const toRadians = degree => degree * Math.PI / 180;
    const getNegArea = data => {
      let area = 0;
      for (let element of data) {
        if (element < 0) area -= element;
      }
      return area;
    };

    function FFT$1(size) {
      this.size = size | 0;
      if (this.size <= 1 || (this.size & this.size - 1) !== 0) throw new Error('FFT size must be a power of two and bigger than 1');
      this._csize = size << 1;

      // NOTE: Use of `var` is intentional for old V8 versions
      var table = new Array(this.size * 2);
      for (var i = 0; i < table.length; i += 2) {
        const angle = Math.PI * i / this.size;
        table[i] = Math.cos(angle);
        table[i + 1] = -Math.sin(angle);
      }
      this.table = table;

      // Find size's power of two
      var power = 0;
      for (var t = 1; this.size > t; t <<= 1) power++;

      // Calculate initial step's width:
      //   * If we are full radix-4 - it is 2x smaller to give inital len=8
      //   * Otherwise it is the same as `power` to give len=4
      this._width = power % 2 === 0 ? power - 1 : power;

      // Pre-compute bit-reversal patterns
      this._bitrev = new Array(1 << this._width);
      for (var j = 0; j < this._bitrev.length; j++) {
        this._bitrev[j] = 0;
        for (var shift = 0; shift < this._width; shift += 2) {
          var revShift = this._width - shift - 2;
          this._bitrev[j] |= (j >>> shift & 3) << revShift;
        }
      }
      this._out = null;
      this._data = null;
      this._inv = 0;
    }
    var fft$1 = FFT$1;
    FFT$1.prototype.fromComplexArray = function fromComplexArray(complex, storage) {
      var res = storage || new Array(complex.length >>> 1);
      for (var i = 0; i < complex.length; i += 2) res[i >>> 1] = complex[i];
      return res;
    };
    FFT$1.prototype.createComplexArray = function createComplexArray() {
      const res = new Array(this._csize);
      for (var i = 0; i < res.length; i++) res[i] = 0;
      return res;
    };
    FFT$1.prototype.toComplexArray = function toComplexArray(input, storage) {
      var res = storage || this.createComplexArray();
      for (var i = 0; i < res.length; i += 2) {
        res[i] = input[i >>> 1];
        res[i + 1] = 0;
      }
      return res;
    };
    FFT$1.prototype.completeSpectrum = function completeSpectrum(spectrum) {
      var size = this._csize;
      var half = size >>> 1;
      for (var i = 2; i < half; i += 2) {
        spectrum[size - i] = spectrum[i];
        spectrum[size - i + 1] = -spectrum[i + 1];
      }
    };
    FFT$1.prototype.transform = function transform(out, data) {
      if (out === data) throw new Error('Input and output buffers must be different');
      this._out = out;
      this._data = data;
      this._inv = 0;
      this._transform4();
      this._out = null;
      this._data = null;
    };
    FFT$1.prototype.realTransform = function realTransform(out, data) {
      if (out === data) throw new Error('Input and output buffers must be different');
      this._out = out;
      this._data = data;
      this._inv = 0;
      this._realTransform4();
      this._out = null;
      this._data = null;
    };
    FFT$1.prototype.inverseTransform = function inverseTransform(out, data) {
      if (out === data) throw new Error('Input and output buffers must be different');
      this._out = out;
      this._data = data;
      this._inv = 1;
      this._transform4();
      for (var i = 0; i < out.length; i++) out[i] /= this.size;
      this._out = null;
      this._data = null;
    };

    // radix-4 implementation
    //
    // NOTE: Uses of `var` are intentional for older V8 version that do not
    // support both `let compound assignments` and `const phi`
    FFT$1.prototype._transform4 = function _transform4() {
      var out = this._out;
      var size = this._csize;

      // Initial step (permute and transform)
      var width = this._width;
      var step = 1 << width;
      var len = size / step << 1;
      var outOff;
      var t;
      var bitrev = this._bitrev;
      if (len === 4) {
        for (outOff = 0, t = 0; outOff < size; outOff += len, t++) {
          const off = bitrev[t];
          this._singleTransform2(outOff, off, step);
        }
      } else {
        // len === 8
        for (outOff = 0, t = 0; outOff < size; outOff += len, t++) {
          const off = bitrev[t];
          this._singleTransform4(outOff, off, step);
        }
      }

      // Loop through steps in decreasing order
      var inv = this._inv ? -1 : 1;
      var table = this.table;
      for (step >>= 2; step >= 2; step >>= 2) {
        len = size / step << 1;
        var quarterLen = len >>> 2;

        // Loop through offsets in the data
        for (outOff = 0; outOff < size; outOff += len) {
          // Full case
          var limit = outOff + quarterLen;
          for (var i = outOff, k = 0; i < limit; i += 2, k += step) {
            const A = i;
            const B = A + quarterLen;
            const C = B + quarterLen;
            const D = C + quarterLen;

            // Original values
            const Ar = out[A];
            const Ai = out[A + 1];
            const Br = out[B];
            const Bi = out[B + 1];
            const Cr = out[C];
            const Ci = out[C + 1];
            const Dr = out[D];
            const Di = out[D + 1];

            // Middle values
            const MAr = Ar;
            const MAi = Ai;
            const tableBr = table[k];
            const tableBi = inv * table[k + 1];
            const MBr = Br * tableBr - Bi * tableBi;
            const MBi = Br * tableBi + Bi * tableBr;
            const tableCr = table[2 * k];
            const tableCi = inv * table[2 * k + 1];
            const MCr = Cr * tableCr - Ci * tableCi;
            const MCi = Cr * tableCi + Ci * tableCr;
            const tableDr = table[3 * k];
            const tableDi = inv * table[3 * k + 1];
            const MDr = Dr * tableDr - Di * tableDi;
            const MDi = Dr * tableDi + Di * tableDr;

            // Pre-Final values
            const T0r = MAr + MCr;
            const T0i = MAi + MCi;
            const T1r = MAr - MCr;
            const T1i = MAi - MCi;
            const T2r = MBr + MDr;
            const T2i = MBi + MDi;
            const T3r = inv * (MBr - MDr);
            const T3i = inv * (MBi - MDi);

            // Final values
            const FAr = T0r + T2r;
            const FAi = T0i + T2i;
            const FCr = T0r - T2r;
            const FCi = T0i - T2i;
            const FBr = T1r + T3i;
            const FBi = T1i - T3r;
            const FDr = T1r - T3i;
            const FDi = T1i + T3r;
            out[A] = FAr;
            out[A + 1] = FAi;
            out[B] = FBr;
            out[B + 1] = FBi;
            out[C] = FCr;
            out[C + 1] = FCi;
            out[D] = FDr;
            out[D + 1] = FDi;
          }
        }
      }
    };

    // radix-2 implementation
    //
    // NOTE: Only called for len=4
    FFT$1.prototype._singleTransform2 = function _singleTransform2(outOff, off, step) {
      const out = this._out;
      const data = this._data;
      const evenR = data[off];
      const evenI = data[off + 1];
      const oddR = data[off + step];
      const oddI = data[off + step + 1];
      const leftR = evenR + oddR;
      const leftI = evenI + oddI;
      const rightR = evenR - oddR;
      const rightI = evenI - oddI;
      out[outOff] = leftR;
      out[outOff + 1] = leftI;
      out[outOff + 2] = rightR;
      out[outOff + 3] = rightI;
    };

    // radix-4
    //
    // NOTE: Only called for len=8
    FFT$1.prototype._singleTransform4 = function _singleTransform4(outOff, off, step) {
      const out = this._out;
      const data = this._data;
      const inv = this._inv ? -1 : 1;
      const step2 = step * 2;
      const step3 = step * 3;

      // Original values
      const Ar = data[off];
      const Ai = data[off + 1];
      const Br = data[off + step];
      const Bi = data[off + step + 1];
      const Cr = data[off + step2];
      const Ci = data[off + step2 + 1];
      const Dr = data[off + step3];
      const Di = data[off + step3 + 1];

      // Pre-Final values
      const T0r = Ar + Cr;
      const T0i = Ai + Ci;
      const T1r = Ar - Cr;
      const T1i = Ai - Ci;
      const T2r = Br + Dr;
      const T2i = Bi + Di;
      const T3r = inv * (Br - Dr);
      const T3i = inv * (Bi - Di);

      // Final values
      const FAr = T0r + T2r;
      const FAi = T0i + T2i;
      const FBr = T1r + T3i;
      const FBi = T1i - T3r;
      const FCr = T0r - T2r;
      const FCi = T0i - T2i;
      const FDr = T1r - T3i;
      const FDi = T1i + T3r;
      out[outOff] = FAr;
      out[outOff + 1] = FAi;
      out[outOff + 2] = FBr;
      out[outOff + 3] = FBi;
      out[outOff + 4] = FCr;
      out[outOff + 5] = FCi;
      out[outOff + 6] = FDr;
      out[outOff + 7] = FDi;
    };

    // Real input radix-4 implementation
    FFT$1.prototype._realTransform4 = function _realTransform4() {
      var out = this._out;
      var size = this._csize;

      // Initial step (permute and transform)
      var width = this._width;
      var step = 1 << width;
      var len = size / step << 1;
      var outOff;
      var t;
      var bitrev = this._bitrev;
      if (len === 4) {
        for (outOff = 0, t = 0; outOff < size; outOff += len, t++) {
          const off = bitrev[t];
          this._singleRealTransform2(outOff, off >>> 1, step >>> 1);
        }
      } else {
        // len === 8
        for (outOff = 0, t = 0; outOff < size; outOff += len, t++) {
          const off = bitrev[t];
          this._singleRealTransform4(outOff, off >>> 1, step >>> 1);
        }
      }

      // Loop through steps in decreasing order
      var inv = this._inv ? -1 : 1;
      var table = this.table;
      for (step >>= 2; step >= 2; step >>= 2) {
        len = size / step << 1;
        var halfLen = len >>> 1;
        var quarterLen = halfLen >>> 1;
        var hquarterLen = quarterLen >>> 1;

        // Loop through offsets in the data
        for (outOff = 0; outOff < size; outOff += len) {
          for (var i = 0, k = 0; i <= hquarterLen; i += 2, k += step) {
            var A = outOff + i;
            var B = A + quarterLen;
            var C = B + quarterLen;
            var D = C + quarterLen;

            // Original values
            var Ar = out[A];
            var Ai = out[A + 1];
            var Br = out[B];
            var Bi = out[B + 1];
            var Cr = out[C];
            var Ci = out[C + 1];
            var Dr = out[D];
            var Di = out[D + 1];

            // Middle values
            var MAr = Ar;
            var MAi = Ai;
            var tableBr = table[k];
            var tableBi = inv * table[k + 1];
            var MBr = Br * tableBr - Bi * tableBi;
            var MBi = Br * tableBi + Bi * tableBr;
            var tableCr = table[2 * k];
            var tableCi = inv * table[2 * k + 1];
            var MCr = Cr * tableCr - Ci * tableCi;
            var MCi = Cr * tableCi + Ci * tableCr;
            var tableDr = table[3 * k];
            var tableDi = inv * table[3 * k + 1];
            var MDr = Dr * tableDr - Di * tableDi;
            var MDi = Dr * tableDi + Di * tableDr;

            // Pre-Final values
            var T0r = MAr + MCr;
            var T0i = MAi + MCi;
            var T1r = MAr - MCr;
            var T1i = MAi - MCi;
            var T2r = MBr + MDr;
            var T2i = MBi + MDi;
            var T3r = inv * (MBr - MDr);
            var T3i = inv * (MBi - MDi);

            // Final values
            var FAr = T0r + T2r;
            var FAi = T0i + T2i;
            var FBr = T1r + T3i;
            var FBi = T1i - T3r;
            out[A] = FAr;
            out[A + 1] = FAi;
            out[B] = FBr;
            out[B + 1] = FBi;

            // Output final middle point
            if (i === 0) {
              var FCr = T0r - T2r;
              var FCi = T0i - T2i;
              out[C] = FCr;
              out[C + 1] = FCi;
              continue;
            }

            // Do not overwrite ourselves
            if (i === hquarterLen) continue;

            // In the flipped case:
            // MAi = -MAi
            // MBr=-MBi, MBi=-MBr
            // MCr=-MCr
            // MDr=MDi, MDi=MDr
            var ST0r = T1r;
            var ST0i = -T1i;
            var ST1r = T0r;
            var ST1i = -T0i;
            var ST2r = -inv * T3i;
            var ST2i = -inv * T3r;
            var ST3r = -inv * T2i;
            var ST3i = -inv * T2r;
            var SFAr = ST0r + ST2r;
            var SFAi = ST0i + ST2i;
            var SFBr = ST1r + ST3i;
            var SFBi = ST1i - ST3r;
            var SA = outOff + quarterLen - i;
            var SB = outOff + halfLen - i;
            out[SA] = SFAr;
            out[SA + 1] = SFAi;
            out[SB] = SFBr;
            out[SB + 1] = SFBi;
          }
        }
      }
    };

    // radix-2 implementation
    //
    // NOTE: Only called for len=4
    FFT$1.prototype._singleRealTransform2 = function _singleRealTransform2(outOff, off, step) {
      const out = this._out;
      const data = this._data;
      const evenR = data[off];
      const oddR = data[off + step];
      const leftR = evenR + oddR;
      const rightR = evenR - oddR;
      out[outOff] = leftR;
      out[outOff + 1] = 0;
      out[outOff + 2] = rightR;
      out[outOff + 3] = 0;
    };

    // radix-4
    //
    // NOTE: Only called for len=8
    FFT$1.prototype._singleRealTransform4 = function _singleRealTransform4(outOff, off, step) {
      const out = this._out;
      const data = this._data;
      const inv = this._inv ? -1 : 1;
      const step2 = step * 2;
      const step3 = step * 3;

      // Original values
      const Ar = data[off];
      const Br = data[off + step];
      const Cr = data[off + step2];
      const Dr = data[off + step3];

      // Pre-Final values
      const T0r = Ar + Cr;
      const T1r = Ar - Cr;
      const T2r = Br + Dr;
      const T3r = inv * (Br - Dr);

      // Final values
      const FAr = T0r + T2r;
      const FBr = T1r;
      const FBi = -T3r;
      const FCr = T0r - T2r;
      const FDr = T1r;
      const FDi = T3r;
      out[outOff] = FAr;
      out[outOff + 1] = 0;
      out[outOff + 2] = FBr;
      out[outOff + 3] = FBi;
      out[outOff + 4] = FCr;
      out[outOff + 5] = 0;
      out[outOff + 6] = FDr;
      out[outOff + 7] = FDi;
    };
    var FFT$2 = fft$1;

    /**
     * This function performs a circular shift to an array
     * Positive values of shifts will shift to the right and negative values will do to the left
     *
     * @example xRotate([1,2,3,4],1) -> [4,1,2,3]
     * @example xRotate([1,2,3,4],-1) -> [2,3,4,1]
     * @param array - array
     * @param shift - shift
     * @returns - rotated array
     */
    function xRotate(array, shift) {
      shift = shift % array.length;
      if (shift < 0) shift += array.length;
      let result = new Float64Array(array.length);
      result.set(array.slice(array.length - shift));
      result.set(array.slice(0, array.length - shift), shift);
      return result;
    }

    /**
     * ReimFFT.
     *
     * @param data - complex spectrum
     * @param options - options.
     * @returns FFT of complex spectrum.
     */
    function reimFFT(data, options = {}) {
      const {
        inverse = false,
        applyZeroShift = false
      } = options;
      let {
        re,
        im
      } = data;
      const size = re.length;
      const csize = size << 1;
      let complexArray = new Float64Array(csize);
      for (let i = 0; i < csize; i += 2) {
        complexArray[i] = re[i >>> 1];
        complexArray[i + 1] = im[i >>> 1];
      }
      let fft = new FFT$2(size);
      let output = new Float64Array(csize);
      if (inverse) {
        if (applyZeroShift) complexArray = zeroShift(complexArray, true);
        fft.inverseTransform(output, complexArray);
      } else {
        fft.transform(output, complexArray);
        if (applyZeroShift) output = zeroShift(output);
      }
      let newRe = new Float64Array(size);
      let newIm = new Float64Array(size);
      for (let i = 0; i < csize; i += 2) {
        newRe[i >>> 1] = output[i];
        newIm[i >>> 1] = output[i + 1];
      }
      return {
        re: newRe,
        im: newIm
      };
    }
    const zeroShift = (data, inverse) => {
      let middle = inverse ? Math.ceil(data.length / 2) : Math.floor(data.length / 2);
      return xRotate(data, middle);
    };

    /**
     * This function returns an array with absolute values
     *
     * @param array - array of data
     * @returns - array with absolute values
     */
    function xAbsolute(array) {
      let tmpArray = array.slice();
      for (let i = 0; i < tmpArray.length; i++) {
        if (tmpArray[i] < 0) tmpArray[i] *= -1;
      }
      return tmpArray;
    }

    // eslint-disable-next-line @typescript-eslint/unbound-method
    const toString$2 = 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(value) {
      const tag = toString$2.call(value);
      return tag.endsWith('Array]') && !tag.includes('Big');
    }

    /**
     * Calculates the median of an array
     *
     * @param input - Array containing values
     * @returns - median
     */
    function xMedian(input) {
      if (!isAnyArray(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;
      let 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);
    }

    /**
     * This function calculates the median after taking the reimAbsolute values of the points
     *
     * @param array - the array for which we want to calculate the absolute value
     * @returns - median
     */
    function xAbsoluteMedian(array) {
      return xMedian(xAbsolute(array));
    }

    /**
     * This function xAdd the first array by the second array or a constant value to each element of the first array
     *
     * @param array1 - the first array
     * @param array2 - the second array or number
     */
    function xAdd(array1, array2) {
      let isConstant = false;
      let constant = 0;
      if (isAnyArray(array2)) {
        if (array1.length !== array2.length) {
          throw new Error('xAdd: size of array1 and array2 must be identical');
        }
      } else {
        isConstant = true;
        constant = array2;
      }
      let array3 = new Float64Array(array1.length);
      if (isConstant) {
        for (let i = 0; i < array1.length; i++) {
          array3[i] = array1[i] + constant;
        }
      } else {
        for (let i = 0; i < array1.length; i++) {
          array3[i] = array1[i] + array2[i];
        }
      }
      return array3;
    }

    /**
     * Will apply a function on each element of the array described as a string
     * By default we will use as variable 'x'
     * In front of sequence of lowercase we will add 'Math.'. This allows to write
     * `sin(x) + cos(x)` and it will be replace internally by (x) => (Math.sin(x) + Math.cos(x))
     * @param array
     * @param options
     * @returns
     */
    function xApplyFunctionStr(array, options = {}) {
      const {
        variableLabel = 'x',
        fctString = variableLabel
      } = options;
      const fct = new Function(variableLabel, `return Number(${fctString.replace(/(?<before>^|\W)(?<after>[\da-z]{2,}\()/g, '$<before>Math.$<after>').replace(/Math\.Math/g, 'Math')})`);
      const toReturn = Float64Array.from(array);
      for (let i = 0; i < array.length; i++) {
        toReturn[i] = fct(array[i]);
        if (Number.isNaN(toReturn[i])) {
          throw new Error(`The callback ${fctString} does not return a number: ${array[i]}`);
        }
      }
      return toReturn;
    }

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

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

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

    function max(input) {
      var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
      if (!isAnyArray(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(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(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(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;
    }

    const indent = ' '.repeat(2);
    const indentData = ' '.repeat(4);
    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, Math.pow(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, Math.pow(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(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(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$1(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$1(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$1(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$1(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$1(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$1(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 += Math.pow(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 += Math.pow(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 += Math.pow(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(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, 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(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(rows, columns);
      }
      static ones(rows, columns) {
        return new Matrix(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(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(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(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(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;
      }
      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(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.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.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') {
        let result = 0;
        if (type === 'max') {
          return this.max();
        } else if (type === 'frobenius') {
          for (let i = 0; i < this.rows; i++) {
            for (let j = 0; j < this.columns; j++) {
              result = result + this.get(i, j) * this.get(i, j);
            }
          }
          return Math.sqrt(result);
        } else {
          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.checkMatrix(other);
        let m = this.rows;
        let n = this.columns;
        let p = other.columns;
        let result = new Matrix(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.checkMatrix(other);
        let result = new Matrix(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.checkMatrix(other);
        let result = new Matrix(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.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 resultat = AbstractMatrix.zeros(2 * c11.rows, 2 * c11.columns);
          resultat = resultat.setSubMatrix(c11, 0, 0);
          resultat = resultat.setSubMatrix(c12, c11.rows, 0);
          resultat = resultat.setSubMatrix(c21, 0, c11.columns);
          resultat = resultat.setSubMatrix(c22, c11.rows, c11.columns);
          return resultat.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(this.rows, this.columns);
        for (let i = 0; i < this.rows; i++) {
          const row = this.getRow(i);
          if (row.length > 0) {
            rescale$1(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(this.rows, this.columns);
        for (let i = 0; i < this.columns; i++) {
          const column = this.getColumn(i);
          if (column.length) {
            rescale$1(column, {
              min: min,
              max: 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.checkMatrix(other);
        let m = this.rows;
        let n = this.columns;
        let p = other.rows;
        let q = other.columns;
        let result = new Matrix(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.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.eye(n, n));
        let IxB = Matrix.eye(m, m).kroneckerProduct(other);
        return AxI.add(IxB);
      }
      transpose() {
        let result = new Matrix(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$1(this, startRow, endRow, startColumn, endColumn);
        let newMatrix = new Matrix(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(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(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.checkMatrix(matrix);
        if (matrix.isEmpty()) {
          return this;
        }
        let endRow = startRow + matrix.rows - 1;
        let endColumn = startColumn + matrix.columns - 1;
        checkRange$1(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(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() {
        let newMatrix = new Matrix(this.rows, this.columns);
        for (let row = 0; row < this.rows; row++) {
          for (let column = 0; column < this.columns; column++) {
            newMatrix.set(row, column, this.get(row, column));
          }
        }
        return newMatrix;
      }
      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(mean)) {
                throw new TypeError('mean must be an array');
              }
              return varianceByRow(this, unbiased, mean);
            }
          case 'column':
            {
              if (!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(center)) {
                throw new TypeError('center must be an array');
              }
              centerByRow(this, center);
              return this;
            }
          case 'column':
            {
              if (!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(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(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);
      }
    }
    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 extends AbstractMatrix {
      constructor(nRows, nColumns) {
        super();
        if (Matrix.isMatrix(nRows)) {
          // eslint-disable-next-line no-constructor-return
          return nRows.clone();
        } else if (Number.isInteger(nRows) && nRows >= 0) {
          // Create an empty matrix
          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');
          }
        } else if (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]));
          }
        } else {
          throw new TypeError('First argument must be a positive number or an array');
        }
        this.rows = nRows;
        this.columns = nColumns;
      }
      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);

    class BaseView extends AbstractMatrix {
      constructor(matrix, rows, columns) {
        super();
        this.matrix = matrix;
        this.rows = rows;
        this.columns = columns;
      }
    }

    class MatrixTransposeView 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 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];
      }
    }

    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.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(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(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.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(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(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(m, nu);
        let V = new Matrix(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.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.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.diag(value));
      }
      inverse() {
        let V = this.V;
        let e = this.threshold;
        let vrows = V.rows;
        let vcols = V.columns;
        let X = new Matrix(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(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.diag(this.s);
      }
    }

    function inverse(matrix, useSVD = false) {
      matrix = WrapperMatrix2D.checkMatrix(matrix);
      if (useSVD) {
        return new SingularValueDecomposition(matrix).inverse();
      } else {
        return solve(matrix, Matrix.eye(matrix.rows));
      }
    }
    function solve(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);
      }
    }

    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(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(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(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));
            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;
              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];
      }
    }

    /**
     * Returns a copy of the data as Float64
     *
     * @param array - array of numbers
     */
    function xEnsureFloat64(array) {
      xCheck(array);
      if (array instanceof Float64Array) {
        return array.slice(0);
      }
      return Float64Array.from(array);
    }

    /**
     * Returns the closest index of a `target`
     *
     * @param array - array of numbers
     * @param target - target
     * @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,
        from,
        to
      } = options;
      if (fromIndex === undefined) {
        if (from !== undefined) {
          fromIndex = xFindClosestIndex(x, from);
        } else {
          fromIndex = 0;
        }
      }
      if (toIndex === undefined) {
        if (to !== undefined) {
          toIndex = xFindClosestIndex(x, to);
        } else {
          toIndex = x.length - 1;
        }
      }
      if (fromIndex < 0) fromIndex = 0;
      if (toIndex < 0) toIndex = 0;
      if (fromIndex >= x.length) fromIndex = x.length - 1;
      if (toIndex >= x.length) toIndex = x.length - 1;
      if (fromIndex > toIndex) [fromIndex, toIndex] = [toIndex, fromIndex];
      return {
        fromIndex,
        toIndex
      };
    }

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

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

    /**
     * Check if the values are separated always by the same difference
     *
     * @param array - Monotone growing array of number
     */
    function xIsEquallySpaced(array, 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) {
        let absoluteDifference = array[i + 1] - array[i];
        if (absoluteDifference < minDx) {
          minDx = absoluteDifference;
        }
        if (absoluteDifference > maxDx) {
          maxDx = absoluteDifference;
        }
      }
      return (maxDx - minDx) / maxDx < tolerance;
    }

    /**
     * Returns true if x is monotone
     *
     * @param array - array of numbers
     */
    function 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;
      }
    }

    /**
     * Computes the mean value of an array of values
     *
     * @param array - array of numbers
     * @param options - options
     */
    function xMean(array, options = {}) {
      xCheck(array);
      const {
        fromIndex,
        toIndex
      } = xGetFromToIndex(array, options);
      let sumValue = array[fromIndex];
      for (let i = fromIndex + 1; i <= toIndex; i++) {
        sumValue += array[i];
      }
      return sumValue / (toIndex - fromIndex + 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 (let value of array) {
        if (value < min) min = value;
        if (value > max) max = value;
      }
      return {
        min,
        max
      };
    }

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

    /**
     * This function calculate the norm of a vector
     *
     * @example xNorm([3, 4]) -> 5
     * @param array - array
     * @returns - calculated norm
     */
    function xNorm(array) {
      let result = 0;
      for (const element of array) {
        result += element ** 2;
      }
      return Math.sqrt(result);
    }

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

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

    /**
     * This function pads an array
     *s
     * @param array - the array that will be padded
     * @param options - options
     */
    function xPadding(array, options = {}) {
      const {
        size = 0,
        value = 0,
        algorithm = ''
      } = options;
      xCheck(array);
      if (!algorithm) {
        if (array instanceof Float64Array) {
          return array.slice();
        } else {
          return Float64Array.from(array);
        }
      }
      let result = new Float64Array(array.length + size * 2);
      for (let i = 0; i < array.length; i++) {
        result[i + size] = array[i];
      }
      let fromEnd = size + array.length;
      let toEnd = 2 * size + array.length;
      switch (algorithm.toLowerCase()) {
        case 'value':
          for (let i = 0; i < size; i++) {
            result[i] = value;
          }
          for (let i = fromEnd; i < toEnd; i++) {
            result[i] = value;
          }
          break;
        case 'duplicate':
          for (let i = 0; i < size; i++) {
            result[i] = array[0];
          }
          for (let i = fromEnd; i < toEnd; i++) {
            result[i] = array[array.length - 1];
          }
          break;
        case 'circular':
          for (let i = 0; i < size; i++) {
            result[i] = array[(array.length - size % array.length + i) % array.length];
          }
          for (let i = 0; i < size; i++) {
            result[i + fromEnd] = array[i % array.length];
          }
          break;
        default:
          throw new Error('xPadding: unknown algorithm');
      }
      return result;
    }

    /** Finds the variance of the data
     *
     * @param values - the values of the array
     * @param options - options
     * @returns variance
     */
    function xVariance(values, options = {}) {
      if (!isAnyArray(values)) {
        throw new TypeError('input must be an array');
      }
      const {
        unbiased = true,
        mean = xMean(values)
      } = options;
      let sqrError = 0;
      for (let i = 0; i < values.length; i++) {
        let x = values[i] - mean;
        sqrError += x * x;
      }
      if (unbiased) {
        return sqrError / (values.length - 1);
      } else {
        return sqrError / values.length;
      }
    }

    /** Finds the standard deviation for the data at hand
     *
     * @param values - values in the data
     * @param options - options
     * @returns standard deviation
     */
    function xStandardDeviation(values, options = {}) {
      return Math.sqrt(xVariance(values, options));
    }

    /**
     * Pareto scaling, which uses the square root of standard deviation as the scaling factor, circumvents the amplification of noise by retaining a small portion of magnitude information.
     * Noda, I. (2008). Scaling techniques to enhance two-dimensional correlation spectra. Journal of Molecular Structure, 883, 216-227.
     * DOI: 10.1016/j.molstruc.2007.12.026
     *
     * @param array - array of number
     */
    function xParetoNormalization(array) {
      xCheck(array);
      let result = [];
      const sqrtSD = Math.sqrt(xStandardDeviation(array));
      for (let item of array) {
        result.push(item / sqrtSD);
      }
      return result;
    }

    /** Function used to rescale data
     *
     * @param input - input for the rescale
     * @param options - options
     * @returns rescaled data
     */
    function xRescale(input, options = {}) {
      xCheck(input);
      const output = getOutputArray(options.output, input.length);
      const currentMin = xMinValue(input);
      const currentMax = xMaxValue(input);
      if (currentMin === currentMax) {
        throw new RangeError('minimum and maximum input values are equal. Cannot rescale a constant array');
      }
      const {
        min = 0,
        max = 1
      } = options;
      if (min >= max) {
        throw new RangeError('min option must be smaller than max option');
      }
      const factor = (max - min) / (currentMax - currentMin);
      for (let i = 0; i < input.length; i++) {
        output[i] = (input[i] - currentMin) * factor + min;
      }
      return output;
    }

    /**
     * This function calculates a rolling average
     *
     * @param array - array
     * @param fct - callback function that from an array returns a value
     * @param options - options
     */
    function xRolling(array, fct, options = {}) {
      xCheck(array);
      if (typeof fct !== 'function') throw new Error('fct has to be a function');
      const {
        window = 5,
        padding = {}
      } = options;
      const {
        size = window - 1,
        algorithm,
        value
      } = padding;
      array = xPadding(array, {
        size,
        algorithm,
        value
      }); // ensure we get a copy and it is float64
      const newArray = [];
      for (let i = 0; i < array.length - window + 1; i++) {
        // we will send a view to the original buffer
        newArray.push(fct(array.subarray(i, i + window)));
      }
      return newArray;
    }

    /**
     * This function calculates a rolling average
     *
     * @param array - array
     * @param options - option
     */
    function xRollingAverage(array, options = {}) {
      return xRolling(array, xMean, options);
    }

    /**
     * This function calculates a rolling average
     *
     * @param array - array
     * @param options - options
     */
    function xRollingMedian(array, options = {}) {
      return xRolling(array, xMedian, options);
    }

    /** Fill an array with sequential numbers
     *
     * @param input - optional destination array (if not provided a new array will be created)
     * @param options - options
     * @return array with sequential numbers
     */
    function xSequentialFill(input = [], options = {}) {
      if (typeof input === 'object' && !isAnyArray(input)) {
        options = input;
        input = [];
      }
      if (!isAnyArray(input)) {
        throw new TypeError('input must be an array');
      }
      // maybe should not specify default step and size
      let {
        from = 0,
        to = 10,
        size = Array.isArray(input) || input.constructor === Float64Array || input.constructor === Uint16Array ? input.length : 0,
        step = null
      } = options;
      if (!size) {
        if (step) {
          size = Math.floor((to - from) / step) + 1;
        } else {
          size = to - from + 1;
        }
      }
      if (!step && size) {
        step = (to - from) / (size - 1);
      }
      if (Array.isArray(input)) {
        // only works with normal array
        input.length = 0;
        for (let i = 0; i < size; i++) {
          input.push(from);
          if (step) from += step;
        }
      } else {
        if (Array.isArray(input) || input.constructor === Float64Array || input.constructor === Uint16Array && input.length !== size) {
          throw new Error('sequentialFill typed array must have the correct length');
        }
        for (let i = 0; i < size; i++) {
          if (Array.isArray(input) || input.constructor === Float64Array || input.constructor === Uint16Array) {
            input[i] = from;
          }
          if (step) {
            from += step;
          }
        }
      }
      return Array.isArray(input) || input.constructor === Float64Array || input.constructor === Uint16Array ? Array.from(input) : [];
    }

    /**
     * This function xSubtract the first array by the second array or a constant value from each element of the first array
     *
     * @param array1 - the array that will be rotated
     * @param array2 - second array or number
     * @returns array after subtraction
     */
    function xSubtract(array1, array2) {
      let isConstant = false;
      let constant = 0;
      if (isAnyArray(array2)) {
        if (array1.length !== array2.length) {
          throw new Error('xSubtract: size of array1 and array2 must be identical');
        }
      } else {
        isConstant = true;
        constant = Number(array2);
      }
      let array3 = new Float64Array(array1.length);
      if (isConstant) {
        for (let i = 0; i < array1.length; i++) {
          array3[i] = array1[i] - constant;
        }
      } else {
        for (let i = 0; i < array1.length; i++) {
          array3[i] = array1[i] - array2[i];
        }
      }
      return array3;
    }

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

    /**
     * Finds the closest point
     *
     * @param data - x array should be sorted and ascending
     * @param target - target to search
     * @returns - closest point
     */
    function xyFindClosestPoint( /** points */
    data, target) {
      const {
        x,
        y
      } = data;
      const index = xFindClosestIndex(x, target);
      return {
        x: x[index],
        y: y[index]
      };
    }

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

    /**
     * Normalize an array of zones:
     * - ensure than from < to
     * - merge overlapping zones
     * - deal with exclusions zones
     * - if no zones is specified add one between -Infinity and +Infinity
     * @param zones - array of zones
     * @param options - options
     * @returns array of zones
     */
    function zonesNormalize(zones = [], options = {}) {
      let {
        from = Number.NEGATIVE_INFINITY,
        to = Number.POSITIVE_INFINITY,
        exclusions = []
      } = options;
      if (from > to) [from, to] = [to, from];
      zones = JSON.parse(JSON.stringify(zones)).map(zone => zone.from > zone.to ? {
        from: zone.to,
        to: zone.from
      } : zone);
      zones = zones.sort((a, b) => {
        if (a.from !== b.from) return a.from - b.from;
        return a.to - b.to;
      });
      if (zones.length === 0) {
        zones.push({
          from,
          to
        });
      }
      for (const zone of zones) {
        if (from > zone.from) zone.from = from;
        if (to < zone.to) zone.to = to;
      }
      zones = zones.filter(zone => zone.from <= zone.to);
      if (zones.length === 0) return [];
      let currentZone = zones[0];
      let beforeExclusionsZones = [currentZone];
      for (let i = 1; i < zones.length; i++) {
        let zone = zones[i];
        if (zone.from <= currentZone.to) {
          if (currentZone.to < zone.to) {
            currentZone.to = zone.to;
          }
        } else {
          currentZone = zone;
          beforeExclusionsZones.push(currentZone);
        }
      }
      if (exclusions.length === 0) return beforeExclusionsZones;
      const normalizedExclusions = zonesNormalize(exclusions);
      let currentExclusionIndex = 0;
      const results = [];
      let counter = 0;
      for (let zoneIndex = 0; zoneIndex < beforeExclusionsZones.length; zoneIndex++) {
        if (counter++ > 5) break;
        const zone = beforeExclusionsZones[zoneIndex];
        if (currentExclusionIndex === normalizedExclusions.length) {
          // we analysed all the exclusion zones
          results.push(zone);
          continue;
        }
        while (currentExclusionIndex < normalizedExclusions.length && normalizedExclusions[currentExclusionIndex].to <= zone.from) {
          currentExclusionIndex++;
        }
        if (currentExclusionIndex === normalizedExclusions.length) {
          // we analysed all the exclusion zones
          results.push(zone);
          continue;
        }
        if (zone.to < normalizedExclusions[currentExclusionIndex].from) {
          // no problems, not yet in exclusion
          results.push(zone);
          continue;
        }
        if (normalizedExclusions[currentExclusionIndex].to >= zone.to) {
          // could be totally excluded
          if (normalizedExclusions[currentExclusionIndex].from <= zone.from) {
            continue;
          }
          results.push({
            from: normalizedExclusions[currentExclusionIndex].to,
            to: zone.to
          });
        }
        // we cut in the middle, we need to create more zones, annoying !
        if (normalizedExclusions[currentExclusionIndex].from > zone.from) {
          results.push({
            from: zone.from,
            to: normalizedExclusions[currentExclusionIndex].from
          });
        }
        zone.from = normalizedExclusions[currentExclusionIndex].to;
        zoneIndex--;
      }
      return results;
    }

    /**
     * Add the number of points per zone to reach a specified total
     *
     * @param zones - array of zones
     * @param numberOfPoints - total number of points to distribute between zones
     * @param options - options
     * @returns array of zones with points
     */
    function zonesWithPoints(zones = [],
    /**
     * total number of points to distribute between zones
     * @default 10
     */
    numberOfPoints = 10, options = {}) {
      if (zones.length === 0) return zones;
      let returnZones = zonesNormalize(zones, options);
      const totalSize = returnZones.reduce((previous, current) => {
        return previous + (current.to - current.from);
      }, 0);
      let unitsPerPoint = totalSize / numberOfPoints;
      let currentTotal = 0;
      for (let i = 0; i < returnZones.length - 1; i++) {
        let zone = returnZones[i];
        zone.numberOfPoints = Math.min(Math.round((zone.to - zone.from) / unitsPerPoint), numberOfPoints - currentTotal);
        currentTotal += zone.numberOfPoints;
      }
      let zone = returnZones[returnZones.length - 1];
      zone.numberOfPoints = numberOfPoints - currentTotal;
      return returnZones;
    }

    /**
     * function that retrieves the getEquallySpacedData with the variant "slot"
     *
     * @param x
     * @param y
     * @param from
     * @param to
     * @param numberOfPoints
     * @return Array of y's equally spaced with the variant "slot"
     */
    function equallySpacedSlot( /** x coordinates */
    x, /** y coordinates */
    y, /** from value */
    from, /** to value */
    to, /** number of points */
    numberOfPoints) {
      let xLength = x.length;
      let step = (to - from) / (numberOfPoints > 1 ? numberOfPoints - 1 : 1);
      let halfStep = step / 2;
      let lastStep = x[x.length - 1] - x[x.length - 2];
      let start = from - halfStep;
      // Changed Array to Float64Array
      let output = new Float64Array(numberOfPoints);
      // Init main variables
      let min = start;
      let max = start + step;
      let previousX = -Number.MAX_VALUE;
      let previousY = 0;
      let nextX = x[0];
      let nextY = y[0];
      let frontOutsideSpectra = 0;
      let backOutsideSpectra = true;
      let currentValue = 0;
      // for slot algorithm
      let currentPoints = 0;
      let i = 1; // index of input
      let j = 0; // index of output
      main: while (true) {
        if (previousX >= nextX) throw new Error('x must be a growing series');
        while (previousX - max > 0) {
          // no overlap with original point, just consume current value
          if (backOutsideSpectra) {
            currentPoints++;
            backOutsideSpectra = false;
          }
          output[j] = currentPoints <= 0 ? 0 : currentValue / currentPoints;
          j++;
          if (j === numberOfPoints) {
            break main;
          }
          min = max;
          max += step;
          currentValue = 0;
          currentPoints = 0;
        }
        if (previousX > min) {
          currentValue += previousY;
          currentPoints++;
        }
        if (previousX === -Number.MAX_VALUE || frontOutsideSpectra > 1) {
          currentPoints--;
        }
        previousX = nextX;
        previousY = nextY;
        if (i < xLength) {
          nextX = x[i];
          nextY = y[i];
          i++;
        } else {
          nextX += lastStep;
          nextY = 0;
          frontOutsideSpectra++;
        }
      }
      return output;
    }

    /**
     * Function that calculates the integral of the line between two
     * x-coordinates, given the slope and intercept of the line.
     * @param x0
     * @param x1
     * @param slope
     * @param intercept
     * @return integral value.
     */
    function integral( /** first coordinate of point */
    x0, /** second coordinate of point */
    x1, /** slope of the line */
    slope, /** intercept of the line on the y axis */
    intercept) {
      return 0.5 * slope * x1 * x1 + intercept * x1 - (0.5 * slope * x0 * x0 + intercept * x0);
    }

    /**
     * function that retrieves the getEquallySpacedData with the variant "smooth"
     *
     * @param x
     * @param y
     * @param from
     * @param to
     * @param numberOfPoints
     * @return - Array of y's equally spaced with the variant "smooth"
     */
    function equallySpacedSmooth( /** x coordinates */
    x, /** y coordinates */
    y, /** from value */
    from, /** to value */
    to, /** number of points */
    numberOfPoints) {
      let xLength = x.length;
      let step = (to - from) / (numberOfPoints > 1 ? numberOfPoints - 1 : 1);
      let halfStep = step / 2;
      // Changed Array to Float64Array
      let output = new Float64Array(numberOfPoints);
      let initialOriginalStep = x[1] - x[0];
      let lastOriginalStep = x[xLength - 1] - x[xLength - 2];
      // Init main variables
      let min = from - halfStep;
      let max = from + halfStep;
      let previousX = Number.MIN_SAFE_INTEGER;
      let previousY = 0;
      let nextX = x[0] - initialOriginalStep;
      let nextY = 0;
      let currentValue = 0;
      let slope = 0;
      let intercept = 0;
      let sumAtMin = 0;
      let sumAtMax = 0;
      let i = 0; // index of input
      let j = 0; // index of output
      let add = 0;
      main: while (true) {
        if (previousX >= nextX) throw new Error('x must be a growing series');
        if (previousX <= min && min <= nextX) {
          add = integral(0, min - previousX, slope, previousY);
          sumAtMin = currentValue + add;
        }
        while (nextX - max >= 0) {
          // no overlap with original point, just consume current value
          add = integral(0, max - previousX, slope, previousY);
          sumAtMax = currentValue + add;
          output[j++] = (sumAtMax - sumAtMin) / step;
          if (j === numberOfPoints) {
            break main;
          }
          min = max;
          max += step;
          sumAtMin = sumAtMax;
        }
        currentValue += integral(previousX, nextX, slope, intercept);
        previousX = nextX;
        previousY = nextY;
        if (i < xLength) {
          nextX = x[i];
          nextY = y[i];
          i++;
        } else if (i === xLength) {
          nextX += lastOriginalStep;
          nextY = 0;
        }
        slope = getSlope(previousX, previousY, nextX, nextY);
        intercept = -slope * previousX + previousY;
      }
      return output;
    }
    function getSlope(x0, y0, x1, y1) {
      return (y1 - y0) / (x1 - x0);
    }

    /**
     * Function that returns a Number array of equally spaced numberOfPoints
     * containing a representation of intensities of the spectra arguments x
     * and y.
     *
     * The options parameter contains an object in the following form:
     * from: starting point
     * to: last point
     * numberOfPoints: number of points between from and to
     * variant: "slot" or "smooth" - smooth is the default option
     *
     * The slot variant consist that each point in an array is calculated
     * averaging the existing points between the slot that belongs to the current
     * value. The smooth variant is the same but takes the integral of the range
     * of the slot and divide by the step size between two points in an array.
     *
     * If exclusions zone are present, zones are ignored !
     *
     * @param data - object containing 2 properties x and y
     * @param options - options
     * @return new object with x / y array with the equally spaced data.
     */
    function xyEquallySpaced(data, options = {}) {
      let {
        x,
        y
      } = data;
      let xLength = x.length;
      const {
        from = x[0],
        to = x[xLength - 1],
        variant = 'smooth',
        numberOfPoints = 100,
        exclusions = [],
        zones = [{
          from,
          to
        }]
      } = options;
      if (from > to) {
        throw new RangeError('from should be larger than to');
      }
      xyCheck(data);
      if (numberOfPoints < 2) {
        throw new RangeError("'numberOfPoints' option must be greater than 1");
      }
      const normalizedZones = zonesNormalize(zones, {
        from,
        to,
        exclusions
      });
      const zonesWithPointsRes = zonesWithPoints(normalizedZones, numberOfPoints, {
        from,
        to
      });
      let xResult = [];
      let yResult = [];
      for (let zone of zonesWithPointsRes) {
        if (!zone.numberOfPoints) {
          zone.numberOfPoints = 0;
        }
        let zoneResult = processZone(Array.from(x), Array.from(y), zone.from, zone.to, zone.numberOfPoints, variant);
        xResult = xResult.concat(zoneResult.x);
        yResult = yResult.concat(zoneResult.y);
      }
      return {
        x: xResult,
        y: yResult
      };
    }
    function processZone(x, y, from, to, numberOfPoints, variant) {
      if (numberOfPoints < 1) {
        throw new RangeError('the number of points must be at least 1');
      }
      let output = variant === 'slot' ? Array.from(equallySpacedSlot(x, y, from, to, numberOfPoints)) : Array.from(equallySpacedSmooth(x, y, from, to, numberOfPoints));
      return {
        x: Array.from(createFromToArray({
          from,
          to,
          length: numberOfPoints
        })),
        y: output
      };
    }

    /**
     * XyExtract zones from a XY data
     *
     * @param data - Object that contains property x (an ordered increasing array) and y (an array)
     * @param options - options
     * @returns - Array of points
     */
    function xyExtract(data, options = {}) {
      xyCheck(data);
      const {
        x,
        y
      } = data;
      let {
        zones
      } = options;
      zones = zonesNormalize(zones);
      if (x === undefined || y === undefined || !Array.isArray(zones) || zones.length === 0) {
        return data;
      }
      let newX = [];
      let newY = [];
      let currentZone = zones[0];
      let position = 0;
      loop: for (let i = 0; i < x.length; i++) {
        while (currentZone.to < x[i]) {
          position++;
          currentZone = zones[position];
          if (!currentZone) {
            i = x.length;
            break loop;
          }
        }
        if (x[i] >= currentZone.from) {
          newX.push(x[i]);
          newY.push(y[i]);
        }
      }
      return {
        x: newX,
        y: newY
      };
    }

    /** Filter an array x/y based on various criteria x points are expected to be sorted
     *
     * @param data - object containing 2 properties x and y
     * @param options - options
     * @return filtered array
     */
    function xyFilterX(data, options = {}) {
      const {
        x,
        y
      } = data;
      const {
        from = x[0],
        to = x[x.length - 1],
        zones = [{
          from,
          to
        }],
        exclusions = []
      } = options;
      let normalizedZones = zonesNormalize(zones, {
        from,
        to,
        exclusions
      });
      let currentZoneIndex = 0;
      let newX = [];
      let newY = [];
      let position = 0;
      while (position < x.length) {
        if (x[position] <= normalizedZones[currentZoneIndex].to && x[position] >= normalizedZones[currentZoneIndex].from) {
          newX.push(x[position]);
          newY.push(y[position]);
        } else if (x[position] > normalizedZones[currentZoneIndex].to) {
          currentZoneIndex++;
          if (!normalizedZones[currentZoneIndex]) break;
        }
        position++;
      }
      return {
        x: newX,
        y: newY
      };
    }

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

    /**
     * Set a value (default 0) to specific zones.
     *
     * @param data - Object that contains property x (an ordered increasing array) and y (an array)
     * @param options - options
     * @returns - Array of points
     */
    function xySetYValue(data, options = {}) {
      xyCheck(data);
      const {
        x,
        y
      } = data;
      let {
        zones,
        value = 0
      } = options;
      if (!Array.isArray(zones) || zones.length === 0) {
        return data;
      }
      zones = zonesNormalize(zones);
      const newX = x.slice();
      const newY = y.slice();
      let currentZone = zones[0];
      let position = 0;
      loop: for (let i = 0; i < x.length; i++) {
        while (currentZone.to < x[i]) {
          position++;
          currentZone = zones[position];
          if (!currentZone) {
            i = x.length;
            break loop;
          }
        }
        if (x[i] >= currentZone.from) {
          newY[i] = value;
        }
      }
      return {
        x: newX,
        y: newY
      };
    }

    function matrixCheck(data) {
      if (data.length === 0 || data[0].length === 0) {
        throw new RangeError('matrix should contain data');
      }
      const firstLength = data[0].length;
      for (let i = 1; i < data.length; i++) {
        if (data[i].length !== firstLength) {
          throw new RangeError('All rows should has the same length');
        }
      }
    }

    /**
     * Get min and max Z
     *
     * @param matrix - matrix [rows][cols].
     */
    function matrixMinMaxZ(matrix) {
      matrixCheck(matrix);
      const nbRows = matrix.length;
      const nbColumns = matrix[0].length;
      let min = matrix[0][0];
      let max = matrix[0][0];
      for (let column = 0; column < nbColumns; column++) {
        for (let row = 0; row < nbRows; row++) {
          if (matrix[row][column] < min) min = matrix[row][column];
          if (matrix[row][column] > max) max = matrix[row][column];
        }
      }
      return {
        min,
        max
      };
    }

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

    /**
     * Create a random array of numbers of a specific length
     *
     * @return - array of random floats normally distributed
     */
    let spare;
    let hasSpare = false;
    function createRandomArray(options = {}) {
      const {
        mean = 0,
        standardDeviation = 1,
        length = 1000,
        range = 1,
        seed,
        distribution = 'normal'
      } = options;
      const generator = new XSadd(seed);
      let returnArray = new Float64Array(length);
      switch (distribution) {
        case 'normal':
          for (let i = 0; i < length; i++) {
            returnArray[i] = generateGaussian(mean, standardDeviation, generator);
          }
          break;
        case 'uniform':
          for (let i = 0; i < length; i++) {
            returnArray[i] = (generator.random() - 0.5) * range + mean;
          }
          break;
        default:
          // eslint-disable-next-line @typescript-eslint/restrict-template-expressions
          throw new Error(`unknown distribution: ${distribution}`);
      }
      return returnArray;
    }
    function generateGaussian(mean, standardDeviation, generator) {
      let val, u, v, s;
      if (hasSpare) {
        hasSpare = false;
        val = spare * standardDeviation + mean;
      } else {
        do {
          u = generator.random() * 2 - 1;
          v = generator.random() * 2 - 1;
          s = u * u + v * v;
        } while (s >= 1 || s === 0);
        s = Math.sqrt(-2 * Math.log(s) / s);
        spare = v * s;
        hasSpare = true;
        val = mean + standardDeviation * u * s;
      }
      return val;
    }

    function addNoise(data, 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)
        };
        let shapeGenerator = getShape1D(shape);
        this.shape = shapeGenerator;
        assertNumber$2(this.from, 'from');
        assertNumber$2(this.to, 'to');
        assertInteger$1(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);
        let {
          width,
          widthLeft,
          widthRight
        } = options;
        /*
         if we don't force the fwhm we just take the one from the shape
         however we have many way to force it:
         - use [x,y,fwhm]
         - define `width` that will be converted to fwhm
         - define `widthLeft` and `widthRight` to define asymmetric peaks
         - have a callback `peakWidthFct`
         This should evolve in the future because we will not always have `fwhm`
         */
        const fwhm = peakFWHM !== undefined ? peakFWHM : peakWidth ? shape.widthToFWHM(peakWidth) : this.peakWidthFct ? this.peakWidthFct(xPosition) : width !== undefined ? width : shape.fwhm;
        if (!widthLeft) widthLeft = fwhm;
        if (!widthRight) widthRight = fwhm;
        if (!widthLeft || !widthRight) {
          throw new Error('Width left or right is undefined or zero');
        }
        let factor = options.factor === undefined ? shape.getFactor() : options.factor;
        const firstValue = xPosition - widthLeft / 2 * factor;
        const lastValue = xPosition + widthRight / 2 * factor;
        const firstPoint = Math.max(0, Math.floor((firstValue - this.from) / this.interval));
        const lastPoint = Math.min(this.nbPoints - 1, Math.ceil((lastValue - this.from) / this.interval));
        const middlePoint = Math.round((xPosition - this.from) / this.interval);
        // PEAK SHAPE MAY BE ASYMMETRC (widthLeft and widthRight) !
        // we calculate the left part of the shape
        shape.fwhm = widthLeft;
        for (let index = firstPoint; index < Math.max(middlePoint, 0); index++) {
          this.data.y[index] += intensity * shape.fct(this.data.x[index] - xPosition);
        }
        // we calculate the right part of the gaussian
        shape.fwhm = widthRight;
        for (let index = Math.min(middlePoint, lastPoint); index <= lastPoint; index++) {
          this.data.y[index] += intensity * shape.fct(this.data.x[index] - xPosition);
        }
      }
      /**
       * Add a baseline to the spectrum.
       * @param baselineFct - Mathematical function producing the baseline you want.
       */
      addBaseline(baselineFct) {
        addBaseline(this.data, baselineFct);
        return this;
      }
      /**
       * Add noise to the spectrum.
       *
       * @param percent - Noise's amplitude in percents of the spectrum max value. Default: 1.
       */
      addNoise(options) {
        addNoise(this.data, options);
        return this;
      }
      /**
       * Get the generated spectrum.
       */
      getSpectrum(options = {}) {
        if (typeof options === 'boolean') {
          options = {
            copy: options
          };
        }
        const {
          copy = true,
          threshold = 0
        } = options;
        if (threshold) {
          let minPeakHeight = this.maxPeakHeight * threshold;
          let x = [];
          let y = [];
          for (let i = 0; i < this.data.x.length; i++) {
            if (this.data.y[i] >= minPeakHeight) {
              x.push(this.data.x[i]);
              y.push(this.data.y[i]);
            }
          }
          return {
            x: 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$1(value, name) {
      if (!Number.isInteger(value)) {
        throw new TypeError(`${name} option must be an integer`);
      }
    }
    function assertNumber$2(value, name) {
      if (!Number.isFinite(value)) {
        throw new TypeError(`${name} option must be a number`);
      }
    }
    /**
     * Generates a spectrum and returns it.
     * @param peaks - List of peaks to put in the spectrum.
     * @param options
     */
    function generateSpectrum(peaks, options = {}) {
      const {
        generator: generatorOptions,
        noise,
        baseline,
        threshold,
        peakOptions
      } = options;
      const generator = new SpectrumGenerator(generatorOptions);
      generator.addPeaks(peaks, peakOptions);
      if (baseline) generator.addBaseline(baseline);
      if (noise) {
        generator.addNoise(noise);
      }
      return generator.getSpectrum({
        threshold
      });
    }

    const axis2D = ['x', 'y'];
    const peakCoordinates = ['x', 'y', 'z'];
    const convertWidthToFWHM = (shape, width) => {
      const widthData = ensureXYNumber$1(width);
      for (let key of axis2D) {
        widthData[key] = shape.widthToFWHM(widthData[key]);
      }
      return widthData;
    };
    class Spectrum2DGenerator {
      constructor(options = {}) {
        let {
          from = 0,
          to = 100,
          nbPoints = 1001,
          peakWidthFct = () => 5,
          shape = {
            kind: 'gaussian'
          }
        } = options;
        from = ensureXYNumber$1(from);
        to = ensureXYNumber$1(to);
        nbPoints = ensureXYNumber$1(nbPoints);
        for (const axis of axis2D) {
          assertNumber$1(from[axis], `from-${axis}`);
          assertNumber$1(to[axis], `to-${axis}`);
          assertInteger(nbPoints[axis], `nbPoints-${axis}`);
        }
        this.from = from;
        this.to = to;
        this.nbPoints = nbPoints;
        this.interval = calculeIntervals(from, to, nbPoints);
        this.peakWidthFct = peakWidthFct;
        this.maxPeakHeight = Number.MIN_SAFE_INTEGER;
        let shapeGenerator = getShape2D(shape);
        this.shape = shapeGenerator;
        this.data = {
          x: new Float64Array(nbPoints.x),
          y: new Float64Array(nbPoints.y),
          z: createMatrix(this.nbPoints)
        };
        for (const axis of axis2D) {
          if (this.to[axis] <= this.from[axis]) {
            throw new RangeError('to option must be larger than from');
          }
        }
        if (typeof this.peakWidthFct !== 'function') {
          throw new TypeError('peakWidthFct option must be a function');
        }
        this.reset();
      }
      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 {
          let nbPeaks = peaks.x.length;
          for (const c of peakCoordinates) {
            if (peaks[c] && Array.isArray(peaks[c])) {
              if (nbPeaks !== peaks[c].length) {
                throw new Error('x, y, z should have the same length');
              }
            }
          }
          for (let i = 0; i < peaks.x.length; i++) {
            this.addPeak([peaks.x[i], peaks.y[i], peaks.z[i]], options);
          }
        }
        return this;
      }
      addPeak(peak, options = {}) {
        if (Array.isArray(peak) && peak.length < 3) {
          throw new Error('peak must be an array with three (or four) values or an object with {x,y,z,width?}');
        }
        if (!Array.isArray(peak) && peakCoordinates.some(e => peak[e] === undefined)) {
          throw new Error('peak must be an array with three (or four) values or an object with {x,y,z,width?}');
        }
        let xPosition;
        let yPosition;
        let intensity;
        let peakFWHM;
        let peakWidth;
        let peakShapeOptions;
        if (Array.isArray(peak)) {
          [xPosition, yPosition, intensity, peakFWHM, peakShapeOptions] = peak;
        } else {
          xPosition = peak.x;
          yPosition = peak.y;
          intensity = peak.z;
          peakFWHM = peak.fwhm;
          peakWidth = peak.width;
          peakShapeOptions = peak.shape;
        }
        const position = {
          x: xPosition,
          y: yPosition
        };
        if (intensity > this.maxPeakHeight) this.maxPeakHeight = intensity;
        let {
          shape: shapeOptions,
          width
        } = options;
        if (peakShapeOptions) {
          shapeOptions = shapeOptions ? {
            ...shapeOptions,
            ...peakShapeOptions
          } : peakShapeOptions;
        }
        const shape = shapeOptions ? getShape2D(shapeOptions) : Object.assign(Object.create(Object.getPrototypeOf(this.shape)), JSON.parse(JSON.stringify(this.shape)));
        let {
          fwhm = peakFWHM !== undefined ? peakFWHM : peakWidth ? convertWidthToFWHM(shape, peakWidth) : width ? convertWidthToFWHM(shape, width) : this.peakWidthFct(xPosition, yPosition)
        } = options;
        fwhm = ensureXYNumber$1(fwhm);
        let factor = options.factor === undefined ? shape.getFactor() : options.factor;
        factor = ensureXYNumber$1(factor);
        const firstPoint = {
          x: 0,
          y: 0
        };
        const lastPoint = {
          x: 0,
          y: 0
        };
        for (const axis of axis2D) {
          const first = position[axis] - fwhm[axis] / 2 * factor[axis];
          const last = position[axis] + fwhm[axis] / 2 * factor[axis];
          firstPoint[axis] = Math.max(0, Math.floor((first - this.from[axis]) / this.interval[axis]));
          lastPoint[axis] = Math.min(this.nbPoints[axis], Math.ceil((last - this.from[axis]) / this.interval[axis]));
        }
        shape.fwhm = fwhm;
        for (let xIndex = firstPoint.x; xIndex < lastPoint.x; xIndex++) {
          for (let yIndex = firstPoint.y; yIndex < lastPoint.y; yIndex++) {
            const value = intensity * shape.fct(this.data.x[xIndex] - position.x, this.data.y[yIndex] - position.y);
            if (value > 1e-6) {
              this.data.z[yIndex][xIndex] += value;
            }
          }
        }
        return this;
      }
      getSpectrum(options = {}) {
        if (typeof options === 'boolean') {
          options = {
            copy: options
          };
        }
        const {
          copy = true
        } = options;
        let minMaxZ = matrixMinMaxZ(this.data.z);
        return {
          minX: this.from.x,
          maxX: this.to.x,
          maxY: this.to.y,
          minY: this.from.y,
          minZ: minMaxZ.min,
          maxZ: minMaxZ.max,
          z: copy ? this.data.z.slice() : this.data.z
        };
      }
      reset() {
        const spectrum = this.data;
        for (const axis of axis2D) {
          for (let i = 0; i < this.nbPoints[axis]; i++) {
            spectrum[axis][i] = this.from[axis] + i * this.interval[axis];
          }
        }
        for (let row of spectrum.z) {
          for (let j = 0; j < row.length; j++) {
            row[j] = 0;
          }
        }
        return this;
      }
    }
    function generateSpectrum2D(peaks, options = {}) {
      const {
        generator: generatorOptions,
        peaks: addPeaksOptions
      } = options;
      const generator = new Spectrum2DGenerator(generatorOptions);
      generator.addPeaks(peaks, addPeaksOptions);
      return generator.getSpectrum();
    }
    function ensureXYNumber$1(input) {
      return typeof input !== 'object' ? {
        x: input,
        y: input
      } : {
        ...input
      };
    }
    function calculeIntervals(from, to, nbPoints) {
      return {
        x: (to.x - from.x) / (nbPoints.x - 1),
        y: (to.y - from.y) / (nbPoints.y - 1)
      };
    }
    function assertInteger(value, name) {
      if (!Number.isInteger(value)) {
        throw new TypeError(`${name} option must be an integer`);
      }
    }
    function assertNumber$1(value, name) {
      if (!Number.isFinite(value)) {
        throw new TypeError(`${name} option must be a number`);
      }
    }
    function createMatrix(nbPoints) {
      const zMatrix = new Array(nbPoints.y);
      for (let i = 0; i < nbPoints.y; i++) {
        zMatrix[i] = new Float64Array(nbPoints.x);
      }
      return zMatrix;
    }

    function hasProperty(data, key) {
      return key in data;
    }

    const {
      parse: parse$7,
      stringify: stringify$7
    } = JSON;
    /**
     * convert width and fwhm to ppm
     */
    function convertWidth(peaks, options) {
      const {
        frequency,
        convertTo,
        output = parse$7(stringify$7(peaks))
      } = options;
      const convert = getConverter(convertTo, frequency);
      for (const peak of output) {
        peak.width = convert(peak.width);
        if (hasProperty(peak, 'shape')) {
          const shape = peak.shape;
          if (shape.fwhm) {
            shape.fwhm = convert(shape.fwhm);
          }
        }
      }
      return output;
    }
    function getConverter(convertTo, frequency) {
      switch (convertTo) {
        case 'ppm':
          return x => x / frequency;
        case 'hz':
          return x => x * frequency;
        default:
          throw new Error(`Does not support convert to ${convertTo}`);
      }
    }

    function convertWidthToPPM(peaks, options) {
      return convertWidth(peaks, {
        ...options,
        convertTo: 'ppm'
      });
    }

    function peaksToXY(peaks, options) {
      const {
        frequency,
        nbPoints = 1024,
        shape
      } = options;
      if (!frequency) {
        throw new Error('frequency is mandatory');
      }
      const newPeaks = convertWidthToPPM(peaks, {
        frequency
      });
      return generateSpectrum(newPeaks, {
        generator: {
          ...getFromTo$1(newPeaks, options),
          nbPoints,
          shape
        }
      });
    }
    function getFromTo$1(newPeaks, options) {
      if ('to' in options && 'from' in options) {
        return {
          from: options.from,
          to: options.to
        };
      }
      newPeaks.sort((a, b) => a.x - b.x);
      const firstPeak = newPeaks[0];
      const lastPeak = newPeaks[newPeaks.length - 1];
      const {
        from = firstPeak.x - firstPeak.width * 2 / options.frequency,
        to = lastPeak.x + lastPeak.width * 2 / options.frequency
      } = options;
      return {
        from,
        to
      };
    }

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

    /**
     * Correction of the x and y coordinates using a quadratic optimizations with the peak and its 3 closest neighbors to determine the true x,y values of the peak.
     * This process is done in place and is very fast.
     * @param data
     * @param peaks
     */
    function optimizeTop(data, peaks) {
      const {
        x,
        y
      } = data;
      for (const peak of peaks) {
        let currentIndex = peak.index;
        // The detected peak could be moved 1 or 2 units to left or right.
        if (y[currentIndex - 1] >= y[currentIndex - 2] && y[currentIndex - 1] >= y[currentIndex]) {
          currentIndex--;
        } else if (y[currentIndex + 1] >= y[currentIndex] && y[currentIndex + 1] >= y[currentIndex + 2]) {
          currentIndex++;
        } else if (y[currentIndex - 2] >= y[currentIndex - 3] && y[currentIndex - 2] >= y[currentIndex - 1]) {
          currentIndex -= 2;
        } else if (y[currentIndex + 2] >= y[currentIndex + 1] && y[currentIndex + 2] >= y[currentIndex + 3]) {
          currentIndex += 2;
        }
        // interpolation to a sin() function
        if (y[currentIndex - 1] > 0 && y[currentIndex + 1] > 0 && y[currentIndex] >= y[currentIndex - 1] && y[currentIndex] >= y[currentIndex + 1] && (y[currentIndex] !== y[currentIndex - 1] || y[currentIndex] !== y[currentIndex + 1])) {
          let alpha = 20 * Math.log10(y[currentIndex - 1]);
          let beta = 20 * Math.log10(y[currentIndex]);
          let gamma = 20 * Math.log10(y[currentIndex + 1]);
          let p = 0.5 * (alpha - gamma) / (alpha - 2 * beta + gamma);
          peak.x = x[currentIndex] + (x[currentIndex] - x[currentIndex - 1]) * p;
          peak.y = y[currentIndex] - 0.25 * (y[currentIndex - 1] - y[currentIndex + 1]) * p;
        }
      }
    }

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

     */
    function gsd(data, options = {}) {
      let {
        sgOptions = {
          windowSize: 9,
          polynomial: 3
        },
        noiseLevel,
        smoothY = false,
        maxCriteria = true,
        minMaxRatio = 0.00025,
        realTopDetection = false
      } = options;
      let {
        x,
        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
      let equallySpaced = xIsEquallySpaced(x);
      if (noiseLevel === undefined) {
        if (equallySpaced) {
          const noiseInfo = xNoiseStandardDeviation(y);
          if (maxCriteria) {
            noiseLevel = noiseInfo.median + 1.5 * noiseInfo.sd;
          } else {
            noiseLevel = -noiseInfo.median + 1.5 * noiseInfo.sd;
          }
        } else {
          noiseLevel = 0;
        }
      } else if (!maxCriteria) {
        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;
          }
        }
      }
      let yData = y;
      let dY, ddY;
      const {
        windowSize,
        polynomial
      } = sgOptions;
      if (equallySpaced) {
        if (smoothY) {
          yData = sgg(y, x[1] - x[0], {
            windowSize,
            polynomial,
            derivative: 0
          });
        }
        dY = sgg(y, x[1] - x[0], {
          windowSize,
          polynomial,
          derivative: 1
        });
        ddY = sgg(y, x[1] - x[0], {
          windowSize,
          polynomial,
          derivative: 2
        });
      } else {
        if (smoothY) {
          yData = sgg(y, x, {
            windowSize,
            polynomial,
            derivative: 0
          });
        }
        dY = sgg(y, x, {
          windowSize,
          polynomial,
          derivative: 1
        });
        ddY = sgg(y, x, {
          windowSize,
          polynomial,
          derivative: 2
        });
      }
      const minY = xMinValue(yData);
      const maxY = xMaxValue(yData);
      if (minY > maxY || minY === maxY) return [];
      const yThreshold = minY + (maxY - minY) * minMaxRatio;
      const dX = x[1] - x[0];
      let lastMax = null;
      let lastMin = null;
      let minddY = [];
      let intervalL = [];
      let intervalR = [];
      // By the intermediate value theorem We cannot find 2 consecutive maximum or minimum
      for (let i = 1; i < yData.length - 1; ++i) {
        if (dY[i] < dY[i - 1] && dY[i] <= dY[i + 1] || dY[i] <= dY[i - 1] && dY[i] < dY[i + 1]) {
          lastMin = {
            x: x[i],
            index: i
          };
          if (dX > 0 && lastMax !== null) {
            intervalL.push(lastMax);
            intervalR.push(lastMin);
          }
        }
        // Maximum in first derivative
        if (dY[i] >= dY[i - 1] && dY[i] > dY[i + 1] || dY[i] > dY[i - 1] && dY[i] >= dY[i + 1]) {
          lastMax = {
            x: x[i],
            index: i
          };
          if (dX < 0 && lastMin !== null) {
            intervalL.push(lastMax);
            intervalR.push(lastMin);
          }
        }
        // Minimum in second derivative
        if (ddY[i] < ddY[i - 1] && ddY[i] < ddY[i + 1]) {
          minddY.push(i);
        }
      }
      let lastK = -1;
      const peaks = [];
      for (const minddYIndex of minddY) {
        let deltaX = x[minddYIndex];
        let possible = -1;
        let k = lastK + 1;
        let minDistance = Number.POSITIVE_INFINITY;
        let currentDistance = 0;
        while (possible === -1 && k < intervalL.length) {
          currentDistance = Math.abs(deltaX - (intervalL[k].x + intervalR[k].x) / 2);
          if (currentDistance < (intervalR[k].x - intervalL[k].x) / 2) {
            possible = k;
            lastK = k;
          }
          ++k;
          // Not getting closer?
          if (currentDistance >= minDistance) {
            break;
          }
          minDistance = currentDistance;
        }
        if (possible !== -1) {
          if (yData[minddYIndex] > yThreshold) {
            let width = Math.abs(intervalR[possible].x - intervalL[possible].x);
            peaks.push({
              id: v4(),
              x: deltaX,
              y: yData[minddYIndex],
              width,
              index: minddYIndex,
              ddY: ddY[minddYIndex],
              inflectionPoints: {
                from: intervalL[possible],
                to: intervalR[possible]
              }
            });
          }
        }
      }
      if (realTopDetection) {
        optimizeTop({
          x,
          y: yData
        }, peaks);
      }
      peaks.forEach(peak => {
        if (!maxCriteria) {
          peak.y *= -1;
          peak.ddY = peak.ddY * -1;
        }
      });
      peaks.sort((a, b) => {
        return a.x - b.x;
      });
      return peaks;
    }

    /**
     * This function returns the sumOfShapes function
     * This function gives sumOfShapes access to the peak list and the associated data
     * @param parameters - parameters
     */
    function getSumOfShapes$1(internalPeaks) {
      return function sumOfShapes(parameters) {
        return x => {
          let totalY = 0;
          for (const peak of internalPeaks) {
            const peakX = parameters[peak.fromIndex];
            const y = parameters[peak.fromIndex + 1];
            for (let i = 2; i < parameters.length; i++) {
              //@ts-expect-error Not simply to solve the issue
              peak.shapeFct[peak.parameters[i]] = parameters[peak.fromIndex + i];
            }
            totalY += y * peak.shapeFct.fct(x - peakX);
          }
          return totalY;
        };
      };
    }

    /**
     * Asserts that value is truthy.
     *
     * @param value - Value to check.
     * @param message - Optional error message to throw.
     */
    function assert(value, message) {
      if (!value) {
        throw new Error(message ? message : 'unreachable');
      }
    }

    const DefaultParameters = {
      x: {
        init: peak => peak.x,
        min: (peak, peakShape) => peak.x - peakShape.fwhm * 2,
        max: (peak, peakShape) => peak.x + peakShape.fwhm * 2,
        gradientDifference: (peak, peakShape) => peakShape.fwhm * 2e-3
      },
      y: {
        init: peak => peak.y,
        min: peak => peak.y < 0 ? -1.1 : 0,
        max: peak => peak.y < 0 ? 0 : 1.1,
        gradientDifference: () => 1e-3
      },
      fwhm: {
        init: (peak, peakShape) => peakShape.fwhm,
        min: (peak, peakShape) => peakShape.fwhm * 0.25,
        max: (peak, peakShape) => peakShape.fwhm * 4,
        gradientDifference: (peak, peakShape) => peakShape.fwhm * 2e-3
      },
      mu: {
        init: (peak, peakShape) => peakShape.mu,
        min: () => 0,
        max: () => 1,
        gradientDifference: () => 0.01
      }
    };

    const properties$1 = ['init', 'min', 'max', 'gradientDifference'];
    /**
     * Return an array of internalPeaks that contains the exact init, min, max values based on the options
     * @param peaks
     * @param options
     * @returns
     */
    function getInternalPeaks(peaks, minMaxY, options = {}) {
      let index = 0;
      let internalPeaks = [];
      const {
        baseline: shiftValue = minMaxY.min
      } = options;
      const normalizedPeaks = peaks.map(peak => {
        return {
          ...peak,
          y: (peak.y - shiftValue) / minMaxY.range
        };
      });
      for (const peak of normalizedPeaks) {
        const {
          id,
          shape = options.shape ? options.shape : {
            kind: 'gaussian'
          }
        } = peak;
        const shapeFct = getShape1D(shape);
        const parameters = ['x', 'y', ...shapeFct.getParameters()];
        const propertiesValues = {
          min: [],
          max: [],
          init: [],
          gradientDifference: []
        };
        for (let parameter of parameters) {
          for (let property of properties$1) {
            // check if the property is specified in the peak
            let propertyValue = peak?.parameters?.[parameter]?.[property];
            if (propertyValue) {
              propertyValue = getNormalizedValue$1(propertyValue, parameter, property, minMaxY, options.baseline);
              propertiesValues[property].push(propertyValue);
              continue;
            }
            // check if there are some global option, it could be a number or a callback
            let generalParameterValue = options?.parameters?.[parameter]?.[property];
            if (generalParameterValue) {
              if (typeof generalParameterValue === 'number') {
                generalParameterValue = getNormalizedValue$1(generalParameterValue, parameter, property, minMaxY, options.baseline);
                propertiesValues[property].push(generalParameterValue);
                continue;
              } else {
                let value = generalParameterValue(peak);
                value = getNormalizedValue$1(value, parameter, property, minMaxY, options.baseline);
                propertiesValues[property].push(value);
                continue;
              }
            }
            // we just need to take the default parameters
            assert(DefaultParameters[parameter], `No default parameter for ${parameter}`);
            const defaultParameterValues = DefaultParameters[parameter][property];
            //@ts-expect-error should never happen
            propertiesValues[property].push(defaultParameterValues(peak, shapeFct));
          }
        }
        const fromIndex = index;
        const toIndex = fromIndex + parameters.length - 1;
        index += toIndex - fromIndex + 1;
        internalPeaks.push({
          id,
          shape,
          shapeFct,
          parameters,
          propertiesValues,
          fromIndex,
          toIndex
        });
      }
      return internalPeaks;
    }
    function getNormalizedValue$1(value, parameter, property, minMaxY, baseline) {
      if (parameter === 'y') {
        if (property === 'gradientDifference') {
          return value;
        } else {
          return baseline !== undefined ? (value - baseline) / minMaxY.range : (value - minMaxY.min) / minMaxY.range;
        }
      }
      return value;
    }

    function checkOptions(data, parameterizedFunction, options) {
      let {
        timeout,
        minValues,
        maxValues,
        initialValues,
        weights = 1,
        damping = 1e-2,
        dampingStepUp = 11,
        dampingStepDown = 9,
        maxIterations = 100,
        errorTolerance = 1e-7,
        centralDifference = false,
        gradientDifference = 10e-2,
        improvementThreshold = 1e-3
      } = options;
      if (damping <= 0) {
        throw new Error('The damping option must be a positive number');
      } else if (!data.x || !data.y) {
        throw new Error('The data parameter must have x and y elements');
      } else if (!isAnyArray(data.x) || data.x.length < 2 || !isAnyArray(data.y) || data.y.length < 2) {
        throw new Error('The data parameter elements must be an array with more than 2 points');
      } else if (data.x.length !== data.y.length) {
        throw new Error('The data parameter elements must have the same size');
      }
      if (!(initialValues && initialValues.length > 0)) {
        throw new Error('The initialValues option is mandatory and must be an array');
      }
      let parameters = initialValues;
      let nbPoints = data.y.length;
      let parLen = parameters.length;
      maxValues = maxValues || new Array(parLen).fill(Number.MAX_SAFE_INTEGER);
      minValues = minValues || new Array(parLen).fill(Number.MIN_SAFE_INTEGER);
      if (maxValues.length !== minValues.length) {
        throw new Error('minValues and maxValues must be the same size');
      }
      if (typeof gradientDifference === 'number') {
        gradientDifference = new Array(parameters.length).fill(gradientDifference);
      } else if (isAnyArray(gradientDifference)) {
        if (gradientDifference.length !== parLen) {
          gradientDifference = new Array(parLen).fill(gradientDifference[0]);
        }
      } else {
        throw new Error('gradientDifference should be a number or array with length equal to the number of parameters');
      }
      let filler;
      if (typeof weights === 'number') {
        let value = 1 / weights ** 2;
        filler = () => value;
      } else if (isAnyArray(weights)) {
        if (weights.length < data.x.length) {
          let value = 1 / weights[0] ** 2;
          filler = () => value;
        } else {
          filler = i => 1 / weights[i] ** 2;
        }
      } else {
        throw new Error('weights should be a number or array with length equal to the number of data points');
      }
      let checkTimeout;
      if (timeout !== undefined) {
        if (typeof timeout !== 'number') {
          throw new Error('timeout should be a number');
        }
        let endTime = Date.now() + timeout * 1000;
        checkTimeout = () => Date.now() > endTime;
      } else {
        checkTimeout = () => false;
      }
      let weightSquare = new Array(data.x.length);
      for (let i = 0; i < nbPoints; i++) {
        weightSquare[i] = filler(i);
      }
      return {
        checkTimeout,
        minValues,
        maxValues,
        parameters,
        weightSquare,
        damping,
        dampingStepUp,
        dampingStepDown,
        maxIterations,
        errorTolerance,
        centralDifference,
        gradientDifference,
        improvementThreshold
      };
    }

    /**
     * the sum of the weighted squares of the errors (or weighted residuals) between the data.y
     * and the curve-fit function.
     * @ignore
     * @param {{x:ArrayLike<number>, y:ArrayLike<number>}} data - Array of points to fit in the format [x1, x2, ... ], [y1, y2, ... ]
     * @param {ArrayLike<number>} parameters - Array of current parameter values
     * @param {function} parameterizedFunction - The parameters and returns a function with the independent variable as a parameter
     * @param {ArrayLike<number>} weightSquare - Square of weights
     * @return {number}
     */
    function errorCalculation(data, parameters, parameterizedFunction, weightSquare) {
      let error = 0;
      const func = parameterizedFunction(parameters);
      for (let i = 0; i < data.x.length; i++) {
        error += Math.pow(data.y[i] - func(data.x[i]), 2) / weightSquare[i];
      }
      return error;
    }

    /**
     * Difference of the matrix function over the parameters
     * @ignore
     * @param {{x:ArrayLike<number>, y:ArrayLike<number>}} data - Array of points to fit in the format [x1, x2, ... ], [y1, y2, ... ]
     * @param {ArrayLike<number>} evaluatedData - Array of previous evaluated function values
     * @param {Array<number>} params - Array of previous parameter values
     * @param {number|array} gradientDifference - The step size to approximate the jacobian matrix
     * @param {boolean} centralDifference - If true the jacobian matrix is approximated by central differences otherwise by forward differences
     * @param {function} paramFunction - The parameters and returns a function with the independent variable as a parameter
     * @return {Matrix}
     */
    function gradientFunction(data, evaluatedData, params, gradientDifference, paramFunction, centralDifference) {
      const nbParams = params.length;
      const nbPoints = data.x.length;
      let ans = Matrix.zeros(nbParams, nbPoints);
      let rowIndex = 0;
      for (let param = 0; param < nbParams; param++) {
        if (gradientDifference[param] === 0) continue;
        let delta = gradientDifference[param];
        let auxParams = params.slice();
        auxParams[param] += delta;
        let funcParam = paramFunction(auxParams);
        if (!centralDifference) {
          for (let point = 0; point < nbPoints; point++) {
            ans.set(rowIndex, point, (evaluatedData[point] - funcParam(data.x[point])) / delta);
          }
        } else {
          auxParams = params.slice();
          auxParams[param] -= delta;
          delta *= 2;
          let funcParam2 = paramFunction(auxParams);
          for (let point = 0; point < nbPoints; point++) {
            ans.set(rowIndex, point, (funcParam2(data.x[point]) - funcParam(data.x[point])) / delta);
          }
        }
        rowIndex++;
      }
      return ans;
    }

    /**
     * Matrix function over the samples
     * @ignore
     * @param {{x:ArrayLike<number>, y:ArrayLike<number>}} data - Array of points to fit in the format [x1, x2, ... ], [y1, y2, ... ]
     * @param {ArrayLike<number>} evaluatedData - Array of previous evaluated function values
     * @return {Matrix}
     */
    function matrixFunction(data, evaluatedData) {
      const m = data.x.length;
      let ans = new Matrix(m, 1);
      for (let point = 0; point < m; point++) {
        ans.set(point, 0, data.y[point] - evaluatedData[point]);
      }
      return ans;
    }
    /**
     * Iteration for Levenberg-Marquardt
     * @ignore
     * @param {{x:ArrayLike<number>, y:ArrayLike<number>}} data - Array of points to fit in the format [x1, x2, ... ], [y1, y2, ... ]
     * @param {Array<number>} params - Array of previous parameter values
     * @param {number} damping - Levenberg-Marquardt parameter
     * @param {number|array} gradientDifference - The step size to approximate the jacobian matrix
     * @param {boolean} centralDifference - If true the jacobian matrix is approximated by central differences otherwise by forward differences
     * @param {function} parameterizedFunction - The parameters and returns a function with the independent variable as a parameter
     */
    function step(data, params, damping, gradientDifference, parameterizedFunction, centralDifference, weights) {
      let value = damping;
      let identity = Matrix.eye(params.length, params.length, value);
      const func = parameterizedFunction(params);
      let evaluatedData = new Float64Array(data.x.length);
      for (let i = 0; i < data.x.length; i++) {
        evaluatedData[i] = func(data.x[i]);
      }
      let gradientFunc = gradientFunction(data, evaluatedData, params, gradientDifference, parameterizedFunction, centralDifference);
      let residualError = matrixFunction(data, evaluatedData);
      let inverseMatrix = inverse(identity.add(gradientFunc.mmul(gradientFunc.transpose().scale('row', {
        scale: weights
      }))));
      let jacobianWeightResidualError = gradientFunc.mmul(residualError.scale('row', {
        scale: weights
      }));
      let perturbations = inverseMatrix.mmul(jacobianWeightResidualError);
      return {
        perturbations,
        jacobianWeightResidualError
      };
    }

    /**
     * Curve fitting algorithm
     * @param {{x:ArrayLike<number>, y:ArrayLike<number>}} data - Array of points to fit in the format [x1, x2, ... ], [y1, y2, ... ]
     * @param {function} parameterizedFunction - Takes an array of parameters and returns a function with the independent variable as its sole argument
     * @param {object} options - Options object
     * @param {ArrayLike<number>} options.initialValues - Array of initial parameter values
     * @param {number|ArrayLike<number>} [options.weights = 1] - weighting vector, if the length does not match with the number of data points, the vector is reconstructed with first value.
     * @param {number} [options.damping = 1e-2] - Levenberg-Marquardt parameter, small values of the damping parameter λ result in a Gauss-Newton update and large
    values of λ result in a gradient descent update
     * @param {number} [options.dampingStepDown = 9] - factor to reduce the damping (Levenberg-Marquardt parameter) when there is not an improvement when updating parameters.
     * @param {number} [options.dampingStepUp = 11] - factor to increase the damping (Levenberg-Marquardt parameter) when there is an improvement when updating parameters.
     * @param {number} [options.improvementThreshold = 1e-3] - the threshold to define an improvement through an update of parameters
     * @param {number|ArrayLike<number>} [options.gradientDifference = 10e-2] - The step size to approximate the jacobian matrix
     * @param {boolean} [options.centralDifference = false] - If true the jacobian matrix is approximated by central differences otherwise by forward differences
     * @param {ArrayLike<number>} [options.minValues] - Minimum allowed values for parameters
     * @param {ArrayLike<number>} [options.maxValues] - Maximum allowed values for parameters
     * @param {number} [options.maxIterations = 100] - Maximum of allowed iterations
     * @param {number} [options.errorTolerance = 10e-3] - Minimum uncertainty allowed for each point.
     * @param {number} [options.timeout] - maximum time running before throw in seconds.
     * @return {{parameterValues: Array<number>, parameterError: number, iterations: number}}
     */
    function levenbergMarquardt(data, parameterizedFunction, options) {
      let {
        checkTimeout,
        minValues,
        maxValues,
        parameters,
        weightSquare,
        damping,
        dampingStepUp,
        dampingStepDown,
        maxIterations,
        errorTolerance,
        centralDifference,
        gradientDifference,
        improvementThreshold
      } = checkOptions(data, parameterizedFunction, options);
      let error = errorCalculation(data, parameters, parameterizedFunction, weightSquare);
      let optimalError = error;
      let optimalParameters = parameters.slice();
      let converged = error <= errorTolerance;
      let iteration = 0;
      for (; iteration < maxIterations && !converged; iteration++) {
        let previousError = error;
        let {
          perturbations,
          jacobianWeightResidualError
        } = step(data, parameters, damping, gradientDifference, parameterizedFunction, centralDifference, weightSquare);
        for (let k = 0; k < parameters.length; k++) {
          parameters[k] = Math.min(Math.max(minValues[k], parameters[k] - perturbations.get(k, 0)), maxValues[k]);
        }
        error = errorCalculation(data, parameters, parameterizedFunction, weightSquare);
        if (isNaN(error)) break;
        if (error < optimalError - errorTolerance) {
          optimalError = error;
          optimalParameters = parameters.slice();
        }
        let improvementMetric = (previousError - error) / perturbations.transpose().mmul(perturbations.mul(damping).add(jacobianWeightResidualError)).get(0, 0);
        if (improvementMetric > improvementThreshold) {
          damping = Math.max(damping / dampingStepDown, 1e-7);
        } else {
          damping = Math.min(damping * dampingStepUp, 1e7);
        }
        if (checkTimeout()) {
          throw new Error(`The execution time is over to ${options.timeout} seconds`);
        }
        converged = error <= errorTolerance;
      }
      return {
        parameterValues: optimalParameters,
        parameterError: optimalError,
        iterations: iteration
      };
    }

    /**
     * Preparata, F. P., & Shamos, M. I. (2012). Computational geometry: an introduction. Springer Science & Business Media.
     * @param {Array} x - The array with x coordinates of the points.
     * @param {Array} y - The array with y coordinates of the points.
     * @return {Array} The indices of the points of anticlockwise lower convex hull
     * @private
     */
    function antiLowerConvexHull(x, y) {
      if (x.length !== y.length) {
        throw new RangeError('X and Y vectors has different dimensions');
      }
      const nbPoints = x.length - 1;
      if (nbPoints === 0) return [0];
      if (nbPoints === 1) return [0, 1];
      let currentPoint = 0;
      let result = new Array(x.length).fill(true);
      while (true) {
        const a = currentPoint;
        const b = moveOn(currentPoint, nbPoints, result);
        const c = moveOn(moveOn(currentPoint, nbPoints, result), nbPoints, result);
        const det = x[c] * (y[a] - y[b]) + x[a] * (y[b] - y[c]) + x[b] * (y[c] - y[a]);
        const leftTurn = det >= 0;
        if (leftTurn) {
          currentPoint = b;
        } else {
          result[b] = false;
          currentPoint = moveBack(currentPoint, nbPoints, result);
        }
        if (c === nbPoints) break;
      }
      return result.map((item, index) => item === false ? false : index).filter(item => item !== false);
    }

    /**
     * @param {number} currentPoint - The index of the current point to make the move
     * @param {number} nbPoints - The total number of points in the array
     * @param {Array} vector - The array with the points
     * @return {number} the index of the point after the move
     * @private
     */

    function moveBack(currentPoint, nbPoints, vector) {
      let counter = currentPoint - 1;
      while (vector[counter] === false) counter--;
      return currentPoint === 0 ? nbPoints : counter;
    }
    function moveOn(currentPoint, nbPoints, vector) {
      let counter = currentPoint + 1;
      while (vector[counter] === false) counter++;
      return currentPoint === nbPoints ? 0 : counter;
    }

    /**
     * Performs a global optimization of required parameters
     * It will return an object containing:
     * - `minFunctionValue`: The minimum value found for the objetive function
     * - `optima`: Array of Array of values for all the variables where the function reach its minimum value
     * - `iterations`: Number of iterations performed in the process
     * - `finalState`: Internal state allowing to continue optimization (initialState)
     * @param {function} objectiveFunction Function to evaluate. It should accept an array of variables
     * @param {Array} lowerBoundaries Array containing for each variable the lower boundary
     * @param {Array} upperBoundaries Array containing for each variable the higher boundary
     * @param {Object} [options={}]
     * @param {number} [options.iterations] - Number of iterations.
     * @param {number} [options.epsilon] - Tolerance to choose best current value.
     * @param {number} [options.tolerance] - Minimum tollerance of the function.
     * @param {number} [options.tolerance2] - Minimum tollerance of the function.
     * @param {Object} [options.initialState={}}] - finalState of previous optimization.
     * @return {Object} {finalState, iterations, minFunctionValue}
     * */

    function direct(objectiveFunction, lowerBoundaries, upperBoundaries, options = {}) {
      const {
        iterations = 50,
        epsilon = 1e-4,
        tolerance = 1e-16,
        tolerance2 = 1e-12,
        initialState = {}
      } = options;
      if (objectiveFunction === undefined || lowerBoundaries === undefined || upperBoundaries === undefined) {
        throw new RangeError('There is something undefined');
      }
      lowerBoundaries = new Float64Array(lowerBoundaries);
      upperBoundaries = new Float64Array(upperBoundaries);
      if (lowerBoundaries.length !== upperBoundaries.length) {
        throw new Error('Lower bounds and Upper bounds for x are not of the same length');
      }

      //-------------------------------------------------------------------------
      //                        STEP 1. Initialization
      //-------------------------------------------------------------------------
      let n = lowerBoundaries.length;
      let diffBorders = upperBoundaries.map((x, i) => x - lowerBoundaries[i]);
      let {
        numberOfRectangles = 0,
        totalIterations = 0,
        unitaryCoordinates = [new Float64Array(n).fill(0.5)],
        middlePoint = new Float64Array(n).map((value, index) => {
          return lowerBoundaries[index] + unitaryCoordinates[0][index] * diffBorders[index];
        }),
        bestCurrentValue = objectiveFunction(middlePoint),
        fCalls = 1,
        smallerDistance = 0,
        edgeSizes = [new Float64Array(n).fill(0.5)],
        diagonalDistances = [Math.sqrt(n * Math.pow(0.5, 2))],
        functionValues = [bestCurrentValue],
        differentDistances = diagonalDistances,
        smallerValuesByDistance = [bestCurrentValue],
        choiceLimit = undefined
      } = initialState;
      if (initialState.originalCoordinates && initialState.originalCoordinates.length > 0) {
        bestCurrentValue = xMinValue(functionValues);
        choiceLimit = epsilon * Math.abs(bestCurrentValue) > 1e-8 ? epsilon * Math.abs(bestCurrentValue) : 1e-8;
        smallerDistance = getMinIndex(functionValues, diagonalDistances, choiceLimit, bestCurrentValue);
        unitaryCoordinates = initialState.originalCoordinates.slice();
        for (let j = 0; j < unitaryCoordinates.length; j++) {
          for (let i = 0; i < lowerBoundaries.length; i++) {
            unitaryCoordinates[j][i] = (unitaryCoordinates[j][i] - lowerBoundaries[i]) / diffBorders[i];
          }
        }
      }
      let iteration = 0;
      //-------------------------------------------------------------------------
      //                          Iteration loop
      //-------------------------------------------------------------------------

      while (iteration < iterations) {
        //----------------------------------------------------------------------
        //  STEP 2. Identify the set S of all potentially optimal rectangles
        //----------------------------------------------------------------------

        let S1 = [];
        let idx = differentDistances.findIndex(
        // eslint-disable-next-line no-loop-func
        e => e === diagonalDistances[smallerDistance]);
        let counter = 0;
        for (let i = idx; i < differentDistances.length; i++) {
          for (let f = 0; f < functionValues.length; f++) {
            if (functionValues[f] === smallerValuesByDistance[i] & diagonalDistances[f] === differentDistances[i]) {
              S1[counter++] = f;
            }
          }
        }
        let optimumValuesIndex, S3;
        if (differentDistances.length - idx > 1) {
          let a1 = diagonalDistances[smallerDistance];
          let b1 = functionValues[smallerDistance];
          let a2 = differentDistances[differentDistances.length - 1];
          let b2 = smallerValuesByDistance[differentDistances.length - 1];
          let slope = (b2 - b1) / (a2 - a1);
          let constant = b1 - slope * a1;
          let S2 = new Uint32Array(counter);
          counter = 0;
          for (let i = 0; i < S2.length; i++) {
            let j = S1[i];
            if (functionValues[j] <= slope * diagonalDistances[j] + constant + tolerance2) {
              S2[counter++] = j;
            }
          }
          let xHull = [];
          let yHull = [];
          for (let i = 0; i < counter; i++) {
            xHull.push(diagonalDistances[S2[i]]);
            yHull.push(functionValues[S2[i]]);
          }
          let lowerIndexHull = antiLowerConvexHull(xHull, yHull);
          S3 = [];
          for (let i = 0; i < lowerIndexHull.length; i++) {
            S3.push(S2[lowerIndexHull[i]]);
          }
        } else {
          S3 = S1.slice(0, counter);
        }
        optimumValuesIndex = S3;
        //--------------------------------------------------------------
        // STEPS 3,5: Select any rectangle j in S
        //--------------------------------------------------------------
        for (let k = 0; k < optimumValuesIndex.length; k++) {
          let j = optimumValuesIndex[k];
          let largerSide = xMaxValue(edgeSizes[j]);
          let largeSidesIndex = new Uint32Array(edgeSizes[j].length);
          counter = 0;
          for (let i = 0; i < edgeSizes[j].length; i++) {
            if (Math.abs(edgeSizes[j][i] - largerSide) < tolerance) {
              largeSidesIndex[counter++] = i;
            }
          }
          let delta = 2 * largerSide / 3;
          let bestFunctionValues = [];
          for (let r = 0; r < counter; r++) {
            let i = largeSidesIndex[r];
            let firstMiddleCenter = unitaryCoordinates[j].slice();
            let secondMiddleCenter = unitaryCoordinates[j].slice();
            firstMiddleCenter[i] += delta;
            secondMiddleCenter[i] -= delta;
            let firstMiddleValue = new Float64Array(firstMiddleCenter.length);
            let secondMiddleValue = new Float64Array(secondMiddleCenter.length);
            for (let i = 0; i < firstMiddleCenter.length; i++) {
              firstMiddleValue[i] = lowerBoundaries[i] + firstMiddleCenter[i] * diffBorders[i];
              secondMiddleValue[i] = lowerBoundaries[i] + secondMiddleCenter[i] * diffBorders[i];
            }
            let firstMinValue = objectiveFunction(firstMiddleValue);
            let secondMinValue = objectiveFunction(secondMiddleValue);
            fCalls += 2;
            bestFunctionValues.push({
              minValue: Math.min(firstMinValue, secondMinValue),
              index: r
            });
            // [Math.min(firstMinValue, secondMinValue), r];
            unitaryCoordinates.push(firstMiddleCenter, secondMiddleCenter);
            functionValues.push(firstMinValue, secondMinValue);
          }
          let b = bestFunctionValues.sort((a, b) => a.minValue - b.minValue);
          for (let r = 0; r < counter; r++) {
            let u = largeSidesIndex[b[r].index];
            let ix1 = numberOfRectangles + 2 * (b[r].index + 1) - 1;
            let ix2 = numberOfRectangles + 2 * (b[r].index + 1);
            edgeSizes[j][u] = delta / 2;
            edgeSizes[ix1] = edgeSizes[j].slice();
            edgeSizes[ix2] = edgeSizes[j].slice();
            diagonalDistances[j] = xNorm(edgeSizes[j]);
            diagonalDistances[ix1] = diagonalDistances[j];
            diagonalDistances[ix2] = diagonalDistances[j];
          }
          numberOfRectangles += 2 * counter;
        }

        //--------------------------------------------------------------
        //                  Update
        //--------------------------------------------------------------

        bestCurrentValue = xMinValue(functionValues);
        choiceLimit = epsilon * Math.abs(bestCurrentValue) > 1e-8 ? epsilon * Math.abs(bestCurrentValue) : 1e-8;
        smallerDistance = getMinIndex(functionValues, diagonalDistances, choiceLimit, bestCurrentValue);
        differentDistances = Array.from(new Set(diagonalDistances));
        differentDistances = differentDistances.sort((a, b) => a - b);
        smallerValuesByDistance = [];
        for (let i = 0; i < differentDistances.length; i++) {
          let minIndex;
          let minValue = Number.POSITIVE_INFINITY;
          for (let k = 0; k < diagonalDistances.length; k++) {
            if (diagonalDistances[k] === differentDistances[i]) {
              if (functionValues[k] < minValue) {
                minValue = functionValues[k];
                minIndex = k;
              }
            }
          }
          smallerValuesByDistance.push(functionValues[minIndex]);
        }
        for (let j = 0; j < functionValues.length; j++) {
          if (functionValues[j] === bestCurrentValue) {
            let temp = [];
            for (let i = 0; i < lowerBoundaries.length; i++) {
              temp.push(lowerBoundaries[i] + unitaryCoordinates[j][i] * diffBorders[i]);
            }
          }
        }
        iteration += 1;
      }
      //--------------------------------------------------------------
      //                  Saving results
      //--------------------------------------------------------------

      let result = {};
      result.minFunctionValue = bestCurrentValue;
      result.iterations = iteration;
      let originalCoordinates = [];
      for (let j = 0; j < numberOfRectangles + 1; j++) {
        let pair = [];
        for (let i = 0; i < lowerBoundaries.length; i++) {
          pair.push(lowerBoundaries[i] + unitaryCoordinates[j][i] * diffBorders[i]);
        }
        originalCoordinates.push(pair);
      }
      result.finalState = {
        numberOfRectangles,
        totalIterations: totalIterations += iterations,
        originalCoordinates,
        middlePoint,
        fCalls,
        smallerDistance,
        edgeSizes,
        diagonalDistances,
        functionValues,
        differentDistances,
        smallerValuesByDistance,
        choiceLimit
      };
      let minimizer = [];
      for (let i = 0; i < functionValues.length; i++) {
        if (functionValues[i] === bestCurrentValue) {
          minimizer.push(originalCoordinates[i]);
        }
      }
      result.optima = minimizer;
      return result;
    }
    function getMinIndex(functionValues, diagonalDistances, choiceLimit, bestCurrentValue) {
      let item = [];
      for (let i = 0; i < functionValues.length; i++) {
        item[i] = Math.abs(functionValues[i] - (bestCurrentValue + choiceLimit)) / diagonalDistances[i];
      }
      const min = xMinValue(item);
      let result = item.findIndex(x => x === min);
      return result;
    }

    function directOptimization$1(data, sumOfShapes, options) {
      const {
        minValues,
        maxValues,
        maxIterations,
        epsilon,
        tolerance,
        tolerance2,
        initialState
      } = options;
      const objectiveFunction = getObjectiveFunction$1(data, sumOfShapes);
      const result = direct(objectiveFunction, minValues, maxValues, {
        iterations: maxIterations,
        epsilon,
        tolerance,
        tolerance2,
        initialState
      });
      const {
        optima
      } = result;
      return {
        parameterError: result.minFunctionValue,
        iterations: result.iterations,
        parameterValues: optima[0]
      };
    }
    function getObjectiveFunction$1(data, sumOfShapes) {
      const {
        x,
        y
      } = data;
      const nbPoints = x.length;
      return parameters => {
        const fct = sumOfShapes(parameters);
        let error = 0;
        for (let i = 0; i < nbPoints; i++) {
          error += Math.pow(y[i] - fct(x[i]), 2);
        }
        return error;
      };
    }

    /** Algorithm to select the method.
     * @param optimizationOptions - Optimization options
     * @returns - The algorithm and optimization options
     */
    function selectMethod(optimizationOptions = {}) {
      let {
        kind = 'lm',
        options
      } = optimizationOptions;
      switch (kind) {
        case 'lm':
        case 'levenbergMarquardt':
          return {
            algorithm: levenbergMarquardt,
            optimizationOptions: {
              damping: 1.5,
              maxIterations: 100,
              errorTolerance: 1e-8,
              ...options
            }
          };
        case 'direct':
          {
            return {
              algorithm: directOptimization$1,
              optimizationOptions: {
                iterations: 20,
                epsilon: 1e-4,
                tolerance: 1e-16,
                tolerance2: 1e-12,
                initialState: {},
                ...options
              }
            };
          }
        default:
          throw new Error(`Unknown fitting algorithm`);
      }
    }

    /**
     * Fits a set of points to the sum of a set of bell functions.
     *
     * @param data - An object containing the x and y data to be fitted.
     * @param peaks - A list of initial parameters to be optimized. e.g. coming from a peak picking [{x, y, width}].
     * @param options - Options for optimize
     * @returns - An object with fitting error and the list of optimized parameters { parameters: [ {x, y, width} ], error } if the kind of shape is pseudoVoigt mu parameter is optimized.
     */
    function optimize(data, peaks, options = {}) {
      // rescale data
      let temp = xMinMaxValues(data.y);
      const minMaxY = {
        ...temp,
        range: temp.max - temp.min
      };
      const internalPeaks = getInternalPeaks(peaks, minMaxY, options);
      // need to rescale what is related to Y
      const {
        baseline: shiftValue = minMaxY.min
      } = options;
      let normalizedY = new Float64Array(data.y.length);
      for (let i = 0; i < data.y.length; i++) {
        normalizedY[i] = (data.y[i] - shiftValue) / minMaxY.range;
      }
      const nbParams = internalPeaks[internalPeaks.length - 1].toIndex + 1;
      const minValues = new Float64Array(nbParams);
      const maxValues = new Float64Array(nbParams);
      const initialValues = new Float64Array(nbParams);
      const gradientDifferences = new Float64Array(nbParams);
      let index = 0;
      for (const peak of internalPeaks) {
        for (let i = 0; i < peak.parameters.length; i++) {
          minValues[index] = peak.propertiesValues.min[i];
          maxValues[index] = peak.propertiesValues.max[i];
          initialValues[index] = peak.propertiesValues.init[i];
          gradientDifferences[index] = peak.propertiesValues.gradientDifference[i];
          index++;
        }
      }
      let {
        algorithm,
        optimizationOptions
      } = selectMethod(options.optimization);
      let sumOfShapes = getSumOfShapes$1(internalPeaks);
      let fitted = algorithm({
        x: data.x,
        y: normalizedY
      }, sumOfShapes, {
        minValues,
        maxValues,
        initialValues,
        gradientDifference: gradientDifferences,
        ...optimizationOptions
      });
      const fittedValues = fitted.parameterValues;
      let newPeaks = [];
      for (let peak of internalPeaks) {
        const {
          id,
          shape,
          parameters,
          fromIndex
        } = peak;
        let newPeak = {
          x: 0,
          y: 0,
          shape
        };
        if (id) {
          newPeak = {
            ...newPeak,
            id
          };
        }
        newPeak.x = fittedValues[fromIndex];
        newPeak.y = fittedValues[fromIndex + 1] * minMaxY.range + shiftValue;
        for (let i = 2; i < parameters.length; i++) {
          //@ts-expect-error should be fixed once
          newPeak.shape[parameters[i]] = fittedValues[fromIndex + i];
        }
        newPeaks.push(newPeak);
      }
      return {
        error: fitted.parameterError,
        iterations: fitted.iterations,
        peaks: newPeaks
      };
    }

    const {
      parse: parse$6,
      stringify: stringify$6
    } = JSON;
    /**
     * add missing property if it does not exist in the peak,
     * if shape exists but fwhm doesn't, it will be calculated from peak.width
     */
    function addMissingShape(peaks, options = {}) {
      const {
        shape = {
          kind: 'gaussian'
        },
        output = parse$6(stringify$6(peaks))
      } = options;
      let shapeInstance = getShape1D(shape);
      return output.map(peak => {
        if (hasShape(peak)) {
          if (!('fwhm' in peak.shape)) {
            const shapeInstance = getShape1D(peak.shape);
            peak.shape.fwhm = shapeInstance.widthToFWHM(peak.width);
          }
          return peak;
        }
        return {
          ...peak,
          shape: {
            fwhm: shapeInstance.widthToFWHM(peak.width),
            ...shape
          }
        };
      });
    }
    function hasShape(peak) {
      return 'shape' in peak;
    }

    /**
     * Group peaks based on factor
     * In order to group peaks we only need the x and width value. This means that
     * in the current implementation we don't take into account the asymmetry of peaks
     */
    function groupPeaks(peaks, options = {}) {
      if (peaks && peaks.length === 0) return [];
      const {
        factor = 1
      } = options;
      peaks = JSON.parse(JSON.stringify(peaks));
      peaks.sort((a, b) => a.x - b.x);
      let previousPeak = peaks[0];
      let currentGroup = [previousPeak];
      let groups = [currentGroup];
      for (let i = 1; i < peaks.length; i++) {
        const peak = peaks[i];
        if ((peak.x - previousPeak.x) / ((peak.width + previousPeak.width) / 2) <= factor) {
          currentGroup.push(peak);
        } else {
          currentGroup = [peak];
          groups.push(currentGroup);
        }
        previousPeak = peak;
      }
      return groups;
    }

    /**
     * Optimize the position (x), max intensity (y), full width at half maximum (fwhm)
     * and the ratio of gaussian contribution (mu) if it's required. It currently supports three kind of shapes: gaussian, lorentzian and pseudovoigt
     * @param data - An object containing the x and y data to be fitted.
     * @param peakList - A list of initial parameters to be optimized. e.g. coming from a peak picking [{x, y, width}].
     */
    function optimizePeaksWithLogs(data, peakList, options = {}) {
      const {
        fromTo = {},
        baseline,
        shape = {
          kind: 'gaussian'
        },
        groupingFactor = 1,
        factorLimits = 2,
        optimization = {
          kind: 'lm',
          options: {
            timeout: 10
          }
        }
      } = options;
      /*
      The optimization algorithm will take some group of peaks.
      We can not simply optimize everything because there would be too many variables to optimize
      and it would be too time consuming.
      */
      let groups = groupPeaks(peakList, {
        factor: groupingFactor
      });
      let logs = [];
      let results = [];
      groups.forEach(peakGroup => {
        const start = Date.now();
        // In order to make optimization we will add fwhm and shape on all the peaks
        const peaks = addMissingShape(peakGroup, {
          shape
        });
        const firstPeak = peaks[0];
        const lastPeak = peaks[peaks.length - 1];
        const {
          from = firstPeak.x - firstPeak.width * factorLimits,
          to = lastPeak.x + lastPeak.width * factorLimits
        } = fromTo;
        const {
          fromIndex,
          toIndex
        } = xGetFromToIndex(data.x, {
          from,
          to
        });
        const x = data.x instanceof Float64Array ? data.x.subarray(fromIndex, toIndex) : data.x.slice(fromIndex, toIndex);
        const y = data.y instanceof Float64Array ? data.y.subarray(fromIndex, toIndex) : data.y.slice(fromIndex, toIndex);
        const log = {
          range: {
            from,
            to
          },
          parameters: optimization,
          groupSize: peakGroup.length,
          time: Date.now() - start
        };
        if (x.length > 5) {
          const {
            iterations,
            error,
            peaks: optimizedPeaks
          } = optimize({
            x,
            y
          }, peaks, {
            shape,
            baseline,
            optimization
          });
          for (let i = 0; i < peaks.length; i++) {
            results.push({
              ...optimizedPeaks[i],
              width: getShape1D(peaks[i].shape).fwhmToWidth(optimizedPeaks[i].shape.fwhm)
            });
          }
          logs.push({
            ...log,
            iterations,
            error,
            message: 'optimization successful'
          });
        } else {
          results.push(...peaks);
          logs.push({
            ...log,
            iterations: 0,
            message: 'x length too small for optimization'
          });
        }
      });
      return {
        logs,
        optimizedPeaks: results
      };
    }

    /**
     * Optimize the position (x), max intensity (y), full width at half maximum (fwhm)
     * and the ratio of gaussian contribution (mu) if it's required. It currently supports three kind of shapes: gaussian, lorentzian and pseudovoigt
     * @param data - An object containing the x and y data to be fitted.
     * @param peakList - A list of initial parameters to be optimized. e.g. coming from a peak picking [{x, y, width}].
     */
    function optimizePeaks(data, peakList, options = {}) {
      return optimizePeaksWithLogs(data, peakList, options).optimizedPeaks;
    }

    const {
      parse: parse$5,
      stringify: stringify$5
    } = JSON;
    function addMissingIDs$1(peaks, options = {}) {
      const {
        output = parse$5(stringify$5(peaks))
      } = options;
      for (const peak of output) {
        if (!('id' in peak)) {
          peak.id = v4();
        }
      }
      return output;
    }

    function joinBroadPeaks(peakList, options = {}) {
      let {
        shape = {
          kind: 'gaussian'
        },
        optimization = {
          kind: 'lm',
          options: {
            timeout: 10
          }
        },
        broadWidth = 0.25,
        broadRatio = 0.0025
      } = options;
      let max = 0;
      let maxI = 0;
      let count = 1;
      const broadLines = [];
      if (peakList.length < 2) {
        return addMissingIDs$1(addMissingShape(peakList.map(getGSDPeakOptimizedStructure), {
          shape
        }));
      }
      let maxDdy = peakList[0].ddY;
      for (let i = 1; i < peakList.length; i++) {
        if (Math.abs(peakList[i].ddY) > maxDdy) maxDdy = Math.abs(peakList[i].ddY);
      }
      const newPeaks = [];
      for (const peak of peakList) {
        if (Math.abs(peak.ddY) <= broadRatio * maxDdy) {
          broadLines.push(peak);
        } else {
          newPeaks.push(getGSDPeakOptimizedStructure(peak));
        }
      }
      //@ts-expect-error Push a feke peak
      broadLines.push({
        x: Number.MAX_VALUE,
        y: 0
      });
      let candidates = {
        x: [broadLines[0].x],
        y: [broadLines[0].y]
      };
      let indexes = [0];
      for (let i = 1; i < broadLines.length; i++) {
        if (Math.abs(broadLines[i - 1].x - broadLines[i].x) < broadWidth) {
          candidates.x.push(broadLines[i].x);
          candidates.y.push(broadLines[i].y);
          if (broadLines[i].y > max) {
            max = broadLines[i].y;
            maxI = i;
          }
          indexes.push(i);
          count++;
        } else {
          if (count > 2) {
            let fitted = optimizePeaks(candidates, [{
              id: v4(),
              x: broadLines[maxI].x,
              y: max,
              width: candidates.x[0] - candidates.x[candidates.x.length - 1]
            }], {
              shape,
              optimization
            });
            newPeaks.push(fitted[0]);
          } else {
            // Put back the candidates to the peak list
            for (const index of indexes) {
              newPeaks.push(getGSDPeakOptimizedStructure(broadLines[index]));
            }
          }
          candidates = {
            x: [broadLines[i].x],
            y: [broadLines[i].y]
          };
          indexes = [i];
          max = broadLines[i].y;
          maxI = i;
          count = 1;
        }
      }
      newPeaks.sort((a, b) => {
        return a.x - b.x;
      });
      return addMissingIDs$1(newPeaks, {
        output: newPeaks
      });
    }
    function getGSDPeakOptimizedStructure(peak) {
      const {
        id,
        shape,
        x,
        y,
        width
      } = peak;
      let newPeak = {
        x,
        y,
        width,
        shape
      };
      if (id) newPeak.id = id;
      return newPeak;
    }

    const {
      parse: parse$4,
      stringify: stringify$4
    } = JSON;
    /**
     * Append 2 properties to the peaks, shape and fwhm
     */
    function setShape(peaks, options = {}) {
      let {
        shape = {
          kind: 'gaussian'
        },
        output = parse$4(stringify$4(peaks))
      } = options;
      let shapeInstance = getShape1D(shape);
      return output.map(peak => ({
        ...peak,
        shape: {
          fwhm: shapeInstance.widthToFWHM(peak.width),
          ...shape
        }
      }));
    }

    function peakToXY(peak, options) {
      const newPeak = addMissingShape([peak])[0];
      const factor = getShape1D(newPeak.shape).getFactor();
      const {
        from = newPeak.x - peak.width * factor / options.frequency,
        to = newPeak.x + peak.width * factor / options.frequency
      } = options;
      return peaksToXY([peak], {
        ...options,
        from,
        to
      });
    }

    var browserPonyfill = {exports: {}};

    (function (module, exports) {
      var global = typeof self !== 'undefined' ? self : commonjsGlobal;
      var __self__ = function () {
        function F() {
          this.fetch = false;
          this.DOMException = global.DOMException;
        }
        F.prototype = global;
        return new F();
      }();
      (function (self) {
        (function (exports) {
          var support = {
            searchParams: 'URLSearchParams' in self,
            iterable: 'Symbol' in self && 'iterator' in Symbol,
            blob: 'FileReader' in self && 'Blob' in self && function () {
              try {
                new Blob();
                return true;
              } catch (e) {
                return false;
              }
            }(),
            formData: 'FormData' in self,
            arrayBuffer: 'ArrayBuffer' in self
          };
          function isDataView(obj) {
            return obj && DataView.prototype.isPrototypeOf(obj);
          }
          if (support.arrayBuffer) {
            var viewClasses = ['[object Int8Array]', '[object Uint8Array]', '[object Uint8ClampedArray]', '[object Int16Array]', '[object Uint16Array]', '[object Int32Array]', '[object Uint32Array]', '[object Float32Array]', '[object Float64Array]'];
            var isArrayBufferView = ArrayBuffer.isView || function (obj) {
              return obj && viewClasses.indexOf(Object.prototype.toString.call(obj)) > -1;
            };
          }
          function normalizeName(name) {
            if (typeof name !== 'string') {
              name = String(name);
            }
            if (/[^a-z0-9\-#$%&'*+.^_`|~]/i.test(name)) {
              throw new TypeError('Invalid character in header field name');
            }
            return name.toLowerCase();
          }
          function normalizeValue(value) {
            if (typeof value !== 'string') {
              value = String(value);
            }
            return value;
          }

          // Build a destructive iterator for the value list
          function iteratorFor(items) {
            var iterator = {
              next: function () {
                var value = items.shift();
                return {
                  done: value === undefined,
                  value: value
                };
              }
            };
            if (support.iterable) {
              iterator[Symbol.iterator] = function () {
                return iterator;
              };
            }
            return iterator;
          }
          function Headers(headers) {
            this.map = {};
            if (headers instanceof Headers) {
              headers.forEach(function (value, name) {
                this.append(name, value);
              }, this);
            } else if (Array.isArray(headers)) {
              headers.forEach(function (header) {
                this.append(header[0], header[1]);
              }, this);
            } else if (headers) {
              Object.getOwnPropertyNames(headers).forEach(function (name) {
                this.append(name, headers[name]);
              }, this);
            }
          }
          Headers.prototype.append = function (name, value) {
            name = normalizeName(name);
            value = normalizeValue(value);
            var oldValue = this.map[name];
            this.map[name] = oldValue ? oldValue + ', ' + value : value;
          };
          Headers.prototype['delete'] = function (name) {
            delete this.map[normalizeName(name)];
          };
          Headers.prototype.get = function (name) {
            name = normalizeName(name);
            return this.has(name) ? this.map[name] : null;
          };
          Headers.prototype.has = function (name) {
            return this.map.hasOwnProperty(normalizeName(name));
          };
          Headers.prototype.set = function (name, value) {
            this.map[normalizeName(name)] = normalizeValue(value);
          };
          Headers.prototype.forEach = function (callback, thisArg) {
            for (var name in this.map) {
              if (this.map.hasOwnProperty(name)) {
                callback.call(thisArg, this.map[name], name, this);
              }
            }
          };
          Headers.prototype.keys = function () {
            var items = [];
            this.forEach(function (value, name) {
              items.push(name);
            });
            return iteratorFor(items);
          };
          Headers.prototype.values = function () {
            var items = [];
            this.forEach(function (value) {
              items.push(value);
            });
            return iteratorFor(items);
          };
          Headers.prototype.entries = function () {
            var items = [];
            this.forEach(function (value, name) {
              items.push([name, value]);
            });
            return iteratorFor(items);
          };
          if (support.iterable) {
            Headers.prototype[Symbol.iterator] = Headers.prototype.entries;
          }
          function consumed(body) {
            if (body.bodyUsed) {
              return Promise.reject(new TypeError('Already read'));
            }
            body.bodyUsed = true;
          }
          function fileReaderReady(reader) {
            return new Promise(function (resolve, reject) {
              reader.onload = function () {
                resolve(reader.result);
              };
              reader.onerror = function () {
                reject(reader.error);
              };
            });
          }
          function readBlobAsArrayBuffer(blob) {
            var reader = new FileReader();
            var promise = fileReaderReady(reader);
            reader.readAsArrayBuffer(blob);
            return promise;
          }
          function readBlobAsText(blob) {
            var reader = new FileReader();
            var promise = fileReaderReady(reader);
            reader.readAsText(blob);
            return promise;
          }
          function readArrayBufferAsText(buf) {
            var view = new Uint8Array(buf);
            var chars = new Array(view.length);
            for (var i = 0; i < view.length; i++) {
              chars[i] = String.fromCharCode(view[i]);
            }
            return chars.join('');
          }
          function bufferClone(buf) {
            if (buf.slice) {
              return buf.slice(0);
            } else {
              var view = new Uint8Array(buf.byteLength);
              view.set(new Uint8Array(buf));
              return view.buffer;
            }
          }
          function Body() {
            this.bodyUsed = false;
            this._initBody = function (body) {
              this._bodyInit = body;
              if (!body) {
                this._bodyText = '';
              } else if (typeof body === 'string') {
                this._bodyText = body;
              } else if (support.blob && Blob.prototype.isPrototypeOf(body)) {
                this._bodyBlob = body;
              } else if (support.formData && FormData.prototype.isPrototypeOf(body)) {
                this._bodyFormData = body;
              } else if (support.searchParams && URLSearchParams.prototype.isPrototypeOf(body)) {
                this._bodyText = body.toString();
              } else if (support.arrayBuffer && support.blob && isDataView(body)) {
                this._bodyArrayBuffer = bufferClone(body.buffer);
                // IE 10-11 can't handle a DataView body.
                this._bodyInit = new Blob([this._bodyArrayBuffer]);
              } else if (support.arrayBuffer && (ArrayBuffer.prototype.isPrototypeOf(body) || isArrayBufferView(body))) {
                this._bodyArrayBuffer = bufferClone(body);
              } else {
                this._bodyText = body = Object.prototype.toString.call(body);
              }
              if (!this.headers.get('content-type')) {
                if (typeof body === 'string') {
                  this.headers.set('content-type', 'text/plain;charset=UTF-8');
                } else if (this._bodyBlob && this._bodyBlob.type) {
                  this.headers.set('content-type', this._bodyBlob.type);
                } else if (support.searchParams && URLSearchParams.prototype.isPrototypeOf(body)) {
                  this.headers.set('content-type', 'application/x-www-form-urlencoded;charset=UTF-8');
                }
              }
            };
            if (support.blob) {
              this.blob = function () {
                var rejected = consumed(this);
                if (rejected) {
                  return rejected;
                }
                if (this._bodyBlob) {
                  return Promise.resolve(this._bodyBlob);
                } else if (this._bodyArrayBuffer) {
                  return Promise.resolve(new Blob([this._bodyArrayBuffer]));
                } else if (this._bodyFormData) {
                  throw new Error('could not read FormData body as blob');
                } else {
                  return Promise.resolve(new Blob([this._bodyText]));
                }
              };
              this.arrayBuffer = function () {
                if (this._bodyArrayBuffer) {
                  return consumed(this) || Promise.resolve(this._bodyArrayBuffer);
                } else {
                  return this.blob().then(readBlobAsArrayBuffer);
                }
              };
            }
            this.text = function () {
              var rejected = consumed(this);
              if (rejected) {
                return rejected;
              }
              if (this._bodyBlob) {
                return readBlobAsText(this._bodyBlob);
              } else if (this._bodyArrayBuffer) {
                return Promise.resolve(readArrayBufferAsText(this._bodyArrayBuffer));
              } else if (this._bodyFormData) {
                throw new Error('could not read FormData body as text');
              } else {
                return Promise.resolve(this._bodyText);
              }
            };
            if (support.formData) {
              this.formData = function () {
                return this.text().then(decode);
              };
            }
            this.json = function () {
              return this.text().then(JSON.parse);
            };
            return this;
          }

          // HTTP methods whose capitalization should be normalized
          var methods = ['DELETE', 'GET', 'HEAD', 'OPTIONS', 'POST', 'PUT'];
          function normalizeMethod(method) {
            var upcased = method.toUpperCase();
            return methods.indexOf(upcased) > -1 ? upcased : method;
          }
          function Request(input, options) {
            options = options || {};
            var body = options.body;
            if (input instanceof Request) {
              if (input.bodyUsed) {
                throw new TypeError('Already read');
              }
              this.url = input.url;
              this.credentials = input.credentials;
              if (!options.headers) {
                this.headers = new Headers(input.headers);
              }
              this.method = input.method;
              this.mode = input.mode;
              this.signal = input.signal;
              if (!body && input._bodyInit != null) {
                body = input._bodyInit;
                input.bodyUsed = true;
              }
            } else {
              this.url = String(input);
            }
            this.credentials = options.credentials || this.credentials || 'same-origin';
            if (options.headers || !this.headers) {
              this.headers = new Headers(options.headers);
            }
            this.method = normalizeMethod(options.method || this.method || 'GET');
            this.mode = options.mode || this.mode || null;
            this.signal = options.signal || this.signal;
            this.referrer = null;
            if ((this.method === 'GET' || this.method === 'HEAD') && body) {
              throw new TypeError('Body not allowed for GET or HEAD requests');
            }
            this._initBody(body);
          }
          Request.prototype.clone = function () {
            return new Request(this, {
              body: this._bodyInit
            });
          };
          function decode(body) {
            var form = new FormData();
            body.trim().split('&').forEach(function (bytes) {
              if (bytes) {
                var split = bytes.split('=');
                var name = split.shift().replace(/\+/g, ' ');
                var value = split.join('=').replace(/\+/g, ' ');
                form.append(decodeURIComponent(name), decodeURIComponent(value));
              }
            });
            return form;
          }
          function parseHeaders(rawHeaders) {
            var headers = new Headers();
            // Replace instances of \r\n and \n followed by at least one space or horizontal tab with a space
            // https://tools.ietf.org/html/rfc7230#section-3.2
            var preProcessedHeaders = rawHeaders.replace(/\r?\n[\t ]+/g, ' ');
            preProcessedHeaders.split(/\r?\n/).forEach(function (line) {
              var parts = line.split(':');
              var key = parts.shift().trim();
              if (key) {
                var value = parts.join(':').trim();
                headers.append(key, value);
              }
            });
            return headers;
          }
          Body.call(Request.prototype);
          function Response(bodyInit, options) {
            if (!options) {
              options = {};
            }
            this.type = 'default';
            this.status = options.status === undefined ? 200 : options.status;
            this.ok = this.status >= 200 && this.status < 300;
            this.statusText = 'statusText' in options ? options.statusText : 'OK';
            this.headers = new Headers(options.headers);
            this.url = options.url || '';
            this._initBody(bodyInit);
          }
          Body.call(Response.prototype);
          Response.prototype.clone = function () {
            return new Response(this._bodyInit, {
              status: this.status,
              statusText: this.statusText,
              headers: new Headers(this.headers),
              url: this.url
            });
          };
          Response.error = function () {
            var response = new Response(null, {
              status: 0,
              statusText: ''
            });
            response.type = 'error';
            return response;
          };
          var redirectStatuses = [301, 302, 303, 307, 308];
          Response.redirect = function (url, status) {
            if (redirectStatuses.indexOf(status) === -1) {
              throw new RangeError('Invalid status code');
            }
            return new Response(null, {
              status: status,
              headers: {
                location: url
              }
            });
          };
          exports.DOMException = self.DOMException;
          try {
            new exports.DOMException();
          } catch (err) {
            exports.DOMException = function (message, name) {
              this.message = message;
              this.name = name;
              var error = Error(message);
              this.stack = error.stack;
            };
            exports.DOMException.prototype = Object.create(Error.prototype);
            exports.DOMException.prototype.constructor = exports.DOMException;
          }
          function fetch(input, init) {
            return new Promise(function (resolve, reject) {
              var request = new Request(input, init);
              if (request.signal && request.signal.aborted) {
                return reject(new exports.DOMException('Aborted', 'AbortError'));
              }
              var xhr = new XMLHttpRequest();
              function abortXhr() {
                xhr.abort();
              }
              xhr.onload = function () {
                var options = {
                  status: xhr.status,
                  statusText: xhr.statusText,
                  headers: parseHeaders(xhr.getAllResponseHeaders() || '')
                };
                options.url = 'responseURL' in xhr ? xhr.responseURL : options.headers.get('X-Request-URL');
                var body = 'response' in xhr ? xhr.response : xhr.responseText;
                resolve(new Response(body, options));
              };
              xhr.onerror = function () {
                reject(new TypeError('Network request failed'));
              };
              xhr.ontimeout = function () {
                reject(new TypeError('Network request failed'));
              };
              xhr.onabort = function () {
                reject(new exports.DOMException('Aborted', 'AbortError'));
              };
              xhr.open(request.method, request.url, true);
              if (request.credentials === 'include') {
                xhr.withCredentials = true;
              } else if (request.credentials === 'omit') {
                xhr.withCredentials = false;
              }
              if ('responseType' in xhr && support.blob) {
                xhr.responseType = 'blob';
              }
              request.headers.forEach(function (value, name) {
                xhr.setRequestHeader(name, value);
              });
              if (request.signal) {
                request.signal.addEventListener('abort', abortXhr);
                xhr.onreadystatechange = function () {
                  // DONE (success or failure)
                  if (xhr.readyState === 4) {
                    request.signal.removeEventListener('abort', abortXhr);
                  }
                };
              }
              xhr.send(typeof request._bodyInit === 'undefined' ? null : request._bodyInit);
            });
          }
          fetch.polyfill = true;
          if (!self.fetch) {
            self.fetch = fetch;
            self.Headers = Headers;
            self.Request = Request;
            self.Response = Response;
          }
          exports.Headers = Headers;
          exports.Request = Request;
          exports.Response = Response;
          exports.fetch = fetch;
          Object.defineProperty(exports, '__esModule', {
            value: true
          });
          return exports;
        })({});
      })(__self__);
      __self__.fetch.ponyfill = true;
      // Remove "polyfill" property added by whatwg-fetch
      delete __self__.fetch.polyfill;
      // Choose between native implementation (global) or custom implementation (__self__)
      // var ctx = global.fetch ? global : __self__;
      var ctx = __self__; // this line disable service worker support temporarily
      exports = ctx.fetch; // To enable: import fetch from 'cross-fetch'
      exports.default = ctx.fetch; // For TypeScript consumers without esModuleInterop.
      exports.fetch = ctx.fetch; // To enable: import {fetch} from 'cross-fetch'
      exports.Headers = ctx.Headers;
      exports.Request = ctx.Request;
      exports.Response = ctx.Response;
      module.exports = exports;
    })(browserPonyfill, browserPonyfill.exports);
    var fetch = /*@__PURE__*/getDefaultExportFromCjs(browserPonyfill.exports);

    /* eslint-env browser */
    var browser = typeof self == 'object' ? self.FormData : window.FormData;
    var FormData$1 = browser;

    let xAtomicNumber = 0;

    /**
     * Tag an atom to be able to visualize it
     * @param {import('openchemlib').Molecule} molecule
     * @param {number} iAtom
     */
    function tagAtom(molecule, iAtom) {
      let customLabel = `${molecule.getAtomLabel(iAtom)}*`;
      molecule.setAtomCustomLabel(iAtom, customLabel);
      if (molecule.getAtomicNo(iAtom) === 1) {
        molecule.setAtomicNo(iAtom, getXAtomicNumber(molecule));
      } else {
        // we can not use X because we would have problems with valencies if it is
        // expanded hydrogens or not
        // we can not only use a custom label because it does not count for the canonisation
        molecule.setAtomMass(iAtom, molecule.getAtomMass(iAtom) + 5);
      }
      return customLabel;
    }
    function getXAtomicNumber(molecule) {
      if (!xAtomicNumber) {
        const OCL = molecule.getOCL();
        xAtomicNumber = OCL.Molecule.getAtomicNoFromLabel('X', OCL.Molecule.cPseudoAtomX);
      }
      return xAtomicNumber;
    }

    /**
     * Add either missing chirality of diastereotopic missing chirality
     * The problem is that sometimes we need to add chiral bond that was not planned because it is the same group
     * This is the case for example for the valine where the 2 C of the methyl groups are diastereotopic
     * @param {import('openchemlib').Molecule} molecule
     * @param {object} [options={}]
     * @param {number} [options.esrType=cESRTypeAnd]
     */
    function addDiastereotopicMissingChirality(molecule, options = {}) {
      const {
        Molecule
      } = molecule.getOCL();
      const {
        esrType = Molecule.cESRTypeAnd
      } = options;
      for (let iAtom = 0; iAtom < molecule.getAllAtoms(); iAtom++) {
        let tempMolecule = molecule.getCompactCopy();
        tagAtom(tempMolecule, iAtom);
        // After copy, helpers must be recalculated
        tempMolecule.ensureHelperArrays(Molecule.cHelperBitsStereo);
        // We need to have >0 and not >1 because there could be unspecified chirality in racemate

        for (let i = 0; i < tempMolecule.getAtoms(); i++) {
          // changed from from handling below; TLS 9.Nov.2015
          if (tempMolecule.isAtomStereoCenter(i) && tempMolecule.getStereoBond(i) === -1) {
            let stereoBond = tempMolecule.getAtomPreferredStereoBond(i);
            if (stereoBond !== -1) {
              molecule.setBondType(stereoBond, Molecule.cBondTypeUp);
              if (molecule.getBondAtom(1, stereoBond) === i) {
                let connAtom = molecule.getBondAtom(0, stereoBond);
                molecule.setBondAtom(0, stereoBond, i);
                molecule.setBondAtom(1, stereoBond, connAtom);
              }
              // To me it seems that we have to add all stereo centers into AND group 0. TLS 9.Nov.2015
              molecule.setAtomESR(i, esrType, 0);
            }
          }
        }
      }
    }

    /**
     *
     * @param {import('openchemlib').Molecule} molecule An instance of a molecule
     * @param {object} [options={}]
     * @param {object} [options.OCL] openchemlib library
     */
    function makeRacemic(molecule) {
      const {
        Molecule
      } = molecule.getOCL();

      // if we don't calculate this we have 2 epimers
      molecule.ensureHelperArrays(Molecule.cHelperCIP);

      // we need to make one group "AND" for chiral (to force to racemic, this means diastereotopic and not enantiotopic)
      for (let i = 0; i < molecule.getAllAtoms(); i++) {
        if (molecule.getAtomParity(i) !== Molecule.cAtomParityNone) {
          molecule.setAtomESR(i, Molecule.cESRTypeAnd, 0); // changed to group 0; TLS 9.Nov.2015
        }
      }
    }

    /**
     * Returns an array of diastereotopic ID (as oclCode)
     * @param {import('openchemlib').Molecule} molecule
     */
    function getDiastereotopicAtomIDs(molecule) {
      const OCL = molecule.getOCL();
      addDiastereotopicMissingChirality(molecule);
      let numberAtoms = molecule.getAllAtoms();
      let ids = [];
      for (let iAtom = 0; iAtom < numberAtoms; iAtom++) {
        let tempMolecule = molecule.getCompactCopy();
        tagAtom(tempMolecule, iAtom);
        makeRacemic(tempMolecule);
        // We need to ensure the helper array in order to get correctly the result of racemisation
        ids[iAtom] = tempMolecule.getCanonizedIDCode(OCL.Molecule.CANONIZER_ENCODE_ATOM_CUSTOM_LABELS);
      }
      return ids;
    }

    function groupDiastereotopicAtomIDs(diaIDs, molecule, options = {}) {
      const {
        atomLabel
      } = options;
      const diaIDsObject = {};
      for (let i = 0; i < diaIDs.length; i++) {
        if (!atomLabel || molecule.getAtomLabel(i) === atomLabel) {
          let diaID = diaIDs[i];
          if (!diaIDsObject[diaID]) {
            diaIDsObject[diaID] = {
              counter: 0,
              atoms: [],
              oclID: diaID,
              atomLabel: molecule.getAtomLabel(i)
            };
          }
          diaIDsObject[diaID].counter++;
          diaIDsObject[diaID].atoms.push(i);
        }
      }
      return Object.keys(diaIDsObject).map(key => diaIDsObject[key]);
    }

    /**
     * This function groups the diasterotopic atomIds of the molecule based on equivalence of atoms. The output object contains
     * a set of chemically equivalent atoms(element.atoms) and the groups of magnetically equivalent atoms (element.magneticGroups)
     * @param {import('openchemlib').Molecule} molecule
     * @param {object} [options={}]
     * @param {string} [options.atomLabel] Select atoms of the given atomLabel. By default it returns all the explicit atoms in the molecule
     * @returns {Array}
     */

    function getGroupedDiastereotopicAtomIDs(molecule, options = {}) {
      let diaIDs = getDiastereotopicAtomIDs(molecule);
      return groupDiastereotopicAtomIDs(diaIDs, molecule, options);
    }

    /**
     * Check if a specific atom is a sp3 carbon
     * @param {import('openchemlib').Molecule} molecule
     * @param {number} atomID
     */

    function isCsp3(molecule, atomID) {
      if (molecule.getAtomicNo(atomID) !== 6) return false;
      if (molecule.getAtomCharge(atomID) !== 0) return false;
      if (molecule.getImplicitHydrogens(atomID) + molecule.getConnAtoms(atomID) !== 4) {
        return false;
      }
      return true;
    }

    const FULL_HOSE_CODE = 1;
    const HOSE_CODE_CUT_C_SP3_SP3 = 2;

    /**
     * Returns the hose code for a specific atom number
     * @param {import('openchemlib').Molecule} originalMolecule - The OCL molecule to be fragmented
     * @param {number[]} rootAtoms
     * @param {object} [options={}]
     * @param {boolean} [options.isTagged] Specify is the atoms are already tagged
     * @param {number} [options.minSphereSize=0] Smallest hose code sphere
     * @param {number} [options.maxSphereSize=4] Largest hose code sphere
     * @param {number} [options.kind=FULL_HOSE_CODE] Kind of hose code, default usual sphere
     */
    function getHoseCodesForAtoms(originalMolecule, rootAtoms = [], options = {}) {
      const OCL = originalMolecule.getOCL();
      const {
        minSphereSize = 0,
        maxSphereSize = 4,
        kind = FULL_HOSE_CODE,
        isTagged = false
      } = options;
      const molecule = originalMolecule.getCompactCopy();
      if (!isTagged) {
        const tags = [];
        for (let i = 0; i < rootAtoms.length; i++) {
          let rootAtom = rootAtoms[i];
          tags.push(tagAtom(molecule, rootAtom));
          molecule.addImplicitHydrogens();
          molecule.addMissingChirality();
          molecule.ensureHelperArrays(OCL.Molecule.cHelperNeighbours);
          // because ensuring helper reorder atoms we need to look again for it
        }

        rootAtoms.length = 0;
        for (let j = 0; j < molecule.getAllAtoms(); j++) {
          if (tags.includes(molecule.getAtomCustomLabel(j))) {
            rootAtoms.push(j);
          }
        }
      }
      let fragment = new OCL.Molecule(0, 0);
      let results = [];
      let min = 0;
      let max = 0;
      let atomMask = new Array(molecule.getAllAtoms());
      let atomList = new Array(molecule.getAllAtoms());
      for (let sphere = 0; sphere <= maxSphereSize; sphere++) {
        if (max === 0) {
          for (let rootAtom of rootAtoms) {
            atomList[max] = rootAtom;
            atomMask[rootAtom] = true;
            max++;
          }
        } else {
          let newMax = max;
          for (let i = min; i < max; i++) {
            let atom = atomList[i];
            for (let j = 0; j < molecule.getAllConnAtoms(atom); j++) {
              let connAtom = molecule.getConnAtom(atom, j);
              if (!atomMask[connAtom]) {
                switch (kind) {
                  case FULL_HOSE_CODE:
                    atomMask[connAtom] = true;
                    atomList[newMax++] = connAtom;
                    break;
                  case HOSE_CODE_CUT_C_SP3_SP3:
                    if (!(isCsp3(molecule, atom) && isCsp3(molecule, connAtom))) {
                      atomMask[connAtom] = true;
                      atomList[newMax++] = connAtom;
                    }
                    break;
                  default:
                    throw new Error('getHoseCoesForAtom unknown kind');
                }
              }
            }
          }
          min = max;
          max = newMax;
        }
        molecule.copyMoleculeByAtoms(fragment, atomMask, true, null);
        if (sphere >= minSphereSize) {
          makeRacemic(fragment);
          results.push(fragment.getCanonizedIDCode(OCL.Molecule.CANONIZER_ENCODE_ATOM_CUSTOM_LABELS));
        }
      }
      return results;
    }

    /**
     * Returns the hose code for a specific atom number
     * @param {import('openchemlib').Molecule} originalMolecule
     * @param {number} rootAtom
     * @param {object} [options={}]
     * @param {boolean} [options.isTagged] Specify is the atom is already tagged
     * @param {number} [options.minSphereSize=0] Smallest hose code sphere
     * @param {number} [options.maxSphereSize=4] Largest hose code sphere
     * @param {number} [options.kind=FULL_HOSE_CODE] Kind of hose code, default usual sphere
     */
    function getHoseCodesForAtom(originalMolecule, rootAtom, options = {}) {
      return getHoseCodesForAtoms(originalMolecule, [rootAtom], options);
    }

    /**
     * Returns the hose code for a specific marked atom
     * @param {import('openchemlib').Molecule} diastereotopicID
     * @param {object} options
     */

    function getHoseCodesFromDiastereotopicID(molecule, options = {}) {
      molecule.addImplicitHydrogens();
      molecule.addMissingChirality();

      // One of the atom has to be marked !
      let atomID = -1;
      for (let i = 0; i < molecule.getAllAtoms(); i++) {
        // we need to find the marked atom
        const atomCustomLabel = molecule.getAtomCustomLabel(i);
        if (atomCustomLabel != null && atomCustomLabel.endsWith('*')) {
          atomID = i;
          break;
        }
      }
      if (atomID >= 0) {
        options.isTagged = true;
        return getHoseCodesForAtom(molecule, atomID, options);
      }
      return undefined;
    }

    let fragment$1;

    /**
     * Returns the hose code for a specific atom number
     * @param {import('openchemlib').Molecule} molecule
     */
    function getHoseCodesForPath(molecule, from, to, maxLength) {
      const OCL = molecule.getOCL();
      const originalFrom = from;
      const originalTo = to;
      molecule = molecule.getCompactCopy();
      let originalAtoms = []; // path before renumbering
      molecule.getPath(originalAtoms, from, to, maxLength + 1);
      let torsion;
      if (originalAtoms.length === 4) {
        torsion = molecule.calculateTorsion(originalAtoms);
      }
      const tag1 = tagAtom(molecule, from);
      const tag2 = tagAtom(molecule, to);
      molecule.addImplicitHydrogens();
      molecule.addMissingChirality();
      molecule.ensureHelperArrays(OCL.Molecule.cHelperNeighbours);
      from = -1;
      to = -1;
      for (let i = 0; i < molecule.getAllAtoms(); i++) {
        if (tag1 === tag2) {
          if (molecule.getAtomCustomLabel(i) === tag1) {
            if (from === -1) {
              from = i;
            } else {
              to = i;
            }
          }
        } else {
          if (tag1 === molecule.getAtomCustomLabel(i)) {
            from = i;
          }
          if (tag2 === molecule.getAtomCustomLabel(i)) {
            to = i;
          }
        }
      }
      if (!fragment$1) fragment$1 = new OCL.Molecule(0, 0);
      let atoms = [];
      molecule.getPath(atoms, from, to, maxLength + 1);
      let min = 0;
      let max = 0;
      let atomMask = new Array(molecule.getAllAtoms()).fill(false);
      let atomList = new Array(molecule.getAllAtoms()).fill(-1);
      let hoses = [];
      for (let sphere = 0; sphere <= 2; sphere++) {
        if (max === 0) {
          for (let atom of atoms) {
            atomMask[atom] = true;
            atomList[max++] = atom;
          }
        } else {
          let newMax = max;
          for (let i = min; i < max; i++) {
            let atom = atomList[i];
            for (let j = 0; j < molecule.getAllConnAtoms(atom); j++) {
              let connAtom = molecule.getConnAtom(atom, j);
              if (!atomMask[connAtom]) {
                atomMask[connAtom] = true;
                atomList[newMax++] = connAtom;
              }
            }
          }
          min = max;
          max = newMax;
        }
        let atomMap = [];
        molecule.copyMoleculeByAtoms(fragment$1, atomMask, true, atomMap);
        makeRacemic(fragment$1);
        let oclID = fragment$1.getCanonizedIDCode(OCL.Molecule.CANONIZER_ENCODE_ATOM_CUSTOM_LABELS);
        hoses.push({
          sphere,
          oclID
        });
      }
      return {
        atoms: originalAtoms,
        from: originalFrom,
        to: originalTo,
        torsion,
        hoses,
        length: originalAtoms.length - 1
      };
    }

    /**
     * Returns various information about atoms in the molecule
     * @param {import('openchemlib').Molecule} [molecule]
     */
    function getAtomsInfo(molecule) {
      const OCL = molecule.getOCL();
      molecule.ensureHelperArrays(OCL.Molecule.cHelperRings);
      let diaIDs = getDiastereotopicAtomIDs(molecule);
      let results = [];
      for (let i = 0; i < diaIDs.length; i++) {
        let result = {
          oclID: diaIDs[i],
          extra: {
            singleBonds: 0,
            doubleBonds: 0,
            tripleBonds: 0,
            aromaticBonds: 0,
            cnoHybridation: 0 // should be 1 (sp), 2 (sp2) or 3 (sp3)
          }
        };

        let extra = result.extra;
        results.push(result);
        result.abnormalValence = molecule.getAtomAbnormalValence(i); // -1 is normal otherwise specified
        result.charge = molecule.getAtomCharge(i);
        result.cipParity = molecule.getAtomCIPParity(i);
        result.color = molecule.getAtomColor(i);
        result.customLabel = molecule.getAtomCustomLabel(i);
        //        result.esrGroup=molecule.getAtomESRGroup(i);
        //        result.esrType=molecule.getAtomESRType(i);
        result.atomicNo = molecule.getAtomicNo(i);
        result.label = molecule.getAtomLabel(i);
        //        result.list=molecule.getAtomList(i);
        //        result.listString=molecule.getAtomListString(i);
        //        result.mapNo=molecule.getAtomMapNo(i);
        result.mass = molecule.getAtomMass(i);
        //        result.parity=molecule.getAtomParity(i);
        //        result.pi=molecule.getAtomPi(i);
        //        result.preferredStereoBond=molecule.getAtomPreferredStereoBond(i);
        //        result.queryFeatures=molecule.getAtomQueryFeatures(i);
        result.radical = molecule.getAtomRadical(i);
        result.ringBondCount = molecule.getAtomRingBondCount(i);
        //        result.ringCount=molecule.getAtomRingCount(i);
        result.ringSize = molecule.getAtomRingSize(i);
        result.x = molecule.getAtomX(i);
        result.y = molecule.getAtomY(i);
        result.z = molecule.getAtomZ(i);
        result.allHydrogens = molecule.getAllHydrogens(i);
        result.connAtoms = molecule.getConnAtoms(i);
        result.allConnAtoms = molecule.getAllConnAtoms(i);
        result.implicitHydrogens = result.allHydrogens + result.connAtoms - result.allConnAtoms;
        result.isAromatic = molecule.isAromaticAtom(i);
        result.isAllylic = molecule.isAllylicAtom(i);
        result.isStereoCenter = molecule.isAtomStereoCenter(i);
        result.isRing = molecule.isRingAtom(i);
        result.isSmallRing = molecule.isSmallRingAtom(i);
        result.isStabilized = molecule.isStabilizedAtom(i);

        // todo HACK to circumvent bug in OCL that consider than an hydrogen is connected to itself
        result.extra.singleBonds = result.atomicNo === 1 ? 0 : result.implicitHydrogens;
        for (let j = 0; j < molecule.getAllConnAtoms(i); j++) {
          let bond = molecule.getConnBond(i, j);
          let bondOrder = molecule.getBondOrder(bond);
          if (molecule.isAromaticBond(bond)) {
            extra.aromaticBonds++;
          } else if (bondOrder === 1) {
            // not an hydrogen
            extra.singleBonds++;
          } else if (bondOrder === 2) {
            extra.doubleBonds++;
          } else if (bondOrder === 3) {
            extra.tripleBonds++;
          }
        }
        result.extra.totalBonds = result.extra.singleBonds + result.extra.doubleBonds + result.extra.tripleBonds + result.extra.aromaticBonds;
        if (result.atomicNo === 6) {
          result.extra.cnoHybridation = result.extra.totalBonds - 1;
        } else if (result.atomicNo === 7) {
          result.extra.cnoHybridation = result.extra.totalBonds;
        } else if (result.atomicNo === 8) {
          result.extra.cnoHybridation = result.extra.totalBonds + 1;
        } else if (result.atomicNo === 1) {
          let connectedAtom = molecule.getAllConnAtoms(i) === 0 ? 0 : molecule.getAtomicNo(molecule.getConnAtom(i, 0));
          result.extra.hydrogenOnAtomicNo = connectedAtom;
          if (connectedAtom === 7 || connectedAtom === 8) {
            result.extra.labileHydrogen = true;
          }
        }
      }
      return results;
    }

    /**
     * Algorithm that finds the shortest distance from one node to the other
     * @param {Matrix} adjMatrix - A squared adjacency matrix
     * @return {Matrix} - Distance from a node to the other, -1 if the node is unreachable
     */
    function floydWarshall(adjMatrix) {
      if (Matrix.isMatrix(adjMatrix) && adjMatrix.columns !== adjMatrix.rows) {
        throw new TypeError('The adjacency matrix should be squared');
      }
      const numVertices = adjMatrix.columns;
      let distMatrix = new Matrix(numVertices, numVertices);
      distMatrix.apply((row, column) => {
        // principal diagonal is 0
        if (row === column) {
          distMatrix.set(row, column, 0);
        } else {
          let val = adjMatrix.get(row, column);
          if (val || Object.is(val, -0)) {
            // edges values remain the same
            distMatrix.set(row, column, val);
          } else {
            // 0 values become infinity
            distMatrix.set(row, column, Number.POSITIVE_INFINITY);
          }
        }
      });
      for (let k = 0; k < numVertices; ++k) {
        for (let i = 0; i < numVertices; ++i) {
          for (let j = 0; j < numVertices; ++j) {
            let dist = distMatrix.get(i, k) + distMatrix.get(k, j);
            if (distMatrix.get(i, j) > dist) {
              distMatrix.set(i, j, dist);
            }
          }
        }
      }
      // When there's no connection the value is -1
      distMatrix.apply((row, column) => {
        if (distMatrix.get(row, column) === Number.POSITIVE_INFINITY) {
          distMatrix.set(row, column, -1);
        }
      });
      return distMatrix;
    }

    /**
     * Returns a connectivity matrix
     * @param {import('openchemlib').Molecule} molecule
     * @param {object} [options={}]
     * @param {boolean} [options.pathLength=false] get the path length between atoms
     * @param {boolean} [options.mass=false] set the nominal mass of the atoms on diagonal
     * @param {boolean} [options.atomicNo=false] set the atomic number of the atom on diagonal
     * @param {boolean} [options.negativeAtomicNo=false] set the atomic number * -1 of the atom on diagonal
     * @param {boolean} [options.sdt=false] set 1, 2 or 3 depending if single, double or triple bond
     * @param {boolean} [options.sdta=false] set 1, 2, 3 or 4 depending if single, double, triple or aromatic  bond
     */
    function getConnectivityMatrix(molecule, options = {}) {
      const OCL = molecule.getOCL();
      molecule.ensureHelperArrays(OCL.Molecule.cHelperNeighbours);
      let nbAtoms = molecule.getAllAtoms();
      let result = new Array(nbAtoms).fill();
      result = result.map(() => new Array(nbAtoms).fill(0));
      if (!options.pathLength) {
        if (options.atomicNo) {
          for (let i = 0; i < nbAtoms; i++) {
            result[i][i] = molecule.getAtomicNo(i);
          }
        } else if (options.negativeAtomicNo) {
          for (let i = 0; i < nbAtoms; i++) {
            result[i][i] = -molecule.getAtomicNo(i);
          }
        } else if (options.mass) {
          for (let i = 0; i < nbAtoms; i++) {
            result[i][i] = OCL.Molecule.cRoundedMass[molecule.getAtomicNo(i)];
          }
        } else {
          for (let i = 0; i < nbAtoms; i++) {
            result[i][i] = 1;
          }
        }
      }
      if (options.sdt) {
        for (let i = 0; i < nbAtoms; i++) {
          let l = molecule.getAllConnAtoms(i);
          for (let j = 0; j < l; j++) {
            result[i][molecule.getConnAtom(i, j)] = molecule.getConnBondOrder(i, j);
          }
        }
      } else if (options.sdta) {
        for (let i = 0; i < nbAtoms; i++) {
          let l = molecule.getAllConnAtoms(i);
          for (let j = 0; j < l; j++) {
            let bondNumber = molecule.getConnBond(i, j);
            if (molecule.isAromaticBond(bondNumber)) {
              result[i][molecule.getConnAtom(i, j)] = 4;
            } else {
              result[i][molecule.getConnAtom(i, j)] = molecule.getConnBondOrder(i, j);
            }
          }
        }
      } else {
        for (let i = 0; i < nbAtoms; i++) {
          let l = molecule.getAllConnAtoms(i);
          for (let j = 0; j < l; j++) {
            result[i][molecule.getConnAtom(i, j)] = 1;
          }
        }
      }
      if (options.pathLength) {
        result = floydWarshall(new Matrix(result)).to2DArray();
      }
      return result;
    }

    /**
     * Calculates the path between 2 atoms
     * @param {import('openchemlib').Molecule} molecule
     * @param {number} from - index of the first atom
     * @param {number} to - index of the end atom
     * @param {number} maxLength - maximal length of the path
     */
    function getPathAndTorsion(molecule, from, to, maxLength) {
      let originalAtoms = []; // path before renumbering
      molecule.getPath(originalAtoms, from, to, maxLength + 1);
      let torsion;
      if (originalAtoms.length === 4) {
        torsion = molecule.calculateTorsion(originalAtoms);
      }
      return {
        atoms: originalAtoms,
        from,
        to,
        torsion,
        length: originalAtoms.length - 1
      };
    }

    let fragment;

    /**
     *
     * @param {import('openchemlib').Molecule} molecule
     * @param {object} [options={}]
     * @param {string} [options.fromLabel='H']
     * @param {string} [options.toLabel='H']
     * @param {number} [options.minLength=1]
     * @param {number} [options.maxLength=4]
     * @param {boolean} [options.withHOSES=false]

     */
    function getPathsInfo(molecule, options = {}) {
      const {
        fromLabel = 'H',
        toLabel = 'H',
        minLength = 1,
        maxLength = 4,
        withHOSES = false
      } = options;
      const OCL = molecule.getOCL();
      if (!fragment) {
        fragment = new OCL.Molecule(0, 0);
      }
      let fromAtomicNumber = OCL.Molecule.getAtomicNoFromLabel(fromLabel);
      let toAtomicNumber = OCL.Molecule.getAtomicNoFromLabel(toLabel);

      // we need to find all the atoms 'fromLabel' and 'toLabel'
      let atomsInfo = getAtomsInfo(molecule);
      let pathLengthMatrix = getConnectivityMatrix(molecule, {
        pathLength: true
      });
      for (let from = 0; from < molecule.getAllAtoms(); from++) {
        atomsInfo[from].paths = [];
        for (let to = 0; to < molecule.getAllAtoms(); to++) {
          if (from !== to) {
            if (molecule.getAtomicNo(from) === fromAtomicNumber) {
              if (molecule.getAtomicNo(to) === toAtomicNumber) {
                let pathLength = pathLengthMatrix[from][to];
                if (pathLength >= minLength && pathLength <= maxLength) {
                  if (withHOSES) {
                    atomsInfo[from].paths.push(getHoseCodesForPath(molecule, from, to, pathLength));
                  } else {
                    atomsInfo[from].paths.push(getPathAndTorsion(molecule, from, to, pathLength));
                  }
                }
              }
            }
          }
        }
      }
      return atomsInfo;
    }

    const result = {};
    for (const {
      value,
      multiplicity,
      names
    } of MultiplicityPatterns) {
      if (multiplicity) result[value] = multiplicity - 1;
      for (const name of names) {
        if (multiplicity) result[name] = multiplicity - 1;
      }
    }
    const couplingValues = result;

    function joinPatterns(patterns, options) {
      const sum = patterns.reduce((sum, pattern) => {
        if (isNaN(couplingValues[pattern])) {
          throw new Error(`pattern ${pattern} is not in ${Object.keys(couplingValues).join(' ')}`);
        }
        return sum + couplingValues[pattern];
      }, 0);
      if (!couplingPatterns[sum]) {
        throw new Error(`The joined pattern from ${patterns.join(',')} doesn't exist`);
      }
      return options?.acsFormat ? couplingACSPatterns[sum] : couplingPatterns[sum];
    }

    const localeCompare$1 = (a, b) => a.localeCompare(b);
    const localeCompareJcouplingKeys$1 = (a, b) => {
      const aa = `${a.diaIDs.sort(localeCompare$1).join(' ')}`;
      const bb = `${b.diaIDs.sort(localeCompare$1).join(' ')}`;
      return localeCompare$1(aa, bb);
    };
    const areThanClose = (a, b, tolerance) => Math.abs(a.coupling - b.coupling) < tolerance;
    const takeCareDiaIDs = (a, b, tolerance) => localeCompareJcouplingKeys$1(a, b) === 0 && areThanClose(a, b, tolerance);
    /**
     * Join couplings smaller than a define tolerance.
     * The resulting coupling should be an average of the existing one.
     * If pathLength is specified and is not always the same this property will be removed.
     */
    function signalJoinCouplings(signal, options = {}) {
      const {
        tolerance = 0.05,
        ignoreDiaIDs = false,
        acsFormat = false
      } = options;
      if (!signal.js || signal.js.length < 2) return signal;
      if (ignoreDiaIDs) {
        checkJs(signal);
        return groupJCouplings(signal, areThanClose, {
          tolerance,
          acsFormat
        });
      } else {
        checkJsAndDiaID(signal);
        return groupJCouplings(signal, takeCareDiaIDs, {
          tolerance,
          acsFormat
        });
      }
    }
    function groupJCouplings(signal, comparator, options) {
      const {
        tolerance,
        acsFormat
      } = options;
      signal.js.sort((a, b) => b.coupling - a.coupling);
      let currentGroup = [signal.js[0]];
      const groups = [currentGroup];
      for (let i = 1; i < signal.js.length; i++) {
        const currentJ = signal.js[i];
        if (comparator(currentGroup[currentGroup.length - 1], currentJ, tolerance)) {
          currentGroup.push(currentJ);
        } else {
          currentGroup = [currentJ];
          groups.push(currentGroup);
        }
      }
      signal.js = [];
      for (const group of groups) {
        let multiplicity = '';
        try {
          multiplicity = joinPatterns(group.filter(group => group.multiplicity).map(group => group.multiplicity), {
            acsFormat
          });
        } catch {
          signal.js.push(...group);
          continue;
        }
        const coupling = xSum(group.map(group => group.coupling)) / group.length;
        const atoms = distinctValues(group.filter(group => group.atoms).map(group => group.atoms).flat());
        const assignment = distinctValues(group.filter(group => group.assignment).map(group => group.assignment).flat()).join(' ');
        const diaIDs = distinctValues(group.filter(group => group.diaIDs).map(group => group.diaIDs).flat());
        const distances = distinctValues(group.map(group => group.pathLength));
        const newJ = {
          coupling,
          multiplicity
        };
        if (diaIDs.length === 1) newJ.diaIDs = diaIDs;
        if (distances.length === 1 && distances[0]) newJ.pathLength = distances[0];
        if (assignment.length > 0) newJ.assignment = assignment;
        if (atoms.length > 0) newJ.atoms = atoms;
        signal.js.push(newJ);
      }
      return signal;
    }
    function distinctValues(array) {
      const onlyDifferents = new Set();
      for (const element of array) {
        onlyDifferents.add(element);
      }
      return Array.from(onlyDifferents);
    }
    function checkJsAndDiaID(signal) {
      if (!signal.js) throw new Error('there is not js');
      for (const jcoupling of signal.js) {
        if (!jcoupling.diaIDs) throw new Error('there is not diaIDs');
      }
    }
    function checkJs(signal) {
      if (!signal.js) throw new Error('there is not js');
    }

    const localeCompare = (a, b) => a.localeCompare(b);
    const localeCompareJcouplingKeys = (a, b) => {
      const aa = `${a.diaIDs.join(' ')}${a.pathLength}`;
      const bb = `${b.diaIDs.join(' ')}${b.pathLength}`;
      return localeCompare(aa, bb);
    };
    function checkForMandatory$2(signals) {
      for (const signal of signals) {
        if (!signal.js) throw new Error('there is not js');
        if (!signal.diaIDs) throw new Error('there is not diaIDs');
        for (const jcoupling of signal.js) {
          if (!jcoupling.diaIDs) throw new Error('there is not diaIDs');
          if (!jcoupling.pathLength) throw new Error('there is not pathLength');
        }
      }
    }
    /**
     * Join signals if all the same diaID
     */
    function signalsJoin(signals, options = {}) {
      checkForMandatory$2(signals);
      const {
        joinCouplings = {}
      } = options;
      const {
        tolerance,
        ignoreDiaIDs
      } = joinCouplings;
      // we group them by diaIDs
      const copySignals = JSON.parse(JSON.stringify(signals));
      const groupedSignals = {};
      for (const signal of copySignals) {
        signal.js = signal.js.sort(localeCompareJcouplingKeys);
        const keyDiaIDs = signal.diaIDs.join(' ');
        const id = `${keyDiaIDs} ${signal.js.map(j => `${j.diaIDs.join(' ')} ${j.pathLength}`).sort(localeCompare).join(' ')}`;
        if (!groupedSignals[id]) {
          groupedSignals[id] = [];
        }
        groupedSignals[id].push(signal);
      }
      // for each group we need to combine assignments and average couplings
      let newSignals = [];
      Object.values(groupedSignals).forEach(group => {
        // joining couplings only if diaID and pathLength are equal
        const js = [];
        for (let i = 0; i < group[0].js.length; i++) {
          const coupling = group[0].js[i];
          js.push({
            diaIDs: coupling.diaIDs,
            pathLength: coupling.pathLength,
            multiplicity: coupling.multiplicity,
            coupling: xMean(group.map(item => item.js[i].coupling))
          });
        }
        const signal = {
          id: v4(),
          nbAtoms: xSum(group.map(item => item.nbAtoms || 0)),
          delta: xMean(group.map(item => item.delta)),
          diaIDs: group[0].diaIDs,
          atoms: group.map(item => item.atoms || []).flat(),
          js
        };
        const assignment = group.map(item => item.assignment).filter(item => item).join(' ');
        if (assignment.length > 0) signal.assignment = assignment;
        newSignals.push(signal);
      });
      newSignals = newSignals.map(signal => {
        const newSignal = signalJoinCouplings(signal, {
          tolerance,
          ignoreDiaIDs
        });
        if (newSignal.js) {
          newSignal.multiplicity = newSignal.js.length > 0 ? newSignal.js.map(j => j.multiplicity).join('') : 's';
        }
        return newSignal;
      }).sort((a, b) => a.delta - b.delta);
      return newSignals;
    }

    function rangeFromSignal(signal, options = {}) {
      const {
        nucleus = '1h',
        frequency = 400
      } = options;
      const {
        tolerance = getTolerance(nucleus) / frequency
      } = options;
      let halfWidth = 0;
      const {
        js: jCouplings = [],
        multiplicity
      } = signal;
      if (jCouplings.length > 0) {
        for (const js of jCouplings) {
          const {
            coupling,
            multiplicity = 'd'
          } = js;
          halfWidth += couplingValues[multiplicity] * coupling / frequency;
        }
      } else if (multiplicity === 'm') {
        halfWidth = 16 / frequency;
      } else if (multiplicity === 'b' || multiplicity === 'br s') {
        halfWidth = 7 / frequency;
      }
      halfWidth = tolerance + halfWidth / 2;
      return {
        from: signal.delta - halfWidth,
        to: signal.delta + halfWidth
      };
    }
    function getTolerance(nucleus) {
      switch (nucleus.toLocaleLowerCase()) {
        case '1h':
          return 1.5;
        case '13C':
          return 3;
        default:
          return 2;
      }
    }

    function checkNbAtoms(signals) {
      for (const signal of signals) {
        if (!signal.nbAtoms) throw new Error('nbAtoms is mandatory');
      }
    }
    function signalsToRanges(signals, options = {}) {
      checkNbAtoms(signals);
      const {
        tolerance = 0.05,
        frequency = 400
      } = options;
      let wrapped = signals.map(signal => ({
        original: signal
      }));
      wrapped.forEach(signal => {
        const fromTo = rangeFromSignal(signal.original, {
          frequency,
          tolerance
        });
        signal.from = fromTo.from;
        signal.to = fromTo.to;
      });
      wrapped = wrapped.sort((signal1, signal2) => signal1.from - signal2.from);
      const ranges = [];
      let range = {};
      for (const signal of wrapped) {
        if (range.from === undefined || signal.from > range.to) {
          range = {
            id: v4(),
            from: signal.from,
            to: signal.to,
            integration: signal.original.nbAtoms,
            signals: [signal.original]
          };
          ranges.push(range);
        } else {
          range.integration += signal.original.nbAtoms;
          if (signal.to > range.to) range.to = signal.to;
          range.signals.push(signal.original);
        }
      }
      return ranges;
    }

    function isEmptyMolecule(molecule) {
      return molecule.getAllAtoms() < 1;
    }

    async function predictProton(molecule, options = {}) {
      if (isEmptyMolecule(molecule)) {
        return {
          molfile: molecule.toMolfile(),
          nucleus: '13C',
          diaIDs: [],
          joinedSignals: [],
          signals: [],
          ranges: [],
          molecule
        };
      }
      const {
        cache
      } = options;
      molecule = molecule.getCompactCopy();
      molecule.addImplicitHydrogens();
      addDiastereotopicMissingChirality(molecule);
      const molfile = molecule.toMolfile();
      let result;
      if (cache) {
        result = cache(molfile);
      }
      if (result === undefined) {
        const formData = new FormData$1();
        formData.append('molfile', molfile);
        const response = await fetch('https://www.nmrdb.org/service/predictor', {
          method: 'POST',
          // @ts-expect-error RequestInit type does not include FormData.
          body: formData
        });
        result = await response.text();
        if (cache) {
          cache(molfile, result);
        }
      }
      const {
        diaIDs = getDiastereotopicAtomIDs(molecule)
      } = options;
      const signals = protonParser(result, molecule, diaIDs);
      const joinedSignals = signalsJoin(signals);
      return {
        molfile,
        diaIDs,
        nucleus: '1H',
        joinedSignals,
        signals,
        ranges: signalsToRanges(joinedSignals),
        molecule
      };
    }
    function protonParser(result, molecule, diaIDs) {
      if (molecule.getAllAtoms() === 0) return [];
      if (result.includes('ERR')) {
        throw Error(`Spinus optimization: ${result}`);
      }
      const distanceMatrix = getConnectivityMatrix(molecule, {
        pathLength: true
      });
      const lines = result.split('\n').filter(line => line);
      const signals = [];
      for (const line of lines) {
        const fields = line.split('\t');
        const couplings = fields.slice(4);
        const atom = Number(fields[0]) - 1;
        const signal = {
          id: v4(),
          atoms: [atom],
          diaIDs: [diaIDs[atom]],
          nbAtoms: 1,
          delta: Number(fields[2]),
          js: []
        };
        if (!signal.js) {
          throw new Error(`For atom js property was not added`);
        }
        for (let i = 0; i < couplings.length; i += 3) {
          const linked = Number(couplings[i]) - 1;
          signal.js.push({
            coupling: Number(couplings[i + 2]),
            atoms: [linked],
            diaIDs: [diaIDs[linked]],
            multiplicity: 'd',
            pathLength: distanceMatrix[atom][linked]
          });
          signal.js.sort((a, b) => b.coupling - a.coupling);
        }
        signals.push(signal);
      }
      return signals;
    }

    const {
      parse: parse$3,
      stringify: stringify$3
    } = JSON;
    function setIDs(peaks, options = {}) {
      const output = options.output || parse$3(stringify$3(peaks));
      for (const peak of output) {
        peak.id = v4();
      }
      return output;
    }

    async function fetchPrediction(molecule, options) {
      const {
        webserviceURL
      } = options;
      const response = await browserPonyfill.exports.fetch(webserviceURL, {
        headers: {
          accept: 'application/json',
          'content-type': 'application/json'
        },
        body: JSON.stringify({
          molfile: molecule.toMolfile()
        }),
        method: 'POST'
      });
      if (response.status >= 400) {
        throw new Error(response.statusText);
      }
      const prediction = (await response.json()).data;
      prediction.molecule = molecule.getOCL().Molecule.fromMolfile(prediction.molfile);
      return prediction;
    }

    function getFilteredIDiaIDs(molecule, options) {
      const {
        maxSphereSize,
        diaIDs
      } = options;
      molecule.addImplicitHydrogens();
      molecule.addMissingChirality();
      addDiastereotopicMissingChirality(molecule);
      const molfile = molecule.toMolfile();
      const groupedDiaIDs = groupDiastereotopicAtomIDs(diaIDs, molecule, {
        atomLabel: 'C'
      });
      const carbonDiaIDs = groupedDiaIDs.sort((a, b) => {
        if (a.atomLabel === b.atomLabel) {
          return b.counter - a.counter;
        }
        return a.atomLabel < b.atomLabel ? 1 : -1;
      });
      const OCL = molecule.getOCL();
      for (const diaId of carbonDiaIDs) {
        diaId.hose = getHoseCodesFromDiastereotopicID(OCL.Molecule.fromIDCode(diaId.oclID), {
          maxSphereSize
        });
      }
      const toReturn = {
        molfile,
        carbonDiaIDs: carbonDiaIDs
      };
      return toReturn;
    }

    function queryByHose(diaIDs, db, options) {
      const {
        maxSphereSize
      } = options;
      const toReturn = [];
      for (const element of diaIDs) {
        let res;
        let level = null;
        for (let k = maxSphereSize; !res && k >= 0; k--) {
          if (db[k]) {
            res = db[k][element.hose[k]];
            level = k;
          }
        }
        for (const atomNumber of element.atoms) {
          const atom = {
            diaIDs: [element.oclID],
            delta: res ? res[0] : null,
            atoms: [atomNumber],
            nbAtoms: 1,
            level,
            statistic: res && res.length > 1 ? {
              mean: res[1],
              sd: res[2],
              min: res[3],
              max: res[4],
              nb: res[5]
            } : undefined
          };
          toReturn.push(atom);
        }
      }
      return toReturn;
    }

    function checkFromPrediction(signal) {
      if (!signal.atoms) throw new Error('There is not atoms');
      if (!signal.diaIDs) throw new Error('There is not diaIDs');
      if (!signal.nbAtoms) throw new Error('There is not nbAtoms');
    }
    /**
     * Make a query to a hose code based database to predict carbon chemical shift
     * @returns {Promise<object>} - object with molfile, diaIDs, signals, joined signals by diaIDs and ranges.
     */
    async function predictCarbon(molecule, options = {}) {
      if (isEmptyMolecule(molecule)) {
        return {
          molfile: molecule.toMolfile(),
          nucleus: '13C',
          diaIDs: [],
          joinedSignals: [],
          signals: [],
          ranges: [],
          molecule
        };
      }
      const {
        database,
        webserviceURL = 'https://nmr-prediction.service.zakodium.com/v1/predict/carbon',
        diaIDs = getDiastereotopicAtomIDs(molecule)
      } = options;
      if (!database) return fetchPrediction(molecule, {
        webserviceURL
      });
      if (!database) {
        throw new Error('There is not a database');
      }
      const maxLevel = database.length - 1;
      let {
        maxSphereSize = maxLevel
      } = options;
      if (maxSphereSize > maxLevel) maxSphereSize = maxLevel;
      const {
        carbonDiaIDs,
        molfile
      } = getFilteredIDiaIDs(molecule, {
        maxSphereSize,
        diaIDs
      });
      const predictions = queryByHose(carbonDiaIDs, database, {
        maxSphereSize
      });
      const signals = formatSignals(predictions);
      const joinedSignals = joinSignalByDiaID(signals);
      return {
        molfile,
        nucleus: '13C',
        diaIDs,
        joinedSignals,
        signals,
        ranges: signalsToRanges(joinedSignals),
        molecule
      };
    }
    function formatSignals(predictions) {
      const signals = [];
      for (const prediction of predictions) {
        const {
          atoms,
          nbAtoms,
          delta,
          diaIDs,
          statistic
        } = prediction;
        const signal = {
          id: v4(),
          delta: delta || NaN,
          atoms,
          diaIDs,
          multiplicity: 's',
          nbAtoms,
          statistic,
          js: []
        };
        signals.push(signal);
      }
      return signals;
    }
    function joinSignalByDiaID(signals) {
      const joinedSignals = {};
      for (const signal of signals) {
        checkFromPrediction(signal);
        const diaID = signal.diaIDs[0];
        if (!joinedSignals[diaID]) {
          joinedSignals[diaID] = JSON.parse(JSON.stringify(signal));
        } else {
          joinedSignals[diaID].nbAtoms += signal.nbAtoms;
          joinedSignals[diaID].atoms.push(...signal.atoms);
        }
      }
      return setIDs(Object.values(joinedSignals));
    }

    function squaredEuclidean(p, q) {
      let d = 0;
      for (let i = 0; i < p.length; i++) {
        d += (p[i] - q[i]) * (p[i] - q[i]);
      }
      return d;
    }
    function euclidean(p, q) {
      return Math.sqrt(squaredEuclidean(p, q));
    }

    /**
     * Computes a distance/similarity matrix given an array of data and a distance/similarity function.
     * @param {Array} data An array of data
     * @param {function} distanceFn  A function that accepts two arguments and computes a distance/similarity between them
     * @return {Array<Array>} The distance/similarity matrix. The matrix is square and has a size equal to the length of
     * the data array
     */
    function distanceMatrix(data, distanceFn) {
      const result = getMatrix(data.length);

      // Compute upper distance matrix
      for (let i = 0; i < data.length; i++) {
        for (let j = 0; j <= i; j++) {
          result[i][j] = distanceFn(data[i], data[j]);
          result[j][i] = result[i][j];
        }
      }
      return result;
    }
    function getMatrix(size) {
      const matrix = [];
      for (let i = 0; i < size; i++) {
        const row = [];
        matrix.push(row);
        for (let j = 0; j < size; j++) {
          row.push(0);
        }
      }
      return matrix;
    }

    var heap$1 = {exports: {}};

    (function (module, exports) {
      // Generated by CoffeeScript 1.8.0
      (function () {
        var Heap, defaultCmp, floor, heapify, heappop, heappush, heappushpop, heapreplace, insort, min, nlargest, nsmallest, updateItem, _siftdown, _siftup;
        floor = Math.floor, min = Math.min;

        /*
        Default comparison function to be used
         */

        defaultCmp = function (x, y) {
          if (x < y) {
            return -1;
          }
          if (x > y) {
            return 1;
          }
          return 0;
        };

        /*
        Insert item x in list a, and keep it sorted assuming a is sorted.
        
        If x is already in a, insert it to the right of the rightmost x.
        
        Optional args lo (default 0) and hi (default a.length) bound the slice
        of a to be searched.
         */

        insort = function (a, x, lo, hi, cmp) {
          var mid;
          if (lo == null) {
            lo = 0;
          }
          if (cmp == null) {
            cmp = defaultCmp;
          }
          if (lo < 0) {
            throw new Error('lo must be non-negative');
          }
          if (hi == null) {
            hi = a.length;
          }
          while (lo < hi) {
            mid = floor((lo + hi) / 2);
            if (cmp(x, a[mid]) < 0) {
              hi = mid;
            } else {
              lo = mid + 1;
            }
          }
          return [].splice.apply(a, [lo, lo - lo].concat(x)), x;
        };

        /*
        Push item onto heap, maintaining the heap invariant.
         */

        heappush = function (array, item, cmp) {
          if (cmp == null) {
            cmp = defaultCmp;
          }
          array.push(item);
          return _siftdown(array, 0, array.length - 1, cmp);
        };

        /*
        Pop the smallest item off the heap, maintaining the heap invariant.
         */

        heappop = function (array, cmp) {
          var lastelt, returnitem;
          if (cmp == null) {
            cmp = defaultCmp;
          }
          lastelt = array.pop();
          if (array.length) {
            returnitem = array[0];
            array[0] = lastelt;
            _siftup(array, 0, cmp);
          } else {
            returnitem = lastelt;
          }
          return returnitem;
        };

        /*
        Pop and return the current smallest value, and add the new item.
        
        This is more efficient than heappop() followed by heappush(), and can be
        more appropriate when using a fixed size heap. Note that the value
        returned may be larger than item! That constrains reasonable use of
        this routine unless written as part of a conditional replacement:
            if item > array[0]
              item = heapreplace(array, item)
         */

        heapreplace = function (array, item, cmp) {
          var returnitem;
          if (cmp == null) {
            cmp = defaultCmp;
          }
          returnitem = array[0];
          array[0] = item;
          _siftup(array, 0, cmp);
          return returnitem;
        };

        /*
        Fast version of a heappush followed by a heappop.
         */

        heappushpop = function (array, item, cmp) {
          var _ref;
          if (cmp == null) {
            cmp = defaultCmp;
          }
          if (array.length && cmp(array[0], item) < 0) {
            _ref = [array[0], item], item = _ref[0], array[0] = _ref[1];
            _siftup(array, 0, cmp);
          }
          return item;
        };

        /*
        Transform list into a heap, in-place, in O(array.length) time.
         */

        heapify = function (array, cmp) {
          var i, _i, _len, _ref1, _results, _results1;
          if (cmp == null) {
            cmp = defaultCmp;
          }
          _ref1 = function () {
            _results1 = [];
            for (var _j = 0, _ref = floor(array.length / 2); 0 <= _ref ? _j < _ref : _j > _ref; 0 <= _ref ? _j++ : _j--) {
              _results1.push(_j);
            }
            return _results1;
          }.apply(this).reverse();
          _results = [];
          for (_i = 0, _len = _ref1.length; _i < _len; _i++) {
            i = _ref1[_i];
            _results.push(_siftup(array, i, cmp));
          }
          return _results;
        };

        /*
        Update the position of the given item in the heap.
        This function should be called every time the item is being modified.
         */

        updateItem = function (array, item, cmp) {
          var pos;
          if (cmp == null) {
            cmp = defaultCmp;
          }
          pos = array.indexOf(item);
          if (pos === -1) {
            return;
          }
          _siftdown(array, 0, pos, cmp);
          return _siftup(array, pos, cmp);
        };

        /*
        Find the n largest elements in a dataset.
         */

        nlargest = function (array, n, cmp) {
          var elem, result, _i, _len, _ref;
          if (cmp == null) {
            cmp = defaultCmp;
          }
          result = array.slice(0, n);
          if (!result.length) {
            return result;
          }
          heapify(result, cmp);
          _ref = array.slice(n);
          for (_i = 0, _len = _ref.length; _i < _len; _i++) {
            elem = _ref[_i];
            heappushpop(result, elem, cmp);
          }
          return result.sort(cmp).reverse();
        };

        /*
        Find the n smallest elements in a dataset.
         */

        nsmallest = function (array, n, cmp) {
          var elem, los, result, _i, _j, _len, _ref, _ref1, _results;
          if (cmp == null) {
            cmp = defaultCmp;
          }
          if (n * 10 <= array.length) {
            result = array.slice(0, n).sort(cmp);
            if (!result.length) {
              return result;
            }
            los = result[result.length - 1];
            _ref = array.slice(n);
            for (_i = 0, _len = _ref.length; _i < _len; _i++) {
              elem = _ref[_i];
              if (cmp(elem, los) < 0) {
                insort(result, elem, 0, null, cmp);
                result.pop();
                los = result[result.length - 1];
              }
            }
            return result;
          }
          heapify(array, cmp);
          _results = [];
          for (_j = 0, _ref1 = min(n, array.length); 0 <= _ref1 ? _j < _ref1 : _j > _ref1; 0 <= _ref1 ? ++_j : --_j) {
            _results.push(heappop(array, cmp));
          }
          return _results;
        };
        _siftdown = function (array, startpos, pos, cmp) {
          var newitem, parent, parentpos;
          if (cmp == null) {
            cmp = defaultCmp;
          }
          newitem = array[pos];
          while (pos > startpos) {
            parentpos = pos - 1 >> 1;
            parent = array[parentpos];
            if (cmp(newitem, parent) < 0) {
              array[pos] = parent;
              pos = parentpos;
              continue;
            }
            break;
          }
          return array[pos] = newitem;
        };
        _siftup = function (array, pos, cmp) {
          var childpos, endpos, newitem, rightpos, startpos;
          if (cmp == null) {
            cmp = defaultCmp;
          }
          endpos = array.length;
          startpos = pos;
          newitem = array[pos];
          childpos = 2 * pos + 1;
          while (childpos < endpos) {
            rightpos = childpos + 1;
            if (rightpos < endpos && !(cmp(array[childpos], array[rightpos]) < 0)) {
              childpos = rightpos;
            }
            array[pos] = array[childpos];
            pos = childpos;
            childpos = 2 * pos + 1;
          }
          array[pos] = newitem;
          return _siftdown(array, startpos, pos, cmp);
        };
        Heap = function () {
          Heap.push = heappush;
          Heap.pop = heappop;
          Heap.replace = heapreplace;
          Heap.pushpop = heappushpop;
          Heap.heapify = heapify;
          Heap.updateItem = updateItem;
          Heap.nlargest = nlargest;
          Heap.nsmallest = nsmallest;
          function Heap(cmp) {
            this.cmp = cmp != null ? cmp : defaultCmp;
            this.nodes = [];
          }
          Heap.prototype.push = function (x) {
            return heappush(this.nodes, x, this.cmp);
          };
          Heap.prototype.pop = function () {
            return heappop(this.nodes, this.cmp);
          };
          Heap.prototype.peek = function () {
            return this.nodes[0];
          };
          Heap.prototype.contains = function (x) {
            return this.nodes.indexOf(x) !== -1;
          };
          Heap.prototype.replace = function (x) {
            return heapreplace(this.nodes, x, this.cmp);
          };
          Heap.prototype.pushpop = function (x) {
            return heappushpop(this.nodes, x, this.cmp);
          };
          Heap.prototype.heapify = function () {
            return heapify(this.nodes, this.cmp);
          };
          Heap.prototype.updateItem = function (x) {
            return updateItem(this.nodes, x, this.cmp);
          };
          Heap.prototype.clear = function () {
            return this.nodes = [];
          };
          Heap.prototype.empty = function () {
            return this.nodes.length === 0;
          };
          Heap.prototype.size = function () {
            return this.nodes.length;
          };
          Heap.prototype.clone = function () {
            var heap;
            heap = new Heap();
            heap.nodes = this.nodes.slice(0);
            return heap;
          };
          Heap.prototype.toArray = function () {
            return this.nodes.slice(0);
          };
          Heap.prototype.insert = Heap.prototype.push;
          Heap.prototype.top = Heap.prototype.peek;
          Heap.prototype.front = Heap.prototype.peek;
          Heap.prototype.has = Heap.prototype.contains;
          Heap.prototype.copy = Heap.prototype.clone;
          return Heap;
        }();
        (function (root, factory) {
          {
            return module.exports = factory();
          }
        })(this, function () {
          return Heap;
        });
      }).call(commonjsGlobal);
    })(heap$1);

    var heap = heap$1.exports;
    var Heap = heap;

    class Cluster {
      constructor() {
        this.children = [];
        this.height = 0;
        this.size = 1;
        this.index = -1;
        this.isLeaf = false;
      }

      /**
       * Creates an array of clusters where the maximum height is smaller than the threshold
       * @param {number} threshold
       * @return {Array<Cluster>}
       */
      cut(threshold) {
        if (typeof threshold !== 'number') {
          throw new TypeError('threshold must be a number');
        }
        if (threshold < 0) {
          throw new RangeError('threshold must be a positive number');
        }
        let list = [this];
        const ans = [];
        while (list.length > 0) {
          const aux = list.shift();
          if (threshold >= aux.height) {
            ans.push(aux);
          } else {
            list = list.concat(aux.children);
          }
        }
        return ans;
      }

      /**
       * Merge the leaves in the minimum way to have `groups` number of clusters.
       * @param {number} groups - Them number of children the first level of the tree should have.
       * @return {Cluster}
       */
      group(groups) {
        if (!Number.isInteger(groups) || groups < 1) {
          throw new RangeError('groups must be a positive integer');
        }
        const heap = new Heap((a, b) => {
          return b.height - a.height;
        });
        heap.push(this);
        while (heap.size() < groups) {
          const first = heap.pop();
          if (first.children.length === 0) {
            break;
          }
          first.children.forEach(child => heap.push(child));
        }
        const root = new Cluster();
        root.children = heap.toArray();
        root.height = this.height;
        return root;
      }

      /**
       * Traverses the tree depth-first and calls the provided callback with each individual node
       * @param {function} cb - The callback to be called on each node encounter
       */
      traverse(cb) {
        function visit(root, callback) {
          callback(root);
          if (root.children) {
            for (const child of root.children) {
              visit(child, callback);
            }
          }
        }
        visit(this, cb);
      }

      /**
       * Returns a list of indices for all the leaves of this cluster.
       * The list is ordered in such a way that a dendrogram could be drawn without crossing branches.
       * @returns {Array<number>}
       */
      indices() {
        const result = [];
        this.traverse(cluster => {
          if (cluster.isLeaf) {
            result.push(cluster.index);
          }
        });
        return result;
      }
    }

    function singleLink(dKI, dKJ) {
      return Math.min(dKI, dKJ);
    }
    function completeLink(dKI, dKJ) {
      return Math.max(dKI, dKJ);
    }
    function averageLink(dKI, dKJ, dIJ, ni, nj) {
      const ai = ni / (ni + nj);
      const aj = nj / (ni + nj);
      return ai * dKI + aj * dKJ;
    }
    function weightedAverageLink(dKI, dKJ) {
      return (dKI + dKJ) / 2;
    }
    function centroidLink(dKI, dKJ, dIJ, ni, nj) {
      const ai = ni / (ni + nj);
      const aj = nj / (ni + nj);
      const b = -(ni * nj) / (ni + nj) ** 2;
      return ai * dKI + aj * dKJ + b * dIJ;
    }
    function medianLink(dKI, dKJ, dIJ) {
      return dKI / 2 + dKJ / 2 - dIJ / 4;
    }
    function wardLink(dKI, dKJ, dIJ, ni, nj, nk) {
      const ai = (ni + nk) / (ni + nj + nk);
      const aj = (nj + nk) / (ni + nj + nk);
      const b = -nk / (ni + nj + nk);
      return ai * dKI + aj * dKJ + b * dIJ;
    }
    function wardLink2(dKI, dKJ, dIJ, ni, nj, nk) {
      const ai = (ni + nk) / (ni + nj + nk);
      const aj = (nj + nk) / (ni + nj + nk);
      const b = -nk / (ni + nj + nk);
      return Math.sqrt(ai * dKI * dKI + aj * dKJ * dKJ + b * dIJ * dIJ);
    }

    /**
     * Continuously merge nodes that have the least dissimilarity
     * @param {Array<Array<number>>} data - Array of points to be clustered
     * @param {object} [options]
     * @param {Function} [options.distanceFunction]
     * @param {string} [options.method] - Default: `'complete'`
     * @param {boolean} [options.isDistanceMatrix] - Is the input already a distance matrix?
     * @constructor
     */
    function agnes(data, options = {}) {
      const {
        distanceFunction = euclidean,
        method = 'complete',
        isDistanceMatrix = false
      } = options;
      let updateFunc;
      if (!isDistanceMatrix) {
        data = distanceMatrix(data, distanceFunction);
      }
      let distanceMatrix$1 = new Matrix(data);
      const numLeaves = distanceMatrix$1.rows;

      // allows to use a string or a given function
      if (typeof method === 'string') {
        switch (method.toLowerCase()) {
          case 'single':
            updateFunc = singleLink;
            break;
          case 'complete':
            updateFunc = completeLink;
            break;
          case 'average':
          case 'upgma':
            updateFunc = averageLink;
            break;
          case 'wpgma':
            updateFunc = weightedAverageLink;
            break;
          case 'centroid':
          case 'upgmc':
            updateFunc = centroidLink;
            break;
          case 'median':
          case 'wpgmc':
            updateFunc = medianLink;
            break;
          case 'ward':
            updateFunc = wardLink;
            break;
          case 'ward2':
            updateFunc = wardLink2;
            break;
          default:
            throw new RangeError(`unknown clustering method: ${method}`);
        }
      } else if (typeof method !== 'function') {
        throw new TypeError('method must be a string or function');
      }
      let clusters = [];
      for (let i = 0; i < numLeaves; i++) {
        const cluster = new Cluster();
        cluster.isLeaf = true;
        cluster.index = i;
        clusters.push(cluster);
      }
      for (let n = 0; n < numLeaves - 1; n++) {
        const [row, column, distance] = getSmallestDistance(distanceMatrix$1);
        const cluster1 = clusters[row];
        const cluster2 = clusters[column];
        const newCluster = new Cluster();
        newCluster.size = cluster1.size + cluster2.size;
        newCluster.children.push(cluster1, cluster2);
        newCluster.height = distance;
        const newClusters = [newCluster];
        const newDistanceMatrix = new Matrix(distanceMatrix$1.rows - 1, distanceMatrix$1.rows - 1);
        const previous = newIndex => getPreviousIndex(newIndex, Math.min(row, column), Math.max(row, column));
        for (let i = 1; i < newDistanceMatrix.rows; i++) {
          const prevI = previous(i);
          const prevICluster = clusters[prevI];
          newClusters.push(prevICluster);
          for (let j = 0; j < i; j++) {
            if (j === 0) {
              const dKI = distanceMatrix$1.get(row, prevI);
              const dKJ = distanceMatrix$1.get(prevI, column);
              const val = updateFunc(dKI, dKJ, distance, cluster1.size, cluster2.size, prevICluster.size);
              newDistanceMatrix.set(i, j, val);
              newDistanceMatrix.set(j, i, val);
            } else {
              // Just copy distance from previous matrix
              const val = distanceMatrix$1.get(prevI, previous(j));
              newDistanceMatrix.set(i, j, val);
              newDistanceMatrix.set(j, i, val);
            }
          }
        }
        clusters = newClusters;
        distanceMatrix$1 = newDistanceMatrix;
      }
      return clusters[0];
    }
    function getSmallestDistance(distance) {
      let smallest = Infinity;
      let smallestI = 0;
      let smallestJ = 0;
      for (let i = 1; i < distance.rows; i++) {
        for (let j = 0; j < i; j++) {
          if (distance.get(i, j) < smallest) {
            smallest = distance.get(i, j);
            smallestI = i;
            smallestJ = j;
          }
        }
      }
      return [smallestI, smallestJ, smallest];
    }
    function getPreviousIndex(newIndex, prev1, prev2) {
      newIndex -= 1;
      if (newIndex >= prev1) newIndex++;
      if (newIndex >= prev2) newIndex++;
      return newIndex;
    }

    const nucleusMap = {
      h: '1H',
      c: '13C'
    };
    function getNuclei(input) {
      return Object.values(input).map(e => nucleusMap[e.toLowerCase()]);
    }

    async function getPredictions(key, molecule, predictOptions, predictor, predictions) {
      let prediction;
      if (predictions?.[key]) {
        prediction = predictions[key];
      } else if (predictor) {
        const fromPredictor = predictor[key];
        prediction = fromPredictor ? await fromPredictor(molecule, predictOptions[key]) : undefined;
      }
      return prediction;
    }

    function checkFromTo(options) {
      if (!options.from || !options.to) {
        throw new Error('options from and to are mandatory');
      }
    }
    async function predict2D(molecule, options = {}) {
      checkFromTo(options);
      molecule.addImplicitHydrogens();
      const {
        from,
        to,
        diaIDs,
        minLength = 0,
        maxLength,
        predictOptions = {},
        predictions,
        predictor,
        joinDistance = {
          H: 0.05,
          C: 0.5
        },
        includeDiagonal = false
      } = options;
      const pathsInfo = getPathsInfo(molecule, {
        fromLabel: from,
        toLabel: to,
        minLength,
        maxLength
      });
      const groupedDiaIDS = diaIDs ? groupDiastereotopicAtomIDs(diaIDs, molecule) : getGroupedDiastereotopicAtomIDs(molecule);
      const diaIDswithAtomInfo = [];
      for (const diaID of groupedDiaIDS) {
        diaIDswithAtomInfo.push({
          ...diaID,
          pathInfo: JSON.parse(JSON.stringify(pathsInfo[diaID.atoms[0]]))
        });
      }
      const xPrediction = await getPredictions(from, molecule, predictOptions, predictor, predictions);
      const yPrediction = from === to ? xPrediction : await getPredictions(to, molecule, predictOptions, predictor, predictions);
      if (!xPrediction || !yPrediction) {
        throw new Error('predictions are not availaible');
      }
      const spectra = {
        x: xPrediction,
        y: yPrediction
      };
      const signalsByDiaID = {
        x: {},
        y: {}
      };
      for (const axis in signalsByDiaID) {
        for (const signal of spectra[axis].joinedSignals) {
          if (!signal.diaIDs) throw new Error('Signal has not diaIDs');
          signalsByDiaID[axis][signal.diaIDs[0]] = signal;
        }
      }
      const group = {};
      for (const diaID of diaIDswithAtomInfo) {
        const pathInfo = diaID.pathInfo;
        if (pathInfo.paths.length < 1) continue;
        if (!signalsByDiaID.x[pathInfo.oclID]) continue;
        const currentPaths = pathInfo.paths;
        for (const path of currentPaths) {
          if (!signalsByDiaID.y[pathsInfo[path.to].oclID]) continue;
          const fromToDiaID = {
            x: pathInfo,
            y: pathsInfo[path.to]
          };
          const key = `${fromToDiaID.x.oclID}-${fromToDiaID.y.oclID}`;
          if (key === `${pathInfo.oclID}-${pathInfo.oclID}` || group[key]) {
            continue;
          }
          const peak = {
            id: v4(),
            z: 100
          };
          const signal = {
            id: v4(),
            x: {},
            y: {}
          };
          for (const axis in fromToDiaID) {
            const diaID = fromToDiaID[axis].oclID;
            peak[axis] = signalsByDiaID[axis][diaID].delta;
            signal[axis].delta = signalsByDiaID[axis][diaID].delta;
            signal[axis].diaIDs = [diaID];
            signal[axis].atoms = signalsByDiaID[axis][diaID].atoms;
          }
          signal.peaks = [peak];
          group[key] = signal;
        }
      }
      if (includeDiagonal) {
        addSelftCorrelation(group, {
          paths: pathsInfo,
          signalsByDiaID
        });
      }
      // clusterize signals by distance
      const joinedSignals = Object.values(group);
      const zones = createZones(joinedSignals, {
        joinDistance,
        from,
        to
      });
      return {
        molfile: molecule.toMolfile(),
        diaIDs: spectra.x.diaIDs,
        nuclei: getNuclei({
          from,
          to
        }),
        joinedSignals,
        signals: splitSignals(joinedSignals),
        zones,
        molecule
      };
    }
    function splitSignals(joinedSignals) {
      const signals = [];
      for (const signal of joinedSignals) {
        for (const xAtom of signal.x.atoms || []) {
          for (const yAtom of signal.y.atoms || []) {
            const newSignal = JSON.parse(JSON.stringify(signal));
            newSignal.x.atoms = [xAtom];
            newSignal.y.atoms = [yAtom];
            signals.push(newSignal);
          }
        }
      }
      return setIDs(signals);
    }
    function addSelftCorrelation(group, options) {
      const {
        paths = [],
        signalsByDiaID
      } = options;
      for (const atom of paths) {
        if (atom.paths.length < 1) continue;
        const diaID = atom.oclID;
        if (!signalsByDiaID.x[diaID]) continue;
        if (group[`${diaID}-${diaID}`]) continue;
        const signal = {
          x: {},
          y: {}
        };
        const peak = {
          z: 1
        };
        for (const axis of ['x', 'y']) {
          peak[axis] = signalsByDiaID[axis][diaID].delta;
          signal[axis].delta = signalsByDiaID[axis][diaID].delta;
          signal[axis].diaIDs = [diaID];
          signal[axis].atoms = signalsByDiaID[axis][diaID].atoms;
        }
        signal.peaks = [peak];
        group[`${atom.oclID}-${atom.oclID}`] = signal;
      }
    }
    function createZones(signals, options) {
      if (signals.length < 1) {
        return [];
      }
      const {
        joinDistance,
        from,
        to
      } = options;
      const deltas = new Matrix(signals.map(e => [e.x.delta, e.y.delta]));
      const minX = deltas.minColumn(0);
      const maxX = deltas.maxColumn(0);
      const minY = deltas.minColumn(1);
      const maxY = deltas.maxColumn(1);
      deltas.mulColumn(0, 1 / (maxX - minX));
      deltas.mulColumn(1, 1 / (maxY - minY));
      const cutOff = Math.sqrt(Math.pow(joinDistance[from] / (maxX - minX), 2) + Math.pow(joinDistance[to] / (maxY - minY), 2));
      const hClusters = agnes(deltas.to2DArray(), {
        method: 'centroid'
      });
      const clusters = splitClusters([hClusters], cutOff);
      const zones = [];
      for (const cluster of clusters) {
        const signal = [];
        if (cluster.isLeaf) signal.push(signals[cluster.index]);
        for (const child of cluster.children) {
          for (const index of child.indices()) {
            signal.push(signals[index]);
          }
        }
        zones.push({
          id: v4(),
          ...fromTo$1(signal, {
            joinDistance,
            from,
            to
          }),
          signals: signal
        });
      }
      return zones;
    }
    function fromTo$1(signals, options) {
      const {
        joinDistance,
        from,
        to
      } = options;
      let minX = Number.MAX_SAFE_INTEGER;
      let minY = Number.MAX_SAFE_INTEGER;
      let maxX = Number.MIN_SAFE_INTEGER;
      let maxY = Number.MIN_SAFE_INTEGER;
      for (const signal of signals) {
        if (signal.x.delta < minX) minX = signal.x.delta;
        if (signal.x.delta > maxX) maxX = signal.x.delta;
        if (signal.y.delta < minY) minY = signal.y.delta;
        if (signal.y.delta > maxY) maxY = signal.y.delta;
      }
      return {
        x: {
          from: minX - joinDistance[from],
          to: maxX + joinDistance[from]
        },
        y: {
          from: minY - joinDistance[to],
          to: maxY + joinDistance[to]
        }
      };
    }
    function splitClusters(clusters, maxDistance) {
      const originalLength = clusters.length;
      for (let i = 0; i < clusters.length; i++) {
        const cluster = clusters[i];
        if (cluster.height > maxDistance) {
          clusters.push(...cluster.children);
          clusters.splice(i--, 1);
        }
      }
      return clusters.length < originalLength ? splitClusters(clusters, maxDistance) : clusters;
    }

    /**
     * Generate the correlation bidimensional nmr peaks based on the number of bonds between a pair of atoms
     * @returns {Promise<object>} - object with molfile, diaIDs, 2D signals joined signals and zones.
     */
    async function predictCOSY(molecule, options = {}) {
      const {
        minLength = 2,
        maxLength = 3,
        predictions,
        predictor = {
          H: predictProton
        },
        predictOptions,
        joinDistance = {
          H: 0.05,
          C: 0.5
        },
        includeDiagonal = true
      } = options;
      return predict2D(molecule, {
        from: 'H',
        to: 'H',
        predictor,
        minLength,
        maxLength,
        joinDistance,
        predictions,
        includeDiagonal,
        predictOptions
      });
    }

    /**
     * Generate the correlation bidimensional nmr peaks based on the number of bonds between a pair of atoms
     * @returns {Promise<object>} - object with molfile, diaIDs, 2D signals joined signals and zones.
     */
    async function predictHSQC(molecule, options = {}) {
      const {
        minLength = 1,
        maxLength = 1,
        predictor = {
          H: predictProton,
          C: predictCarbon
        },
        predictions,
        joinDistance = {
          H: 0.05,
          C: 0.5
        },
        predictOptions
      } = options;
      return predict2D(molecule, {
        from: 'H',
        to: 'C',
        predictor,
        minLength,
        maxLength,
        predictions,
        predictOptions,
        joinDistance
      });
    }

    async function predictHMBC(molecule, options = {}) {
      const {
        minLength = 2,
        maxLength = 3,
        predictor = {
          H: predictProton,
          C: predictCarbon
        },
        predictOptions,
        predictions,
        joinDistance = {
          H: 0.05,
          C: 0.5
        }
      } = options;
      return predict2D(molecule, {
        from: 'H',
        to: 'C',
        predictor,
        minLength,
        maxLength,
        predictions,
        predictOptions,
        joinDistance
      });
    }

    /**
     * Generate the correlation bidimensional nmr peaks based on the number of bonds between a pair of atoms
     * @returns {Promise<object>} - object with molfile, diaIDs, 1D and 2D signals, joined signals, ranges and zones.
     */
    async function predictAll(molecule, options = {}) {
      const {
        logger,
        predictor = {
          H: predictProton,
          C: predictCarbon
        },
        joinDistance = {
          H: 0.05,
          C: 0.5
        },
        predictOptions = {}
      } = options;
      const diaIDs = getDiastereotopicAtomIDs(molecule);
      let {
        predictions
      } = options;
      const xPrediction = await getPredictions('H', molecule, predictOptions, predictor, predictions);
      const yPrediction = await getPredictions('C', molecule, predictOptions, predictor, predictions);
      if (!xPrediction || !yPrediction) {
        throw new Error('predictions are not availaible');
      }
      const spectra = {};
      if (check1DPrediction(xPrediction)) {
        spectra.proton = xPrediction;
      }
      if (check1DPrediction(yPrediction)) {
        spectra.carbon = yPrediction;
      }
      const {
        molfile
      } = xPrediction;
      predictions = {
        H: xPrediction,
        C: yPrediction
      };
      if (spectra.proton) {
        spectra.cosy = await predictCOSY(molecule, {
          predictions,
          joinDistance
        });
      } else if (logger) {
        logger.warn('Proton prediction is missing so COSY could not be generated.');
      }
      if (spectra.carbon && spectra.proton) {
        spectra.hsqc = await predictHSQC(molecule, {
          predictions,
          joinDistance
        });
        spectra.hmbc = await predictHMBC(molecule, {
          predictions,
          joinDistance
        });
      } else if (logger) {
        logger.warn('Carbon and / or proton prediction is missing so HSQC and HMBC could not be generated.');
      }
      for (const key in spectra) {
        delete spectra[key].molfile;
        delete spectra[key].diaIDs;
      }
      return {
        molfile,
        diaIDs,
        spectra
      };
    }
    function check1DPrediction(prediction) {
      if (!prediction) return false;
      if (prediction.ranges.length === 0) return false;
      const {
        signals,
        diaIDs
      } = prediction;
      for (const signal of signals) {
        const isCorrect = signal.diaIDs?.every(diaID => diaIDs.includes(diaID));
        if (!isCorrect) return false;
      }
      return true;
    }

    function signals2DToZ(signals, options = {}) {
      const {
        from = -1,
        to = 12,
        nbPoints = 512,
        factor = 3
      } = options;
      let {
        width = 0.02
      } = options;
      const peaks = signals.reduce((acc, {
        x,
        y
      }) => {
        acc.x.push(x.delta);
        acc.y.push(y.delta);
        acc.z.push(1e6);
        return acc;
      }, {
        x: [],
        y: [],
        z: []
      });
      width = ensureXYNumber(width);
      return generateSpectrum2D(mergeClosePeaks(peaks), {
        generator: {
          from: ensureXYNumber(from),
          to: ensureXYNumber(to),
          nbPoints: ensureXYNumber(nbPoints)
        },
        peaks: {
          width,
          factor
        }
      });
    }
    function ensureXYNumber(input) {
      return typeof input !== 'object' ? {
        x: input,
        y: input
      } : {
        ...input
      };
    }
    function mergeClosePeaks(peaksIn) {
      const peaks = JSON.parse(JSON.stringify(peaksIn));
      for (let i = 0; i < peaks.x.length; i++) {
        for (let j = i + 1; j < peaks.x.length; j++) {
          if (peaks.x[i] === peaks.x[j] && peaks.y[i] === peaks.y[j]) {
            peaks.z[i] += peaks.z[j];
            peaks.x.splice(j, 1);
            peaks.y.splice(j, 1);
            peaks.z.splice(j, 1);
            j--;
          }
        }
      }
      return peaks;
    }

    /**
     * Created by acastillo on 8/8/16.
     */
    const defOptions = {
      threshold: 0,
      out: "assignment"
    };
    //TODO Consider a matrix of distances too
    var src$2 = function fullClusterGenerator(conMat, opt) {
      const options = Object.assign({}, defOptions, opt);
      var clList, i, j, k;
      if (typeof conMat[0] === "number") {
        clList = fullClusterGeneratorVector(conMat);
      } else {
        if (typeof conMat[0] === "object") {
          var nRows = conMat.length;
          var conn = new Array(nRows * (nRows + 1) / 2);
          var index = 0;
          for (var i = 0; i < nRows; i++) {
            for (var j = i; j < nRows; j++) {
              if (conMat[i][j] > options.threshold) conn[index++] = 1;else conn[index++] = 0;
            }
          }
          clList = fullClusterGeneratorVector(conn);
        }
      }
      if (options.out === "indexes" || options.out === "values") {
        var result = new Array(clList.length);
        for (i = 0; i < clList.length; i++) {
          result[i] = [];
          for (j = 0; j < clList[i].length; j++) {
            if (clList[i][j] != 0) {
              result[i].push(j);
            }
          }
        }
        if (options.out === "values") {
          var resultAsMatrix = new Array(result.length);
          for (i = 0; i < result.length; i++) {
            resultAsMatrix[i] = new Array(result[i].length);
            for (j = 0; j < result[i].length; j++) {
              resultAsMatrix[i][j] = new Array(result[i].length);
              for (k = 0; k < result[i].length; k++) {
                resultAsMatrix[i][j][k] = conMat[result[i][j]][result[i][k]];
              }
            }
          }
          return resultAsMatrix;
        } else {
          return result;
        }
      }
      return clList;
    };
    function fullClusterGeneratorVector(conn) {
      var nRows = Math.sqrt(conn.length * 2 + 0.25) - 0.5;
      var clusterList = [];
      var available = new Array(nRows);
      var remaining = nRows,
        i = 0;
      var cluster = [];
      //Mark all the elements as available
      for (i = nRows - 1; i >= 0; i--) {
        available[i] = 1;
      }
      var nextAv = -1;
      var toInclude = [];
      while (remaining > 0) {
        if (toInclude.length === 0) {
          //If there is no more elements to include. Start a new cluster
          cluster = new Array(nRows);
          for (i = 0; i < nRows; i++) cluster[i] = 0;
          clusterList.push(cluster);
          for (nextAv = 0; available[nextAv] == 0; nextAv++) {}
        } else {
          nextAv = toInclude.splice(0, 1);
        }
        cluster[nextAv] = 1;
        available[nextAv] = 0;
        remaining--;
        //Copy the next available row
        var row = new Array(nRows);
        for (i = 0; i < nRows; i++) {
          var c = Math.max(nextAv, i);
          var r = Math.min(nextAv, i);
          //The element in the conn matrix
          //console.log("index: "+r*(2*nRows-r-1)/2+c)
          row[i] = conn[r * (2 * nRows - r - 1) / 2 + c];
          //There is new elements to include in this row?
          //Then, include it to the current cluster
          if (row[i] == 1 && available[i] == 1 && cluster[i] == 0) {
            toInclude.push(i);
            cluster[i] = 1;
          }
        }
      }
      return clusterList;
    }
    var simpleClustering = src$2;

    function ensureSpinSystemProps(spinSystem) {
      const {
        couplingConstants,
        connectivity
      } = spinSystem;
      const nCouplingConstants = Matrix.checkMatrix(couplingConstants);
      const nConnectivity = connectivity ? Matrix.checkMatrix(connectivity) : connectivityMatrixFromCouplingMatrix(nCouplingConstants);
      const {
        levels = new Array(nConnectivity.rows).fill(2),
        clusters = simpleClustering(nConnectivity.to2DArray(), {
          out: 'indexes'
        })
      } = spinSystem;
      return {
        ...spinSystem,
        levels,
        clusters,
        connectivity: nConnectivity,
        couplingConstants: nCouplingConstants
      };
    }
    function connectivityMatrixFromCouplingMatrix(couplingConstants) {
      const connectivity = Matrix.ones(couplingConstants.rows, couplingConstants.rows);
      for (let i = 0; i < couplingConstants.rows; i++) {
        for (let j = i; j < couplingConstants.columns; j++) {
          if (couplingConstants.get(i, j) === 0) {
            connectivity.set(i, j, 0);
            connectivity.set(j, i, 0);
          }
        }
      }
      return connectivity;
    }

    function checkForMandatory$1(signals) {
      for (const signal of signals) {
        if (!signal.js) throw new Error('there is not js');
        if (!signal.atoms) throw new Error('there is not atoms');
        for (const jcoupling of signal.js) {
          if (!jcoupling.atoms) throw new Error('there is not atoms');
        }
      }
    }
    function signalsToSpinSystem(signals) {
      checkForMandatory$1(signals);
      const nSpins = signals.length;
      const chemicalShifts = new Array(nSpins);
      const couplingConstants = Matrix.zeros(nSpins, nSpins);
      //create a list of assignments
      const ids = {};
      for (let i = 0; i < nSpins; i++) {
        chemicalShifts[i] = signals[i].delta;
        const index = signals[i].atoms[0];
        ids[index] = i;
      }
      //create the coupling matrix
      for (let i = 0; i < nSpins; i++) {
        const {
          atoms: signalAssignment,
          js: jCoupling
        } = signals[i];
        const fromIndex = signalAssignment[0];
        for (const jcoupling of jCoupling) {
          const {
            coupling,
            atoms
          } = jcoupling;
          const toIndex = atoms[0];
          couplingConstants.set(ids[fromIndex], ids[toIndex], coupling);
          couplingConstants.set(ids[toIndex], ids[fromIndex], coupling);
        }
      }
      return ensureSpinSystemProps({
        couplingConstants,
        chemicalShifts
      });
    }

    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;

    var numSort = {};

    function assertNumber(number) {
      if (typeof number !== 'number') {
        throw new TypeError('Expected a number');
      }
    }
    var ascending = numSort.ascending = (left, right) => {
      assertNumber(left);
      assertNumber(right);
      if (Number.isNaN(left)) {
        return -1;
      }
      if (Number.isNaN(right)) {
        return 1;
      }
      return left - right;
    };
    numSort.descending = (left, right) => {
      assertNumber(left);
      assertNumber(right);
      if (Number.isNaN(left)) {
        return 1;
      }
      if (Number.isNaN(right)) {
        return -1;
      }
      return right - left;
    };

    const largestPrime = 0x7fffffff;
    const primeNumbers = [
    // chunk #0
    largestPrime,
    // 2^31-1

    // chunk #1
    5, 11, 23, 47, 97, 197, 397, 797, 1597, 3203, 6421, 12853, 25717, 51437, 102877, 205759, 411527, 823117, 1646237, 3292489, 6584983, 13169977, 26339969, 52679969, 105359939, 210719881, 421439783, 842879579, 1685759167,
    // chunk #2
    433, 877, 1759, 3527, 7057, 14143, 28289, 56591, 113189, 226379, 452759, 905551, 1811107, 3622219, 7244441, 14488931, 28977863, 57955739, 115911563, 231823147, 463646329, 927292699, 1854585413,
    // chunk #3
    953, 1907, 3821, 7643, 15287, 30577, 61169, 122347, 244703, 489407, 978821, 1957651, 3915341, 7830701, 15661423, 31322867, 62645741, 125291483, 250582987, 501165979, 1002331963, 2004663929,
    // chunk #4
    1039, 2081, 4177, 8363, 16729, 33461, 66923, 133853, 267713, 535481, 1070981, 2141977, 4283963, 8567929, 17135863, 34271747, 68543509, 137087021, 274174111, 548348231, 1096696463,
    // chunk #5
    31, 67, 137, 277, 557, 1117, 2237, 4481, 8963, 17929, 35863, 71741, 143483, 286973, 573953, 1147921, 2295859, 4591721, 9183457, 18366923, 36733847, 73467739, 146935499, 293871013, 587742049, 1175484103,
    // chunk #6
    599, 1201, 2411, 4831, 9677, 19373, 38747, 77509, 155027, 310081, 620171, 1240361, 2480729, 4961459, 9922933, 19845871, 39691759, 79383533, 158767069, 317534141, 635068283, 1270136683,
    // chunk #7
    311, 631, 1277, 2557, 5119, 10243, 20507, 41017, 82037, 164089, 328213, 656429, 1312867, 2625761, 5251529, 10503061, 21006137, 42012281, 84024581, 168049163, 336098327, 672196673, 1344393353,
    // chunk #8
    3, 7, 17, 37, 79, 163, 331, 673, 1361, 2729, 5471, 10949, 21911, 43853, 87719, 175447, 350899, 701819, 1403641, 2807303, 5614657, 11229331, 22458671, 44917381, 89834777, 179669557, 359339171, 718678369, 1437356741,
    // chunk #9
    43, 89, 179, 359, 719, 1439, 2879, 5779, 11579, 23159, 46327, 92657, 185323, 370661, 741337, 1482707, 2965421, 5930887, 11861791, 23723597, 47447201, 94894427, 189788857, 379577741, 759155483, 1518310967,
    // chunk #10
    379, 761, 1523, 3049, 6101, 12203, 24407, 48817, 97649, 195311, 390647, 781301, 1562611, 3125257, 6250537, 12501169, 25002389, 50004791, 100009607, 200019221, 400038451, 800076929, 1600153859,
    // chunk #11
    13, 29, 59, 127, 257, 521, 1049, 2099, 4201, 8419, 16843, 33703, 67409, 134837, 269683, 539389, 1078787, 2157587, 4315183, 8630387, 17260781, 34521589, 69043189, 138086407, 276172823, 552345671, 1104691373,
    // chunk #12
    19, 41, 83, 167, 337, 677, 1361, 2729, 5471, 10949, 21911, 43853, 87719, 175447, 350899, 701819, 1403641, 2807303, 5614657, 11229331, 22458671, 44917381, 89834777, 179669557, 359339171, 718678369, 1437356741,
    // chunk #13
    53, 107, 223, 449, 907, 1823, 3659, 7321, 14653, 29311, 58631, 117269, 234539, 469099, 938207, 1876417, 3752839, 7505681, 15011389, 30022781, 60045577, 120091177, 240182359, 480364727, 960729461, 1921458943];
    primeNumbers.sort(ascending);
    function nextPrime(value) {
      let index = binarySearch$1(primeNumbers, value, ascending);
      if (index < 0) {
        index = ~index;
      }
      return primeNumbers[index];
    }

    const FREE = 0;
    const FULL = 1;
    const REMOVED = 2;
    const defaultInitialCapacity = 150;
    const defaultMinLoadFactor = 1 / 6;
    const defaultMaxLoadFactor = 2 / 3;
    class HashTable {
      constructor(options = {}) {
        if (options instanceof HashTable) {
          this.table = options.table.slice();
          this.values = options.values.slice();
          this.state = options.state.slice();
          this.minLoadFactor = options.minLoadFactor;
          this.maxLoadFactor = options.maxLoadFactor;
          this.distinct = options.distinct;
          this.freeEntries = options.freeEntries;
          this.lowWaterMark = options.lowWaterMark;
          this.highWaterMark = options.maxLoadFactor;
          return;
        }
        const initialCapacity = options.initialCapacity === undefined ? defaultInitialCapacity : options.initialCapacity;
        if (initialCapacity < 0) {
          throw new RangeError(`initial capacity must not be less than zero: ${initialCapacity}`);
        }
        const minLoadFactor = options.minLoadFactor === undefined ? defaultMinLoadFactor : options.minLoadFactor;
        const maxLoadFactor = options.maxLoadFactor === undefined ? defaultMaxLoadFactor : options.maxLoadFactor;
        if (minLoadFactor < 0 || minLoadFactor >= 1) {
          throw new RangeError(`invalid minLoadFactor: ${minLoadFactor}`);
        }
        if (maxLoadFactor <= 0 || maxLoadFactor >= 1) {
          throw new RangeError(`invalid maxLoadFactor: ${maxLoadFactor}`);
        }
        if (minLoadFactor >= maxLoadFactor) {
          throw new RangeError(`minLoadFactor (${minLoadFactor}) must be smaller than maxLoadFactor (${maxLoadFactor})`);
        }
        let capacity = initialCapacity;
        // User wants to put at least capacity elements. We need to choose the size based on the maxLoadFactor to
        // avoid the need to rehash before this capacity is reached.
        // actualCapacity * maxLoadFactor >= capacity
        capacity = capacity / maxLoadFactor | 0;
        capacity = nextPrime(capacity);
        if (capacity === 0) capacity = 1;
        this.table = newArray(capacity);
        this.values = newArray(capacity);
        this.state = newArray(capacity);
        this.minLoadFactor = minLoadFactor;
        if (capacity === largestPrime) {
          this.maxLoadFactor = 1;
        } else {
          this.maxLoadFactor = maxLoadFactor;
        }
        this.distinct = 0;
        this.freeEntries = capacity;
        this.lowWaterMark = 0;
        this.highWaterMark = chooseHighWaterMark(capacity, this.maxLoadFactor);
      }
      clone() {
        return new HashTable(this);
      }
      get size() {
        return this.distinct;
      }
      get(key) {
        const i = this.indexOfKey(key);
        if (i < 0) return 0;
        return this.values[i];
      }
      set(key, value) {
        let i = this.indexOfInsertion(key);
        if (i < 0) {
          i = -i - 1;
          this.values[i] = value;
          return false;
        }
        if (this.distinct > this.highWaterMark) {
          const newCapacity = chooseGrowCapacity(this.distinct + 1, this.minLoadFactor, this.maxLoadFactor);
          this.rehash(newCapacity);
          return this.set(key, value);
        }
        this.table[i] = key;
        this.values[i] = value;
        if (this.state[i] === FREE) this.freeEntries--;
        this.state[i] = FULL;
        this.distinct++;
        if (this.freeEntries < 1) {
          const newCapacity = chooseGrowCapacity(this.distinct + 1, this.minLoadFactor, this.maxLoadFactor);
          this.rehash(newCapacity);
        }
        return true;
      }
      remove(key, noRehash) {
        const i = this.indexOfKey(key);
        if (i < 0) return false;
        this.state[i] = REMOVED;
        this.distinct--;
        if (!noRehash) this.maybeShrinkCapacity();
        return true;
      }
      delete(key, noRehash) {
        const i = this.indexOfKey(key);
        if (i < 0) return false;
        this.state[i] = FREE;
        this.distinct--;
        if (!noRehash) this.maybeShrinkCapacity();
        return true;
      }
      maybeShrinkCapacity() {
        if (this.distinct < this.lowWaterMark) {
          const newCapacity = chooseShrinkCapacity(this.distinct, this.minLoadFactor, this.maxLoadFactor);
          this.rehash(newCapacity);
        }
      }
      containsKey(key) {
        return this.indexOfKey(key) >= 0;
      }
      indexOfKey(key) {
        const table = this.table;
        const state = this.state;
        const length = this.table.length;
        const hash = key & 0x7fffffff;
        let i = hash % length;
        let decrement = hash % (length - 2);
        if (decrement === 0) decrement = 1;
        while (state[i] !== FREE && (state[i] === REMOVED || table[i] !== key)) {
          i -= decrement;
          if (i < 0) i += length;
        }
        if (state[i] === FREE) return -1;
        return i;
      }
      containsValue(value) {
        return this.indexOfValue(value) >= 0;
      }
      indexOfValue(value) {
        const values = this.values;
        const state = this.state;
        for (var i = 0; i < state.length; i++) {
          if (state[i] === FULL && values[i] === value) {
            return i;
          }
        }
        return -1;
      }
      indexOfInsertion(key) {
        const table = this.table;
        const state = this.state;
        const length = table.length;
        const hash = key & 0x7fffffff;
        let i = hash % length;
        let decrement = hash % (length - 2);
        if (decrement === 0) decrement = 1;
        while (state[i] === FULL && table[i] !== key) {
          i -= decrement;
          if (i < 0) i += length;
        }
        if (state[i] === REMOVED) {
          const j = i;
          while (state[i] !== FREE && (state[i] === REMOVED || table[i] !== key)) {
            i -= decrement;
            if (i < 0) i += length;
          }
          if (state[i] === FREE) i = j;
        }
        if (state[i] === FULL) {
          return -i - 1;
        }
        return i;
      }
      ensureCapacity(minCapacity) {
        if (this.table.length < minCapacity) {
          const newCapacity = nextPrime(minCapacity);
          this.rehash(newCapacity);
        }
      }
      rehash(newCapacity) {
        const oldCapacity = this.table.length;
        if (newCapacity <= this.distinct) throw new Error('Unexpected');
        const oldTable = this.table;
        const oldValues = this.values;
        const oldState = this.state;
        const newTable = newArray(newCapacity);
        const newValues = newArray(newCapacity);
        const newState = newArray(newCapacity);
        this.lowWaterMark = chooseLowWaterMark(newCapacity, this.minLoadFactor);
        this.highWaterMark = chooseHighWaterMark(newCapacity, this.maxLoadFactor);
        this.table = newTable;
        this.values = newValues;
        this.state = newState;
        this.freeEntries = newCapacity - this.distinct;
        for (var i = 0; i < oldCapacity; i++) {
          if (oldState[i] === FULL) {
            var element = oldTable[i];
            var index = this.indexOfInsertion(element);
            newTable[index] = element;
            newValues[index] = oldValues[i];
            newState[index] = FULL;
          }
        }
      }
      forEachKey(callback) {
        for (var i = 0; i < this.state.length; i++) {
          if (this.state[i] === FULL) {
            if (!callback(this.table[i])) return false;
          }
        }
        return true;
      }
      forEachValue(callback) {
        for (var i = 0; i < this.state.length; i++) {
          if (this.state[i] === FULL) {
            if (!callback(this.values[i])) return false;
          }
        }
        return true;
      }
      forEachPair(callback) {
        for (var i = 0; i < this.state.length; i++) {
          if (this.state[i] === FULL) {
            if (!callback(this.table[i], this.values[i])) return false;
          }
        }
        return true;
      }
    }
    function chooseLowWaterMark(capacity, minLoad) {
      return capacity * minLoad | 0;
    }
    function chooseHighWaterMark(capacity, maxLoad) {
      return Math.min(capacity - 2, capacity * maxLoad | 0);
    }
    function chooseGrowCapacity(size, minLoad, maxLoad) {
      return nextPrime(Math.max(size + 1, 4 * size / (3 * minLoad + maxLoad) | 0));
    }
    function chooseShrinkCapacity(size, minLoad, maxLoad) {
      return nextPrime(Math.max(size + 1, 4 * size / (minLoad + 3 * maxLoad) | 0));
    }
    function newArray(size) {
      return Array(size).fill(0);
    }

    /* eslint-disable no-eval */
    class SparseMatrix {
      constructor(rows, columns, options = {}) {
        if (rows instanceof SparseMatrix) {
          // clone
          const other = rows;
          this._init(other.rows, other.columns, other.elements.clone(), other.threshold);
          return;
        }
        if (Array.isArray(rows)) {
          const matrix = rows;
          rows = matrix.length;
          options = columns || {};
          columns = matrix[0].length;
          this._init(rows, columns, new HashTable(options), options.threshold);
          for (let i = 0; i < rows; i++) {
            for (let j = 0; j < columns; j++) {
              let value = matrix[i][j];
              if (this.threshold && Math.abs(value) < this.threshold) value = 0;
              if (value !== 0) {
                this.elements.set(i * columns + j, matrix[i][j]);
              }
            }
          }
        } else {
          this._init(rows, columns, new HashTable(options), options.threshold);
        }
      }
      _init(rows, columns, elements, threshold) {
        this.rows = rows;
        this.columns = columns;
        this.elements = elements;
        this.threshold = threshold || 0;
      }
      static eye(rows = 1, columns = rows) {
        const min = Math.min(rows, columns);
        const matrix = new SparseMatrix(rows, columns, {
          initialCapacity: min
        });
        for (let i = 0; i < min; i++) {
          matrix.set(i, i, 1);
        }
        return matrix;
      }
      clone() {
        return new SparseMatrix(this);
      }
      to2DArray() {
        const copy = new Array(this.rows);
        for (let i = 0; i < this.rows; i++) {
          copy[i] = new Array(this.columns);
          for (let j = 0; j < this.columns; j++) {
            copy[i][j] = this.get(i, j);
          }
        }
        return copy;
      }
      isSquare() {
        return this.rows === this.columns;
      }
      isSymmetric() {
        if (!this.isSquare()) return false;
        let symmetric = true;
        this.forEachNonZero((i, j, v) => {
          if (this.get(j, i) !== v) {
            symmetric = false;
            return false;
          }
          return v;
        });
        return symmetric;
      }

      /**
       * Search for the wither band in the main diagonals
       * @return {number}
       */
      bandWidth() {
        let min = this.columns;
        let max = -1;
        this.forEachNonZero((i, j, v) => {
          let diff = i - j;
          min = Math.min(min, diff);
          max = Math.max(max, diff);
          return v;
        });
        return max - min;
      }

      /**
       * Test if a matrix is consider banded using a threshold
       * @param {number} width
       * @return {boolean}
       */
      isBanded(width) {
        let bandWidth = this.bandWidth();
        return bandWidth <= width;
      }
      get cardinality() {
        return this.elements.size;
      }
      get size() {
        return this.rows * this.columns;
      }
      get(row, column) {
        return this.elements.get(row * this.columns + column);
      }
      set(row, column, value) {
        if (this.threshold && Math.abs(value) < this.threshold) value = 0;
        if (value === 0) {
          this.elements.remove(row * this.columns + column);
        } else {
          this.elements.set(row * this.columns + column, value);
        }
        return this;
      }
      mmul(other) {
        if (this.columns !== other.rows) {
          // eslint-disable-next-line no-console
          console.warn('Number of columns of left matrix are not equal to number of rows of right matrix.');
        }
        const m = this.rows;
        const p = other.columns;
        const result = new SparseMatrix(m, p);
        this.forEachNonZero((i, j, v1) => {
          other.forEachNonZero((k, l, v2) => {
            if (j === k) {
              result.set(i, l, result.get(i, l) + v1 * v2);
            }
            return v2;
          });
          return v1;
        });
        return result;
      }
      kroneckerProduct(other) {
        const m = this.rows;
        const n = this.columns;
        const p = other.rows;
        const q = other.columns;
        const result = new SparseMatrix(m * p, n * q, {
          initialCapacity: this.cardinality * other.cardinality
        });
        this.forEachNonZero((i, j, v1) => {
          other.forEachNonZero((k, l, v2) => {
            result.set(p * i + k, q * j + l, v1 * v2);
            return v2;
          });
          return v1;
        });
        return result;
      }
      forEachNonZero(callback) {
        this.elements.forEachPair((key, value) => {
          const i = key / this.columns | 0;
          const j = key % this.columns;
          let r = callback(i, j, value);
          if (r === false) return false; // stop iteration
          if (this.threshold && Math.abs(r) < this.threshold) r = 0;
          if (r !== value) {
            if (r === 0) {
              this.elements.remove(key, true);
            } else {
              this.elements.set(key, r);
            }
          }
          return true;
        });
        this.elements.maybeShrinkCapacity();
        return this;
      }
      getNonZeros() {
        const cardinality = this.cardinality;
        const rows = new Array(cardinality);
        const columns = new Array(cardinality);
        const values = new Array(cardinality);
        let idx = 0;
        this.forEachNonZero((i, j, value) => {
          rows[idx] = i;
          columns[idx] = j;
          values[idx] = value;
          idx++;
          return value;
        });
        return {
          rows,
          columns,
          values
        };
      }
      setThreshold(newThreshold) {
        if (newThreshold !== 0 && newThreshold !== this.threshold) {
          this.threshold = newThreshold;
          this.forEachNonZero((i, j, v) => v);
        }
        return this;
      }

      /**
       * @return {SparseMatrix} - New transposed sparse matrix
       */
      transpose() {
        let trans = new SparseMatrix(this.columns, this.rows, {
          initialCapacity: this.cardinality
        });
        this.forEachNonZero((i, j, value) => {
          trans.set(j, i, value);
          return value;
        });
        return trans;
      }
      isEmpty() {
        return this.rows === 0 || this.columns === 0;
      }
    }
    SparseMatrix.prototype.klass = 'Matrix';
    SparseMatrix.identity = SparseMatrix.eye;
    SparseMatrix.prototype.tensorProduct = SparseMatrix.prototype.kroneckerProduct;

    /*
     Add dynamically instance and static methods for mathematical operations
     */

    let inplaceOperator = `
(function %name%(value) {
    if (typeof value === 'number') return this.%name%S(value);
    return this.%name%M(value);
})
`;
    let inplaceOperatorScalar = `
(function %name%S(value) {
    this.forEachNonZero((i, j, v) => v %op% value);
    return this;
})
`;
    let inplaceOperatorMatrix = `
(function %name%M(matrix) {
    matrix.forEachNonZero((i, j, v) => {
        this.set(i, j, this.get(i, j) %op% v);
        return v;
    });
    return this;
})
`;
    let staticOperator = `
(function %name%(matrix, value) {
    var newMatrix = new SparseMatrix(matrix);
    return newMatrix.%name%(value);
})
`;
    let inplaceMethod = `
(function %name%() {
    this.forEachNonZero((i, j, v) => %method%(v));
    return this;
})
`;
    let staticMethod = `
(function %name%(matrix) {
    var newMatrix = new SparseMatrix(matrix);
    return newMatrix.%name%();
})
`;
    const operators = [
    // Arithmetic operators
    ['+', 'add'], ['-', 'sub', 'subtract'], ['*', 'mul', 'multiply'], ['/', 'div', 'divide'], ['%', 'mod', 'modulus'],
    // Bitwise operators
    ['&', 'and'], ['|', 'or'], ['^', 'xor'], ['<<', 'leftShift'], ['>>', 'signPropagatingRightShift'], ['>>>', 'rightShift', 'zeroFillRightShift']];
    for (const operator of operators) {
      for (let i = 1; i < operator.length; i++) {
        SparseMatrix.prototype[operator[i]] = eval(fillTemplateFunction(inplaceOperator, {
          name: operator[i],
          op: operator[0]
        }));
        SparseMatrix.prototype[`${operator[i]}S`] = eval(fillTemplateFunction(inplaceOperatorScalar, {
          name: `${operator[i]}S`,
          op: operator[0]
        }));
        SparseMatrix.prototype[`${operator[i]}M`] = eval(fillTemplateFunction(inplaceOperatorMatrix, {
          name: `${operator[i]}M`,
          op: operator[0]
        }));
        SparseMatrix[operator[i]] = eval(fillTemplateFunction(staticOperator, {
          name: operator[i]
        }));
      }
    }
    let methods = [['~', 'not']];
    ['abs', 'acos', 'acosh', 'asin', 'asinh', 'atan', 'atanh', 'cbrt', 'ceil', 'clz32', 'cos', 'cosh', 'exp', 'expm1', 'floor', 'fround', 'log', 'log1p', 'log10', 'log2', 'round', 'sign', 'sin', 'sinh', 'sqrt', 'tan', 'tanh', 'trunc'].forEach(function (mathMethod) {
      methods.push([`Math.${mathMethod}`, mathMethod]);
    });
    for (const method of methods) {
      for (let i = 1; i < method.length; i++) {
        SparseMatrix.prototype[method[i]] = eval(fillTemplateFunction(inplaceMethod, {
          name: method[i],
          method: method[0]
        }));
        SparseMatrix[method[i]] = eval(fillTemplateFunction(staticMethod, {
          name: method[i]
        }));
      }
    }
    function fillTemplateFunction(template, values) {
      for (const i in values) {
        template = template.replace(new RegExp(`%${i}%`, 'g'), values[i]);
      }
      return template;
    }

    function createPauli(mult) {
      const spin = (mult - 1) / 2;
      const prjs = new Array(mult);
      const temp = new Array(mult);
      for (let i = 0; i < mult; i++) {
        prjs[i] = mult - 1 - i - spin;
        temp[i] = Math.sqrt(spin * (spin + 1) - prjs[i] * (prjs[i] + 1));
      }
      const p = diag(temp, 1, mult, mult);
      for (let i = 0; i < mult; i++) {
        temp[i] = Math.sqrt(spin * (spin + 1) - prjs[i] * (prjs[i] - 1));
      }
      const m = diag(temp, -1, mult, mult);
      const x = p.clone().add(m).mul(0.5);
      const y = m.clone().mul(-1).add(p).mul(-0.5);
      const z = diag(prjs, 0, mult, mult);
      return {
        x,
        y,
        z,
        m,
        p
      };
    }
    function diag(A, d, n, m) {
      const diag = new SparseMatrix(n, m, {
        initialCapacity: 20
      });
      for (let i = 0; i < A.length; i++) {
        if (i - d >= 0 && i - d < n && i < m) {
          diag.set(i - d, i, A[i]);
        }
      }
      return diag;
    }
    const pauli2 = createPauli(2);
    function getPauliMatrix(mult) {
      if (mult === 2) return pauli2;else return createPauli(mult);
    }

    const smallValue = 1e-2;
    function simulateXYPeaks(
    /**
     * The SpinSystem object to be simulated
     */
    spinSystemInput, options = {}) {
      const {
        lineWidth = 1,
        maxClusterSize = 8,
        frequency: frequencyMHz = 400
      } = options;
      const spinSystem = ensureSpinSystemProps(spinSystemInput);
      const chemicalShifts = spinSystem.chemicalShifts.slice();
      for (let i = 0; i < chemicalShifts.length; i++) {
        chemicalShifts[i] = chemicalShifts[i] * frequencyMHz;
      }
      const levels = spinSystem.levels;
      const xyPeaks = [];
      for (const cluster of spinSystem.clusters) {
        const clusterFake = cluster.map(cluster => cluster < 0 ? -cluster - 1 : cluster);
        let weight = 1;
        let sumI = 0;
        const frequencies = [];
        const intensities = [];
        if (cluster.length > maxClusterSize) {
          // This is a single spin, but the cluster exceeds the maxClusterSize criteria
          // we use the simple multiplicity algorithm
          // Add the central peak. It will be split with every single J coupling.
          let index = 0;
          while (cluster[index++] < 0);
          index = cluster[index - 1];
          frequencies.push(-chemicalShifts[index]);
          for (let i = 0; i < cluster.length; i++) {
            if (cluster[i] < 0) {
              const jc = spinSystem.couplingConstants.get(index, clusterFake[i]) / 2;
              const currentSize = frequencies.length;
              for (let j = 0; j < currentSize; j++) {
                frequencies.push(frequencies[j] + jc);
                frequencies[j] -= jc;
              }
            }
          }
          frequencies.sort((a, b) => a - b);
          sumI = frequencies.length;
          weight = 1;
          for (let i = 0; i < sumI; i++) {
            intensities.push(1);
          }
        } else {
          const hamiltonian = getHamiltonian(chemicalShifts, spinSystem.couplingConstants, levels, spinSystem.connectivity, clusterFake);
          const hamSize = hamiltonian.rows;
          // TODO: add support for sparse matrix in matrix types.
          // @ts-expect-error sparse matrix not supported
          const evd = new EigenvalueDecomposition(hamiltonian);
          const V = evd.eigenvectorMatrix;
          const diagB = evd.realEigenvalues;
          const assignmentMatrix = new SparseMatrix(hamSize, hamSize);
          const multLen = cluster.length;
          weight = 0;
          for (let n = 0; n < multLen; n++) {
            const L = getPauliMatrix(levels[clusterFake[n]]);
            let temp = 1;
            for (let j = 0; j < n; j++) {
              temp *= levels[clusterFake[j]];
            }
            const A = SparseMatrix.eye(temp);
            temp = 1;
            for (let j = n + 1; j < multLen; j++) {
              temp *= levels[clusterFake[j]];
            }
            const B = SparseMatrix.eye(temp);
            const tempMat = A.kroneckerProduct(L.m).kroneckerProduct(B);
            if (cluster[n] >= 0) {
              assignmentMatrix.add(tempMat.mul(cluster[n] + 1));
              weight++;
            } else {
              assignmentMatrix.add(tempMat.mul(cluster[n]));
            }
          }
          let rhoip = Matrix.zeros(hamSize, hamSize);
          assignmentMatrix.forEachNonZero((i, j, v) => {
            if (v > 0) {
              for (let k = 0; k < V.columns; k++) {
                const element = V.get(j, k);
                if (element !== 0) {
                  rhoip.set(i, k, rhoip.get(i, k) + element);
                }
              }
            }
            return v;
          });
          let rhoip2 = rhoip.clone();
          assignmentMatrix.forEachNonZero((i, j, v) => {
            if (v < 0) {
              for (let k = 0; k < V.columns; k++) {
                const element = V.get(j, k);
                if (element !== 0) {
                  rhoip2.set(i, k, rhoip2.get(i, k) + element);
                }
              }
            }
            return v;
          });
          const tV = V.transpose();
          rhoip = tV.mmul(rhoip);
          const sparseRhoip = new SparseMatrix(rhoip.to2DArray(), {
            threshold: smallValue
          });
          triuTimesAbs(sparseRhoip, smallValue);
          rhoip2 = tV.mmul(rhoip2);
          const sparseRhoip2 = new SparseMatrix(rhoip2.to2DArray(), {
            threshold: smallValue
          });
          sparseRhoip2.forEachNonZero((i, j, v) => {
            return v;
          });
          triuTimesAbs(sparseRhoip2, smallValue);
          sparseRhoip2.forEachNonZero((i, j, v) => {
            let val = rhoip.get(i, j);
            val = Math.min(Math.abs(val), Math.abs(v));
            val *= val;
            sumI += val;
            const valFreq = diagB[i] - diagB[j];
            const insertIn = binarySearch$1(frequencies, valFreq, (a, b) => a - b);
            if (insertIn < 0) {
              frequencies.splice(-1 - insertIn, 0, valFreq);
              intensities.splice(-1 - insertIn, 0, val);
            } else {
              intensities[insertIn] += val;
            }
          });
        }
        const numFreq = frequencies.length;
        if (numFreq > 0) {
          weight /= sumI;
          const diff = lineWidth / 64;
          let valFreq = frequencies[0];
          let inte = intensities[0];
          let count = 1;
          for (let i = 1; i < numFreq; i++) {
            if (Math.abs(frequencies[i] - valFreq / count) < diff) {
              inte += intensities[i];
              valFreq += frequencies[i];
              count++;
            } else {
              xyPeaks.push({
                x: -valFreq / count / frequencyMHz,
                y: inte * weight
              });
              valFreq = frequencies[i];
              inte = intensities[i];
              count = 1;
            }
          }
          xyPeaks.push({
            x: -valFreq / count / frequencyMHz,
            y: inte * weight
          });
        }
      }
      return xyPeaks.filter(peak => peak.x < 1000);
    }
    function triuTimesAbs(A, val) {
      A.forEachNonZero((i, j, v) => {
        if (Math.abs(v) <= val) return 0;
        return v;
      });
    }
    /**
     * Create a hamiltonian matrix for the given spinsystem
     * @param {Array} chemicalShifts - An array containing the chemical shift in Hz
     * @param {Array} couplingConstants - An array containing the coupling constants in Hz
     * @param {Array} multiplicity - An array specifiying the multiplicities of each scalar coupling
     * @param {Array} conMatrix - A one step connectivity matrix for the given spin system
     * @param {Array} cluster - An binary array specifiying the spins to be considered for this hamiltonial
     * @return {object}
     */
    function getHamiltonian(chemicalShifts, couplingConstants, multiplicity, conMatrix, cluster) {
      let hamSize = 1;
      for (const element of cluster) {
        hamSize *= multiplicity[element];
      }
      const clusterHam = new SparseMatrix(hamSize, hamSize);
      for (let pos = 0; pos < cluster.length; pos++) {
        const n = cluster[pos];
        const L = getPauliMatrix(multiplicity[n]);
        let temp = 1;
        for (let i = 0; i < pos; i++) {
          temp *= multiplicity[cluster[i]];
        }
        const A1 = SparseMatrix.eye(temp);
        temp = 1;
        for (let i = pos + 1; i < cluster.length; i++) {
          temp *= multiplicity[cluster[i]];
        }
        const B1 = SparseMatrix.eye(temp);
        const alpha = chemicalShifts[n];
        const kronProd = A1.kroneckerProduct(L.z).kroneckerProduct(B1);
        clusterHam.add(kronProd.mul(alpha));
        for (let pos2 = 0; pos2 < cluster.length; pos2++) {
          const k = cluster[pos2];
          if (conMatrix.get(n, k) === 1) {
            const S = getPauliMatrix(multiplicity[k]);
            let temp = 1;
            for (let i = 0; i < pos2; i++) {
              temp *= multiplicity[cluster[i]];
            }
            const A2 = SparseMatrix.eye(temp);
            temp = 1;
            for (let i = pos2 + 1; i < cluster.length; i++) {
              temp *= multiplicity[cluster[i]];
            }
            const B2 = SparseMatrix.eye(temp);
            const kron1 = A1.kroneckerProduct(L.x).kroneckerProduct(B1).mmul(A2.kroneckerProduct(S.x).kroneckerProduct(B2));
            kron1.add(A1.kroneckerProduct(L.y).kroneckerProduct(B1).mul(-1).mmul(A2.kroneckerProduct(S.y).kroneckerProduct(B2)));
            kron1.add(A1.kroneckerProduct(L.z).kroneckerProduct(B1).mmul(A2.kroneckerProduct(S.z).kroneckerProduct(B2)));
            clusterHam.add(kron1.mul(couplingConstants.get(n, k) / 2));
          }
        }
      }
      return clusterHam;
    }

    /**
     * This function simulates a one dimensional nmr spectrum. This function returns an array containing the relative intensities of the spectrum in the specified simulation window (from-to).
     */
    function simulate1D(spinSystemInput, options = {}) {
      const {
        lineWidth = 1,
        frequency: frequencyMHz = 400,
        from = 0,
        to = 10,
        nbPoints = 1024,
        shape = {
          kind: 'gaussian'
        }
      } = options;
      const spinSystem = ensureSpinSystemProps(spinSystemInput);
      const peakWidth = lineWidth / frequencyMHz;
      const xyPeaks = simulateXYPeaks(spinSystem, options);
      return generateSpectrum(xyPeaks, {
        generator: {
          from,
          to,
          nbPoints
        },
        peakOptions: {
          shape,
          width: peakWidth
        }
      });
    }

    function splitSpinSystem(spinSystemInput, options = {}) {
      const spinSystem = ensureSpinSystemProps(spinSystemInput);
      const {
        chemicalShifts,
        couplingConstants,
        connectivity
      } = spinSystem;
      const {
        frequency = 400,
        maxClusterSize = 8
      } = options;
      const betas = calculateBetas(chemicalShifts, couplingConstants, frequency);
      const initClusters = agnes(betas, {
        method: 'single',
        isDistanceMatrix: true
      });
      const clusterList = [];
      const nSpins = chemicalShifts.length;
      splitCluster(initClusters, clusterList, {
        maxClusterSize,
        force: false,
        nSpins,
        connectivity
      });
      const mergedClusters = mergeClusters(clusterList, maxClusterSize);
      const nClusters = mergedClusters.length;
      const clusters = new Array(nClusters);
      for (let j = 0; j < nClusters; j++) {
        clusters[j] = [];
        for (let i = 0; i < nSpins; i++) {
          const element = mergedClusters[j][i];
          if (element === 0) continue;
          clusters[j].push(element < 0 ? -(i + 1) : i);
        }
      }
      return clusters;
    }
    function splitCluster(cluster, clusterList, options) {
      const {
        maxClusterSize,
        force,
        nSpins,
        connectivity
      } = options;
      if (!force && cluster.size <= maxClusterSize) {
        clusterList.push(getMembers(cluster.indices(), nSpins));
      } else {
        for (const child of cluster.children) {
          if (child.size <= maxClusterSize) {
            const members = getMembers(child.indices(), nSpins);
            // Add the neighbors that shares at least 1 coupling with the given cluster
            let count = 0;
            for (let i = 0; i < nSpins; i++) {
              if (members[i] === 1) {
                for (let j = 0; j < nSpins; j++) {
                  if (connectivity.get(i, j) === 1 && members[j] === 0) {
                    members[j] = -1;
                    count++;
                  }
                }
                count++;
              }
            }
            if (count <= maxClusterSize) {
              clusterList.push(members);
            } else if (child.index < 0) {
              splitCluster(child, clusterList, {
                maxClusterSize,
                force: true,
                nSpins,
                connectivity
              });
            } else {
              // We have to threat this spin alone and use the resurrection algorithm instead of the simulation
              members[child.index] = 2;
              clusterList.push(members);
            }
          } else {
            splitCluster(child, clusterList, {
              maxClusterSize,
              force: false,
              nSpins,
              connectivity
            });
          }
        }
      }
    }
    function calculateBetas(chemicalShifts, couplingConstants, frequency) {
      const nRows = couplingConstants.rows;
      const nColumns = couplingConstants.columns;
      const betas = Matrix.zeros(nRows, nRows);
      // Before clustering, we must add hidden couplingConstants, we could use molecular information if available
      for (let i = 0; i < nRows; i++) {
        for (let j = i; j < nColumns; j++) {
          const element = couplingConstants.get(i, j);
          if (chemicalShifts[i] - chemicalShifts[j] !== 0) {
            const value = 1 - Math.abs(element / ((chemicalShifts[i] - chemicalShifts[j]) * frequency));
            betas.set(i, j, value);
            betas.set(j, i, value);
          } else if (!(i === j || element !== 0)) {
            betas.set(i, j, 1);
            betas.set(j, i, 1);
          }
        }
      }
      return betas.to2DArray();
    }
    function mergeClusters(list, maxClusterSize) {
      for (let i = list.length - 1; i >= 0; i--) {
        const clusterA = list[i];
        const nElements = clusterA.length;
        let index = 0;
        // Is it a candidate to be merged?
        while (index < nElements && clusterA[index++] !== -1);
        if (index >= nElements) continue;
        for (let j = list.length - 1; j >= i + 1; j--) {
          const clusterB = list[j];
          // Do they have common elements?
          let count = 0;
          let common = 0;
          for (let index = 0; index < nElements; index++) {
            if (clusterA[index] * clusterB[index] === -1) common++;
            if (clusterA[index] !== 0 || clusterB[index] !== 0) count++;
          }
          if (common > 0 && count <= maxClusterSize) {
            // Then we can merge those 2 clusters
            for (let index = 0; index < nElements; index++) {
              if (clusterB[index] === 1) {
                clusterA[index] = 1;
              } else if (clusterB[index] === -1 && clusterA[index] !== 1) {
                clusterA[index] = -1;
              }
            }
            list.splice(j, 1);
          }
        }
      }
      return list;
    }
    function getMembers(cluster, nSpins) {
      const members = new Int16Array(nSpins);
      for (const e of cluster) {
        members[e] = 1;
      }
      return members;
    }

    function checkForMandatory(signals) {
      for (const signal of signals) {
        if (!signal.js) throw new Error('There is not js');
        // if (!signal.diaID) throw new Error('There is not diaID');
        if (!signal.atoms) throw new Error('There is not atoms');
        for (const j of signal.js) {
          // if (!j.diaID) throw new Error('There is not diaID');
          if (!j.atoms) throw new Error('There is not atoms');
        }
      }
    }
    /**
     * Generate a spectrum from an array of singals
     */
    function signalsToXY(signals, options = {}) {
      checkForMandatory(signals);
      const {
        frequency = 400,
        shape = {
          kind: 'gaussian'
        },
        from = 0,
        to = 10,
        lineWidth = 1,
        nbPoints = 16 * 1024,
        maxValue = 1e8,
        maxClusterSize = 8
      } = options;
      if (signals.length === 0) {
        return {
          x: xSequentialFill({
            from,
            to,
            size: nbPoints
          }),
          y: Array.from(new Float64Array(nbPoints))
        };
      }
      const spinSystem = signalsToSpinSystem(signals);
      spinSystem.clusters = splitSpinSystem(spinSystem, {
        frequency,
        maxClusterSize
      });
      const spectrum = simulate1D(spinSystem, {
        frequency,
        from,
        to,
        nbPoints,
        lineWidth,
        maxClusterSize,
        shape
      });
      if (maxValue && xIsMonotonic(spectrum.y) === 0) {
        spectrum.y = xRescale(spectrum.y, {
          max: maxValue
        });
      }
      return spectrum;
    }

    /**
     * calculate the frequency of a nucleus with respect to a reference nucleus
     */
    function getFrequency(
    /**
     * nucleus to calculate the frequency
     */
    nucleus, observedNucleusData) {
      const {
        nucleus: obsNucleus,
        frequency
      } = observedNucleusData;
      checkExistence$1(nucleus, obsNucleus);
      return frequency * gyromagneticRatio[nucleus] / gyromagneticRatio[obsNucleus];
    }
    function checkExistence$1(...nucleus) {
      const nuclei = [];
      for (const n of nucleus) {
        if (!(n in gyromagneticRatio)) {
          nuclei.push(n);
        }
      }
      if (nuclei.length !== 0) {
        throw new Error(`${nuclei.join(' , ')} is not supported`);
      }
    }

    async function predictAllSpectra(molecule, options = {}) {
      const {
        simulation: simulationOptions = {
          oneD: {},
          twoD: {}
        },
        prediction: predictionOptions = {}
      } = options;
      const predictions = await predictAll(molecule, predictionOptions);
      const {
        molfile,
        spectra
      } = predictions;
      const oneDOptions = {
        ...{
          proton: {
            from: 0,
            to: 14
          },
          carbon: {
            from: 0,
            to: 200
          },
          nbPoints: 65536,
          lineWidth: 1
        },
        ...(simulationOptions.oneD || {})
      };
      const twoDOptions = {
        ...{
          from: {
            x: oneDOptions.proton.from,
            y: oneDOptions.carbon.from
          },
          to: {
            x: oneDOptions.proton.to,
            y: oneDOptions.carbon.to
          },
          nbPoints: {
            x: 1024,
            y: 1024
          }
        },
        ...(simulationOptions.twoD || {})
      };
      const newSpectra = [];
      for (const experiment in spectra) {
        switch (experiment) {
          case 'carbon':
          case 'proton':
            {
              newSpectra.push(get1DSpectrum(spectra[experiment], {
                nbPoints: oneDOptions.nbPoints,
                lineWidth: oneDOptions.lineWidth,
                ...oneDOptions[experiment],
                experiment,
                frequency: simulationOptions.frequency
              }));
              break;
            }
          case 'cosy':
          case 'hsqc':
          case 'hmbc':
            {
              newSpectra.push(get2DSpectrum(spectra[experiment], {
                ...twoDOptions,
                experiment,
                frequency: simulationOptions.frequency
              }));
              break;
            }
        }
      }
      return {
        spectra: newSpectra,
        molecules: [{
          molfile
        }]
      };
    }
    function get2DSpectrum(prediction, options) {
      const {
        signals,
        zones,
        nuclei
      } = prediction;
      const {
        frequency: baseFrequency,
        experiment
      } = options;
      const width = get2DWidth(nuclei);
      const frequency = calculateFrequency(nuclei, baseFrequency);
      const minMaxContent = signals2DToZ(signals, {
        ...options,
        width,
        factor: 3
      });
      const spectrum = {
        data: {
          rr: {
            ...minMaxContent,
            noise: 0.01
          }
        },
        info: {
          nucleus: nuclei,
          originFrequency: frequency,
          baseFrequency: frequency,
          pulseSequence: experiment,
          experiment: '2d'
        },
        zones: {
          values: zones
        }
      };
      return spectrum;
    }
    function get2DWidth(nucleus) {
      return nucleus[0] === nucleus[1] ? 0.02 : {
        x: 0.02,
        y: 0.2133
      };
    }
    function get1DSpectrum(prediction, options) {
      const {
        frequency = 400,
        experiment
      } = options;
      const {
        signals,
        nucleus
      } = prediction;
      const realFrequency = calculateFrequency(nucleus, frequency);
      const {
        x,
        y
      } = signalsToXY(signals, {
        ...options,
        frequency: realFrequency
      });
      const spectrum = {
        // todo Array.from is temporary for the developement
        data: {
          x: Array.from(x),
          re: Array.from(y),
          im: null
        },
        info: {
          nucleus: prediction.nucleus,
          dimension: 1,
          isComplex: false,
          originFrequency: realFrequency,
          baseFrequency: realFrequency,
          pulseSequence: 'prediction',
          experiment,
          isFt: true
        },
        ranges: {
          values: prediction.ranges
        }
      };
      return spectrum;
    }
    function calculateFrequency(nucleus, frequency) {
      if (typeof nucleus === 'string') {
        return getFrequency(nucleus, {
          nucleus: '1H',
          frequency
        });
      } else if (nucleus[0] === nucleus[1]) {
        return `${frequency},${frequency}`;
      } else {
        return `${frequency},${getFrequency(nucleus[1], {
      nucleus: nucleus[0],
      frequency
    })}`;
      }
    }

    /**
     * Created by acastillo on 9/3/16.
     */

    class TreeSet {
      constructor(compatator) {
        this.length = 0;
        this.elements = [];
        if (compatator) this.compatator = compatator;else this.compatator = function (a, b) {
          return a - b;
        };
      }
      size() {
        return this.elements.length;
      }
      last() {
        return this.elements[this.length - 1];
      }
      first() {
        return this.elements[0];
      }
      isEmpty() {
        return this.size() === 0;
      }
      pollLast() {
        if (this.length > 0) {
          this.length--;
          return this.elements.splice(this.length, 1);
        }
        return null;
      }
      pollFirst() {
        if (this.length > 0) {
          this.length--;
          return this.elements.splice(0, 1);
        }
        return null;
      }
      add(element) {
        let index = this.binarySearch(element);
        if (index < 0) {
          index = -index - 1;
        }
        this.elements.splice(index, 0, element);
        this.length++;
      }

      /**
       * Performs a binary search of value in array
       * @param {number[]} array - Array in which value will be searched. It must be sorted.
       * @param {number} value - Value to search in array
       * @return {number} If value is found, returns its index in array. Otherwise, returns a negative number indicating where the value should be inserted: -(index + 1)
       */
      binarySearch(value) {
        var low = 0;
        var high = this.elements.length - 1;
        while (low <= high) {
          var mid = low + high >>> 1;
          var midValue = this.elements[mid];
          var cmp = this.compatator(midValue, value);
          if (cmp < 0) {
            low = mid + 1;
          } else if (cmp > 0) {
            high = mid - 1;
          } else {
            return mid;
          }
        }
        return -(low + 1);
      }
    }
    var src$1 = TreeSet;
    var TreeSet$1 = src$1;

    function createMapPossibleAssignments(props) {
      const {
        restrictionByCS,
        predictions,
        targets,
        useIntegrationRestriction
      } = props;
      const {
        tolerance: toleranceCS,
        chemicalShiftRestriction
      } = restrictionByCS;
      const errorAbs = Math.abs(toleranceCS);
      const expandMap = {};
      for (const diaID in predictions) {
        const prediction = predictions[diaID];
        if (prediction.error) prediction.error = Math.abs(prediction.error);
        expandMap[diaID] = [];
        if (targets) {
          for (const targetID in targets) {
            const target = targets[targetID];
            const {
              nbAtoms
            } = prediction;
            const {
              integration
            } = target;
            const couldBeAssigned = useIntegrationRestriction ? integration > 0 ? nbAtoms - integration < 1 : true : true;
            if (couldBeAssigned) {
              if (!chemicalShiftRestriction || typeof prediction.delta === 'undefined') {
                // Chemical shift is not a restriction
                expandMap[diaID].push(targetID);
              } else {
                let error = errorAbs;
                if (prediction.error) {
                  error = Math.max(error, prediction.error);
                }
                const delta = target.signals && target.signals.length > 0 ? target.signals[0].delta : (target.to + target.from) / 2;
                const distAfterLimit = Math.abs(prediction.delta - delta - errorAbs);
                if (distAfterLimit < 4 * errorAbs) {
                  expandMap[diaID].push(targetID);
                }
              }
            }
          }
        }
        expandMap[diaID].push('*');
      }
      return expandMap;
    }

    function partialScore$1(partial, options) {
      const {
        useIntegrationRestriction,
        diaIDPeerPossibleAssignment,
        nbAllowedUnAssigned,
        restrictionByCS,
        predictions,
        targets
      } = options;
      const {
        useChemicalShiftScore
      } = restrictionByCS;
      let countStars = 0;
      const totalPartial = partial.length;
      const partialInverse = {};
      for (let i = 0; i < partial.length; i++) {
        const targetID = partial[i];
        if (targetID && targetID !== '*') {
          if (!partialInverse[targetID]) {
            partialInverse[targetID] = [];
          }
          partialInverse[targetID].push(diaIDPeerPossibleAssignment[i]);
        }
        if (targetID === '*') countStars++;
      }
      if (countStars > nbAllowedUnAssigned) return 0;
      const activeDomainOnTarget = Object.keys(partialInverse);
      if (activeDomainOnTarget.length === 0) {
        return 0;
      }
      if (useIntegrationRestriction) {
        for (const targetID of activeDomainOnTarget) {
          const targetToSource = partialInverse[targetID];
          let total = 0;
          for (const diaID of targetToSource) {
            const prediction = predictions[diaID];
            total += prediction.allHydrogens;
          }
          const {
            integration
          } = targets[targetID];
          if (total - integration >= 0.5) {
            return 0;
          }
        }
      }
      //chemical shift score
      const chemicalShiftScore = useChemicalShiftScore ? chemicalShiftScoring(partial, options) : 1;
      const penaltyByStarts = countStars / totalPartial;
      return chemicalShiftScore - penaltyByStarts;
    }
    function chemicalShiftScoring(partial, options) {
      const {
        tolerance
      } = options.restrictionByCS;
      const {
        diaIDPeerPossibleAssignment,
        predictions,
        targets
      } = options;
      let chemicalShiftScore = 0;
      let count = 0;
      for (let index = 0; index < partial.length; index++) {
        const targetID = partial[index];
        if (targetID && targetID !== '*') {
          count++;
          const diaID = diaIDPeerPossibleAssignment[index];
          const source = predictions[diaID];
          const target = targets[targetID];
          let error = tolerance;
          if (source.error) {
            error = Math.max(source.error, tolerance);
          }
          if (typeof source.delta === 'undefined') {
            // Chemical shift is not a restriction
            chemicalShiftScore += 1;
          } else {
            const delta = target.signals && target.signals.length > 0 ? target.signals[0].delta : (target.to + target.from) / 2;
            let diff = Math.abs(source.delta - delta);
            if (diff < error) {
              chemicalShiftScore += 1;
            } else {
              diff = Math.abs(diff - error);
              chemicalShiftScore += -0.25 / error * diff + 1;
            }
          }
        }
      }
      if (count > 0) {
        chemicalShiftScore /= count;
      }
      return chemicalShiftScore;
    }

    function exploreTreeRec(props, currentIndex, partial, store) {
      const {
        nSources,
        restrictionByCS,
        timeout,
        timeStart,
        maxSolutions,
        targets,
        predictions,
        lowerBoundScore,
        nbAllowedUnAssigned,
        possibleAssignmentMap,
        useIntegrationRestriction,
        diaIDPeerPossibleAssignment
      } = props;
      if (Date.now() - timeStart > timeout) {
        return store;
      }
      const diaID = diaIDPeerPossibleAssignment[currentIndex];
      const possibleAssignments = possibleAssignmentMap[diaID];
      for (const targetID of possibleAssignments) {
        partial[currentIndex] = targetID;
        const score = partialScore$1(partial, {
          useIntegrationRestriction,
          diaIDPeerPossibleAssignment,
          nbAllowedUnAssigned,
          restrictionByCS,
          predictions,
          targets
        });
        if (score === 0) {
          if (targetID === '*') {
            partial[currentIndex] = null;
          }
          continue;
        }
        if (currentIndex === nSources - 1 && score >= lowerBoundScore) {
          addSolution$1(store, {
            predictions,
            partial,
            score,
            maxSolutions
          });
        } else if (currentIndex < nSources - 1) {
          exploreTreeRec({
            nSources,
            restrictionByCS,
            timeout,
            timeStart,
            maxSolutions,
            targets,
            predictions,
            lowerBoundScore,
            nbAllowedUnAssigned,
            possibleAssignmentMap,
            useIntegrationRestriction,
            diaIDPeerPossibleAssignment
          }, currentIndex + 1, JSON.parse(JSON.stringify(partial)), store);
        }
      }
    }
    function addSolution$1(store, props) {
      const {
        score,
        maxSolutions,
        partial,
        predictions
      } = props;
      store.nSolutions++;
      const solution = {
        assignment: JSON.parse(JSON.stringify(partial)),
        score: score / doubleAssignmentPenalty$1(partial, predictions)
      };
      if (store.nSolutions >= maxSolutions) {
        if (solution.score > store.solutions.last().score) {
          store.solutions.pollLast();
          store.solutions.add(solution);
        }
      } else {
        store.solutions.add(solution);
        store.nSolutions++;
      }
    }
    function doubleAssignmentPenalty$1(partial, predictions) {
      const nbSources = Object.keys(predictions).length;
      const assignments = new Set(partial);
      const nbDoubleAssignment = nbSources - assignments.size;
      return nbDoubleAssignment > 0 ? 2 * nbDoubleAssignment : 1;
    }

    const comparator$1 = (a, b) => {
      return b.score - a.score;
    };
    async function buildAssignments$1(props) {
      const {
        restrictionByCS = {},
        useIntegrationRestriction,
        timeout,
        minScore,
        nbAllowedUnAssigned,
        maxSolutions,
        targets,
        joinedSignals
      } = props;
      const {
        tolerance = 1,
        useChemicalShiftScore = false,
        chemicalShiftRestriction = true
      } = restrictionByCS;
      let store = {
        solutions: new TreeSet$1(comparator$1),
        nSolutions: 0
      };
      const nSources = joinedSignals.length;
      const predictions = {};
      for (const prediction of joinedSignals) {
        const diaID = prediction.diaIDs[0];
        const index = prediction.atoms[0];
        predictions[diaID] = {
          ...prediction,
          diaIDIndex: index,
          allHydrogens: prediction.nbAtoms
        };
      }
      const possibleAssignmentMap = createMapPossibleAssignments({
        restrictionByCS: {
          tolerance,
          useChemicalShiftScore,
          chemicalShiftRestriction
        },
        useIntegrationRestriction,
        predictions,
        targets
      });
      const diaIDPeerPossibleAssignment = Object.keys(possibleAssignmentMap);
      const partial = fillPartial$1(nSources);
      store = {
        solutions: new TreeSet$1(comparator$1),
        nSolutions: 0
      };
      const timeStart = Date.now();
      exploreTreeRec({
        nSources,
        restrictionByCS: {
          tolerance,
          useChemicalShiftScore,
          chemicalShiftRestriction
        },
        timeout,
        timeStart,
        targets,
        predictions,
        maxSolutions,
        lowerBoundScore: minScore,
        nbAllowedUnAssigned,
        possibleAssignmentMap,
        diaIDPeerPossibleAssignment,
        useIntegrationRestriction
      }, 0, partial, store);
      const assignments = [];
      for (const solution of store.solutions.elements) {
        const {
          assignment,
          score
        } = solution;
        const currentAssignment = JSON.parse(JSON.stringify(targets));
        for (let i = 0; i < assignment.length; i++) {
          const range = currentAssignment[assignment[i]];
          if (!range.diaIDs) range.diaIDs = [];
          if (assignment[i]) range.diaIDs.push(diaIDPeerPossibleAssignment[i]);
        }
        assignments.push({
          score,
          assignment: Object.values(currentAssignment)
        });
      }
      return assignments;
    }
    function fillPartial$1(nSources, value = null) {
      const partial = new Array(nSources);
      for (let i = 0; i < nSources; i++) {
        partial[i] = value;
      }
      return partial;
    }

    function checkAtomsAndDiaIDs$1(signals) {
      for (const signal of signals) {
        if (!signal.atoms) throw new Error('signal has not atoms property');
        if (!signal.diaIDs) throw new Error('signal has not diaIDs property');
        if (!signal.nbAtoms) throw new Error('signal has not nbAtoms property');
      }
    }
    function checkForIntegration(ranges) {
      for (const range of ranges) {
        if (range.integration === undefined) {
          throw new Error('ranges has not integration property');
        }
      }
    }
    async function get1HAssignments(ranges, molecule, options = {}) {
      const {
        restrictionByCS,
        minScore = 1,
        maxSolutions = 10,
        nbAllowedUnAssigned = 0,
        timeout = 6000,
        predictionOptions = {}
      } = options;
      if (!molecule) {
        throw new Error('It is needed a OCL molecule instance to assign');
      }
      molecule.addImplicitHydrogens();
      addDiastereotopicMissingChirality(molecule);
      const {
        joinedSignals
      } = await predictProton(molecule, predictionOptions);
      checkForIntegration(ranges);
      checkAtomsAndDiaIDs$1(joinedSignals);
      const targets = {};
      for (const range of ranges) {
        const {
          id = v4()
        } = range;
        targets[id] = JSON.parse(JSON.stringify(range));
      }
      return buildAssignments$1({
        restrictionByCS,
        timeout,
        minScore,
        nbAllowedUnAssigned,
        maxSolutions,
        targets,
        joinedSignals,
        useIntegrationRestriction: true
      });
    }

    function checkAtomsAndDiaIDs(signals) {
      for (const signal of signals) {
        if (!signal.atoms) throw new Error('signal has not atoms property');
        if (!signal.diaIDs) throw new Error('signal has not diaIDs property');
        if (!signal.nbAtoms) throw new Error('signal has not nbAtoms property');
      }
    }
    function checkIntegration(ranges) {
      for (const range of ranges) {
        if (range.integration === undefined) range.integration = 0;
      }
      return ranges;
    }
    async function get13CAssignments(ranges, molecule, options = {}) {
      const {
        restrictionByCS = {},
        minScore = 1,
        maxSolutions = 10,
        nbAllowedUnAssigned = 0,
        timeout = 6000,
        predictionOptions = {}
      } = options;
      if (!molecule) {
        throw new Error('It is needed a OCL molecule instance to assign');
      }
      addDiastereotopicMissingChirality(molecule);
      const {
        joinedSignals
      } = await predictCarbon(molecule, predictionOptions);
      checkAtomsAndDiaIDs(joinedSignals);
      const copyRanges = checkIntegration(ranges);
      const targets = {};
      for (const range of copyRanges) {
        const {
          id = v4()
        } = range;
        targets[id] = JSON.parse(JSON.stringify(range));
      }
      return buildAssignments$1({
        restrictionByCS,
        timeout,
        minScore,
        nbAllowedUnAssigned,
        maxSolutions,
        targets,
        joinedSignals,
        useIntegrationRestriction: false
      });
    }

    /**
     * Returns the number of atoms per atom type for a given molecular formula.
     *
     * @param {string} mf
     */
    function getAtomCounts(mf) {
      const elements = mf ? mf.match(/[A-Z][a-z]{0,1}/g) : [];
      const counts = {};
      if (elements) {
        elements.forEach(elem => {
          const regex = new RegExp(`(${elem}\\d+)`, 'g');
          const match = regex.exec(mf);
          let count = 1;
          if (match) {
            count = Number(match[0].split(elem)[1]);
          }
          counts[elem] = count;
        });
      }
      return counts;
    }

    /**
     * Returns the array index of a correlation.
     *
     * @param {Values} correlations
     * @param {Correlation} correlation
     */
    function getCorrelationIndex(correlations, correlation) {
      return correlations.findIndex(_correlation => _correlation.id === correlation.id);
    }

    /**
     * Returns all correlations of a certain atom type.
     *
     * @param {Values} correlations
     * @param {string} atomType
     */
    function getCorrelationsByAtomType(correlations, atomType) {
      return correlations ? correlations.filter(correlation => correlation.atomType === atomType) : [];
    }

    /**
     * Builds a state from current values and an optional molecular formula.
     *
     * @param {Values} values
     * @param {string} mf
     */
    function buildState(values, mf) {
      const state = {};
      const atoms = getAtomCounts(mf);
      const atomTypesInCorrelations = values.reduce((array, correlation) => array.includes(correlation.atomType) ? array : array.concat(correlation.atomType), []);
      atomTypesInCorrelations.forEach(atomType => {
        const correlationsAtomType = getCorrelationsByAtomType(values, atomType);
        // create state for specific atom type only if there is at least one real correlation
        if (correlationsAtomType.some(correlation => !correlation.pseudo)) {
          // create state error
          const stateAtomTypeError = {};
          const atomCount = atoms[atomType];
          let atomCountAtomType = correlationsAtomType.reduce((sum, correlation) => !correlation.pseudo ? sum + correlation.equivalence : sum, 0);
          if (atomType === 'H') {
            // add protons count from pseudo correlations without any pseudo HSQC correlation
            values.forEach(correlation => {
              if (correlation.pseudo && correlation.atomType !== 'H' && correlation.protonsCount.length === 1 && !correlation.link.some(link => link.experimentType === 'hsqc')) {
                atomCountAtomType += correlation.protonsCount[0];
              }
            });
            // determine the number of pseudo correlations
            const pseudoCorrelationCount = correlationsAtomType.reduce((sum, correlation) => correlation.pseudo ? sum + 1 : sum, 0);
            // determine the not attached protons
            const notAttached = correlationsAtomType.reduce((array, correlation) => Object.keys(correlation.attachment).length === 0 ? array.concat(getCorrelationIndex(values, correlation)) : array, []);
            if (notAttached.length > 0) {
              stateAtomTypeError.notAttached = notAttached;
            }
            atomCountAtomType -= notAttached.length - pseudoCorrelationCount;
            if (atomCountAtomType < 0) {
              atomCountAtomType = 0;
            }
            // determine the ambiguous attached protons
            const ambiguousAttachment = correlationsAtomType.reduce((array, correlation) => Object.keys(correlation.attachment).length > 1 || Object.keys(correlation.attachment).some(otherAtomType => correlation.attachment[otherAtomType].length > 1) ? array.concat(getCorrelationIndex(values, correlation)) : array, []);
            if (ambiguousAttachment.length > 0) {
              stateAtomTypeError.ambiguousAttachment = ambiguousAttachment;
            }
          }
          if (atomCount !== undefined) {
            const outOfLimit = correlationsAtomType.some((correlation, k) => !correlation.pseudo && correlation.atomType === atomType && k >= atomCount);
            if (outOfLimit) {
              stateAtomTypeError.outOfLimit = true;
            }
          }
          const complete = atomCount === undefined ? undefined : atomCountAtomType === atomCount;
          if (complete === false) {
            stateAtomTypeError.incomplete = true;
          }
          state[atomType] = {
            current: atomCountAtomType,
            total: atomCount,
            complete,
            error: stateAtomTypeError
          };
        }
      });
      return state;
    }

    /**
     * Checks if `value` is classified as an `Array` object.
     *
     * @static
     * @memberOf _
     * @since 0.1.0
     * @category Lang
     * @param {*} value The value to check.
     * @returns {boolean} Returns `true` if `value` is an array, else `false`.
     * @example
     *
     * _.isArray([1, 2, 3]);
     * // => true
     *
     * _.isArray(document.body.children);
     * // => false
     *
     * _.isArray('abc');
     * // => false
     *
     * _.isArray(_.noop);
     * // => false
     */
    var isArray$8 = Array.isArray;
    var isArray_1 = isArray$8;

    /** Detect free variable `global` from Node.js. */
    var freeGlobal$1 = typeof commonjsGlobal == 'object' && commonjsGlobal && commonjsGlobal.Object === Object && commonjsGlobal;
    var _freeGlobal = freeGlobal$1;

    var freeGlobal = _freeGlobal;

    /** Detect free variable `self`. */
    var freeSelf = typeof self == 'object' && self && self.Object === Object && self;

    /** Used as a reference to the global object. */
    var root$8 = freeGlobal || freeSelf || Function('return this')();
    var _root = root$8;

    var root$7 = _root;

    /** Built-in value references. */
    var Symbol$7 = root$7.Symbol;
    var _Symbol = Symbol$7;

    var Symbol$6 = _Symbol;

    /** Used for built-in method references. */
    var objectProto$f = Object.prototype;

    /** Used to check objects for own properties. */
    var hasOwnProperty$c = objectProto$f.hasOwnProperty;

    /**
     * Used to resolve the
     * [`toStringTag`](http://ecma-international.org/ecma-262/7.0/#sec-object.prototype.tostring)
     * of values.
     */
    var nativeObjectToString$1 = objectProto$f.toString;

    /** Built-in value references. */
    var symToStringTag$1 = Symbol$6 ? Symbol$6.toStringTag : undefined;

    /**
     * A specialized version of `baseGetTag` which ignores `Symbol.toStringTag` values.
     *
     * @private
     * @param {*} value The value to query.
     * @returns {string} Returns the raw `toStringTag`.
     */
    function getRawTag$1(value) {
      var isOwn = hasOwnProperty$c.call(value, symToStringTag$1),
        tag = value[symToStringTag$1];
      try {
        value[symToStringTag$1] = undefined;
        var unmasked = true;
      } catch (e) {}
      var result = nativeObjectToString$1.call(value);
      if (unmasked) {
        if (isOwn) {
          value[symToStringTag$1] = tag;
        } else {
          delete value[symToStringTag$1];
        }
      }
      return result;
    }
    var _getRawTag = getRawTag$1;

    /** Used for built-in method references. */
    var objectProto$e = Object.prototype;

    /**
     * Used to resolve the
     * [`toStringTag`](http://ecma-international.org/ecma-262/7.0/#sec-object.prototype.tostring)
     * of values.
     */
    var nativeObjectToString = objectProto$e.toString;

    /**
     * Converts `value` to a string using `Object.prototype.toString`.
     *
     * @private
     * @param {*} value The value to convert.
     * @returns {string} Returns the converted string.
     */
    function objectToString$1(value) {
      return nativeObjectToString.call(value);
    }
    var _objectToString = objectToString$1;

    var Symbol$5 = _Symbol,
      getRawTag = _getRawTag,
      objectToString = _objectToString;

    /** `Object#toString` result references. */
    var nullTag = '[object Null]',
      undefinedTag = '[object Undefined]';

    /** Built-in value references. */
    var symToStringTag = Symbol$5 ? Symbol$5.toStringTag : undefined;

    /**
     * The base implementation of `getTag` without fallbacks for buggy environments.
     *
     * @private
     * @param {*} value The value to query.
     * @returns {string} Returns the `toStringTag`.
     */
    function baseGetTag$6(value) {
      if (value == null) {
        return value === undefined ? undefinedTag : nullTag;
      }
      return symToStringTag && symToStringTag in Object(value) ? getRawTag(value) : objectToString(value);
    }
    var _baseGetTag = baseGetTag$6;

    /**
     * Checks if `value` is object-like. A value is object-like if it's not `null`
     * and has a `typeof` result of "object".
     *
     * @static
     * @memberOf _
     * @since 4.0.0
     * @category Lang
     * @param {*} value The value to check.
     * @returns {boolean} Returns `true` if `value` is object-like, else `false`.
     * @example
     *
     * _.isObjectLike({});
     * // => true
     *
     * _.isObjectLike([1, 2, 3]);
     * // => true
     *
     * _.isObjectLike(_.noop);
     * // => false
     *
     * _.isObjectLike(null);
     * // => false
     */
    function isObjectLike$8(value) {
      return value != null && typeof value == 'object';
    }
    var isObjectLike_1 = isObjectLike$8;

    var baseGetTag$5 = _baseGetTag,
      isObjectLike$7 = isObjectLike_1;

    /** `Object#toString` result references. */
    var symbolTag$3 = '[object Symbol]';

    /**
     * Checks if `value` is classified as a `Symbol` primitive or object.
     *
     * @static
     * @memberOf _
     * @since 4.0.0
     * @category Lang
     * @param {*} value The value to check.
     * @returns {boolean} Returns `true` if `value` is a symbol, else `false`.
     * @example
     *
     * _.isSymbol(Symbol.iterator);
     * // => true
     *
     * _.isSymbol('abc');
     * // => false
     */
    function isSymbol$3(value) {
      return typeof value == 'symbol' || isObjectLike$7(value) && baseGetTag$5(value) == symbolTag$3;
    }
    var isSymbol_1 = isSymbol$3;

    var isArray$7 = isArray_1,
      isSymbol$2 = isSymbol_1;

    /** Used to match property names within property paths. */
    var reIsDeepProp = /\.|\[(?:[^[\]]*|(["'])(?:(?!\1)[^\\]|\\.)*?\1)\]/,
      reIsPlainProp = /^\w*$/;

    /**
     * Checks if `value` is a property name and not a property path.
     *
     * @private
     * @param {*} value The value to check.
     * @param {Object} [object] The object to query keys on.
     * @returns {boolean} Returns `true` if `value` is a property name, else `false`.
     */
    function isKey$1(value, object) {
      if (isArray$7(value)) {
        return false;
      }
      var type = typeof value;
      if (type == 'number' || type == 'symbol' || type == 'boolean' || value == null || isSymbol$2(value)) {
        return true;
      }
      return reIsPlainProp.test(value) || !reIsDeepProp.test(value) || object != null && value in Object(object);
    }
    var _isKey = isKey$1;

    /**
     * Checks if `value` is the
     * [language type](http://www.ecma-international.org/ecma-262/7.0/#sec-ecmascript-language-types)
     * of `Object`. (e.g. arrays, functions, objects, regexes, `new Number(0)`, and `new String('')`)
     *
     * @static
     * @memberOf _
     * @since 0.1.0
     * @category Lang
     * @param {*} value The value to check.
     * @returns {boolean} Returns `true` if `value` is an object, else `false`.
     * @example
     *
     * _.isObject({});
     * // => true
     *
     * _.isObject([1, 2, 3]);
     * // => true
     *
     * _.isObject(_.noop);
     * // => true
     *
     * _.isObject(null);
     * // => false
     */
    function isObject$5(value) {
      var type = typeof value;
      return value != null && (type == 'object' || type == 'function');
    }
    var isObject_1 = isObject$5;

    var baseGetTag$4 = _baseGetTag,
      isObject$4 = isObject_1;

    /** `Object#toString` result references. */
    var asyncTag = '[object AsyncFunction]',
      funcTag$2 = '[object Function]',
      genTag$1 = '[object GeneratorFunction]',
      proxyTag = '[object Proxy]';

    /**
     * Checks if `value` is classified as a `Function` object.
     *
     * @static
     * @memberOf _
     * @since 0.1.0
     * @category Lang
     * @param {*} value The value to check.
     * @returns {boolean} Returns `true` if `value` is a function, else `false`.
     * @example
     *
     * _.isFunction(_);
     * // => true
     *
     * _.isFunction(/abc/);
     * // => false
     */
    function isFunction$2(value) {
      if (!isObject$4(value)) {
        return false;
      }
      // The use of `Object#toString` avoids issues with the `typeof` operator
      // in Safari 9 which returns 'object' for typed arrays and other constructors.
      var tag = baseGetTag$4(value);
      return tag == funcTag$2 || tag == genTag$1 || tag == asyncTag || tag == proxyTag;
    }
    var isFunction_1 = isFunction$2;

    var root$6 = _root;

    /** Used to detect overreaching core-js shims. */
    var coreJsData$1 = root$6['__core-js_shared__'];
    var _coreJsData = coreJsData$1;

    var coreJsData = _coreJsData;

    /** Used to detect methods masquerading as native. */
    var maskSrcKey = function () {
      var uid = /[^.]+$/.exec(coreJsData && coreJsData.keys && coreJsData.keys.IE_PROTO || '');
      return uid ? 'Symbol(src)_1.' + uid : '';
    }();

    /**
     * Checks if `func` has its source masked.
     *
     * @private
     * @param {Function} func The function to check.
     * @returns {boolean} Returns `true` if `func` is masked, else `false`.
     */
    function isMasked$1(func) {
      return !!maskSrcKey && maskSrcKey in func;
    }
    var _isMasked = isMasked$1;

    /** Used for built-in method references. */
    var funcProto$2 = Function.prototype;

    /** Used to resolve the decompiled source of functions. */
    var funcToString$2 = funcProto$2.toString;

    /**
     * Converts `func` to its source code.
     *
     * @private
     * @param {Function} func The function to convert.
     * @returns {string} Returns the source code.
     */
    function toSource$2(func) {
      if (func != null) {
        try {
          return funcToString$2.call(func);
        } catch (e) {}
        try {
          return func + '';
        } catch (e) {}
      }
      return '';
    }
    var _toSource = toSource$2;

    var isFunction$1 = isFunction_1,
      isMasked = _isMasked,
      isObject$3 = isObject_1,
      toSource$1 = _toSource;

    /**
     * Used to match `RegExp`
     * [syntax characters](http://ecma-international.org/ecma-262/7.0/#sec-patterns).
     */
    var reRegExpChar = /[\\^$.*+?()[\]{}|]/g;

    /** Used to detect host constructors (Safari). */
    var reIsHostCtor = /^\[object .+?Constructor\]$/;

    /** Used for built-in method references. */
    var funcProto$1 = Function.prototype,
      objectProto$d = Object.prototype;

    /** Used to resolve the decompiled source of functions. */
    var funcToString$1 = funcProto$1.toString;

    /** Used to check objects for own properties. */
    var hasOwnProperty$b = objectProto$d.hasOwnProperty;

    /** Used to detect if a method is native. */
    var reIsNative = RegExp('^' + funcToString$1.call(hasOwnProperty$b).replace(reRegExpChar, '\\$&').replace(/hasOwnProperty|(function).*?(?=\\\()| for .+?(?=\\\])/g, '$1.*?') + '$');

    /**
     * The base implementation of `_.isNative` without bad shim checks.
     *
     * @private
     * @param {*} value The value to check.
     * @returns {boolean} Returns `true` if `value` is a native function,
     *  else `false`.
     */
    function baseIsNative$1(value) {
      if (!isObject$3(value) || isMasked(value)) {
        return false;
      }
      var pattern = isFunction$1(value) ? reIsNative : reIsHostCtor;
      return pattern.test(toSource$1(value));
    }
    var _baseIsNative = baseIsNative$1;

    /**
     * Gets the value at `key` of `object`.
     *
     * @private
     * @param {Object} [object] The object to query.
     * @param {string} key The key of the property to get.
     * @returns {*} Returns the property value.
     */
    function getValue$1(object, key) {
      return object == null ? undefined : object[key];
    }
    var _getValue = getValue$1;

    var baseIsNative = _baseIsNative,
      getValue = _getValue;

    /**
     * Gets the native function at `key` of `object`.
     *
     * @private
     * @param {Object} object The object to query.
     * @param {string} key The key of the method to get.
     * @returns {*} Returns the function if it's native, else `undefined`.
     */
    function getNative$7(object, key) {
      var value = getValue(object, key);
      return baseIsNative(value) ? value : undefined;
    }
    var _getNative = getNative$7;

    var getNative$6 = _getNative;

    /* Built-in method references that are verified to be native. */
    var nativeCreate$4 = getNative$6(Object, 'create');
    var _nativeCreate = nativeCreate$4;

    var nativeCreate$3 = _nativeCreate;

    /**
     * Removes all key-value entries from the hash.
     *
     * @private
     * @name clear
     * @memberOf Hash
     */
    function hashClear$1() {
      this.__data__ = nativeCreate$3 ? nativeCreate$3(null) : {};
      this.size = 0;
    }
    var _hashClear = hashClear$1;

    /**
     * Removes `key` and its value from the hash.
     *
     * @private
     * @name delete
     * @memberOf Hash
     * @param {Object} hash The hash to modify.
     * @param {string} key The key of the value to remove.
     * @returns {boolean} Returns `true` if the entry was removed, else `false`.
     */
    function hashDelete$1(key) {
      var result = this.has(key) && delete this.__data__[key];
      this.size -= result ? 1 : 0;
      return result;
    }
    var _hashDelete = hashDelete$1;

    var nativeCreate$2 = _nativeCreate;

    /** Used to stand-in for `undefined` hash values. */
    var HASH_UNDEFINED$2 = '__lodash_hash_undefined__';

    /** Used for built-in method references. */
    var objectProto$c = Object.prototype;

    /** Used to check objects for own properties. */
    var hasOwnProperty$a = objectProto$c.hasOwnProperty;

    /**
     * Gets the hash value for `key`.
     *
     * @private
     * @name get
     * @memberOf Hash
     * @param {string} key The key of the value to get.
     * @returns {*} Returns the entry value.
     */
    function hashGet$1(key) {
      var data = this.__data__;
      if (nativeCreate$2) {
        var result = data[key];
        return result === HASH_UNDEFINED$2 ? undefined : result;
      }
      return hasOwnProperty$a.call(data, key) ? data[key] : undefined;
    }
    var _hashGet = hashGet$1;

    var nativeCreate$1 = _nativeCreate;

    /** Used for built-in method references. */
    var objectProto$b = Object.prototype;

    /** Used to check objects for own properties. */
    var hasOwnProperty$9 = objectProto$b.hasOwnProperty;

    /**
     * Checks if a hash value for `key` exists.
     *
     * @private
     * @name has
     * @memberOf Hash
     * @param {string} key The key of the entry to check.
     * @returns {boolean} Returns `true` if an entry for `key` exists, else `false`.
     */
    function hashHas$1(key) {
      var data = this.__data__;
      return nativeCreate$1 ? data[key] !== undefined : hasOwnProperty$9.call(data, key);
    }
    var _hashHas = hashHas$1;

    var nativeCreate = _nativeCreate;

    /** Used to stand-in for `undefined` hash values. */
    var HASH_UNDEFINED$1 = '__lodash_hash_undefined__';

    /**
     * Sets the hash `key` to `value`.
     *
     * @private
     * @name set
     * @memberOf Hash
     * @param {string} key The key of the value to set.
     * @param {*} value The value to set.
     * @returns {Object} Returns the hash instance.
     */
    function hashSet$1(key, value) {
      var data = this.__data__;
      this.size += this.has(key) ? 0 : 1;
      data[key] = nativeCreate && value === undefined ? HASH_UNDEFINED$1 : value;
      return this;
    }
    var _hashSet = hashSet$1;

    var hashClear = _hashClear,
      hashDelete = _hashDelete,
      hashGet = _hashGet,
      hashHas = _hashHas,
      hashSet = _hashSet;

    /**
     * Creates a hash object.
     *
     * @private
     * @constructor
     * @param {Array} [entries] The key-value pairs to cache.
     */
    function Hash$1(entries) {
      var index = -1,
        length = entries == null ? 0 : entries.length;
      this.clear();
      while (++index < length) {
        var entry = entries[index];
        this.set(entry[0], entry[1]);
      }
    }

    // Add methods to `Hash`.
    Hash$1.prototype.clear = hashClear;
    Hash$1.prototype['delete'] = hashDelete;
    Hash$1.prototype.get = hashGet;
    Hash$1.prototype.has = hashHas;
    Hash$1.prototype.set = hashSet;
    var _Hash = Hash$1;

    /**
     * Removes all key-value entries from the list cache.
     *
     * @private
     * @name clear
     * @memberOf ListCache
     */
    function listCacheClear$1() {
      this.__data__ = [];
      this.size = 0;
    }
    var _listCacheClear = listCacheClear$1;

    /**
     * Performs a
     * [`SameValueZero`](http://ecma-international.org/ecma-262/7.0/#sec-samevaluezero)
     * comparison between two values to determine if they are equivalent.
     *
     * @static
     * @memberOf _
     * @since 4.0.0
     * @category Lang
     * @param {*} value The value to compare.
     * @param {*} other The other value to compare.
     * @returns {boolean} Returns `true` if the values are equivalent, else `false`.
     * @example
     *
     * var object = { 'a': 1 };
     * var other = { 'a': 1 };
     *
     * _.eq(object, object);
     * // => true
     *
     * _.eq(object, other);
     * // => false
     *
     * _.eq('a', 'a');
     * // => true
     *
     * _.eq('a', Object('a'));
     * // => false
     *
     * _.eq(NaN, NaN);
     * // => true
     */
    function eq$3(value, other) {
      return value === other || value !== value && other !== other;
    }
    var eq_1 = eq$3;

    var eq$2 = eq_1;

    /**
     * Gets the index at which the `key` is found in `array` of key-value pairs.
     *
     * @private
     * @param {Array} array The array to inspect.
     * @param {*} key The key to search for.
     * @returns {number} Returns the index of the matched value, else `-1`.
     */
    function assocIndexOf$4(array, key) {
      var length = array.length;
      while (length--) {
        if (eq$2(array[length][0], key)) {
          return length;
        }
      }
      return -1;
    }
    var _assocIndexOf = assocIndexOf$4;

    var assocIndexOf$3 = _assocIndexOf;

    /** Used for built-in method references. */
    var arrayProto = Array.prototype;

    /** Built-in value references. */
    var splice = arrayProto.splice;

    /**
     * Removes `key` and its value from the list cache.
     *
     * @private
     * @name delete
     * @memberOf ListCache
     * @param {string} key The key of the value to remove.
     * @returns {boolean} Returns `true` if the entry was removed, else `false`.
     */
    function listCacheDelete$1(key) {
      var data = this.__data__,
        index = assocIndexOf$3(data, key);
      if (index < 0) {
        return false;
      }
      var lastIndex = data.length - 1;
      if (index == lastIndex) {
        data.pop();
      } else {
        splice.call(data, index, 1);
      }
      --this.size;
      return true;
    }
    var _listCacheDelete = listCacheDelete$1;

    var assocIndexOf$2 = _assocIndexOf;

    /**
     * Gets the list cache value for `key`.
     *
     * @private
     * @name get
     * @memberOf ListCache
     * @param {string} key The key of the value to get.
     * @returns {*} Returns the entry value.
     */
    function listCacheGet$1(key) {
      var data = this.__data__,
        index = assocIndexOf$2(data, key);
      return index < 0 ? undefined : data[index][1];
    }
    var _listCacheGet = listCacheGet$1;

    var assocIndexOf$1 = _assocIndexOf;

    /**
     * Checks if a list cache value for `key` exists.
     *
     * @private
     * @name has
     * @memberOf ListCache
     * @param {string} key The key of the entry to check.
     * @returns {boolean} Returns `true` if an entry for `key` exists, else `false`.
     */
    function listCacheHas$1(key) {
      return assocIndexOf$1(this.__data__, key) > -1;
    }
    var _listCacheHas = listCacheHas$1;

    var assocIndexOf = _assocIndexOf;

    /**
     * Sets the list cache `key` to `value`.
     *
     * @private
     * @name set
     * @memberOf ListCache
     * @param {string} key The key of the value to set.
     * @param {*} value The value to set.
     * @returns {Object} Returns the list cache instance.
     */
    function listCacheSet$1(key, value) {
      var data = this.__data__,
        index = assocIndexOf(data, key);
      if (index < 0) {
        ++this.size;
        data.push([key, value]);
      } else {
        data[index][1] = value;
      }
      return this;
    }
    var _listCacheSet = listCacheSet$1;

    var listCacheClear = _listCacheClear,
      listCacheDelete = _listCacheDelete,
      listCacheGet = _listCacheGet,
      listCacheHas = _listCacheHas,
      listCacheSet = _listCacheSet;

    /**
     * Creates an list cache object.
     *
     * @private
     * @constructor
     * @param {Array} [entries] The key-value pairs to cache.
     */
    function ListCache$4(entries) {
      var index = -1,
        length = entries == null ? 0 : entries.length;
      this.clear();
      while (++index < length) {
        var entry = entries[index];
        this.set(entry[0], entry[1]);
      }
    }

    // Add methods to `ListCache`.
    ListCache$4.prototype.clear = listCacheClear;
    ListCache$4.prototype['delete'] = listCacheDelete;
    ListCache$4.prototype.get = listCacheGet;
    ListCache$4.prototype.has = listCacheHas;
    ListCache$4.prototype.set = listCacheSet;
    var _ListCache = ListCache$4;

    var getNative$5 = _getNative,
      root$5 = _root;

    /* Built-in method references that are verified to be native. */
    var Map$4 = getNative$5(root$5, 'Map');
    var _Map = Map$4;

    var Hash = _Hash,
      ListCache$3 = _ListCache,
      Map$3 = _Map;

    /**
     * Removes all key-value entries from the map.
     *
     * @private
     * @name clear
     * @memberOf MapCache
     */
    function mapCacheClear$1() {
      this.size = 0;
      this.__data__ = {
        'hash': new Hash(),
        'map': new (Map$3 || ListCache$3)(),
        'string': new Hash()
      };
    }
    var _mapCacheClear = mapCacheClear$1;

    /**
     * Checks if `value` is suitable for use as unique object key.
     *
     * @private
     * @param {*} value The value to check.
     * @returns {boolean} Returns `true` if `value` is suitable, else `false`.
     */
    function isKeyable$1(value) {
      var type = typeof value;
      return type == 'string' || type == 'number' || type == 'symbol' || type == 'boolean' ? value !== '__proto__' : value === null;
    }
    var _isKeyable = isKeyable$1;

    var isKeyable = _isKeyable;

    /**
     * Gets the data for `map`.
     *
     * @private
     * @param {Object} map The map to query.
     * @param {string} key The reference key.
     * @returns {*} Returns the map data.
     */
    function getMapData$4(map, key) {
      var data = map.__data__;
      return isKeyable(key) ? data[typeof key == 'string' ? 'string' : 'hash'] : data.map;
    }
    var _getMapData = getMapData$4;

    var getMapData$3 = _getMapData;

    /**
     * Removes `key` and its value from the map.
     *
     * @private
     * @name delete
     * @memberOf MapCache
     * @param {string} key The key of the value to remove.
     * @returns {boolean} Returns `true` if the entry was removed, else `false`.
     */
    function mapCacheDelete$1(key) {
      var result = getMapData$3(this, key)['delete'](key);
      this.size -= result ? 1 : 0;
      return result;
    }
    var _mapCacheDelete = mapCacheDelete$1;

    var getMapData$2 = _getMapData;

    /**
     * Gets the map value for `key`.
     *
     * @private
     * @name get
     * @memberOf MapCache
     * @param {string} key The key of the value to get.
     * @returns {*} Returns the entry value.
     */
    function mapCacheGet$1(key) {
      return getMapData$2(this, key).get(key);
    }
    var _mapCacheGet = mapCacheGet$1;

    var getMapData$1 = _getMapData;

    /**
     * Checks if a map value for `key` exists.
     *
     * @private
     * @name has
     * @memberOf MapCache
     * @param {string} key The key of the entry to check.
     * @returns {boolean} Returns `true` if an entry for `key` exists, else `false`.
     */
    function mapCacheHas$1(key) {
      return getMapData$1(this, key).has(key);
    }
    var _mapCacheHas = mapCacheHas$1;

    var getMapData = _getMapData;

    /**
     * Sets the map `key` to `value`.
     *
     * @private
     * @name set
     * @memberOf MapCache
     * @param {string} key The key of the value to set.
     * @param {*} value The value to set.
     * @returns {Object} Returns the map cache instance.
     */
    function mapCacheSet$1(key, value) {
      var data = getMapData(this, key),
        size = data.size;
      data.set(key, value);
      this.size += data.size == size ? 0 : 1;
      return this;
    }
    var _mapCacheSet = mapCacheSet$1;

    var mapCacheClear = _mapCacheClear,
      mapCacheDelete = _mapCacheDelete,
      mapCacheGet = _mapCacheGet,
      mapCacheHas = _mapCacheHas,
      mapCacheSet = _mapCacheSet;

    /**
     * Creates a map cache object to store key-value pairs.
     *
     * @private
     * @constructor
     * @param {Array} [entries] The key-value pairs to cache.
     */
    function MapCache$3(entries) {
      var index = -1,
        length = entries == null ? 0 : entries.length;
      this.clear();
      while (++index < length) {
        var entry = entries[index];
        this.set(entry[0], entry[1]);
      }
    }

    // Add methods to `MapCache`.
    MapCache$3.prototype.clear = mapCacheClear;
    MapCache$3.prototype['delete'] = mapCacheDelete;
    MapCache$3.prototype.get = mapCacheGet;
    MapCache$3.prototype.has = mapCacheHas;
    MapCache$3.prototype.set = mapCacheSet;
    var _MapCache = MapCache$3;

    var MapCache$2 = _MapCache;

    /** Error message constants. */
    var FUNC_ERROR_TEXT = 'Expected a function';

    /**
     * Creates a function that memoizes the result of `func`. If `resolver` is
     * provided, it determines the cache key for storing the result based on the
     * arguments provided to the memoized function. By default, the first argument
     * provided to the memoized function is used as the map cache key. The `func`
     * is invoked with the `this` binding of the memoized function.
     *
     * **Note:** The cache is exposed as the `cache` property on the memoized
     * function. Its creation may be customized by replacing the `_.memoize.Cache`
     * constructor with one whose instances implement the
     * [`Map`](http://ecma-international.org/ecma-262/7.0/#sec-properties-of-the-map-prototype-object)
     * method interface of `clear`, `delete`, `get`, `has`, and `set`.
     *
     * @static
     * @memberOf _
     * @since 0.1.0
     * @category Function
     * @param {Function} func The function to have its output memoized.
     * @param {Function} [resolver] The function to resolve the cache key.
     * @returns {Function} Returns the new memoized function.
     * @example
     *
     * var object = { 'a': 1, 'b': 2 };
     * var other = { 'c': 3, 'd': 4 };
     *
     * var values = _.memoize(_.values);
     * values(object);
     * // => [1, 2]
     *
     * values(other);
     * // => [3, 4]
     *
     * object.a = 2;
     * values(object);
     * // => [1, 2]
     *
     * // Modify the result cache.
     * values.cache.set(object, ['a', 'b']);
     * values(object);
     * // => ['a', 'b']
     *
     * // Replace `_.memoize.Cache`.
     * _.memoize.Cache = WeakMap;
     */
    function memoize$1(func, resolver) {
      if (typeof func != 'function' || resolver != null && typeof resolver != 'function') {
        throw new TypeError(FUNC_ERROR_TEXT);
      }
      var memoized = function () {
        var args = arguments,
          key = resolver ? resolver.apply(this, args) : args[0],
          cache = memoized.cache;
        if (cache.has(key)) {
          return cache.get(key);
        }
        var result = func.apply(this, args);
        memoized.cache = cache.set(key, result) || cache;
        return result;
      };
      memoized.cache = new (memoize$1.Cache || MapCache$2)();
      return memoized;
    }

    // Expose `MapCache`.
    memoize$1.Cache = MapCache$2;
    var memoize_1 = memoize$1;

    var memoize = memoize_1;

    /** Used as the maximum memoize cache size. */
    var MAX_MEMOIZE_SIZE = 500;

    /**
     * A specialized version of `_.memoize` which clears the memoized function's
     * cache when it exceeds `MAX_MEMOIZE_SIZE`.
     *
     * @private
     * @param {Function} func The function to have its output memoized.
     * @returns {Function} Returns the new memoized function.
     */
    function memoizeCapped$1(func) {
      var result = memoize(func, function (key) {
        if (cache.size === MAX_MEMOIZE_SIZE) {
          cache.clear();
        }
        return key;
      });
      var cache = result.cache;
      return result;
    }
    var _memoizeCapped = memoizeCapped$1;

    var memoizeCapped = _memoizeCapped;

    /** Used to match property names within property paths. */
    var rePropName = /[^.[\]]+|\[(?:(-?\d+(?:\.\d+)?)|(["'])((?:(?!\2)[^\\]|\\.)*?)\2)\]|(?=(?:\.|\[\])(?:\.|\[\]|$))/g;

    /** Used to match backslashes in property paths. */
    var reEscapeChar = /\\(\\)?/g;

    /**
     * Converts `string` to a property path array.
     *
     * @private
     * @param {string} string The string to convert.
     * @returns {Array} Returns the property path array.
     */
    var stringToPath$1 = memoizeCapped(function (string) {
      var result = [];
      if (string.charCodeAt(0) === 46 /* . */) {
        result.push('');
      }
      string.replace(rePropName, function (match, number, quote, subString) {
        result.push(quote ? subString.replace(reEscapeChar, '$1') : number || match);
      });
      return result;
    });
    var _stringToPath = stringToPath$1;

    /**
     * A specialized version of `_.map` for arrays without support for iteratee
     * shorthands.
     *
     * @private
     * @param {Array} [array] The array to iterate over.
     * @param {Function} iteratee The function invoked per iteration.
     * @returns {Array} Returns the new mapped array.
     */
    function arrayMap$2(array, iteratee) {
      var index = -1,
        length = array == null ? 0 : array.length,
        result = Array(length);
      while (++index < length) {
        result[index] = iteratee(array[index], index, array);
      }
      return result;
    }
    var _arrayMap = arrayMap$2;

    var Symbol$4 = _Symbol,
      arrayMap$1 = _arrayMap,
      isArray$6 = isArray_1,
      isSymbol$1 = isSymbol_1;

    /** Used as references for various `Number` constants. */
    var INFINITY$1 = 1 / 0;

    /** Used to convert symbols to primitives and strings. */
    var symbolProto$2 = Symbol$4 ? Symbol$4.prototype : undefined,
      symbolToString = symbolProto$2 ? symbolProto$2.toString : undefined;

    /**
     * The base implementation of `_.toString` which doesn't convert nullish
     * values to empty strings.
     *
     * @private
     * @param {*} value The value to process.
     * @returns {string} Returns the string.
     */
    function baseToString$1(value) {
      // Exit early for strings to avoid a performance hit in some environments.
      if (typeof value == 'string') {
        return value;
      }
      if (isArray$6(value)) {
        // Recursively convert values (susceptible to call stack limits).
        return arrayMap$1(value, baseToString$1) + '';
      }
      if (isSymbol$1(value)) {
        return symbolToString ? symbolToString.call(value) : '';
      }
      var result = value + '';
      return result == '0' && 1 / value == -INFINITY$1 ? '-0' : result;
    }
    var _baseToString = baseToString$1;

    var baseToString = _baseToString;

    /**
     * Converts `value` to a string. An empty string is returned for `null`
     * and `undefined` values. The sign of `-0` is preserved.
     *
     * @static
     * @memberOf _
     * @since 4.0.0
     * @category Lang
     * @param {*} value The value to convert.
     * @returns {string} Returns the converted string.
     * @example
     *
     * _.toString(null);
     * // => ''
     *
     * _.toString(-0);
     * // => '-0'
     *
     * _.toString([1, 2, 3]);
     * // => '1,2,3'
     */
    function toString$1(value) {
      return value == null ? '' : baseToString(value);
    }
    var toString_1 = toString$1;

    var isArray$5 = isArray_1,
      isKey = _isKey,
      stringToPath = _stringToPath,
      toString = toString_1;

    /**
     * Casts `value` to a path array if it's not one.
     *
     * @private
     * @param {*} value The value to inspect.
     * @param {Object} [object] The object to query keys on.
     * @returns {Array} Returns the cast property path array.
     */
    function castPath$3(value, object) {
      if (isArray$5(value)) {
        return value;
      }
      return isKey(value, object) ? [value] : stringToPath(toString(value));
    }
    var _castPath = castPath$3;

    var isSymbol = isSymbol_1;

    /** Used as references for various `Number` constants. */
    var INFINITY = 1 / 0;

    /**
     * Converts `value` to a string key if it's not a string or symbol.
     *
     * @private
     * @param {*} value The value to inspect.
     * @returns {string|symbol} Returns the key.
     */
    function toKey$2(value) {
      if (typeof value == 'string' || isSymbol(value)) {
        return value;
      }
      var result = value + '';
      return result == '0' && 1 / value == -INFINITY ? '-0' : result;
    }
    var _toKey = toKey$2;

    var castPath$2 = _castPath,
      toKey$1 = _toKey;

    /**
     * The base implementation of `_.get` without support for default values.
     *
     * @private
     * @param {Object} object The object to query.
     * @param {Array|string} path The path of the property to get.
     * @returns {*} Returns the resolved value.
     */
    function baseGet$2(object, path) {
      path = castPath$2(path, object);
      var index = 0,
        length = path.length;
      while (object != null && index < length) {
        object = object[toKey$1(path[index++])];
      }
      return index && index == length ? object : undefined;
    }
    var _baseGet = baseGet$2;

    var baseGet$1 = _baseGet;

    /**
     * Gets the value at `path` of `object`. If the resolved value is
     * `undefined`, the `defaultValue` is returned in its place.
     *
     * @static
     * @memberOf _
     * @since 3.7.0
     * @category Object
     * @param {Object} object The object to query.
     * @param {Array|string} path The path of the property to get.
     * @param {*} [defaultValue] The value returned for `undefined` resolved values.
     * @returns {*} Returns the resolved value.
     * @example
     *
     * var object = { 'a': [{ 'b': { 'c': 3 } }] };
     *
     * _.get(object, 'a[0].b.c');
     * // => 3
     *
     * _.get(object, ['a', '0', 'b', 'c']);
     * // => 3
     *
     * _.get(object, 'a.b.c', 'default');
     * // => 'default'
     */
    function get(object, path, defaultValue) {
      var result = object == null ? undefined : baseGet$1(object, path);
      return result === undefined ? defaultValue : result;
    }
    var get_1 = get;
    var lodashGet = get_1;

    /**
     * Checks for a numeric match within a certain tolerance.
     *
     * @param {number} value1
     * @param {number} value2
     * @param {number} tolerance
     */
    function checkMatch(value1, value2, tolerance) {
      return value1 - tolerance <= value2 && value2 <= value1 + tolerance;
    }

    function getLinkDim(link) {
      return link.experimentType === '1d' ? 1 : 2;
    }

    function getLinkDelta(link) {
      if (!link.pseudo) {
        return getLinkDim(link) === 1 ? link.signal.delta : link.axis === 'x' ? link.signal.x.delta : link.signal.y.delta;
      }
    }

    function getCorrelationDelta(correlation) {
      if (correlation.link.length > 0) return getLinkDelta(correlation.link[0]);
    }

    /**
     * Sets proton counts from DEPT90 signals and DEPT135/edited HSQC signals.
     *
     * @param {Values} correlations
     * @param {Array<{ delta: number }>} signals90
     * @param {Array<{ delta: number; sign?: number }>} signals135
     * @param {number} toleranceAtomType
     */
    function setProtonsCount(correlations, signals90, signals135, toleranceAtomType) {
      // eslint-disable-next-line @typescript-eslint/prefer-for-of
      for (let i = 0; i < correlations.length; i++) {
        const correlation = correlations[i];
        if (correlation.edited.protonsCount) {
          // do not overwrite a manually edited value
          continue;
        }
        const correlationDelta = getCorrelationDelta(correlation);
        const match = [-1, -1];
        for (let k = 0; k < signals90.length; k++) {
          if (
          // signals90[k].sign === 1 &&
          correlationDelta !== undefined && checkMatch(correlationDelta, signals90[k].delta, toleranceAtomType)) {
            match[0] = k;
            break;
          }
        }
        for (let k = 0; k < signals135.length; k++) {
          if (correlationDelta !== undefined && checkMatch(correlationDelta, signals135[k].delta, toleranceAtomType)) {
            match[1] = k;
            break;
          }
        }
        if (match[0] >= 0) {
          // signal match in DEPT90
          // CH
          correlation.protonsCount = [1];
          continue;
        }
        // no signal match in DEPT90
        if (match[1] >= 0) {
          // signal match in DEPT135
          if (signals135[match[1]].sign === 1) {
            // positive signal
            if (signals90.length > 0) {
              // in case of both DEPT90 and DEPT135 are given
              // CH3
              correlation.protonsCount = [3];
            } else {
              // in case of DEPT135 is given only
              // CH or CH3
              correlation.protonsCount = [1, 3];
            }
          } else {
            // negative signal
            // CH2
            correlation.protonsCount = [2];
          }
        } else if (signals135.length > 0) {
          // no signal match in both spectra
          // qC
          correlation.protonsCount = [0];
        } else {
          // no information
          correlation.protonsCount = [];
        }
      }
      return correlations;
    }

    /**
     * Sets proton counts from DEPT90 signals and DEPT135 signals.
     *
     * @param {Values} correlations
     * @param {Experiment1DSignals} signalsDEPT
     * @param {Tolerance} tolerance
     * @param {string} atomType
     */
    function setProtonsCountFromDEPT(correlations, signalsDEPT, tolerance, atomType) {
      const correlationsAtomType = getCorrelationsByAtomType(correlations, atomType).filter(correlation => !correlation.pseudo);
      const signalsDEPT90 = lodashGet(signalsDEPT, '90', []).filter(signalDEPT90 => signalDEPT90.atomType === atomType).map(signalDEPT90 => signalDEPT90.signal);
      const signalsDEPT135 = lodashGet(signalsDEPT, '135', []).filter(signalDEPT135 => signalDEPT135.atomType === atomType).map(signalDEPT135 => signalDEPT135.signal);
      setProtonsCount(correlationsAtomType, signalsDEPT90, signalsDEPT135, tolerance[atomType]);
      return correlations;
    }

    /**
     * Sets proton counts from edited HSQC signals.
     *
     * @param {Values} correlations
     * @param {Experiment2DSignals} signals2D
     * @param {Tolerance} tolerance
     * @param {string} heavyAtomType
     */
    function setProtonsCountFromEditedHSQC(correlations, signals2D, tolerance, heavyAtomType) {
      const correlationsAtomTypeHSQC = correlations.filter(correlation => !correlation.pseudo && correlation.atomType === heavyAtomType);
      const signalsEditedHSQC = lodashGet(signals2D, 'hsqc', []).filter(signal2D => signal2D.atomType[1] === heavyAtomType && signal2D.signal.sign !== 0).map(signal2D => {
        return {
          delta: signal2D.signal.y.delta,
          sign: signal2D.signal.sign
        };
      });
      setProtonsCount(correlationsAtomTypeHSQC, [], signalsEditedHSQC, tolerance[heavyAtomType]);
      return correlations;
    }

    /**
     * Sets proton counts from DEPT90 and/or DEPT135/edited HSQC signals, depending on what spectra data is given.
     *
     * @param {Values} correlations
     * @param {Experiment1DSignals} signalsDEPT
     * @param {Experiment2DSignals} signals2D
     * @param {Tolerance} tolerance
     */
    function setProtonsCountFromData(correlations, signalsDEPT, signals2D, tolerance) {
      const heavyAtomTypes = [];
      correlations.forEach(correlation => {
        if (!correlation.pseudo && correlation.atomType !== 'H' && !heavyAtomTypes.includes(correlation.atomType)) {
          heavyAtomTypes.push(correlation.atomType);
          if (Object.keys(signalsDEPT).length > 0) {
            setProtonsCountFromDEPT(correlations, signalsDEPT, tolerance, correlation.atomType);
          } else {
            setProtonsCountFromEditedHSQC(correlations, signals2D, tolerance, correlation.atomType);
          }
        }
      });
      return correlations;
    }

    /**
     * Get all different experiments from spectra data.
     *
     * @param {Spectra} spectraData
     */
    function getExperiments(spectraData) {
      if (!spectraData) return {};
      const experiments = {};
      for (const spectrum of spectraData) {
        if (spectrum.info.isFid) continue;
        if (!experiments[`${spectrum.info.dimension}D`]) {
          experiments[`${spectrum.info.dimension}D`] = {};
        }
        const experiment = spectrum.info.experiment;
        if (!experiments[`${spectrum.info.dimension}D`][`${experiment}`]) {
          experiments[`${spectrum.info.dimension}D`][`${experiment}`] = [];
        }
        experiments[`${spectrum.info.dimension}D`][`${experiment}`].push(spectrum);
      }
      return experiments;
    }

    /**
     * Returns the atom type from nucleus.
     * @param {string} nucleus
     */
    function getAtomTypeFromNucleus(nucleus) {
      return nucleus.length > 0 ? nucleus.split(/\d+/)[1] : '';
    }

    /**
     * Add experiment of certain type to experiments.
     *
     * @param {Experiments} experiments
     * @param {ExperimentsType} experimentsType
     * @param {string} type
     * @param {boolean} checkAtomType
     * @param {string} experimentKey
     */
    function addToExperiments(experiments, experimentsType, type, checkAtomType, experimentKey) {
      const _experiments = lodashGet(experiments, `${type}`, []) // don't consider DEPT etc. here
      .filter(_experiment => {
        const hasValues = lodashGet(_experiment, type.includes('1D') ? 'ranges.values' : 'zones.values', []).length > 0;
        return checkAtomType ? getAtomTypeFromNucleus(_experiment.info.nucleus) === experimentKey && hasValues : hasValues;
      });
      if (_experiments.length > 0) {
        experimentsType[experimentKey] = _experiments;
      }
    }

    /**
     * Get "plain" 1D experiments containing ranges, i.e. without DEPT etc..
     *
     * @param {Spectra} spectraData
     */
    function getExperiments1D(experiments) {
      const _experiments1D = {};
      lodashGet(experiments, '1D.1d', []).map(experiment => getAtomTypeFromNucleus(experiment.info.nucleus)).forEach(atomType => {
        addToExperiments(experiments, _experiments1D, '1D.1d', true, atomType);
      });
      return _experiments1D;
    }

    /**
     * Get "extra" 1D experiments containing ranges, e.g. DEPT.
     *
     * @param {Spectra} spectraData
     */
    function getExperiments1DExtra(experiments) {
      const _experiments1DExtra = {};
      Object.keys(lodashGet(experiments, `1D`, {})).filter(experimentType => experimentType !== '1d') // don't consider "plain" 1D experiments here
      .forEach(experimentType => {
        addToExperiments(experiments, _experiments1DExtra, `1D.${experimentType}`, false, experimentType);
      });
      return _experiments1DExtra;
    }

    /**
     * Get 2D experiments containing zones.
     *
     * @param {Spectra} spectraData
     */
    function getExperiments2D(experiments) {
      const _experiments2D = {};
      Object.keys(lodashGet(experiments, '2D', {})).forEach(experimentType => {
        addToExperiments(experiments, _experiments2D, `2D.${experimentType}`, false, experimentType);
      });
      return _experiments2D;
    }

    const allowedSignalKinds = ['signal'];

    /**
     * Get all different 1D signals from experiments with allowed signal kinds in "signalKindsToInclude".
     *
     * @param {ExperimentsType} experiments1D
     */
    function getSignals1D(experiments1D) {
      // store valid signals from 1D experiments
      const signals1DByAtomType = {};
      for (const atomType in experiments1D) {
        const signals = [];
        const experiment = experiments1D[`${atomType}`] || [];
        if (experiment.length === 0) continue;
        const index = 0;
        const spectrum1D = experiment[index];
        for (const range of spectrum1D.ranges.values) {
          for (const signal of range.signals) {
            if (!signal.kind || !allowedSignalKinds.includes(signal.kind)) continue;
            if (!checkExistence(signal, signals)) {
              signals.push({
                experimentType: '1d',
                experimentID: spectrum1D.id,
                integration: signal.integration ? signal.integration : range.integration,
                atomType,
                signal: {
                  ...signal
                }
              });
            }
          }
        }
        signals1DByAtomType[atomType] = signals;
      }
      return signals1DByAtomType;
    }
    function checkExistence(current, group) {
      for (const element of group) {
        if (checkMatch(element.signal.delta, current.delta, 0.0)) {
          return true;
        }
      }
      return false;
    }

    var ListCache$2 = _ListCache;

    /**
     * Removes all key-value entries from the stack.
     *
     * @private
     * @name clear
     * @memberOf Stack
     */
    function stackClear$1() {
      this.__data__ = new ListCache$2();
      this.size = 0;
    }
    var _stackClear = stackClear$1;

    /**
     * Removes `key` and its value from the stack.
     *
     * @private
     * @name delete
     * @memberOf Stack
     * @param {string} key The key of the value to remove.
     * @returns {boolean} Returns `true` if the entry was removed, else `false`.
     */
    function stackDelete$1(key) {
      var data = this.__data__,
        result = data['delete'](key);
      this.size = data.size;
      return result;
    }
    var _stackDelete = stackDelete$1;

    /**
     * Gets the stack value for `key`.
     *
     * @private
     * @name get
     * @memberOf Stack
     * @param {string} key The key of the value to get.
     * @returns {*} Returns the entry value.
     */
    function stackGet$1(key) {
      return this.__data__.get(key);
    }
    var _stackGet = stackGet$1;

    /**
     * Checks if a stack value for `key` exists.
     *
     * @private
     * @name has
     * @memberOf Stack
     * @param {string} key The key of the entry to check.
     * @returns {boolean} Returns `true` if an entry for `key` exists, else `false`.
     */
    function stackHas$1(key) {
      return this.__data__.has(key);
    }
    var _stackHas = stackHas$1;

    var ListCache$1 = _ListCache,
      Map$2 = _Map,
      MapCache$1 = _MapCache;

    /** Used as the size to enable large array optimizations. */
    var LARGE_ARRAY_SIZE = 200;

    /**
     * Sets the stack `key` to `value`.
     *
     * @private
     * @name set
     * @memberOf Stack
     * @param {string} key The key of the value to set.
     * @param {*} value The value to set.
     * @returns {Object} Returns the stack cache instance.
     */
    function stackSet$1(key, value) {
      var data = this.__data__;
      if (data instanceof ListCache$1) {
        var pairs = data.__data__;
        if (!Map$2 || pairs.length < LARGE_ARRAY_SIZE - 1) {
          pairs.push([key, value]);
          this.size = ++data.size;
          return this;
        }
        data = this.__data__ = new MapCache$1(pairs);
      }
      data.set(key, value);
      this.size = data.size;
      return this;
    }
    var _stackSet = stackSet$1;

    var ListCache = _ListCache,
      stackClear = _stackClear,
      stackDelete = _stackDelete,
      stackGet = _stackGet,
      stackHas = _stackHas,
      stackSet = _stackSet;

    /**
     * Creates a stack cache object to store key-value pairs.
     *
     * @private
     * @constructor
     * @param {Array} [entries] The key-value pairs to cache.
     */
    function Stack$2(entries) {
      var data = this.__data__ = new ListCache(entries);
      this.size = data.size;
    }

    // Add methods to `Stack`.
    Stack$2.prototype.clear = stackClear;
    Stack$2.prototype['delete'] = stackDelete;
    Stack$2.prototype.get = stackGet;
    Stack$2.prototype.has = stackHas;
    Stack$2.prototype.set = stackSet;
    var _Stack = Stack$2;

    /**
     * A specialized version of `_.forEach` for arrays without support for
     * iteratee shorthands.
     *
     * @private
     * @param {Array} [array] The array to iterate over.
     * @param {Function} iteratee The function invoked per iteration.
     * @returns {Array} Returns `array`.
     */
    function arrayEach$1(array, iteratee) {
      var index = -1,
        length = array == null ? 0 : array.length;
      while (++index < length) {
        if (iteratee(array[index], index, array) === false) {
          break;
        }
      }
      return array;
    }
    var _arrayEach = arrayEach$1;

    var getNative$4 = _getNative;
    var defineProperty$2 = function () {
      try {
        var func = getNative$4(Object, 'defineProperty');
        func({}, '', {});
        return func;
      } catch (e) {}
    }();
    var _defineProperty = defineProperty$2;

    var defineProperty$1 = _defineProperty;

    /**
     * The base implementation of `assignValue` and `assignMergeValue` without
     * value checks.
     *
     * @private
     * @param {Object} object The object to modify.
     * @param {string} key The key of the property to assign.
     * @param {*} value The value to assign.
     */
    function baseAssignValue$2(object, key, value) {
      if (key == '__proto__' && defineProperty$1) {
        defineProperty$1(object, key, {
          'configurable': true,
          'enumerable': true,
          'value': value,
          'writable': true
        });
      } else {
        object[key] = value;
      }
    }
    var _baseAssignValue = baseAssignValue$2;

    var baseAssignValue$1 = _baseAssignValue,
      eq$1 = eq_1;

    /** Used for built-in method references. */
    var objectProto$a = Object.prototype;

    /** Used to check objects for own properties. */
    var hasOwnProperty$8 = objectProto$a.hasOwnProperty;

    /**
     * Assigns `value` to `key` of `object` if the existing value is not equivalent
     * using [`SameValueZero`](http://ecma-international.org/ecma-262/7.0/#sec-samevaluezero)
     * for equality comparisons.
     *
     * @private
     * @param {Object} object The object to modify.
     * @param {string} key The key of the property to assign.
     * @param {*} value The value to assign.
     */
    function assignValue$2(object, key, value) {
      var objValue = object[key];
      if (!(hasOwnProperty$8.call(object, key) && eq$1(objValue, value)) || value === undefined && !(key in object)) {
        baseAssignValue$1(object, key, value);
      }
    }
    var _assignValue = assignValue$2;

    var assignValue$1 = _assignValue,
      baseAssignValue = _baseAssignValue;

    /**
     * Copies properties of `source` to `object`.
     *
     * @private
     * @param {Object} source The object to copy properties from.
     * @param {Array} props The property identifiers to copy.
     * @param {Object} [object={}] The object to copy properties to.
     * @param {Function} [customizer] The function to customize copied values.
     * @returns {Object} Returns `object`.
     */
    function copyObject$5(source, props, object, customizer) {
      var isNew = !object;
      object || (object = {});
      var index = -1,
        length = props.length;
      while (++index < length) {
        var key = props[index];
        var newValue = customizer ? customizer(object[key], source[key], key, object, source) : undefined;
        if (newValue === undefined) {
          newValue = source[key];
        }
        if (isNew) {
          baseAssignValue(object, key, newValue);
        } else {
          assignValue$1(object, key, newValue);
        }
      }
      return object;
    }
    var _copyObject = copyObject$5;

    /**
     * The base implementation of `_.times` without support for iteratee shorthands
     * or max array length checks.
     *
     * @private
     * @param {number} n The number of times to invoke `iteratee`.
     * @param {Function} iteratee The function invoked per iteration.
     * @returns {Array} Returns the array of results.
     */
    function baseTimes$1(n, iteratee) {
      var index = -1,
        result = Array(n);
      while (++index < n) {
        result[index] = iteratee(index);
      }
      return result;
    }
    var _baseTimes = baseTimes$1;

    var baseGetTag$3 = _baseGetTag,
      isObjectLike$6 = isObjectLike_1;

    /** `Object#toString` result references. */
    var argsTag$3 = '[object Arguments]';

    /**
     * The base implementation of `_.isArguments`.
     *
     * @private
     * @param {*} value The value to check.
     * @returns {boolean} Returns `true` if `value` is an `arguments` object,
     */
    function baseIsArguments$1(value) {
      return isObjectLike$6(value) && baseGetTag$3(value) == argsTag$3;
    }
    var _baseIsArguments = baseIsArguments$1;

    var baseIsArguments = _baseIsArguments,
      isObjectLike$5 = isObjectLike_1;

    /** Used for built-in method references. */
    var objectProto$9 = Object.prototype;

    /** Used to check objects for own properties. */
    var hasOwnProperty$7 = objectProto$9.hasOwnProperty;

    /** Built-in value references. */
    var propertyIsEnumerable$1 = objectProto$9.propertyIsEnumerable;

    /**
     * Checks if `value` is likely an `arguments` object.
     *
     * @static
     * @memberOf _
     * @since 0.1.0
     * @category Lang
     * @param {*} value The value to check.
     * @returns {boolean} Returns `true` if `value` is an `arguments` object,
     *  else `false`.
     * @example
     *
     * _.isArguments(function() { return arguments; }());
     * // => true
     *
     * _.isArguments([1, 2, 3]);
     * // => false
     */
    var isArguments$2 = baseIsArguments(function () {
      return arguments;
    }()) ? baseIsArguments : function (value) {
      return isObjectLike$5(value) && hasOwnProperty$7.call(value, 'callee') && !propertyIsEnumerable$1.call(value, 'callee');
    };
    var isArguments_1 = isArguments$2;

    var isBuffer$3 = {exports: {}};

    /**
     * This method returns `false`.
     *
     * @static
     * @memberOf _
     * @since 4.13.0
     * @category Util
     * @returns {boolean} Returns `false`.
     * @example
     *
     * _.times(2, _.stubFalse);
     * // => [false, false]
     */
    function stubFalse() {
      return false;
    }
    var stubFalse_1 = stubFalse;

    (function (module, exports) {
      var root = _root,
        stubFalse = stubFalse_1;

      /** Detect free variable `exports`. */
      var freeExports = exports && !exports.nodeType && exports;

      /** Detect free variable `module`. */
      var freeModule = freeExports && 'object' == 'object' && module && !module.nodeType && module;

      /** Detect the popular CommonJS extension `module.exports`. */
      var moduleExports = freeModule && freeModule.exports === freeExports;

      /** Built-in value references. */
      var Buffer = moduleExports ? root.Buffer : undefined;

      /* Built-in method references for those with the same name as other `lodash` methods. */
      var nativeIsBuffer = Buffer ? Buffer.isBuffer : undefined;

      /**
       * Checks if `value` is a buffer.
       *
       * @static
       * @memberOf _
       * @since 4.3.0
       * @category Lang
       * @param {*} value The value to check.
       * @returns {boolean} Returns `true` if `value` is a buffer, else `false`.
       * @example
       *
       * _.isBuffer(new Buffer(2));
       * // => true
       *
       * _.isBuffer(new Uint8Array(2));
       * // => false
       */
      var isBuffer = nativeIsBuffer || stubFalse;
      module.exports = isBuffer;
    })(isBuffer$3, isBuffer$3.exports);

    /** Used as references for various `Number` constants. */
    var MAX_SAFE_INTEGER$1 = 9007199254740991;

    /** Used to detect unsigned integer values. */
    var reIsUint = /^(?:0|[1-9]\d*)$/;

    /**
     * Checks if `value` is a valid array-like index.
     *
     * @private
     * @param {*} value The value to check.
     * @param {number} [length=MAX_SAFE_INTEGER] The upper bounds of a valid index.
     * @returns {boolean} Returns `true` if `value` is a valid index, else `false`.
     */
    function isIndex$1(value, length) {
      var type = typeof value;
      length = length == null ? MAX_SAFE_INTEGER$1 : length;
      return !!length && (type == 'number' || type != 'symbol' && reIsUint.test(value)) && value > -1 && value % 1 == 0 && value < length;
    }
    var _isIndex = isIndex$1;

    /** Used as references for various `Number` constants. */
    var MAX_SAFE_INTEGER = 9007199254740991;

    /**
     * Checks if `value` is a valid array-like length.
     *
     * **Note:** This method is loosely based on
     * [`ToLength`](http://ecma-international.org/ecma-262/7.0/#sec-tolength).
     *
     * @static
     * @memberOf _
     * @since 4.0.0
     * @category Lang
     * @param {*} value The value to check.
     * @returns {boolean} Returns `true` if `value` is a valid length, else `false`.
     * @example
     *
     * _.isLength(3);
     * // => true
     *
     * _.isLength(Number.MIN_VALUE);
     * // => false
     *
     * _.isLength(Infinity);
     * // => false
     *
     * _.isLength('3');
     * // => false
     */
    function isLength$2(value) {
      return typeof value == 'number' && value > -1 && value % 1 == 0 && value <= MAX_SAFE_INTEGER;
    }
    var isLength_1 = isLength$2;

    var baseGetTag$2 = _baseGetTag,
      isLength$1 = isLength_1,
      isObjectLike$4 = isObjectLike_1;

    /** `Object#toString` result references. */
    var argsTag$2 = '[object Arguments]',
      arrayTag$2 = '[object Array]',
      boolTag$3 = '[object Boolean]',
      dateTag$3 = '[object Date]',
      errorTag$2 = '[object Error]',
      funcTag$1 = '[object Function]',
      mapTag$5 = '[object Map]',
      numberTag$3 = '[object Number]',
      objectTag$4 = '[object Object]',
      regexpTag$3 = '[object RegExp]',
      setTag$5 = '[object Set]',
      stringTag$3 = '[object String]',
      weakMapTag$2 = '[object WeakMap]';
    var arrayBufferTag$3 = '[object ArrayBuffer]',
      dataViewTag$4 = '[object DataView]',
      float32Tag$2 = '[object Float32Array]',
      float64Tag$2 = '[object Float64Array]',
      int8Tag$2 = '[object Int8Array]',
      int16Tag$2 = '[object Int16Array]',
      int32Tag$2 = '[object Int32Array]',
      uint8Tag$2 = '[object Uint8Array]',
      uint8ClampedTag$2 = '[object Uint8ClampedArray]',
      uint16Tag$2 = '[object Uint16Array]',
      uint32Tag$2 = '[object Uint32Array]';

    /** Used to identify `toStringTag` values of typed arrays. */
    var typedArrayTags = {};
    typedArrayTags[float32Tag$2] = typedArrayTags[float64Tag$2] = typedArrayTags[int8Tag$2] = typedArrayTags[int16Tag$2] = typedArrayTags[int32Tag$2] = typedArrayTags[uint8Tag$2] = typedArrayTags[uint8ClampedTag$2] = typedArrayTags[uint16Tag$2] = typedArrayTags[uint32Tag$2] = true;
    typedArrayTags[argsTag$2] = typedArrayTags[arrayTag$2] = typedArrayTags[arrayBufferTag$3] = typedArrayTags[boolTag$3] = typedArrayTags[dataViewTag$4] = typedArrayTags[dateTag$3] = typedArrayTags[errorTag$2] = typedArrayTags[funcTag$1] = typedArrayTags[mapTag$5] = typedArrayTags[numberTag$3] = typedArrayTags[objectTag$4] = typedArrayTags[regexpTag$3] = typedArrayTags[setTag$5] = typedArrayTags[stringTag$3] = typedArrayTags[weakMapTag$2] = false;

    /**
     * The base implementation of `_.isTypedArray` without Node.js optimizations.
     *
     * @private
     * @param {*} value The value to check.
     * @returns {boolean} Returns `true` if `value` is a typed array, else `false`.
     */
    function baseIsTypedArray$1(value) {
      return isObjectLike$4(value) && isLength$1(value.length) && !!typedArrayTags[baseGetTag$2(value)];
    }
    var _baseIsTypedArray = baseIsTypedArray$1;

    /**
     * The base implementation of `_.unary` without support for storing metadata.
     *
     * @private
     * @param {Function} func The function to cap arguments for.
     * @returns {Function} Returns the new capped function.
     */
    function baseUnary$3(func) {
      return function (value) {
        return func(value);
      };
    }
    var _baseUnary = baseUnary$3;

    var _nodeUtil = {exports: {}};

    (function (module, exports) {
      var freeGlobal = _freeGlobal;

      /** Detect free variable `exports`. */
      var freeExports = exports && !exports.nodeType && exports;

      /** Detect free variable `module`. */
      var freeModule = freeExports && 'object' == 'object' && module && !module.nodeType && module;

      /** Detect the popular CommonJS extension `module.exports`. */
      var moduleExports = freeModule && freeModule.exports === freeExports;

      /** Detect free variable `process` from Node.js. */
      var freeProcess = moduleExports && freeGlobal.process;

      /** Used to access faster Node.js helpers. */
      var nodeUtil = function () {
        try {
          // Use `util.types` for Node.js 10+.
          var types = freeModule && freeModule.require && freeModule.require('util').types;
          if (types) {
            return types;
          }

          // Legacy `process.binding('util')` for Node.js < 10.
          return freeProcess && freeProcess.binding && freeProcess.binding('util');
        } catch (e) {}
      }();
      module.exports = nodeUtil;
    })(_nodeUtil, _nodeUtil.exports);

    var baseIsTypedArray = _baseIsTypedArray,
      baseUnary$2 = _baseUnary,
      nodeUtil$2 = _nodeUtil.exports;

    /* Node.js helper references. */
    var nodeIsTypedArray = nodeUtil$2 && nodeUtil$2.isTypedArray;

    /**
     * Checks if `value` is classified as a typed array.
     *
     * @static
     * @memberOf _
     * @since 3.0.0
     * @category Lang
     * @param {*} value The value to check.
     * @returns {boolean} Returns `true` if `value` is a typed array, else `false`.
     * @example
     *
     * _.isTypedArray(new Uint8Array);
     * // => true
     *
     * _.isTypedArray([]);
     * // => false
     */
    var isTypedArray$2 = nodeIsTypedArray ? baseUnary$2(nodeIsTypedArray) : baseIsTypedArray;
    var isTypedArray_1 = isTypedArray$2;

    var baseTimes = _baseTimes,
      isArguments$1 = isArguments_1,
      isArray$4 = isArray_1,
      isBuffer$2 = isBuffer$3.exports,
      isIndex = _isIndex,
      isTypedArray$1 = isTypedArray_1;

    /** Used for built-in method references. */
    var objectProto$8 = Object.prototype;

    /** Used to check objects for own properties. */
    var hasOwnProperty$6 = objectProto$8.hasOwnProperty;

    /**
     * Creates an array of the enumerable property names of the array-like `value`.
     *
     * @private
     * @param {*} value The value to query.
     * @param {boolean} inherited Specify returning inherited property names.
     * @returns {Array} Returns the array of property names.
     */
    function arrayLikeKeys$2(value, inherited) {
      var isArr = isArray$4(value),
        isArg = !isArr && isArguments$1(value),
        isBuff = !isArr && !isArg && isBuffer$2(value),
        isType = !isArr && !isArg && !isBuff && isTypedArray$1(value),
        skipIndexes = isArr || isArg || isBuff || isType,
        result = skipIndexes ? baseTimes(value.length, String) : [],
        length = result.length;
      for (var key in value) {
        if ((inherited || hasOwnProperty$6.call(value, key)) && !(skipIndexes && (
        // Safari 9 has enumerable `arguments.length` in strict mode.
        key == 'length' ||
        // Node.js 0.10 has enumerable non-index properties on buffers.
        isBuff && (key == 'offset' || key == 'parent') ||
        // PhantomJS 2 has enumerable non-index properties on typed arrays.
        isType && (key == 'buffer' || key == 'byteLength' || key == 'byteOffset') ||
        // Skip index properties.
        isIndex(key, length)))) {
          result.push(key);
        }
      }
      return result;
    }
    var _arrayLikeKeys = arrayLikeKeys$2;

    /** Used for built-in method references. */
    var objectProto$7 = Object.prototype;

    /**
     * Checks if `value` is likely a prototype object.
     *
     * @private
     * @param {*} value The value to check.
     * @returns {boolean} Returns `true` if `value` is a prototype, else `false`.
     */
    function isPrototype$3(value) {
      var Ctor = value && value.constructor,
        proto = typeof Ctor == 'function' && Ctor.prototype || objectProto$7;
      return value === proto;
    }
    var _isPrototype = isPrototype$3;

    /**
     * Creates a unary function that invokes `func` with its argument transformed.
     *
     * @private
     * @param {Function} func The function to wrap.
     * @param {Function} transform The argument transform.
     * @returns {Function} Returns the new function.
     */
    function overArg$2(func, transform) {
      return function (arg) {
        return func(transform(arg));
      };
    }
    var _overArg = overArg$2;

    var overArg$1 = _overArg;

    /* Built-in method references for those with the same name as other `lodash` methods. */
    var nativeKeys$1 = overArg$1(Object.keys, Object);
    var _nativeKeys = nativeKeys$1;

    var isPrototype$2 = _isPrototype,
      nativeKeys = _nativeKeys;

    /** Used for built-in method references. */
    var objectProto$6 = Object.prototype;

    /** Used to check objects for own properties. */
    var hasOwnProperty$5 = objectProto$6.hasOwnProperty;

    /**
     * The base implementation of `_.keys` which doesn't treat sparse arrays as dense.
     *
     * @private
     * @param {Object} object The object to query.
     * @returns {Array} Returns the array of property names.
     */
    function baseKeys$1(object) {
      if (!isPrototype$2(object)) {
        return nativeKeys(object);
      }
      var result = [];
      for (var key in Object(object)) {
        if (hasOwnProperty$5.call(object, key) && key != 'constructor') {
          result.push(key);
        }
      }
      return result;
    }
    var _baseKeys = baseKeys$1;

    var isFunction = isFunction_1,
      isLength = isLength_1;

    /**
     * Checks if `value` is array-like. A value is considered array-like if it's
     * not a function and has a `value.length` that's an integer greater than or
     * equal to `0` and less than or equal to `Number.MAX_SAFE_INTEGER`.
     *
     * @static
     * @memberOf _
     * @since 4.0.0
     * @category Lang
     * @param {*} value The value to check.
     * @returns {boolean} Returns `true` if `value` is array-like, else `false`.
     * @example
     *
     * _.isArrayLike([1, 2, 3]);
     * // => true
     *
     * _.isArrayLike(document.body.children);
     * // => true
     *
     * _.isArrayLike('abc');
     * // => true
     *
     * _.isArrayLike(_.noop);
     * // => false
     */
    function isArrayLike$2(value) {
      return value != null && isLength(value.length) && !isFunction(value);
    }
    var isArrayLike_1 = isArrayLike$2;

    var arrayLikeKeys$1 = _arrayLikeKeys,
      baseKeys = _baseKeys,
      isArrayLike$1 = isArrayLike_1;

    /**
     * Creates an array of the own enumerable property names of `object`.
     *
     * **Note:** Non-object values are coerced to objects. See the
     * [ES spec](http://ecma-international.org/ecma-262/7.0/#sec-object.keys)
     * for more details.
     *
     * @static
     * @since 0.1.0
     * @memberOf _
     * @category Object
     * @param {Object} object The object to query.
     * @returns {Array} Returns the array of property names.
     * @example
     *
     * function Foo() {
     *   this.a = 1;
     *   this.b = 2;
     * }
     *
     * Foo.prototype.c = 3;
     *
     * _.keys(new Foo);
     * // => ['a', 'b'] (iteration order is not guaranteed)
     *
     * _.keys('hi');
     * // => ['0', '1']
     */
    function keys$3(object) {
      return isArrayLike$1(object) ? arrayLikeKeys$1(object) : baseKeys(object);
    }
    var keys_1 = keys$3;

    var copyObject$4 = _copyObject,
      keys$2 = keys_1;

    /**
     * The base implementation of `_.assign` without support for multiple sources
     * or `customizer` functions.
     *
     * @private
     * @param {Object} object The destination object.
     * @param {Object} source The source object.
     * @returns {Object} Returns `object`.
     */
    function baseAssign$1(object, source) {
      return object && copyObject$4(source, keys$2(source), object);
    }
    var _baseAssign = baseAssign$1;

    /**
     * This function is like
     * [`Object.keys`](http://ecma-international.org/ecma-262/7.0/#sec-object.keys)
     * except that it includes inherited enumerable properties.
     *
     * @private
     * @param {Object} object The object to query.
     * @returns {Array} Returns the array of property names.
     */
    function nativeKeysIn$1(object) {
      var result = [];
      if (object != null) {
        for (var key in Object(object)) {
          result.push(key);
        }
      }
      return result;
    }
    var _nativeKeysIn = nativeKeysIn$1;

    var isObject$2 = isObject_1,
      isPrototype$1 = _isPrototype,
      nativeKeysIn = _nativeKeysIn;

    /** Used for built-in method references. */
    var objectProto$5 = Object.prototype;

    /** Used to check objects for own properties. */
    var hasOwnProperty$4 = objectProto$5.hasOwnProperty;

    /**
     * The base implementation of `_.keysIn` which doesn't treat sparse arrays as dense.
     *
     * @private
     * @param {Object} object The object to query.
     * @returns {Array} Returns the array of property names.
     */
    function baseKeysIn$1(object) {
      if (!isObject$2(object)) {
        return nativeKeysIn(object);
      }
      var isProto = isPrototype$1(object),
        result = [];
      for (var key in object) {
        if (!(key == 'constructor' && (isProto || !hasOwnProperty$4.call(object, key)))) {
          result.push(key);
        }
      }
      return result;
    }
    var _baseKeysIn = baseKeysIn$1;

    var arrayLikeKeys = _arrayLikeKeys,
      baseKeysIn = _baseKeysIn,
      isArrayLike = isArrayLike_1;

    /**
     * Creates an array of the own and inherited enumerable property names of `object`.
     *
     * **Note:** Non-object values are coerced to objects.
     *
     * @static
     * @memberOf _
     * @since 3.0.0
     * @category Object
     * @param {Object} object The object to query.
     * @returns {Array} Returns the array of property names.
     * @example
     *
     * function Foo() {
     *   this.a = 1;
     *   this.b = 2;
     * }
     *
     * Foo.prototype.c = 3;
     *
     * _.keysIn(new Foo);
     * // => ['a', 'b', 'c'] (iteration order is not guaranteed)
     */
    function keysIn$3(object) {
      return isArrayLike(object) ? arrayLikeKeys(object, true) : baseKeysIn(object);
    }
    var keysIn_1 = keysIn$3;

    var copyObject$3 = _copyObject,
      keysIn$2 = keysIn_1;

    /**
     * The base implementation of `_.assignIn` without support for multiple sources
     * or `customizer` functions.
     *
     * @private
     * @param {Object} object The destination object.
     * @param {Object} source The source object.
     * @returns {Object} Returns `object`.
     */
    function baseAssignIn$1(object, source) {
      return object && copyObject$3(source, keysIn$2(source), object);
    }
    var _baseAssignIn = baseAssignIn$1;

    var _cloneBuffer = {exports: {}};

    (function (module, exports) {
      var root = _root;

      /** Detect free variable `exports`. */
      var freeExports = exports && !exports.nodeType && exports;

      /** Detect free variable `module`. */
      var freeModule = freeExports && 'object' == 'object' && module && !module.nodeType && module;

      /** Detect the popular CommonJS extension `module.exports`. */
      var moduleExports = freeModule && freeModule.exports === freeExports;

      /** Built-in value references. */
      var Buffer = moduleExports ? root.Buffer : undefined,
        allocUnsafe = Buffer ? Buffer.allocUnsafe : undefined;

      /**
       * Creates a clone of  `buffer`.
       *
       * @private
       * @param {Buffer} buffer The buffer to clone.
       * @param {boolean} [isDeep] Specify a deep clone.
       * @returns {Buffer} Returns the cloned buffer.
       */
      function cloneBuffer(buffer, isDeep) {
        if (isDeep) {
          return buffer.slice();
        }
        var length = buffer.length,
          result = allocUnsafe ? allocUnsafe(length) : new buffer.constructor(length);
        buffer.copy(result);
        return result;
      }
      module.exports = cloneBuffer;
    })(_cloneBuffer, _cloneBuffer.exports);

    /**
     * Copies the values of `source` to `array`.
     *
     * @private
     * @param {Array} source The array to copy values from.
     * @param {Array} [array=[]] The array to copy values to.
     * @returns {Array} Returns `array`.
     */
    function copyArray$1(source, array) {
      var index = -1,
        length = source.length;
      array || (array = Array(length));
      while (++index < length) {
        array[index] = source[index];
      }
      return array;
    }
    var _copyArray = copyArray$1;

    /**
     * A specialized version of `_.filter` for arrays without support for
     * iteratee shorthands.
     *
     * @private
     * @param {Array} [array] The array to iterate over.
     * @param {Function} predicate The function invoked per iteration.
     * @returns {Array} Returns the new filtered array.
     */
    function arrayFilter$1(array, predicate) {
      var index = -1,
        length = array == null ? 0 : array.length,
        resIndex = 0,
        result = [];
      while (++index < length) {
        var value = array[index];
        if (predicate(value, index, array)) {
          result[resIndex++] = value;
        }
      }
      return result;
    }
    var _arrayFilter = arrayFilter$1;

    /**
     * This method returns a new empty array.
     *
     * @static
     * @memberOf _
     * @since 4.13.0
     * @category Util
     * @returns {Array} Returns the new empty array.
     * @example
     *
     * var arrays = _.times(2, _.stubArray);
     *
     * console.log(arrays);
     * // => [[], []]
     *
     * console.log(arrays[0] === arrays[1]);
     * // => false
     */
    function stubArray$2() {
      return [];
    }
    var stubArray_1 = stubArray$2;

    var arrayFilter = _arrayFilter,
      stubArray$1 = stubArray_1;

    /** Used for built-in method references. */
    var objectProto$4 = Object.prototype;

    /** Built-in value references. */
    var propertyIsEnumerable = objectProto$4.propertyIsEnumerable;

    /* Built-in method references for those with the same name as other `lodash` methods. */
    var nativeGetSymbols$1 = Object.getOwnPropertySymbols;

    /**
     * Creates an array of the own enumerable symbols of `object`.
     *
     * @private
     * @param {Object} object The object to query.
     * @returns {Array} Returns the array of symbols.
     */
    var getSymbols$3 = !nativeGetSymbols$1 ? stubArray$1 : function (object) {
      if (object == null) {
        return [];
      }
      object = Object(object);
      return arrayFilter(nativeGetSymbols$1(object), function (symbol) {
        return propertyIsEnumerable.call(object, symbol);
      });
    };
    var _getSymbols = getSymbols$3;

    var copyObject$2 = _copyObject,
      getSymbols$2 = _getSymbols;

    /**
     * Copies own symbols of `source` to `object`.
     *
     * @private
     * @param {Object} source The object to copy symbols from.
     * @param {Object} [object={}] The object to copy symbols to.
     * @returns {Object} Returns `object`.
     */
    function copySymbols$1(source, object) {
      return copyObject$2(source, getSymbols$2(source), object);
    }
    var _copySymbols = copySymbols$1;

    /**
     * Appends the elements of `values` to `array`.
     *
     * @private
     * @param {Array} array The array to modify.
     * @param {Array} values The values to append.
     * @returns {Array} Returns `array`.
     */
    function arrayPush$3(array, values) {
      var index = -1,
        length = values.length,
        offset = array.length;
      while (++index < length) {
        array[offset + index] = values[index];
      }
      return array;
    }
    var _arrayPush = arrayPush$3;

    var overArg = _overArg;

    /** Built-in value references. */
    var getPrototype$3 = overArg(Object.getPrototypeOf, Object);
    var _getPrototype = getPrototype$3;

    var arrayPush$2 = _arrayPush,
      getPrototype$2 = _getPrototype,
      getSymbols$1 = _getSymbols,
      stubArray = stubArray_1;

    /* Built-in method references for those with the same name as other `lodash` methods. */
    var nativeGetSymbols = Object.getOwnPropertySymbols;

    /**
     * Creates an array of the own and inherited enumerable symbols of `object`.
     *
     * @private
     * @param {Object} object The object to query.
     * @returns {Array} Returns the array of symbols.
     */
    var getSymbolsIn$2 = !nativeGetSymbols ? stubArray : function (object) {
      var result = [];
      while (object) {
        arrayPush$2(result, getSymbols$1(object));
        object = getPrototype$2(object);
      }
      return result;
    };
    var _getSymbolsIn = getSymbolsIn$2;

    var copyObject$1 = _copyObject,
      getSymbolsIn$1 = _getSymbolsIn;

    /**
     * Copies own and inherited symbols of `source` to `object`.
     *
     * @private
     * @param {Object} source The object to copy symbols from.
     * @param {Object} [object={}] The object to copy symbols to.
     * @returns {Object} Returns `object`.
     */
    function copySymbolsIn$1(source, object) {
      return copyObject$1(source, getSymbolsIn$1(source), object);
    }
    var _copySymbolsIn = copySymbolsIn$1;

    var arrayPush$1 = _arrayPush,
      isArray$3 = isArray_1;

    /**
     * The base implementation of `getAllKeys` and `getAllKeysIn` which uses
     * `keysFunc` and `symbolsFunc` to get the enumerable property names and
     * symbols of `object`.
     *
     * @private
     * @param {Object} object The object to query.
     * @param {Function} keysFunc The function to get the keys of `object`.
     * @param {Function} symbolsFunc The function to get the symbols of `object`.
     * @returns {Array} Returns the array of property names and symbols.
     */
    function baseGetAllKeys$2(object, keysFunc, symbolsFunc) {
      var result = keysFunc(object);
      return isArray$3(object) ? result : arrayPush$1(result, symbolsFunc(object));
    }
    var _baseGetAllKeys = baseGetAllKeys$2;

    var baseGetAllKeys$1 = _baseGetAllKeys,
      getSymbols = _getSymbols,
      keys$1 = keys_1;

    /**
     * Creates an array of own enumerable property names and symbols of `object`.
     *
     * @private
     * @param {Object} object The object to query.
     * @returns {Array} Returns the array of property names and symbols.
     */
    function getAllKeys$2(object) {
      return baseGetAllKeys$1(object, keys$1, getSymbols);
    }
    var _getAllKeys = getAllKeys$2;

    var baseGetAllKeys = _baseGetAllKeys,
      getSymbolsIn = _getSymbolsIn,
      keysIn$1 = keysIn_1;

    /**
     * Creates an array of own and inherited enumerable property names and
     * symbols of `object`.
     *
     * @private
     * @param {Object} object The object to query.
     * @returns {Array} Returns the array of property names and symbols.
     */
    function getAllKeysIn$2(object) {
      return baseGetAllKeys(object, keysIn$1, getSymbolsIn);
    }
    var _getAllKeysIn = getAllKeysIn$2;

    var getNative$3 = _getNative,
      root$4 = _root;

    /* Built-in method references that are verified to be native. */
    var DataView$2 = getNative$3(root$4, 'DataView');
    var _DataView = DataView$2;

    var getNative$2 = _getNative,
      root$3 = _root;

    /* Built-in method references that are verified to be native. */
    var Promise$2 = getNative$2(root$3, 'Promise');
    var _Promise = Promise$2;

    var getNative$1 = _getNative,
      root$2 = _root;

    /* Built-in method references that are verified to be native. */
    var Set$2 = getNative$1(root$2, 'Set');
    var _Set = Set$2;

    var getNative = _getNative,
      root$1 = _root;

    /* Built-in method references that are verified to be native. */
    var WeakMap$1 = getNative(root$1, 'WeakMap');
    var _WeakMap = WeakMap$1;

    var DataView$1 = _DataView,
      Map$1 = _Map,
      Promise$1 = _Promise,
      Set$1 = _Set,
      WeakMap = _WeakMap,
      baseGetTag$1 = _baseGetTag,
      toSource = _toSource;

    /** `Object#toString` result references. */
    var mapTag$4 = '[object Map]',
      objectTag$3 = '[object Object]',
      promiseTag = '[object Promise]',
      setTag$4 = '[object Set]',
      weakMapTag$1 = '[object WeakMap]';
    var dataViewTag$3 = '[object DataView]';

    /** Used to detect maps, sets, and weakmaps. */
    var dataViewCtorString = toSource(DataView$1),
      mapCtorString = toSource(Map$1),
      promiseCtorString = toSource(Promise$1),
      setCtorString = toSource(Set$1),
      weakMapCtorString = toSource(WeakMap);

    /**
     * Gets the `toStringTag` of `value`.
     *
     * @private
     * @param {*} value The value to query.
     * @returns {string} Returns the `toStringTag`.
     */
    var getTag$4 = baseGetTag$1;

    // Fallback for data views, maps, sets, and weak maps in IE 11 and promises in Node.js < 6.
    if (DataView$1 && getTag$4(new DataView$1(new ArrayBuffer(1))) != dataViewTag$3 || Map$1 && getTag$4(new Map$1()) != mapTag$4 || Promise$1 && getTag$4(Promise$1.resolve()) != promiseTag || Set$1 && getTag$4(new Set$1()) != setTag$4 || WeakMap && getTag$4(new WeakMap()) != weakMapTag$1) {
      getTag$4 = function (value) {
        var result = baseGetTag$1(value),
          Ctor = result == objectTag$3 ? value.constructor : undefined,
          ctorString = Ctor ? toSource(Ctor) : '';
        if (ctorString) {
          switch (ctorString) {
            case dataViewCtorString:
              return dataViewTag$3;
            case mapCtorString:
              return mapTag$4;
            case promiseCtorString:
              return promiseTag;
            case setCtorString:
              return setTag$4;
            case weakMapCtorString:
              return weakMapTag$1;
          }
        }
        return result;
      };
    }
    var _getTag = getTag$4;

    /** Used for built-in method references. */
    var objectProto$3 = Object.prototype;

    /** Used to check objects for own properties. */
    var hasOwnProperty$3 = objectProto$3.hasOwnProperty;

    /**
     * Initializes an array clone.
     *
     * @private
     * @param {Array} array The array to clone.
     * @returns {Array} Returns the initialized clone.
     */
    function initCloneArray$1(array) {
      var length = array.length,
        result = new array.constructor(length);

      // Add properties assigned by `RegExp#exec`.
      if (length && typeof array[0] == 'string' && hasOwnProperty$3.call(array, 'index')) {
        result.index = array.index;
        result.input = array.input;
      }
      return result;
    }
    var _initCloneArray = initCloneArray$1;

    var root = _root;

    /** Built-in value references. */
    var Uint8Array$3 = root.Uint8Array;
    var _Uint8Array = Uint8Array$3;

    var Uint8Array$2 = _Uint8Array;

    /**
     * Creates a clone of `arrayBuffer`.
     *
     * @private
     * @param {ArrayBuffer} arrayBuffer The array buffer to clone.
     * @returns {ArrayBuffer} Returns the cloned array buffer.
     */
    function cloneArrayBuffer$3(arrayBuffer) {
      var result = new arrayBuffer.constructor(arrayBuffer.byteLength);
      new Uint8Array$2(result).set(new Uint8Array$2(arrayBuffer));
      return result;
    }
    var _cloneArrayBuffer = cloneArrayBuffer$3;

    var cloneArrayBuffer$2 = _cloneArrayBuffer;

    /**
     * Creates a clone of `dataView`.
     *
     * @private
     * @param {Object} dataView The data view to clone.
     * @param {boolean} [isDeep] Specify a deep clone.
     * @returns {Object} Returns the cloned data view.
     */
    function cloneDataView$1(dataView, isDeep) {
      var buffer = isDeep ? cloneArrayBuffer$2(dataView.buffer) : dataView.buffer;
      return new dataView.constructor(buffer, dataView.byteOffset, dataView.byteLength);
    }
    var _cloneDataView = cloneDataView$1;

    /** Used to match `RegExp` flags from their coerced string values. */
    var reFlags = /\w*$/;

    /**
     * Creates a clone of `regexp`.
     *
     * @private
     * @param {Object} regexp The regexp to clone.
     * @returns {Object} Returns the cloned regexp.
     */
    function cloneRegExp$1(regexp) {
      var result = new regexp.constructor(regexp.source, reFlags.exec(regexp));
      result.lastIndex = regexp.lastIndex;
      return result;
    }
    var _cloneRegExp = cloneRegExp$1;

    var Symbol$3 = _Symbol;

    /** Used to convert symbols to primitives and strings. */
    var symbolProto$1 = Symbol$3 ? Symbol$3.prototype : undefined,
      symbolValueOf$1 = symbolProto$1 ? symbolProto$1.valueOf : undefined;

    /**
     * Creates a clone of the `symbol` object.
     *
     * @private
     * @param {Object} symbol The symbol object to clone.
     * @returns {Object} Returns the cloned symbol object.
     */
    function cloneSymbol$1(symbol) {
      return symbolValueOf$1 ? Object(symbolValueOf$1.call(symbol)) : {};
    }
    var _cloneSymbol = cloneSymbol$1;

    var cloneArrayBuffer$1 = _cloneArrayBuffer;

    /**
     * Creates a clone of `typedArray`.
     *
     * @private
     * @param {Object} typedArray The typed array to clone.
     * @param {boolean} [isDeep] Specify a deep clone.
     * @returns {Object} Returns the cloned typed array.
     */
    function cloneTypedArray$1(typedArray, isDeep) {
      var buffer = isDeep ? cloneArrayBuffer$1(typedArray.buffer) : typedArray.buffer;
      return new typedArray.constructor(buffer, typedArray.byteOffset, typedArray.length);
    }
    var _cloneTypedArray = cloneTypedArray$1;

    var cloneArrayBuffer = _cloneArrayBuffer,
      cloneDataView = _cloneDataView,
      cloneRegExp = _cloneRegExp,
      cloneSymbol = _cloneSymbol,
      cloneTypedArray = _cloneTypedArray;

    /** `Object#toString` result references. */
    var boolTag$2 = '[object Boolean]',
      dateTag$2 = '[object Date]',
      mapTag$3 = '[object Map]',
      numberTag$2 = '[object Number]',
      regexpTag$2 = '[object RegExp]',
      setTag$3 = '[object Set]',
      stringTag$2 = '[object String]',
      symbolTag$2 = '[object Symbol]';
    var arrayBufferTag$2 = '[object ArrayBuffer]',
      dataViewTag$2 = '[object DataView]',
      float32Tag$1 = '[object Float32Array]',
      float64Tag$1 = '[object Float64Array]',
      int8Tag$1 = '[object Int8Array]',
      int16Tag$1 = '[object Int16Array]',
      int32Tag$1 = '[object Int32Array]',
      uint8Tag$1 = '[object Uint8Array]',
      uint8ClampedTag$1 = '[object Uint8ClampedArray]',
      uint16Tag$1 = '[object Uint16Array]',
      uint32Tag$1 = '[object Uint32Array]';

    /**
     * Initializes an object clone based on its `toStringTag`.
     *
     * **Note:** This function only supports cloning values with tags of
     * `Boolean`, `Date`, `Error`, `Map`, `Number`, `RegExp`, `Set`, or `String`.
     *
     * @private
     * @param {Object} object The object to clone.
     * @param {string} tag The `toStringTag` of the object to clone.
     * @param {boolean} [isDeep] Specify a deep clone.
     * @returns {Object} Returns the initialized clone.
     */
    function initCloneByTag$1(object, tag, isDeep) {
      var Ctor = object.constructor;
      switch (tag) {
        case arrayBufferTag$2:
          return cloneArrayBuffer(object);
        case boolTag$2:
        case dateTag$2:
          return new Ctor(+object);
        case dataViewTag$2:
          return cloneDataView(object, isDeep);
        case float32Tag$1:
        case float64Tag$1:
        case int8Tag$1:
        case int16Tag$1:
        case int32Tag$1:
        case uint8Tag$1:
        case uint8ClampedTag$1:
        case uint16Tag$1:
        case uint32Tag$1:
          return cloneTypedArray(object, isDeep);
        case mapTag$3:
          return new Ctor();
        case numberTag$2:
        case stringTag$2:
          return new Ctor(object);
        case regexpTag$2:
          return cloneRegExp(object);
        case setTag$3:
          return new Ctor();
        case symbolTag$2:
          return cloneSymbol(object);
      }
    }
    var _initCloneByTag = initCloneByTag$1;

    var isObject$1 = isObject_1;

    /** Built-in value references. */
    var objectCreate = Object.create;

    /**
     * The base implementation of `_.create` without support for assigning
     * properties to the created object.
     *
     * @private
     * @param {Object} proto The object to inherit from.
     * @returns {Object} Returns the new object.
     */
    var baseCreate$1 = function () {
      function object() {}
      return function (proto) {
        if (!isObject$1(proto)) {
          return {};
        }
        if (objectCreate) {
          return objectCreate(proto);
        }
        object.prototype = proto;
        var result = new object();
        object.prototype = undefined;
        return result;
      };
    }();
    var _baseCreate = baseCreate$1;

    var baseCreate = _baseCreate,
      getPrototype$1 = _getPrototype,
      isPrototype = _isPrototype;

    /**
     * Initializes an object clone.
     *
     * @private
     * @param {Object} object The object to clone.
     * @returns {Object} Returns the initialized clone.
     */
    function initCloneObject$1(object) {
      return typeof object.constructor == 'function' && !isPrototype(object) ? baseCreate(getPrototype$1(object)) : {};
    }
    var _initCloneObject = initCloneObject$1;

    var getTag$3 = _getTag,
      isObjectLike$3 = isObjectLike_1;

    /** `Object#toString` result references. */
    var mapTag$2 = '[object Map]';

    /**
     * The base implementation of `_.isMap` without Node.js optimizations.
     *
     * @private
     * @param {*} value The value to check.
     * @returns {boolean} Returns `true` if `value` is a map, else `false`.
     */
    function baseIsMap$1(value) {
      return isObjectLike$3(value) && getTag$3(value) == mapTag$2;
    }
    var _baseIsMap = baseIsMap$1;

    var baseIsMap = _baseIsMap,
      baseUnary$1 = _baseUnary,
      nodeUtil$1 = _nodeUtil.exports;

    /* Node.js helper references. */
    var nodeIsMap = nodeUtil$1 && nodeUtil$1.isMap;

    /**
     * Checks if `value` is classified as a `Map` object.
     *
     * @static
     * @memberOf _
     * @since 4.3.0
     * @category Lang
     * @param {*} value The value to check.
     * @returns {boolean} Returns `true` if `value` is a map, else `false`.
     * @example
     *
     * _.isMap(new Map);
     * // => true
     *
     * _.isMap(new WeakMap);
     * // => false
     */
    var isMap$1 = nodeIsMap ? baseUnary$1(nodeIsMap) : baseIsMap;
    var isMap_1 = isMap$1;

    var getTag$2 = _getTag,
      isObjectLike$2 = isObjectLike_1;

    /** `Object#toString` result references. */
    var setTag$2 = '[object Set]';

    /**
     * The base implementation of `_.isSet` without Node.js optimizations.
     *
     * @private
     * @param {*} value The value to check.
     * @returns {boolean} Returns `true` if `value` is a set, else `false`.
     */
    function baseIsSet$1(value) {
      return isObjectLike$2(value) && getTag$2(value) == setTag$2;
    }
    var _baseIsSet = baseIsSet$1;

    var baseIsSet = _baseIsSet,
      baseUnary = _baseUnary,
      nodeUtil = _nodeUtil.exports;

    /* Node.js helper references. */
    var nodeIsSet = nodeUtil && nodeUtil.isSet;

    /**
     * Checks if `value` is classified as a `Set` object.
     *
     * @static
     * @memberOf _
     * @since 4.3.0
     * @category Lang
     * @param {*} value The value to check.
     * @returns {boolean} Returns `true` if `value` is a set, else `false`.
     * @example
     *
     * _.isSet(new Set);
     * // => true
     *
     * _.isSet(new WeakSet);
     * // => false
     */
    var isSet$1 = nodeIsSet ? baseUnary(nodeIsSet) : baseIsSet;
    var isSet_1 = isSet$1;

    var Stack$1 = _Stack,
      arrayEach = _arrayEach,
      assignValue = _assignValue,
      baseAssign = _baseAssign,
      baseAssignIn = _baseAssignIn,
      cloneBuffer = _cloneBuffer.exports,
      copyArray = _copyArray,
      copySymbols = _copySymbols,
      copySymbolsIn = _copySymbolsIn,
      getAllKeys$1 = _getAllKeys,
      getAllKeysIn$1 = _getAllKeysIn,
      getTag$1 = _getTag,
      initCloneArray = _initCloneArray,
      initCloneByTag = _initCloneByTag,
      initCloneObject = _initCloneObject,
      isArray$2 = isArray_1,
      isBuffer$1 = isBuffer$3.exports,
      isMap = isMap_1,
      isObject = isObject_1,
      isSet = isSet_1,
      keys = keys_1,
      keysIn = keysIn_1;

    /** Used to compose bitmasks for cloning. */
    var CLONE_DEEP_FLAG$2 = 1,
      CLONE_FLAT_FLAG$1 = 2,
      CLONE_SYMBOLS_FLAG$2 = 4;

    /** `Object#toString` result references. */
    var argsTag$1 = '[object Arguments]',
      arrayTag$1 = '[object Array]',
      boolTag$1 = '[object Boolean]',
      dateTag$1 = '[object Date]',
      errorTag$1 = '[object Error]',
      funcTag = '[object Function]',
      genTag = '[object GeneratorFunction]',
      mapTag$1 = '[object Map]',
      numberTag$1 = '[object Number]',
      objectTag$2 = '[object Object]',
      regexpTag$1 = '[object RegExp]',
      setTag$1 = '[object Set]',
      stringTag$1 = '[object String]',
      symbolTag$1 = '[object Symbol]',
      weakMapTag = '[object WeakMap]';
    var arrayBufferTag$1 = '[object ArrayBuffer]',
      dataViewTag$1 = '[object DataView]',
      float32Tag = '[object Float32Array]',
      float64Tag = '[object Float64Array]',
      int8Tag = '[object Int8Array]',
      int16Tag = '[object Int16Array]',
      int32Tag = '[object Int32Array]',
      uint8Tag = '[object Uint8Array]',
      uint8ClampedTag = '[object Uint8ClampedArray]',
      uint16Tag = '[object Uint16Array]',
      uint32Tag = '[object Uint32Array]';

    /** Used to identify `toStringTag` values supported by `_.clone`. */
    var cloneableTags = {};
    cloneableTags[argsTag$1] = cloneableTags[arrayTag$1] = cloneableTags[arrayBufferTag$1] = cloneableTags[dataViewTag$1] = cloneableTags[boolTag$1] = cloneableTags[dateTag$1] = cloneableTags[float32Tag] = cloneableTags[float64Tag] = cloneableTags[int8Tag] = cloneableTags[int16Tag] = cloneableTags[int32Tag] = cloneableTags[mapTag$1] = cloneableTags[numberTag$1] = cloneableTags[objectTag$2] = cloneableTags[regexpTag$1] = cloneableTags[setTag$1] = cloneableTags[stringTag$1] = cloneableTags[symbolTag$1] = cloneableTags[uint8Tag] = cloneableTags[uint8ClampedTag] = cloneableTags[uint16Tag] = cloneableTags[uint32Tag] = true;
    cloneableTags[errorTag$1] = cloneableTags[funcTag] = cloneableTags[weakMapTag] = false;

    /**
     * The base implementation of `_.clone` and `_.cloneDeep` which tracks
     * traversed objects.
     *
     * @private
     * @param {*} value The value to clone.
     * @param {boolean} bitmask The bitmask flags.
     *  1 - Deep clone
     *  2 - Flatten inherited properties
     *  4 - Clone symbols
     * @param {Function} [customizer] The function to customize cloning.
     * @param {string} [key] The key of `value`.
     * @param {Object} [object] The parent object of `value`.
     * @param {Object} [stack] Tracks traversed objects and their clone counterparts.
     * @returns {*} Returns the cloned value.
     */
    function baseClone$2(value, bitmask, customizer, key, object, stack) {
      var result,
        isDeep = bitmask & CLONE_DEEP_FLAG$2,
        isFlat = bitmask & CLONE_FLAT_FLAG$1,
        isFull = bitmask & CLONE_SYMBOLS_FLAG$2;
      if (customizer) {
        result = object ? customizer(value, key, object, stack) : customizer(value);
      }
      if (result !== undefined) {
        return result;
      }
      if (!isObject(value)) {
        return value;
      }
      var isArr = isArray$2(value);
      if (isArr) {
        result = initCloneArray(value);
        if (!isDeep) {
          return copyArray(value, result);
        }
      } else {
        var tag = getTag$1(value),
          isFunc = tag == funcTag || tag == genTag;
        if (isBuffer$1(value)) {
          return cloneBuffer(value, isDeep);
        }
        if (tag == objectTag$2 || tag == argsTag$1 || isFunc && !object) {
          result = isFlat || isFunc ? {} : initCloneObject(value);
          if (!isDeep) {
            return isFlat ? copySymbolsIn(value, baseAssignIn(result, value)) : copySymbols(value, baseAssign(result, value));
          }
        } else {
          if (!cloneableTags[tag]) {
            return object ? value : {};
          }
          result = initCloneByTag(value, tag, isDeep);
        }
      }
      // Check for circular references and return its corresponding clone.
      stack || (stack = new Stack$1());
      var stacked = stack.get(value);
      if (stacked) {
        return stacked;
      }
      stack.set(value, result);
      if (isSet(value)) {
        value.forEach(function (subValue) {
          result.add(baseClone$2(subValue, bitmask, customizer, subValue, value, stack));
        });
      } else if (isMap(value)) {
        value.forEach(function (subValue, key) {
          result.set(key, baseClone$2(subValue, bitmask, customizer, key, value, stack));
        });
      }
      var keysFunc = isFull ? isFlat ? getAllKeysIn$1 : getAllKeys$1 : isFlat ? keysIn : keys;
      var props = isArr ? undefined : keysFunc(value);
      arrayEach(props || value, function (subValue, key) {
        if (props) {
          key = subValue;
          subValue = value[key];
        }
        // Recursively populate clone (susceptible to call stack limits).
        assignValue(result, key, baseClone$2(subValue, bitmask, customizer, key, value, stack));
      });
      return result;
    }
    var _baseClone = baseClone$2;

    var baseClone$1 = _baseClone;

    /** Used to compose bitmasks for cloning. */
    var CLONE_DEEP_FLAG$1 = 1,
      CLONE_SYMBOLS_FLAG$1 = 4;

    /**
     * This method is like `_.clone` except that it recursively clones `value`.
     *
     * @static
     * @memberOf _
     * @since 1.0.0
     * @category Lang
     * @param {*} value The value to recursively clone.
     * @returns {*} Returns the deep cloned value.
     * @see _.clone
     * @example
     *
     * var objects = [{ 'a': 1 }, { 'b': 2 }];
     *
     * var deep = _.cloneDeep(objects);
     * console.log(deep[0] === objects[0]);
     * // => false
     */
    function cloneDeep(value) {
      return baseClone$1(value, CLONE_DEEP_FLAG$1 | CLONE_SYMBOLS_FLAG$1);
    }
    var cloneDeep_1 = cloneDeep;
    var lodashCloneDeep = cloneDeep_1;

    /** Used to stand-in for `undefined` hash values. */
    var HASH_UNDEFINED = '__lodash_hash_undefined__';

    /**
     * Adds `value` to the array cache.
     *
     * @private
     * @name add
     * @memberOf SetCache
     * @alias push
     * @param {*} value The value to cache.
     * @returns {Object} Returns the cache instance.
     */
    function setCacheAdd$1(value) {
      this.__data__.set(value, HASH_UNDEFINED);
      return this;
    }
    var _setCacheAdd = setCacheAdd$1;

    /**
     * Checks if `value` is in the array cache.
     *
     * @private
     * @name has
     * @memberOf SetCache
     * @param {*} value The value to search for.
     * @returns {number} Returns `true` if `value` is found, else `false`.
     */
    function setCacheHas$1(value) {
      return this.__data__.has(value);
    }
    var _setCacheHas = setCacheHas$1;

    var MapCache = _MapCache,
      setCacheAdd = _setCacheAdd,
      setCacheHas = _setCacheHas;

    /**
     *
     * Creates an array cache object to store unique values.
     *
     * @private
     * @constructor
     * @param {Array} [values] The values to cache.
     */
    function SetCache$1(values) {
      var index = -1,
        length = values == null ? 0 : values.length;
      this.__data__ = new MapCache();
      while (++index < length) {
        this.add(values[index]);
      }
    }

    // Add methods to `SetCache`.
    SetCache$1.prototype.add = SetCache$1.prototype.push = setCacheAdd;
    SetCache$1.prototype.has = setCacheHas;
    var _SetCache = SetCache$1;

    /**
     * A specialized version of `_.some` for arrays without support for iteratee
     * shorthands.
     *
     * @private
     * @param {Array} [array] The array to iterate over.
     * @param {Function} predicate The function invoked per iteration.
     * @returns {boolean} Returns `true` if any element passes the predicate check,
     *  else `false`.
     */
    function arraySome$1(array, predicate) {
      var index = -1,
        length = array == null ? 0 : array.length;
      while (++index < length) {
        if (predicate(array[index], index, array)) {
          return true;
        }
      }
      return false;
    }
    var _arraySome = arraySome$1;

    /**
     * Checks if a `cache` value for `key` exists.
     *
     * @private
     * @param {Object} cache The cache to query.
     * @param {string} key The key of the entry to check.
     * @returns {boolean} Returns `true` if an entry for `key` exists, else `false`.
     */
    function cacheHas$1(cache, key) {
      return cache.has(key);
    }
    var _cacheHas = cacheHas$1;

    var SetCache = _SetCache,
      arraySome = _arraySome,
      cacheHas = _cacheHas;

    /** Used to compose bitmasks for value comparisons. */
    var COMPARE_PARTIAL_FLAG$3 = 1,
      COMPARE_UNORDERED_FLAG$1 = 2;

    /**
     * A specialized version of `baseIsEqualDeep` for arrays with support for
     * partial deep comparisons.
     *
     * @private
     * @param {Array} array The array to compare.
     * @param {Array} other The other array to compare.
     * @param {number} bitmask The bitmask flags. See `baseIsEqual` for more details.
     * @param {Function} customizer The function to customize comparisons.
     * @param {Function} equalFunc The function to determine equivalents of values.
     * @param {Object} stack Tracks traversed `array` and `other` objects.
     * @returns {boolean} Returns `true` if the arrays are equivalent, else `false`.
     */
    function equalArrays$2(array, other, bitmask, customizer, equalFunc, stack) {
      var isPartial = bitmask & COMPARE_PARTIAL_FLAG$3,
        arrLength = array.length,
        othLength = other.length;
      if (arrLength != othLength && !(isPartial && othLength > arrLength)) {
        return false;
      }
      // Check that cyclic values are equal.
      var arrStacked = stack.get(array);
      var othStacked = stack.get(other);
      if (arrStacked && othStacked) {
        return arrStacked == other && othStacked == array;
      }
      var index = -1,
        result = true,
        seen = bitmask & COMPARE_UNORDERED_FLAG$1 ? new SetCache() : undefined;
      stack.set(array, other);
      stack.set(other, array);

      // Ignore non-index properties.
      while (++index < arrLength) {
        var arrValue = array[index],
          othValue = other[index];
        if (customizer) {
          var compared = isPartial ? customizer(othValue, arrValue, index, other, array, stack) : customizer(arrValue, othValue, index, array, other, stack);
        }
        if (compared !== undefined) {
          if (compared) {
            continue;
          }
          result = false;
          break;
        }
        // Recursively compare arrays (susceptible to call stack limits).
        if (seen) {
          if (!arraySome(other, function (othValue, othIndex) {
            if (!cacheHas(seen, othIndex) && (arrValue === othValue || equalFunc(arrValue, othValue, bitmask, customizer, stack))) {
              return seen.push(othIndex);
            }
          })) {
            result = false;
            break;
          }
        } else if (!(arrValue === othValue || equalFunc(arrValue, othValue, bitmask, customizer, stack))) {
          result = false;
          break;
        }
      }
      stack['delete'](array);
      stack['delete'](other);
      return result;
    }
    var _equalArrays = equalArrays$2;

    /**
     * Converts `map` to its key-value pairs.
     *
     * @private
     * @param {Object} map The map to convert.
     * @returns {Array} Returns the key-value pairs.
     */
    function mapToArray$1(map) {
      var index = -1,
        result = Array(map.size);
      map.forEach(function (value, key) {
        result[++index] = [key, value];
      });
      return result;
    }
    var _mapToArray = mapToArray$1;

    /**
     * Converts `set` to an array of its values.
     *
     * @private
     * @param {Object} set The set to convert.
     * @returns {Array} Returns the values.
     */
    function setToArray$1(set) {
      var index = -1,
        result = Array(set.size);
      set.forEach(function (value) {
        result[++index] = value;
      });
      return result;
    }
    var _setToArray = setToArray$1;

    var Symbol$2 = _Symbol,
      Uint8Array$1 = _Uint8Array,
      eq = eq_1,
      equalArrays$1 = _equalArrays,
      mapToArray = _mapToArray,
      setToArray = _setToArray;

    /** Used to compose bitmasks for value comparisons. */
    var COMPARE_PARTIAL_FLAG$2 = 1,
      COMPARE_UNORDERED_FLAG = 2;

    /** `Object#toString` result references. */
    var boolTag = '[object Boolean]',
      dateTag = '[object Date]',
      errorTag = '[object Error]',
      mapTag = '[object Map]',
      numberTag = '[object Number]',
      regexpTag = '[object RegExp]',
      setTag = '[object Set]',
      stringTag = '[object String]',
      symbolTag = '[object Symbol]';
    var arrayBufferTag = '[object ArrayBuffer]',
      dataViewTag = '[object DataView]';

    /** Used to convert symbols to primitives and strings. */
    var symbolProto = Symbol$2 ? Symbol$2.prototype : undefined,
      symbolValueOf = symbolProto ? symbolProto.valueOf : undefined;

    /**
     * A specialized version of `baseIsEqualDeep` for comparing objects of
     * the same `toStringTag`.
     *
     * **Note:** This function only supports comparing values with tags of
     * `Boolean`, `Date`, `Error`, `Number`, `RegExp`, or `String`.
     *
     * @private
     * @param {Object} object The object to compare.
     * @param {Object} other The other object to compare.
     * @param {string} tag The `toStringTag` of the objects to compare.
     * @param {number} bitmask The bitmask flags. See `baseIsEqual` for more details.
     * @param {Function} customizer The function to customize comparisons.
     * @param {Function} equalFunc The function to determine equivalents of values.
     * @param {Object} stack Tracks traversed `object` and `other` objects.
     * @returns {boolean} Returns `true` if the objects are equivalent, else `false`.
     */
    function equalByTag$1(object, other, tag, bitmask, customizer, equalFunc, stack) {
      switch (tag) {
        case dataViewTag:
          if (object.byteLength != other.byteLength || object.byteOffset != other.byteOffset) {
            return false;
          }
          object = object.buffer;
          other = other.buffer;
        case arrayBufferTag:
          if (object.byteLength != other.byteLength || !equalFunc(new Uint8Array$1(object), new Uint8Array$1(other))) {
            return false;
          }
          return true;
        case boolTag:
        case dateTag:
        case numberTag:
          // Coerce booleans to `1` or `0` and dates to milliseconds.
          // Invalid dates are coerced to `NaN`.
          return eq(+object, +other);
        case errorTag:
          return object.name == other.name && object.message == other.message;
        case regexpTag:
        case stringTag:
          // Coerce regexes to strings and treat strings, primitives and objects,
          // as equal. See http://www.ecma-international.org/ecma-262/7.0/#sec-regexp.prototype.tostring
          // for more details.
          return object == other + '';
        case mapTag:
          var convert = mapToArray;
        case setTag:
          var isPartial = bitmask & COMPARE_PARTIAL_FLAG$2;
          convert || (convert = setToArray);
          if (object.size != other.size && !isPartial) {
            return false;
          }
          // Assume cyclic values are equal.
          var stacked = stack.get(object);
          if (stacked) {
            return stacked == other;
          }
          bitmask |= COMPARE_UNORDERED_FLAG;

          // Recursively compare objects (susceptible to call stack limits).
          stack.set(object, other);
          var result = equalArrays$1(convert(object), convert(other), bitmask, customizer, equalFunc, stack);
          stack['delete'](object);
          return result;
        case symbolTag:
          if (symbolValueOf) {
            return symbolValueOf.call(object) == symbolValueOf.call(other);
          }
      }
      return false;
    }
    var _equalByTag = equalByTag$1;

    var getAllKeys = _getAllKeys;

    /** Used to compose bitmasks for value comparisons. */
    var COMPARE_PARTIAL_FLAG$1 = 1;

    /** Used for built-in method references. */
    var objectProto$2 = Object.prototype;

    /** Used to check objects for own properties. */
    var hasOwnProperty$2 = objectProto$2.hasOwnProperty;

    /**
     * A specialized version of `baseIsEqualDeep` for objects with support for
     * partial deep comparisons.
     *
     * @private
     * @param {Object} object The object to compare.
     * @param {Object} other The other object to compare.
     * @param {number} bitmask The bitmask flags. See `baseIsEqual` for more details.
     * @param {Function} customizer The function to customize comparisons.
     * @param {Function} equalFunc The function to determine equivalents of values.
     * @param {Object} stack Tracks traversed `object` and `other` objects.
     * @returns {boolean} Returns `true` if the objects are equivalent, else `false`.
     */
    function equalObjects$1(object, other, bitmask, customizer, equalFunc, stack) {
      var isPartial = bitmask & COMPARE_PARTIAL_FLAG$1,
        objProps = getAllKeys(object),
        objLength = objProps.length,
        othProps = getAllKeys(other),
        othLength = othProps.length;
      if (objLength != othLength && !isPartial) {
        return false;
      }
      var index = objLength;
      while (index--) {
        var key = objProps[index];
        if (!(isPartial ? key in other : hasOwnProperty$2.call(other, key))) {
          return false;
        }
      }
      // Check that cyclic values are equal.
      var objStacked = stack.get(object);
      var othStacked = stack.get(other);
      if (objStacked && othStacked) {
        return objStacked == other && othStacked == object;
      }
      var result = true;
      stack.set(object, other);
      stack.set(other, object);
      var skipCtor = isPartial;
      while (++index < objLength) {
        key = objProps[index];
        var objValue = object[key],
          othValue = other[key];
        if (customizer) {
          var compared = isPartial ? customizer(othValue, objValue, key, other, object, stack) : customizer(objValue, othValue, key, object, other, stack);
        }
        // Recursively compare objects (susceptible to call stack limits).
        if (!(compared === undefined ? objValue === othValue || equalFunc(objValue, othValue, bitmask, customizer, stack) : compared)) {
          result = false;
          break;
        }
        skipCtor || (skipCtor = key == 'constructor');
      }
      if (result && !skipCtor) {
        var objCtor = object.constructor,
          othCtor = other.constructor;

        // Non `Object` object instances with different constructors are not equal.
        if (objCtor != othCtor && 'constructor' in object && 'constructor' in other && !(typeof objCtor == 'function' && objCtor instanceof objCtor && typeof othCtor == 'function' && othCtor instanceof othCtor)) {
          result = false;
        }
      }
      stack['delete'](object);
      stack['delete'](other);
      return result;
    }
    var _equalObjects = equalObjects$1;

    var Stack = _Stack,
      equalArrays = _equalArrays,
      equalByTag = _equalByTag,
      equalObjects = _equalObjects,
      getTag = _getTag,
      isArray$1 = isArray_1,
      isBuffer = isBuffer$3.exports,
      isTypedArray = isTypedArray_1;

    /** Used to compose bitmasks for value comparisons. */
    var COMPARE_PARTIAL_FLAG = 1;

    /** `Object#toString` result references. */
    var argsTag = '[object Arguments]',
      arrayTag = '[object Array]',
      objectTag$1 = '[object Object]';

    /** Used for built-in method references. */
    var objectProto$1 = Object.prototype;

    /** Used to check objects for own properties. */
    var hasOwnProperty$1 = objectProto$1.hasOwnProperty;

    /**
     * A specialized version of `baseIsEqual` for arrays and objects which performs
     * deep comparisons and tracks traversed objects enabling objects with circular
     * references to be compared.
     *
     * @private
     * @param {Object} object The object to compare.
     * @param {Object} other The other object to compare.
     * @param {number} bitmask The bitmask flags. See `baseIsEqual` for more details.
     * @param {Function} customizer The function to customize comparisons.
     * @param {Function} equalFunc The function to determine equivalents of values.
     * @param {Object} [stack] Tracks traversed `object` and `other` objects.
     * @returns {boolean} Returns `true` if the objects are equivalent, else `false`.
     */
    function baseIsEqualDeep$1(object, other, bitmask, customizer, equalFunc, stack) {
      var objIsArr = isArray$1(object),
        othIsArr = isArray$1(other),
        objTag = objIsArr ? arrayTag : getTag(object),
        othTag = othIsArr ? arrayTag : getTag(other);
      objTag = objTag == argsTag ? objectTag$1 : objTag;
      othTag = othTag == argsTag ? objectTag$1 : othTag;
      var objIsObj = objTag == objectTag$1,
        othIsObj = othTag == objectTag$1,
        isSameTag = objTag == othTag;
      if (isSameTag && isBuffer(object)) {
        if (!isBuffer(other)) {
          return false;
        }
        objIsArr = true;
        objIsObj = false;
      }
      if (isSameTag && !objIsObj) {
        stack || (stack = new Stack());
        return objIsArr || isTypedArray(object) ? equalArrays(object, other, bitmask, customizer, equalFunc, stack) : equalByTag(object, other, objTag, bitmask, customizer, equalFunc, stack);
      }
      if (!(bitmask & COMPARE_PARTIAL_FLAG)) {
        var objIsWrapped = objIsObj && hasOwnProperty$1.call(object, '__wrapped__'),
          othIsWrapped = othIsObj && hasOwnProperty$1.call(other, '__wrapped__');
        if (objIsWrapped || othIsWrapped) {
          var objUnwrapped = objIsWrapped ? object.value() : object,
            othUnwrapped = othIsWrapped ? other.value() : other;
          stack || (stack = new Stack());
          return equalFunc(objUnwrapped, othUnwrapped, bitmask, customizer, stack);
        }
      }
      if (!isSameTag) {
        return false;
      }
      stack || (stack = new Stack());
      return equalObjects(object, other, bitmask, customizer, equalFunc, stack);
    }
    var _baseIsEqualDeep = baseIsEqualDeep$1;

    var baseIsEqualDeep = _baseIsEqualDeep,
      isObjectLike$1 = isObjectLike_1;

    /**
     * The base implementation of `_.isEqual` which supports partial comparisons
     * and tracks traversed objects.
     *
     * @private
     * @param {*} value The value to compare.
     * @param {*} other The other value to compare.
     * @param {boolean} bitmask The bitmask flags.
     *  1 - Unordered comparison
     *  2 - Partial comparison
     * @param {Function} [customizer] The function to customize comparisons.
     * @param {Object} [stack] Tracks traversed `value` and `other` objects.
     * @returns {boolean} Returns `true` if the values are equivalent, else `false`.
     */
    function baseIsEqual$1(value, other, bitmask, customizer, stack) {
      if (value === other) {
        return true;
      }
      if (value == null || other == null || !isObjectLike$1(value) && !isObjectLike$1(other)) {
        return value !== value && other !== other;
      }
      return baseIsEqualDeep(value, other, bitmask, customizer, baseIsEqual$1, stack);
    }
    var _baseIsEqual = baseIsEqual$1;

    var baseIsEqual = _baseIsEqual;

    /**
     * Performs a deep comparison between two values to determine if they are
     * equivalent.
     *
     * **Note:** This method supports comparing arrays, array buffers, booleans,
     * date objects, error objects, maps, numbers, `Object` objects, regexes,
     * sets, strings, symbols, and typed arrays. `Object` objects are compared
     * by their own, not inherited, enumerable properties. Functions and DOM
     * nodes are compared by strict equality, i.e. `===`.
     *
     * @static
     * @memberOf _
     * @since 0.1.0
     * @category Lang
     * @param {*} value The value to compare.
     * @param {*} other The other value to compare.
     * @returns {boolean} Returns `true` if the values are equivalent, else `false`.
     * @example
     *
     * var object = { 'a': 1 };
     * var other = { 'a': 1 };
     *
     * _.isEqual(object, other);
     * // => true
     *
     * object === other;
     * // => false
     */
    function isEqual(value, other) {
      return baseIsEqual(value, other);
    }
    var isEqual_1 = isEqual;
    var lodashIsEqual = isEqual_1;

    function isEditedHSQC(experiment) {
      // detection whether experiment is an edited HSQC
      return experiment.info.pulseSequence.includes('hsqced');
    }

    /**
     * Get all different 2D signals from experiments with allowed signal kinds in "signalKindsToInclude".
     *
     * @param {ExperimentsType} experiments1D
     */
    function getSignals2D(experiments2D) {
      // store valid signals from 2D experiments
      const _signals2D = {};
      Object.keys(experiments2D).forEach(experimentType => {
        const _signals = [];
        // for now we use the first occurring spectrum only, for each experiment type (current loop) and nuclei combination
        const indices = [];
        const nuclei = [];
        experiments2D[experimentType].forEach((_experiment, i) => {
          const experiment = _experiment;
          if (!nuclei.some(_nuclei => lodashIsEqual(_nuclei, experiment.info.nucleus))) {
            nuclei.push(experiment.info.nucleus);
            indices.push(i);
          }
        });
        indices.forEach(index => {
          const spectrum2D = experiments2D[experimentType][index];
          const atomType = spectrum2D.info.nucleus.map(nucleus => getAtomTypeFromNucleus(nucleus));
          const __signals = spectrum2D.zones.values.map(zone => zone.signals.filter(signal => signal.kind && allowedSignalKinds.includes(signal.kind))).flat();
          __signals.forEach(signal => {
            if (!_signals.some(_signal => checkMatch(_signal.signal.x.delta, signal.x.delta, 0.0) && checkMatch(_signal.signal.y.delta, signal.y.delta, 0.0))) {
              _signals.push({
                experimentType,
                experimentID: spectrum2D.id,
                atomType,
                // @TODO here we assume that only one peak exists for the signal and its intensity indicates the sign
                signal: {
                  ...lodashCloneDeep(signal),
                  sign: isEditedHSQC(spectrum2D) && signal.peaks ? signal.peaks[0].z >= 0 ? 1 : -1 : 0
                }
              });
            }
          });
        });
        _signals2D[experimentType] = _signals;
      });
      return _signals2D;
    }

    /**
     * Get all DEPT signals from experiments with allowed signal kinds in "signalKindsToInclude".
     *
     * @param {ExperimentsType} experiments1DExtra
     */
    function getSignalsDEPT(experiments1DExtra) {
      // store valid signals from 1D extra experiments, e.g. DEPT, APT
      const _signalsDEPT = {};
      // store valid signals from 2D experiments
      Object.keys(experiments1DExtra).filter(experimentType => experimentType === 'dept').forEach(experimentType => experiments1DExtra[experimentType].forEach(_experimentDEPT => {
        const experimentDEPT = _experimentDEPT;
        const _signals = [];
        const match = experimentDEPT.info.pulseSequence.match(/\d/g);
        if (match) {
          const mode = match.reduce((_mode, digit) => _mode + digit);
          const atomType = getAtomTypeFromNucleus(experimentDEPT.info.nucleus);
          const __signals = experimentDEPT.ranges.values.map(range => range.signals.filter(signal => signal.kind && allowedSignalKinds.includes(signal.kind)).map(signal => {
            return {
              ...signal,
              sign: range.absolute > 0 ? 1 : -1
            };
          })).flat();
          __signals.forEach(signal => {
            if (!_signals.some(_signal => checkMatch(_signal.signal.delta, signal.delta, 0.0))) {
              _signals.push({
                experimentType,
                experimentID: experimentDEPT.id,
                mode,
                atomType,
                signal: lodashCloneDeep(signal)
              });
            }
          });
          _signalsDEPT[mode] = _signals;
        }
      }));
      return _signalsDEPT;
    }

    // general remark for all experiment types:
    // build an array of experiments, because one could have more than
    // one spectrum in spectra list for one atom type or experiment type
    /**
     * Get all signals from experiments with allowed signal kinds in "signalKindsToInclude".
     *
     * @param {Spectra} spectraData
     */
    function getSignals(spectraData) {
      const experiments = getExperiments(spectraData);
      const experiments1D = getExperiments1D(experiments);
      const experiments1DExtra = getExperiments1DExtra(experiments);
      const experiments2D = getExperiments2D(experiments);
      const signals1D = getSignals1D(experiments1D);
      const signals2D = getSignals2D(experiments2D);
      const signalsDEPT = getSignalsDEPT(experiments1DExtra);
      return {
        signals1D,
        signals2D,
        signalsDEPT
      };
    }

    const BASE62 = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
    const LENGTH = 8;
    function generateID() {
      let id = '';
      for (let i = 0; i < LENGTH; i++) {
        id += BASE62.charAt(Math.floor(Math.random() * 62));
      }
      return id;
    }

    /**
     * Builds a link object with some existing default values.
     *
     * @param {LinkOptions} options
     */
    function buildLink(options) {
      return {
        id: options.id || generateID(),
        experimentType: options.experimentType,
        experimentID: options.experimentID,
        atomType: options.atomType,
        signal: options.signal,
        axis: options.axis,
        match: options.match || [],
        experimentLabel: options.experimentLabel || '',
        pseudo: options.pseudo || false,
        edited: options.edited || {}
      };
    }

    function findLinkedCorrelationsBySignalID(correlations, signalID) {
      return correlations.filter(_correlation => _correlation.link.some(_link => _link.signal.id === signalID));
    }

    /**
     * Checks whether a correlation contains a link by id search.
     *
     * @param {Correlation} correlation
     * @param {Link} link
     */
    function containsLink(correlation, link) {
      return correlation.link.some(_link => _link.experimentType === link.experimentType && _link.experimentID === link.experimentID && lodashIsEqual(_link.atomType, link.atomType) && _link.signal.id === link.signal.id && _link.axis === link.axis);
    }

    /**
     * Checks whether a correlation has links.
     *
     * @param {Correlation} correlation
     */
    function hasLinks(correlation) {
      return correlation.link.length > 0;
    }

    function addSignal(matchedCorrelationIndices, atomType, link, correlations) {
      if (matchedCorrelationIndices.length === 0) {
        // in case of no signal match -> add new signal from 2D
        const newCorrelation = buildCorrelation({
          atomType
        });
        addLink(newCorrelation, link);
        const pseudoIndex = correlations.findIndex(correlation => correlation.atomType === atomType && correlation.pseudo && !hasLinks(correlation));
        if (pseudoIndex >= 0) {
          correlations[pseudoIndex] = newCorrelation;
        } else {
          correlations.push(newCorrelation);
        }
      } else if (!containsLink(correlations[matchedCorrelationIndices[0]], link)) {
        // if allowed then add links from 2D data in first match only
        addLink(correlations[matchedCorrelationIndices[0]], link);
      }
    }

    /**
     * Adds new correlations from 1D and 2D data or adds links to already existing ones.
     *
     * @param {Values} correlations
     * * @param {Experiment1DSignals} signals1D
     * @param {Experiment2DSignals} signals2D
     * @param {Tolerance} tolerance
     */
    function addFromData(correlations, signals1D, signals2D, tolerance) {
      // remove non-pseudo correlation objects without links
      correlations = correlations.filter(correlation => correlation.link.length > 0 || correlation.pseudo);
      // add from 1D data
      Object.keys(signals1D).forEach(atomType => {
        signals1D[atomType].forEach(signal1D => {
          const linkedCorrelations = findLinkedCorrelationsBySignalID(correlations, signal1D.signal.id || '');
          if (linkedCorrelations.length === 0) {
            const matchedCorrelationIndices = correlations.map((correlation, k) => {
              const correlationDelta = getCorrelationDelta(correlation);
              return !correlation.pseudo && correlation.atomType === atomType && correlationDelta !== undefined && checkMatch(correlationDelta, signal1D.signal.delta, tolerance[atomType]) ? k : -1;
            }).filter(index => index >= 0).filter((index, i, a) => a.indexOf(index) === i);
            const link = buildLink({
              experimentType: signal1D.experimentType,
              experimentID: signal1D.experimentID,
              signal: signal1D.signal,
              atomType: [atomType]
            });
            addSignal(matchedCorrelationIndices, atomType, link, correlations);
          }
        });
      });
      // add from 2D
      // add potential new correlations and push new links via shift matches between 1D vs. 2D and 2D vs. 2D
      Object.keys(signals2D).forEach(experimentType => signals2D[experimentType].forEach(signal2D => {
        const linkedCorrelations = findLinkedCorrelationsBySignalID(correlations, signal2D.signal.id || '');
        if (linkedCorrelations.length === 0) {
          signal2D.atomType.forEach((atomType, dim) => {
            const axis = dim === 0 ? 'x' : 'y';
            const matchedCorrelationIndices = correlations.map((correlation, k) => {
              const correlationDelta = getCorrelationDelta(correlation);
              return !correlation.pseudo && correlation.atomType === atomType && correlationDelta !== undefined && checkMatch(correlationDelta, signal2D.signal[axis].delta, tolerance[atomType]) ? k : -1;
            }).filter(index => index >= 0).filter((index, i, a) => a.indexOf(index) === i);
            const link = buildLink({
              experimentType: signal2D.experimentType,
              experimentID: signal2D.experimentID,
              signal: signal2D.signal,
              axis,
              atomType: signal2D.atomType
            });
            addSignal(matchedCorrelationIndices, atomType, link, correlations);
          });
        }
      }));
      return correlations;
    }

    /**
     * Builds a correlation object with some existing default values.
     *
     * @param {CorrelationOptions} options
     */
    function buildCorrelation(options) {
      return {
        id: options.id || generateID(),
        atomType: options.atomType,
        label: options.label || {},
        link: options.link || [],
        equivalence: options.equivalence || 1,
        attachment: options.attachment || {},
        protonsCount: options.protonsCount || [],
        hybridization: options.hybridization || [],
        pseudo: options.pseudo || false,
        edited: options.edited || {}
      };
    }

    function addPseudoCorrelations(correlations, atoms) {
      for (const atomType in atoms) {
        const atomTypeCount = getCorrelationsByAtomType(correlations, atomType).reduce((sum, correlation) => sum + correlation.equivalence, 0);
        // add missing pseudo correlations
        for (let i = atomTypeCount; i < atoms[atomType]; i++) {
          correlations.push(buildCorrelation({
            atomType,
            pseudo: true
          }));
        }
      }
      return correlations;
    }

    /**
     * Removes a link by id search.
     *
     * @param {Correlation} correlation
     * @param {string} id
     */
    function removeLink(correlation, id) {
      correlation.link = correlation.link.filter(_link => _link.id !== id);
      return correlation;
    }

    /**
     * Removes a match index from a link.
     *
     * @param {Link} link
     * @param {number} index
     */
    function removeMatch(link, index) {
      const indexOf = link.match.indexOf(index);
      if (indexOf >= 0) {
        link.match.splice(indexOf, 1);
      }
      return link;
    }

    function checkPseudoCorrelations(correlations, atoms) {
      for (const atomType in atoms) {
        // consider also pseudo correlations
        const correlationsAtomType = getCorrelationsByAtomType(correlations, atomType);
        if (correlationsAtomType.length > atoms[atomType]) {
          // remove pseudo correlations which are out of limit and not linked
          const pseudoCorrelationsAtomType = correlationsAtomType.filter(correlation => correlation.pseudo && correlation.equivalence === 1 && !hasLinks(correlation));
          for (let i = correlationsAtomType.length - 1; i >= atoms[atomType]; i--) {
            if (pseudoCorrelationsAtomType.length === 0) {
              break;
            }
            const pseudoCorrelationToRemove = pseudoCorrelationsAtomType.pop();
            if (pseudoCorrelationToRemove) {
              correlations.splice(correlations.indexOf(pseudoCorrelationToRemove), 1);
            }
          }
        }
      }
      // check for deleted links and correct proton counts if no HSQC link exists
      for (const pseudoCorrelation of correlations) {
        if (!pseudoCorrelation.pseudo || pseudoCorrelation.equivalence > 1 || hasLinks(pseudoCorrelation)) {
          continue;
        }
        // remove wrong (old) match indices and empty links
        const linksToRemove = [];
        const pseudoCorrelationIndex = getCorrelationIndex(correlations, pseudoCorrelation);
        for (const pseudoLink of pseudoCorrelation.link) {
          for (const matchIndex of pseudoLink.match) {
            if (!correlations[matchIndex] || !correlations[matchIndex].link.some(_link => _link.match.includes(pseudoCorrelationIndex))) {
              removeMatch(pseudoLink, matchIndex);
            }
          }
          if (pseudoLink.match.length === 0) {
            linksToRemove.push(pseudoLink);
          }
        }
        for (const pseudoLink of linksToRemove) {
          removeLink(pseudoCorrelation, pseudoLink.id);
        }
        // correct protons count if no HSQC link was found anymore and the field was not edited manually
        if (!pseudoCorrelation.edited.protonsCount && !pseudoCorrelation.link.some(pseudoLink => pseudoLink.experimentType === 'hsqc')) {
          pseudoCorrelation.protonsCount = [];
        }
      }
      // filter out correlations with unknown atom types from previous molecular formula
      return Object.keys(atoms).length > 0 ? correlations.filter(correlation => Object.keys(atoms).includes(correlation.atomType)) : correlations;
    }

    function replacePseudoCorrelationsByEquivalences(correlations, atoms) {
      for (const atomType in atoms) {
        // remove pseudo correlations to be replaced by equivalences, starting at the end
        const correlationsAtomType = getCorrelationsByAtomType(correlations, atomType);
        const validCorrelationsAtomType = correlationsAtomType.filter(correlation => !correlation.pseudo || correlation.equivalence > 1 || hasLinks(correlation));
        const atomTypeEquivalencesCount = validCorrelationsAtomType.reduce((sum, correlation) => sum + (correlation.equivalence - 1), 0);
        const pseudoCorrelationsAtomType = correlationsAtomType.filter(correlation => correlation.pseudo && !validCorrelationsAtomType.some(validCorrelation => validCorrelation.id === correlation.id));
        for (let i = 0; i < correlationsAtomType.length - (atoms[atomType] - atomTypeEquivalencesCount); i++) {
          if (pseudoCorrelationsAtomType.length === 0) {
            break;
          }
          const pseudoCorrelationToRemove = pseudoCorrelationsAtomType.pop();
          if (pseudoCorrelationToRemove) {
            correlations.splice(correlations.indexOf(pseudoCorrelationToRemove), 1);
          }
        }
      }
      return correlations;
    }

    /**
     * Adds pseudo correlations if needed, checks for replacement
     * by equivalent atoms and cleanup.
     * @param {Values} correlations
     * @param {string} mf
     */
    function updatePseudoCorrelations(correlations, mf) {
      const atoms = getAtomCounts(mf);
      if (Object.keys(atoms).length === 0) {
        correlations = correlations.filter(correlation => !correlation.pseudo);
      }
      // add pseudo correlations
      correlations = addPseudoCorrelations(correlations, atoms);
      // remove pseudo correlations to be replaced by equivalences
      correlations = replacePseudoCorrelationsByEquivalences(correlations, atoms);
      // remove pseudo correlations which are out of limit, clean up links and proton counts
      correlations = checkPseudoCorrelations(correlations, atoms);
      return correlations;
    }

    /**
     * Removes non-pseudo correlations which signal id can not be found or have no links anymore.
     *
     * @param {Values} values
     * @param {Experiment1DSignals} signals1D
     * @param {Experiment2DSignals} signals2D
     */
    function removeObsoleteLinksAndNotLinkedCorrelations(correlations, signals1D, signals2D) {
      const _correlations = correlations.filter(correlation => !correlation.pseudo);
      const removeList = _correlations.slice();
      _correlations.forEach(correlation => {
        for (const link of correlation.link) {
          if (link.experimentType === '1d') {
            // search in 1D data
            if (lodashGet(signals1D, link.atomType[0], []).some(signal1D => signal1D.signal.id === link.signal.id)) {
              const index = removeList.indexOf(correlation);
              if (index >= 0) {
                removeList.splice(index, 1);
              }
            } else if (!link.pseudo) {
              // remove obsolete link to not anymore existing signal
              removeLink(correlation, link.id);
            }
          } else if (lodashGet(signals2D, `${link.experimentType}`, []).some(signal2D => signal2D.signal.id === link.signal.id)) {
            // search in 2D data
            const index = removeList.indexOf(correlation);
            if (index >= 0) {
              removeList.splice(index, 1);
            }
          } else if (!link.pseudo) {
            // remove obsolete link to not anymore existing signal
            removeLink(correlation, link.id);
          }
        }
      });
      removeList.forEach(correlation => {
        const index = correlations.indexOf(correlation); // in case we already removed previously
        if (index >= 0) {
          correlations.splice(index, 1);
        }
      });
      return correlations;
    }

    /**
     * Checks whether an atom type key in attachments already exists.
     *
     * @param {Correlation} correlation
     * @param {string} atomType
     */
    function hasAttachmentAtomType(correlation, atomType) {
      return Boolean(correlation.attachment[atomType] && correlation.attachment[atomType].length > 0);
    }

    /**
     * Creates an attachment array for a certain atom type in a correlation.
     *
     * @param {Correlation} correlation
     * @param {string} atomType
     */
    function addAttachmentAtomType(correlation, atomType) {
      if (!hasAttachmentAtomType(correlation, atomType)) {
        correlation.attachment[atomType] = [];
      }
      return correlation;
    }

    /**
     * Adds an attachment for a certain atom type to a correlation.
     *
     * @param {Correlation} correlation
     * @param {string} atomType
     * @param {number} attachment
     */
    function addAttachment(correlation, atomType, attachment) {
      addAttachmentAtomType(correlation, atomType);
      if (!correlation.attachment[atomType].includes(attachment)) {
        correlation.attachment[atomType].push(attachment);
      }
      return correlation;
    }

    /**
     * Removes all attachments of a correlation.
     *
     * @param {Correlation} correlation
     */
    function removeAttachments(correlation) {
      correlation.attachment = {};
      return correlation;
    }

    /**
     * Sets the attachment information (indices of attached atoms).
     * And from that, the equivalence value for protons can be set too.
     *
     * @param {Values} values
     */
    function setAttachmentsAndProtonEquivalences(correlations) {
      // update attachment information between heavy atoms and protons via HSQC or HMQC
      for (const correlation of correlations) {
        // remove previous set attachments
        removeAttachments(correlation);
        // add attachments
        const linksFiltered = correlation.link.filter(link => link.experimentType === 'hsqc' || link.experimentType === 'hmqc');
        for (const link of linksFiltered) {
          const otherAtomType = link.atomType[link.axis === 'x' ? 1 : 0];
          for (let matchIndex of link.match) {
            addAttachment(correlation, otherAtomType, matchIndex);
          }
        }
      }
      // set previously set equivalences of protons to 0
      for (const correlation of correlations) {
        if (correlation.atomType === 'H') {
          correlation.equivalence = 0;
        }
      }
      // reset previously set proton equivalences and set new ones
      // check heavy atoms with an unambiguous protons count
      for (const correlation of correlations) {
        if (correlation.atomType !== 'H' && correlation.protonsCount.length === 1 && hasAttachmentAtomType(correlation, 'H')) {
          const {
            equivalence,
            protonsCount
          } = correlation;
          let equivalences = 1;
          if (protonsCount[0] === 3) {
            equivalences = 3;
          } else if (protonsCount[0] === 2) {
            equivalences = 2;
          }
          const sharedEquivalences = equivalence * equivalences / correlation.attachment.H.length;
          for (const attachedProtonIndex of correlation.attachment.H) {
            correlations[attachedProtonIndex].equivalence += sharedEquivalences;
          }
        }
      }
      // set unchanged equivalence of protons back to 1
      for (const correlation of correlations) {
        if (correlation.atomType === 'H' && correlation.equivalence === 0) {
          correlation.equivalence = 1;
        }
      }
      return correlations;
    }

    /**
     * Sets the origin (default) label for each correlation, e.g. C1 or H2, depending on its order.
     *
     * @param {Values} values
     */
    function setLabels(correlations) {
      const atomTypeCounts = {};
      correlations.forEach(correlation => {
        if (!lodashGet(atomTypeCounts, correlation.atomType, false)) {
          atomTypeCounts[correlation.atomType] = 0;
        }
        atomTypeCounts[correlation.atomType]++;
        correlation.label.origin = `${correlation.atomType}${atomTypeCounts[correlation.atomType]}`;
      });
      return correlations;
    }

    /**
     * Adds an index to the match array of a link.
     *
     * @param {Link} link
     * @param {number} index
     */
    function addMatch(link, index) {
      if (!link.match.includes(index)) {
        link.match.push(index);
      }
      return link;
    }

    /**
     * Removes all match indices in a link.
     *
     * @param {Link} link
     */
    function removeMatches(link) {
      link.match = [];
      return link;
    }

    /**
     * Sets the match indices for each link within a correlation.
     *
     * @param {Values} values
     */
    function setMatches(correlations) {
      correlations.forEach(correlation => {
        correlation.link.forEach(link => {
          // remove previously added matches
          removeMatches(link);
          // add matches
          const otherAtomType = link.axis === 'x' ? link.atomType[1] : link.atomType[0];
          getCorrelationsByAtomType(correlations, otherAtomType).forEach(correlationOtherAtomType => {
            if (correlation.id !== correlationOtherAtomType.id) {
              const correlationIndexOtherAtomType = getCorrelationIndex(correlations, correlationOtherAtomType);
              correlationOtherAtomType.link.forEach(linkOtherAtomType => {
                // check for correlation match and avoid possible duplicates
                if (linkOtherAtomType.experimentType === link.experimentType && linkOtherAtomType.experimentID === link.experimentID && lodashIsEqual(linkOtherAtomType.atomType, link.atomType) && linkOtherAtomType.signal.id === link.signal.id && linkOtherAtomType.axis !== link.axis) {
                  addMatch(link, correlationIndexOtherAtomType);
                }
              });
            }
          });
        });
      });
      // remove links without any matches
      correlations.forEach(correlation => {
        const linksToRemove = correlation.link.filter(link => {
          var _a;
          return link.match.length === 0 && link.experimentType !== '1d' && !((_a = link.edited) === null || _a === void 0 ? void 0 : _a.moved);
        });
        linksToRemove.forEach(link => removeLink(correlation, link.id));
      });
      return correlations;
    }

    /**
     * Sorts the correlations according to its atom type value and signal delta.
     *
     * @param {Values} values
     */
    function sortCorrelations(correlations) {
      const compareAtomTypes = (atomType1, atomType2) => {
        // C and H highest priority
        if (atomType1 === 'C') {
          return -1;
        }
        if (atomType2 === 'C') {
          return 1;
        }
        if (atomType1 === 'H') {
          return -1;
        }
        if (atomType2 === 'H') {
          return 1;
        }
        // alphabetical sort
        if (atomType1 < atomType2) {
          return -1;
        }
        if (atomType1 > atomType2) {
          return 1;
        }
        return 0;
      };
      const compareCorrelations = (corr1, corr2) => {
        if (!corr1.pseudo && !corr2.pseudo && corr1.link.length > 0 && corr2.link.length > 0) {
          const corr1Delta = getCorrelationDelta(corr1);
          const corr2Delta = getCorrelationDelta(corr2);
          if (corr1Delta !== undefined && corr2Delta !== undefined) {
            if (corr1Delta < corr2Delta) {
              return -1;
            } else if (corr1Delta > corr2Delta) {
              return 1;
            }
          }
        }
        if (!corr1.pseudo && corr2.pseudo) {
          return -1;
        }
        if (corr1.pseudo && !corr2.pseudo) {
          return 1;
        }
        if (corr1.label.origin < corr2.label.origin) {
          return -1;
        }
        if (corr1.label.origin > corr2.label.origin) {
          return 1;
        }
        return 0;
      };
      let sortedCorrelations = [];
      const atomTypes = correlations.map(correlation => correlation.atomType).filter((atomType, i, a) => a.indexOf(atomType) === i);
      atomTypes.sort(compareAtomTypes);
      atomTypes.forEach(atomType => {
        const correlationsAtomType = getCorrelationsByAtomType(correlations, atomType);
        correlationsAtomType.sort(compareCorrelations);
        sortedCorrelations = sortedCorrelations.concat(correlationsAtomType);
      });
      return sortedCorrelations;
    }

    /**
     * Builds the correlation data values from given input spectra and options.
     * @param {Spectra} spectra
     * @param {Options} options
     */
    function buildValues(spectra, options) {
      const {
        tolerance = {},
        mf = '',
        values: prevValues = []
      } = options;
      const signals = getSignals(spectra);
      let _correlations = prevValues ? prevValues.slice() : [];
      if (options.skipDataUpdate !== true) {
        // remove obsolete links/correlations
        _correlations = removeObsoleteLinksAndNotLinkedCorrelations(_correlations, signals.signals1D, signals.signals2D);
        // add signals from either 1D or 2D if not already existing as correlation
        // if a signal already exists then add a link within matched correlation
        _correlations = addFromData(_correlations, signals.signals1D, signals.signals2D, tolerance);
      } else {
        // in case an external movement has led to en empty link array within a correlation
        _correlations = _correlations.filter(correlation => correlation.link.length > 0 || correlation.pseudo);
      }
      // set the number of attached protons via DEPT or edited HSQC
      _correlations = setProtonsCountFromData(_correlations, signals.signalsDEPT, signals.signals2D, tolerance);
      // sort by atom type and shift value
      _correlations = sortCorrelations(_correlations);
      // link signals via matches to same 2D signal: e.g. 13C -> HSQC <- 1H
      setMatches(_correlations);
      // set attachments via HSQC or HMQC
      setAttachmentsAndProtonEquivalences(_correlations);
      // update pseudo correlation
      _correlations = updatePseudoCorrelations(_correlations, mf);
      // set labels
      setLabels(_correlations);
      return _correlations;
    }

    /**
     * Builds the correlation data from spectra data and some options.
     *
     * @param {Spectra} spectra
     * @param {Options} options
     */
    function buildCorrelationData(spectra, options) {
      const values = spectra ? buildValues(spectra, options) : [];
      delete options.values;
      delete options.skipDataUpdate;
      return {
        values,
        options,
        state: buildState(values, options.mf || '')
      };
    }

    /**
     * Adds a link to a correlation.
     *
     * @param {Correlation} correlation
     * @param {Link} link
     */
    function addLink(correlation, link) {
      correlation.link.push(link);
      return correlation;
    }

    function createMapPossibleAssignment(expandMap, props) {
      const {
        restrictionByCS,
        predictions,
        targets
      } = props;
      const {
        tolerance: toleranceCS,
        chemicalShiftRestriction
      } = restrictionByCS;
      const atomTypes = Object.keys(predictions);
      for (const atomType of atomTypes) {
        const errorAbs = toleranceCS[atomType];
        const predictionByAtomType = predictions[atomType];
        const targetByAtomType = targets[atomType];
        if (!expandMap[atomType]) expandMap[atomType] = {};
        for (const diaID in predictionByAtomType) {
          const prediction = predictionByAtomType[diaID];
          expandMap[atomType][diaID] = [];
          if (targetByAtomType) {
            for (const targetID in targetByAtomType) {
              const target = targetByAtomType[targetID];
              const {
                nbAtoms,
                protonsCount: protonsCountFromPrediction
              } = prediction;
              const {
                integration,
                protonsCount
              } = target;
              const couldBeAssigned = !integration ? true : atomType === 'H' ? nbAtoms - integration < 1 : protonsCount.length > 0 ? protonsCount.some(count => protonsCountFromPrediction === count) : true;
              if (couldBeAssigned) {
                if (!chemicalShiftRestriction || typeof prediction.delta === 'undefined') {
                  // Chemical shift is not a restriction
                  expandMap[atomType][diaID].push(targetID);
                } else {
                  const targetDelta = getCorrelationDelta(target);
                  if (targetDelta === undefined) {
                    throw new Error(`Correlation has not delta`);
                  }
                  const distAfterLimit = Math.abs(prediction.delta - targetDelta - errorAbs);
                  if (distAfterLimit < 4 * errorAbs) {
                    expandMap[atomType][diaID].push(targetID);
                  }
                }
              }
            }
          }
          expandMap[atomType][diaID].push('*');
        }
      }
      return expandMap;
    }

    function groupCarbonTargetByIntegrationZone(activeDomainOnTarget, targets, correlations) {
      const targetID = activeDomainOnTarget[0];
      const {
        H: attachments = []
      } = targets[targetID].attachment;
      const targetByIntegral = [{
        targetIDs: [targetID],
        attachments: new Set(attachments)
      }];
      for (let i = 1; i < activeDomainOnTarget.length; i++) {
        const targetID = activeDomainOnTarget[i];
        const target = targets[targetID];
        const {
          H: attachments = []
        } = target.attachment;
        let alone = true;
        for (const group of targetByIntegral) {
          const pertain = attachments.some(attachment => group.attachments.has(attachment));
          if (pertain) {
            alone = false;
            group.targetIDs.push(targetID);
            for (const attachment of attachments) {
              group.attachments.add(attachment);
            }
            break;
          }
        }
        if (alone) {
          targetByIntegral.push({
            targetIDs: [targetID],
            attachments: new Set(attachments)
          });
        }
      }
      return targetByIntegral.map(t => ({
        atomType: 'C',
        targetIDs: t.targetIDs,
        integration: correlations ? Array.from(t.attachments).reduce((sum, index) => {
          return correlations[index].integration + sum;
        }, 0) : 0
      }));
    }

    function partialScore(partial, props) {
      const {
        diaIDPeerPossibleAssignment,
        infoByAtomTypes,
        restrictionByCS,
        predictions,
        targets,
        correlations
      } = props;
      const {
        tolerance: toleranceCS,
        useChemicalShiftScore
      } = restrictionByCS;
      let totalStarts = 0;
      let totalPartial = 0;
      const partialInverse = {};
      const activeDomainOnPrediction = {};
      const atomTypes = Object.keys(partial);
      for (const atomType of atomTypes) {
        let countStars = 0;
        const {
          nbAllowedUnAssigned
        } = infoByAtomTypes[atomType];
        const currentPartialInverse = {};
        const partialAssignment = partial[atomType] || [];
        partialInverse[atomType] = currentPartialInverse;
        activeDomainOnPrediction[atomType] = [];
        totalPartial += partialAssignment.length;
        for (let i = 0; i < partialAssignment.length; i++) {
          const targetID = partialAssignment[i];
          if (targetID && targetID !== '*') {
            activeDomainOnPrediction[atomType].push(i);
            if (!currentPartialInverse[targetID]) {
              currentPartialInverse[targetID] = [];
            }
            currentPartialInverse[targetID].push(diaIDPeerPossibleAssignment[atomType][i]);
          }
          if (targetID === '*') countStars++;
        }
        if (countStars > nbAllowedUnAssigned) return 0;
        totalStarts += countStars;
      }
      const activeDomainOnTarget = {};
      for (const atomType in partialInverse) {
        activeDomainOnTarget[atomType] = Object.keys(partialInverse[atomType]);
      }
      const valuesActiveDomainOnTarget = Object.values(activeDomainOnTarget);
      if (valuesActiveDomainOnTarget.reduce((sum, e) => e.length === 0 ? sum + 1 : sum, 0) === valuesActiveDomainOnTarget.length) {
        return 0;
      }
      const getPredictionByDiaID = getPrediction.bind({}, predictions);
      // check the integration
      const targetByIntegral = [];
      for (const atomType in partial) {
        if (atomType === 'C' && activeDomainOnTarget[atomType].length > 0) {
          targetByIntegral.push(...groupCarbonTargetByIntegrationZone(activeDomainOnTarget[atomType], targets[atomType], correlations));
        } else {
          for (const targetID of activeDomainOnTarget[atomType]) {
            targetByIntegral.push({
              atomType,
              targetIDs: [targetID],
              integration: targets.H[targetID].integration
            });
          }
        }
      }
      for (const group of targetByIntegral) {
        const {
          integration,
          atomType
        } = group;
        if (integration === undefined || isNaN(integration)) continue;
        let total = 0;
        for (const targetID of group.targetIDs) {
          const targetToSource = partialInverse[atomType][targetID];
          for (const diaID of targetToSource) {
            const {
              prediction,
              atomType: atomOfPrediction
            } = getPredictionByDiaID(diaID);
            if (atomType === atomOfPrediction) total += prediction.allHydrogens;
          }
        }
        if (total - integration >= 5) {
          return 0;
        }
      }
      //chemical shift score
      let count = 1;
      let chemicalShiftScore = 1;
      if (useChemicalShiftScore) {
        chemicalShiftScore = 0;
        count = 0;
        for (const atomType of Object.keys(partial)) {
          const partialPeerAtomType = partial[atomType];
          const targetsPeerAtomType = targets[atomType];
          for (let index = 0; index < partialPeerAtomType.length; index++) {
            const targetID = partialPeerAtomType[index];
            if (targetID && targetID !== '*') {
              count++;
              const diaID = diaIDPeerPossibleAssignment[atomType][index];
              const source = predictions[atomType][diaID];
              const target = targetsPeerAtomType[targetID];
              const error = toleranceCS[atomType];
              // if (source.error) {
              //   error = Math.max(source.error, toleranceCS);
              // }
              if (typeof source.delta === 'undefined') {
                // Chemical shift is not a restriction
                chemicalShiftScore += 1;
              } else {
                const targetDelta = getCorrelationDelta(target);
                if (targetDelta === undefined) {
                  throw new Error('correlation has not delta');
                }
                let diff = Math.abs(source.delta - targetDelta);
                if (diff < error) {
                  //@TODO: check for a better discriminant
                  chemicalShiftScore += 1;
                } else {
                  diff = Math.abs(diff - error);
                  chemicalShiftScore += -0.25 / error * diff + 1;
                }
              }
            }
          }
        }
        if (count > 0) {
          chemicalShiftScore /= count;
        }
      }
      let scoreOn2D = 0;
      if (howManyActived(activeDomainOnTarget) > 1) {
        const andConstrains = {};
        let activeDomain = [];
        for (const atomType of Object.keys(activeDomainOnPrediction)) {
          activeDomain = activeDomain.concat(activeDomainOnPrediction[atomType].map(e => ({
            index: e,
            atomType
          })));
        }
        for (let i = 0; i < activeDomain.length; i++) {
          const {
            atomType: atomTypeI,
            index: indexI
          } = activeDomain[i];
          const predictionI = predictions[atomTypeI][diaIDPeerPossibleAssignment[atomTypeI][indexI]];
          for (let j = i + 1; j < activeDomain.length; j++) {
            const {
              atomType: atomTypeJ,
              index: indexJ
            } = activeDomain[j];
            const predictionJ = predictions[atomTypeJ][diaIDPeerPossibleAssignment[atomTypeJ][indexJ]];
            const pathLength = predictionI.pathLength[predictionJ.diaIDIndex];
            const isPossible = pathLength < 5;
            const partialI = partial[atomTypeI][indexI];
            const partialJ = partial[atomTypeJ][indexJ];
            if (!partialI || !partialJ) continue;
            const keyOnTargertMap = partialI > partialJ ? `${partialJ} ${partialI}` : `${partialI} ${partialJ}`;
            const areLinked = checkLinking({
              from: {
                targetID: partialI,
                atomType: atomTypeI
              },
              to: {
                targetID: partialJ,
                atomType: atomTypeJ
              }
            }, targets);
            const partialScore2D = isPossible ? areLinked ? 1 : 0 : !areLinked ? 1 : 0;
            andConstrains[keyOnTargertMap] = andConstrains[keyOnTargertMap] ? Math.max(andConstrains[keyOnTargertMap], partialScore2D) : partialScore2D;
          }
        }
        let sumAnd = 0;
        for (const key in andConstrains) {
          sumAnd += andConstrains[key];
        }
        scoreOn2D = sumAnd / (activeDomain.length * (activeDomain.length - 1) / 2);
      }
      const penaltyByStarts = totalStarts / totalPartial;
      if (chemicalShiftScore === 0) return scoreOn2D - penaltyByStarts;
      if (scoreOn2D === 0) return chemicalShiftScore - penaltyByStarts;
      return (chemicalShiftScore + scoreOn2D) / 2 - penaltyByStarts;
    }
    function checkLinking(partials, correlations) {
      const {
        from,
        to
      } = partials;
      if (from.targetID === to.targetID) return true;
      const correlationI = correlations[from.atomType][from.targetID];
      const correlationJ = correlations[to.atomType][to.targetID];
      for (const linkJ of correlationJ.link) {
        for (const link of correlationI.link) {
          if (link.signal.id === linkJ.signal.id) return true;
        }
      }
      return false;
    }
    function getPrediction(predictions, diaID) {
      const atomTypes = Object.keys(predictions);
      for (const atomType of atomTypes) {
        const predictionByAtomType = predictions[atomType];
        if (!predictionByAtomType) throw new Error(`prediction by ${atomType}`);
        if (predictionByAtomType[diaID]) {
          return {
            atomType,
            prediction: predictionByAtomType[diaID]
          };
        }
      }
      throw new Error(`There is not prediction for ${diaID}`);
    }
    function howManyActived(actived) {
      let sum = 0;
      for (const atom in actived) {
        sum += actived[atom].length;
      }
      return sum;
    }

    function exploreTree(props, infoByAtomTypes, partial, store) {
      const {
        currentAtomTypes,
        restrictionByCS,
        timeout,
        timeStart,
        maxSolutions,
        targets,
        predictions,
        correlations,
        lowerBoundScore,
        possibleAssignmentMap,
        diaIDPeerPossibleAssignment
      } = props;
      if (Date.now() - timeStart > timeout) {
        return store;
      }
      for (const atomType of currentAtomTypes) {
        const {
          currentIndex,
          nSources
        } = infoByAtomTypes[atomType];
        if (currentIndex >= nSources - 1 && partial[atomType][currentIndex] !== null) {
          continue;
        }
        const diaID = diaIDPeerPossibleAssignment[atomType][currentIndex];
        const possibleAssignments = possibleAssignmentMap[atomType][diaID];
        for (const targetID of possibleAssignments) {
          partial[atomType][currentIndex] = targetID;
          const score = partialScore(partial, {
            diaIDPeerPossibleAssignment,
            infoByAtomTypes,
            restrictionByCS,
            predictions,
            correlations,
            targets
          });
          if (score === 0) {
            continue;
          }
          if (isLastOne(currentAtomTypes, infoByAtomTypes) && score >= lowerBoundScore) {
            addSolution(store, {
              predictions,
              partial,
              score,
              maxSolutions
            });
          } else {
            const newInfo = JSON.parse(JSON.stringify(infoByAtomTypes));
            newInfo[atomType].currentIndex += 1;
            exploreTree({
              currentAtomTypes,
              restrictionByCS,
              timeout,
              timeStart,
              maxSolutions,
              targets,
              predictions,
              correlations,
              lowerBoundScore,
              possibleAssignmentMap,
              diaIDPeerPossibleAssignment
            }, newInfo, JSON.parse(JSON.stringify(partial)), store);
          }
        }
      }
    }
    function addSolution(store, props) {
      const {
        score,
        maxSolutions,
        partial,
        predictions
      } = props;
      const solution = {
        assignment: JSON.parse(JSON.stringify(partial)),
        score: score / doubleAssignmentPenalty(partial, predictions)
      };
      if (store.nSolutions >= maxSolutions) {
        if (solution.score > store.solutions.last().score) {
          store.solutions.pollLast();
          store.solutions.add(solution);
        }
      } else {
        store.solutions.add(solution);
        store.nSolutions++;
      }
    }
    function isLastOne(currentAtomTypes, infoByAtomTypes) {
      let lastOne = true;
      for (const atomType of currentAtomTypes) {
        const {
          currentIndex,
          nSources
        } = infoByAtomTypes[atomType];
        lastOne = lastOne && currentIndex >= nSources - 1;
      }
      return lastOne;
    }
    function doubleAssignmentPenalty(partial, predictions) {
      let nbDoubleAssignment = 0;
      for (const atomType in predictions) {
        const nbSources = Object.keys(predictions[atomType]).length;
        const assignments = new Set(partial[atomType]);
        nbDoubleAssignment += nbSources - assignments.size;
      }
      return nbDoubleAssignment > 0 ? 2 * nbDoubleAssignment : 1;
    }

    function isSpectraData1D(spectrum) {
      return 'ranges' in spectrum;
    }

    function searchIndices(signalId, spectra) {
      for (let spectrumIndex = 0; spectrumIndex < spectra.length; spectrumIndex++) {
        const spectrum = spectra[spectrumIndex];
        const data = isSpectraData1D(spectrum) ? spectrum.ranges : spectrum.zones;
        for (let elementIndex = 0; elementIndex < data.length; elementIndex++) {
          const signals = data[elementIndex].signals || [];
          for (let signalIndex = 0; signalIndex < signals.length; signalIndex++) {
            if (signalId === signals[signalIndex].id) {
              return {
                spectrumIndex,
                signalIndex,
                elementIndex
              };
            }
          }
        }
      }
      throw new Error(`There is not a signal with ${signalId} ID`);
    }

    const comparator = (a, b) => {
      return b.score - a.score;
    };
    const predictor = {
      H: predictProton,
      C: predictCarbon
    };
    function checkNMRSignal1D(signals) {
      const keys = ['nbAtoms', 'diaIDs', 'atoms'];
      for (const signal of signals) {
        for (const key of keys) {
          if (!signal[key]) throw new Error(`property ${key} does not exist`);
        }
      }
    }
    const getAllHydrogens = {
      C: (m, i) => m.getAllHydrogens(i),
      H: () => 1
    };
    async function buildAssignments(props) {
      const {
        spectra,
        molecule,
        restrictionByCS,
        timeout,
        minScore,
        nbAllowedUnAssigned = {},
        maxSolutions,
        correlations,
        assignmentOrder,
        predictionOptions,
        predictions: inputPrediction = {},
        targets
      } = props;
      const lowerBoundScore = minScore;
      let store = {
        solutions: new TreeSet$1(comparator),
        nSolutions: 0
      };
      const pathLengthMatrix = getConnectivityMatrix(molecule, {
        pathLength: true
      });
      const infoByAtomType = {};
      const predictions = {};
      let possibleAssignmentMap = {};
      let diaIDPeerPossibleAssignment = {};
      for (const atomTypesToPredict of assignmentOrder) {
        for (const atomType of atomTypesToPredict) {
          const options = predictionOptions[atomType];
          const predictedSignals = inputPrediction[atomType];
          const {
            joinedSignals
          } = predictedSignals ? {
            joinedSignals: predictedSignals
          } : await predictor[atomType](molecule, options);
          checkNMRSignal1D(joinedSignals);
          if (!predictions[atomType]) predictions[atomType] = {};
          let totalHydrogens = 0;
          for (const prediction of joinedSignals) {
            const diaID = prediction.diaIDs[0];
            const index = prediction.atoms[0];
            const allHydrogens = getAllHydrogens[atomType](molecule, index);
            totalHydrogens += prediction.nbAtoms * allHydrogens;
            predictions[atomType][diaID] = {
              ...prediction,
              diaIDIndex: index,
              allHydrogens: prediction.nbAtoms * allHydrogens,
              protonsCount: allHydrogens,
              pathLength: pathLengthMatrix[index]
            };
          }
          for (const diaID in predictions[atomType]) {
            predictions[atomType][diaID].allHydrogens *= 100 / totalHydrogens;
          }
          infoByAtomType[atomType] = {
            nSources: joinedSignals.length,
            currentIndex: 0,
            nbAllowedUnAssigned: nbAllowedUnAssigned[atomType] || 0
          };
        }
        possibleAssignmentMap = createMapPossibleAssignment(possibleAssignmentMap, {
          restrictionByCS,
          predictions,
          targets
        });
        diaIDPeerPossibleAssignment = {};
        for (const atomType in possibleAssignmentMap) {
          diaIDPeerPossibleAssignment[atomType] = Object.keys(possibleAssignmentMap[atomType]);
        }
        const sourceOfPartials = getSourceOfPartials(store, infoByAtomType, atomTypesToPredict);
        store = {
          solutions: new TreeSet$1(comparator),
          nSolutions: 0
        };
        const timeStart = Date.now();
        for (const partial of sourceOfPartials) {
          exploreTree({
            currentAtomTypes: atomTypesToPredict,
            restrictionByCS,
            timeout,
            timeStart,
            targets,
            predictions,
            correlations,
            maxSolutions,
            lowerBoundScore,
            possibleAssignmentMap,
            diaIDPeerPossibleAssignment
          }, infoByAtomType, partial, store);
        }
      }
      return annotateSpectraData({
        store,
        spectra,
        diaIDPeerPossibleAssignment,
        targets
      });
    }
    function annotateSpectraData(input) {
      const {
        store,
        spectra,
        diaIDPeerPossibleAssignment,
        targets
      } = input;
      const {
        solutions
      } = store;
      const mapSignalId = {};
      const atomTypes = Object.keys(targets);
      for (const atomType of atomTypes) {
        const targetByAtomType = targets[atomType];
        for (const targetId in targetByAtomType) {
          const target = targetByAtomType[targetId];
          for (const link of target.link) {
            const signalId = link.signal.id;
            if (mapSignalId[signalId]) continue;
            mapSignalId[link.signal.id] = searchIndices(signalId, spectra);
          }
        }
      }
      const result = [];
      for (const solution of solutions.elements) {
        const spectraResult = JSON.parse(JSON.stringify(spectra));
        const {
          assignment,
          score
        } = solution;
        const atomTypes = Object.keys(assignment);
        for (const atomType of atomTypes) {
          const targetByAtomType = targets[atomType];
          const assignmentPeerAtomType = assignment[atomType];
          for (let index = 0; index < assignmentPeerAtomType.length; index++) {
            const targetID = assignmentPeerAtomType[index];
            if (targetID === '*' || !targetID) continue;
            const target = targetByAtomType[targetID];
            const diaId = diaIDPeerPossibleAssignment[atomType][index];
            for (const link of target.link) {
              const {
                spectrumIndex,
                elementIndex,
                signalIndex
              } = mapSignalId[link.signal.id];
              const spectrum = spectraResult[spectrumIndex];
              if (isSpectraData1D(spectrum)) {
                const {
                  ranges
                } = spectrum;
                const range = ranges[elementIndex];
                const signal = range.signals[signalIndex];
                if (!signal.diaIDs) signal.diaIDs = [];
                if (signal.diaIDs.includes(diaId)) continue;
                signal.diaIDs.push(diaId);
              } else {
                const axis = link.axis;
                const signal = spectrum.zones[elementIndex].signals[signalIndex];
                if (!signal[axis].diaIDs) signal[axis].diaIDs = [];
                if (signal[axis].diaIDs?.includes(diaId)) continue;
                signal[axis].diaIDs?.push(diaId);
              }
            }
          }
        }
        result.push({
          score,
          assignment: spectraResult
        });
      }
      return result;
    }
    function getSourceOfPartials(store, infoByAtomType, currentAtoms) {
      return store.nSolutions > 0 ? store.solutions.elements.map(e => {
        const currentAssignment = e.assignment;
        for (const atom of currentAtoms) {
          currentAssignment[atom] = fillPartial(infoByAtomType[atom].nSources);
        }
        return currentAssignment;
      }) : initializePartials(infoByAtomType, currentAtoms);
    }
    function initializePartials(infoByAtomType, currentAtoms) {
      const partial = {};
      const atomsType = Object.keys(infoByAtomType);
      for (const atom of atomsType) {
        const value = currentAtoms.includes(atom) ? null : '*';
        partial[atom] = fillPartial(infoByAtomType[atom].nSources, value);
      }
      return [partial];
    }
    function fillPartial(nSources, value = null) {
      const partial = new Array(nSources);
      for (let i = 0; i < nSources; i++) {
        partial[i] = value;
      }
      return partial;
    }

    function checkIDs(input = []) {
      const inputClone = JSON.parse(JSON.stringify(input));
      const spectra = [];
      for (const spectraData of inputClone) {
        if (!('info' in spectraData)) {
          throw new Error('each spectrum should contain info properties with nucleus information');
        }
        const {
          info,
          id
        } = spectraData;
        if (isSpectraData1D(spectraData)) {
          const ranges = addIDs(spectraData.ranges);
          spectra.push({
            id,
            info,
            ranges
          });
        } else {
          const data = addIDs(spectraData.zones);
          spectra.push({
            id,
            info,
            zones: data
          });
        }
      }
      return spectra;
    }
    function hasIDs(data) {
      for (const element of data) {
        if (!element.id) throw new Error('A range/zone has not an ID');
        for (const signal of element.signals || []) {
          if (!signal.id) throw new Error('A signal has not an ID');
        }
      }
    }
    function addIDs(data) {
      for (const element of data) {
        if (!element.id) element.id = v4();
        for (const signal of element.signals || []) {
          if (!signal.id) signal.id = v4();
        }
      }
      hasIDs(data);
      return data;
    }

    function formatData(input = []) {
      const inputClone = JSON.parse(JSON.stringify(input));
      const spectra = [];
      for (const spectraData of inputClone) {
        const {
          id,
          info
        } = spectraData;
        if (isSpectraData1D(spectraData)) {
          const ranges = rescaleIntegration({
            id,
            info,
            ranges: spectraData.ranges
          });
          spectra.push({
            id,
            info,
            ranges: {
              values: ranges
            }
          });
        } else {
          spectra.push({
            id,
            info,
            zones: {
              values: spectraData.zones
            }
          });
        }
      }
      return spectra;
    }
    function rescaleIntegration(spectrum) {
      if (spectrum.info.nucleus !== '1H') return spectrum.ranges;
      const totalIntegration = spectrum.ranges.reduce((total, range) => range.integration ? total + range.integration : total, 0);
      for (const range of spectrum.ranges) {
        if (range.integration) {
          range.integration *= 100 / totalIntegration;
          if (range.signals) {
            const totalSignalIntegration = range.signals.reduce((total, signal) => signal.integration ? total + signal.integration : total, 0);
            for (const signal of range.signals) {
              if (signal.integration) {
                signal.integration *= range.integration / totalSignalIntegration;
              } else {
                signal.integration = range.integration;
              }
            }
          }
        }
      }
      return spectrum.ranges;
    }

    function getIntegrationOfAttachedProtons(target, correlations) {
      const {
        H: attachment = []
      } = target.attachment;
      let integration = 0;
      for (const match of attachment) {
        const correlation = correlations[match];
        integration += Number(correlation.link[0].signal.integration);
      }
      return integration;
    }

    function getTargetsAndCorrelations(spectra, options = {}) {
      //add indirect links, if a carbon C1 is attached to a proton H1 that correlating
      //with carbon C2, so the carbon C1 and C2 are also correlating
      const {
        tolerance = {
          C: 0.25,
          H: 0.05
        }
      } = options;
      const spectraData = formatData(spectra);
      const {
        values: correlations
      } = buildCorrelationData(spectraData, {
        tolerance
      });
      const targets = {};
      for (const correlation of correlations) {
        if (correlation.pseudo) continue;
        const {
          id: targetID,
          atomType
        } = correlation;
        if (!targets[atomType]) targets[atomType] = {};
        targets[atomType][targetID] = correlation;
        if (atomType === 'H') {
          targets[atomType][targetID].integration = correlation.link[0].signal.integration;
        } else {
          targets[atomType][targetID].integration = getIntegrationOfAttachedProtons(correlation, correlations);
        }
      }
      return {
        targets,
        correlations
      };
    }

    /**
     * if hsqc is present in experimentTypes we can just predict 13C
     * spectrum and try to assign the carbons first.
     * @param {*} correlations
     * @param {*} justAssign
     */
    function getWorkFlow(correlations, justAssign = []) {
      const experimentTypes = extractExperimentType(correlations);
      const {
        hasHSQC,
        types
      } = experimentTypes;
      if (justAssign.length > 0) {
        return {
          assignmentOrder: justAssign,
          experimentTypes
        };
      }
      const assignmentOrder = [];
      if (hasHSQC) {
        assignmentOrder.push(['C'], ['H']);
      } else {
        assignmentOrder.push(types.filter(type => type === 'H' || type === 'C'));
      }
      return {
        assignmentOrder,
        experimentTypes
      };
    }
    function extractExperimentType(correlations) {
      const types = [];
      for (const correlation of correlations) {
        let experimentType = correlation.experimentType;
        if (experimentType === '1d') {
          experimentType = `${correlation.atomType.toUpperCase()}`;
        }
        if (!types.includes(experimentType)) {
          types.push(experimentType);
        }
        for (const link of correlation.link) {
          experimentType = link.experimentType;
          if (experimentType === '1d') {
            experimentType = `${link.atomType[0].toUpperCase()}`;
          }
          if (!types.includes(experimentType)) {
            types.push(experimentType);
          }
        }
      }
      return {
        types,
        hasProton: types.includes('H'),
        hasCarbon: types.includes('C'),
        hasHSQC: types.includes('hsqc')
      };
    }

    async function getAssignments(input, options = {}) {
      const {
        restrictionByCS = {},
        justAssign,
        minScore = 0.01,
        maxSolutions = 10,
        nbAllowedUnAssigned,
        timeout = 6000,
        predictionOptions = {},
        predictions = {},
        correlation: correlationOptions = {}
      } = options;
      const {
        tolerance = {
          H: 0.2,
          C: 1
        },
        useChemicalShiftScore = false,
        chemicalShiftRestriction = true
      } = restrictionByCS;
      const molecule = input.molecule;
      if (!molecule) throw new Error('It is needed a molecule to assign');
      molecule.addImplicitHydrogens();
      addDiastereotopicMissingChirality(molecule);
      const spectra = checkIDs(input.spectra);
      const {
        targets,
        correlations
      } = getTargetsAndCorrelations(spectra, correlationOptions);
      const {
        assignmentOrder
      } = getWorkFlow(correlations, justAssign);
      const solutions = await buildAssignments({
        restrictionByCS: {
          tolerance,
          useChemicalShiftScore,
          chemicalShiftRestriction
        },
        spectra,
        molecule,
        timeout,
        minScore,
        maxSolutions,
        assignmentOrder,
        nbAllowedUnAssigned,
        correlations,
        targets,
        predictionOptions,
        predictions
      });
      return solutions;
    }

    function selectACSPattern(pattern) {
      const multiplicity = couplingValues[pattern];
      if (!multiplicity) {
        throw new Error(`multiplicity pattern ${pattern} is not supported`);
      }
      return couplingACSPatterns[multiplicity];
    }

    /**
     * Return
     * @param {*} signal
     */
    function signalMultiplicityPattern(signal, options) {
      const js = signal.js;
      let pattern = '';
      if (js && js.length > 0) {
        for (const coupling of js) {
          const {
            multiplicity = ''
          } = coupling;
          pattern += options?.acsFormat ? selectACSPattern(multiplicity) : multiplicity;
        }
      } else if (signal.delta) {
        pattern = '';
      } else {
        pattern = 'm';
      }
      return pattern;
    }

    const globalOptions = {
      h: {
        nucleus: '1H',
        nbDecimalDelta: 2,
        nbDecimalJ: 1,
        observedFrequency: 400
      },
      c: {
        nucleus: '13C',
        nbDecimalDelta: 1,
        nbDecimalJ: 1,
        observedFrequency: 100
      },
      f: {
        nucleus: '19F',
        nbDecimalDelta: 2,
        nbDecimalJ: 1,
        observedFrequency: 400
      }
    };
    function rangesToACS(ranges, options = {}) {
      if (!options.nucleus) options.nucleus = '1H';
      const nucleus = options.nucleus.toLowerCase().replace(/[0-9]/g, '');
      const defaultOptions = globalOptions[nucleus];
      options = {
        ...defaultOptions,
        ascending: false,
        format: 'IMJA',
        ...options
      };
      ranges = JSON.parse(JSON.stringify(ranges));
      if (options.ascending === true) {
        ranges.sort((a, b) => {
          const fromA = Math.min(a.from, a.to);
          const fromB = Math.min(b.from, b.to);
          return fromA - fromB;
        });
      }
      let acsString = formatAcs(ranges, options);
      if (acsString.length > 0) acsString += '.';
      return acsString;
    }
    function formatAcs(ranges, options) {
      let acs = spectroInformation(options);
      if (acs.length === 0) acs = 'δ ';
      const acsRanges = [];
      for (const range of ranges) {
        if (uselessKind(range.kind, options.filter)) continue;
        pushDelta(range, acsRanges, options);
      }
      if (acsRanges.length > 0) {
        return acs + acsRanges.join(', ');
      } else {
        return '';
      }
    }
    function spectroInformation(options) {
      const parenthesis = [];
      let strings = `${formatNucleus(options.nucleus)} NMR`;
      if (options.solvent) {
        parenthesis.push(formatMF(options.solvent));
      }
      if (options.observedFrequency) {
        parenthesis.push(`${(options.observedFrequency * 1).toFixed(0)} MHz`);
      }
      if (parenthesis.length > 0) {
        strings += ` (${parenthesis.join(', ')}): δ `;
      } else {
        strings += ': δ ';
      }
      return strings;
    }
    function pushDelta(range, acsRanges, options) {
      let strings = '';
      let parenthesis = [];
      const fromTo = [range.from, range.to];
      if (range.signals) {
        range.signals = range.signals.filter(signal => !uselessKind(signal.kind, options.filter));
      }
      if (range.signals && range.signals.length > 0) {
        const signals = range.signals;
        if (signals.length > 1) {
          if (options.ascending === true) {
            signals.sort((a, b) => {
              return a.delta - b.delta;
            });
          }
          strings += `${Math.min(...fromTo).toFixed(options.nbDecimalDelta)}-${Math.max(...fromTo).toFixed(options.nbDecimalDelta)}`;
          strings += ` (${getIntegral(range, options)}`;
          if (signals.some(signal => signal.multiplicity || signal.js && signal.js.length > 0)) {
            for (const signal of signals) {
              parenthesis = [];
              if (signal.delta !== undefined) {
                strings = appendSeparator(strings);
                strings += signal.delta.toFixed(options.nbDecimalDelta);
              }
              const range = {
                from: Number.MIN_SAFE_INTEGER,
                to: Number.MAX_SAFE_INTEGER
              };
              switchFormat(range, signal, parenthesis, options);
              if (parenthesis.length > 0) strings += ` (${parenthesis.join(', ')})`;
            }
          }
          strings += ')';
        } else {
          parenthesis = [];
          if (signals[0].delta !== undefined) {
            strings += signals[0].delta.toFixed(options.nbDecimalDelta);
            switchFormat(range, signals[0], parenthesis, options);
            if (parenthesis.length > 0) strings += ` (${parenthesis.join(', ')})`;
          } else {
            strings += `${Math.min(...fromTo).toFixed(options.nbDecimalDelta)}-${Math.max(...fromTo).toFixed(options.nbDecimalDelta)}`;
            switchFormat(range, signals[0], parenthesis, options);
            if (parenthesis.length > 0) strings += ` (${parenthesis})`;
          }
        }
      } else {
        strings += `${Math.min(...fromTo).toFixed(options.nbDecimalDelta)}-${Math.max(...fromTo).toFixed(options.nbDecimalDelta)}`;
        const signal = {};
        switchFormat(range, signal, parenthesis, options);
        if (parenthesis.length > 0) strings += ` (${parenthesis.join(', ')})`;
      }
      acsRanges.push(strings);
    }
    function getIntegral(range, options) {
      let integration = '';
      if (range.pubIntegral) {
        integration = String(range.pubIntegral);
      } else if (range.integration) {
        const {
          nucleus
        } = options;
        integration = range.integration.toFixed(0) + nucleus[nucleus.length - 1];
      }
      return integration;
    }
    function pushIntegral(range, parenthesis, options) {
      const integration = getIntegral(range, options);
      if (integration.length > 0) parenthesis.push(integration);
    }
    function pushmultiplicityFromSignal(signal, parenthesis) {
      let multiplicity = signal.multiplicity;
      if (!multiplicity) {
        const joinedCouplings = signalJoinCouplings(signal, {
          acsFormat: true,
          tolerance: 0.05,
          ignoreDiaIDs: true
        });
        multiplicity = signalMultiplicityPattern(joinedCouplings);
      }
      if (multiplicity.length > 0) parenthesis.push(multiplicity);
    }
    function switchFormat(range, signal, parenthesis, options) {
      for (const char of options.format) {
        switch (char.toUpperCase()) {
          case 'I':
            pushIntegral(range, parenthesis, options);
            break;
          case 'M':
            pushmultiplicityFromSignal(signal, parenthesis);
            break;
          case 'A':
            pushAssignment(signal, parenthesis);
            break;
          case 'J':
            pushCoupling(signal, parenthesis, options);
            break;
          default:
            throw new Error(`Unknow format letter: ${char}`);
        }
      }
    }
    function formatMF(mf) {
      return mf.replace(/(?<num>[0-9]+)/g, '<sub>$<num></sub>');
    }
    function formatNucleus(nucleus) {
      return nucleus.replace(/(?<num>[0-9]+)/g, '<sup>$<num></sup>');
    }
    function appendSeparator(strings) {
      if (strings.length > 0 && !/ $/.exec(strings) && !/\($/.exec(strings)) {
        strings += ', ';
      }
      return strings;
    }
    function formatAssignment(assignment) {
      assignment = assignment.replace(/(?<num>[0-9]+)/g, '<sub>$<num></sub>');
      assignment = assignment.replace(/"(?<i>[^"]*)"/g, '<i>$<i></i>');
      return assignment;
    }
    function pushCoupling(signal, parenthesis, options) {
      if (Array.isArray(signal.js) && signal.js.length > 0) {
        signal.js.sort((a, b) => {
          return b.coupling - a.coupling;
        });
        const values = [];
        for (const j of signal.js) {
          if (j.coupling !== undefined) {
            values.push(j.coupling.toFixed(options.nbDecimalJ));
          }
        }
        if (values.length > 0) {
          parenthesis.push(`<i>J</i> = ${values.join(', ')} Hz`);
        }
      }
    }
    function pushAssignment(signal, parenthesis) {
      if (signal.assignment) {
        parenthesis.push(formatAssignment(signal.assignment));
      }
    }
    function uselessKind(kind = '', filter = true) {
      kind = kind.toLowerCase();
      if (filter && (kind === 'impurity' || kind === 'solvent')) return true;
      return false;
    }

    function addDummySignals(signals) {
      const newSignals = JSON.parse(JSON.stringify(signals));
      let nextAtomToAssign = signals.reduce((nextAtomToAssign, signal) => {
        const {
          atoms = []
        } = signal;
        return Math.max(...atoms) > nextAtomToAssign ? Math.max(...atoms) : nextAtomToAssign;
      }, 0) + 1;
      signals.forEach((signal, s) => {
        const {
          js: jCouplings = [],
          atoms: signalAssignment = [nextAtomToAssign++]
        } = signal;
        const {
          newCouplings,
          tempSignals
        } = checkCouplings(jCouplings, signalAssignment, nextAtomToAssign + signals.length);
        if (tempSignals.length > 0) newSignals.push(...tempSignals);
        newSignals[s].js = newCouplings;
        newSignals[s].atoms = signalAssignment;
      });
      return newSignals.sort((a, b) => a.delta - b.delta);
    }
    function checkCouplings(jCouplings, signalAssignment, nextAtomToAssign) {
      const tempSignals = [];
      const newCouplings = jCouplings.reduce((newCouplings, jCoupling) => {
        const {
          atoms = [],
          multiplicity,
          coupling
        } = jCoupling;
        if (atoms.length === 0) {
          if (coupling && multiplicity) {
            const tempCouplings = [];
            const nbLinks = couplingPatterns.indexOf(multiplicity);
            for (let i = 0; i < nbLinks; i++) {
              nextAtomToAssign++;
              tempCouplings.push({
                coupling,
                atoms: [nextAtomToAssign]
              });
              tempSignals.push(formatSignal(coupling, [nextAtomToAssign], signalAssignment));
            }
            newCouplings.push(...tempCouplings);
          } else {
            newCouplings.push(jCoupling);
          }
        }
        return newCouplings;
      }, []);
      return {
        newCouplings,
        tempSignals
      };
    }
    function formatSignal(coupling, newSignalAssignment, signalAssignment) {
      return {
        delta: 100000,
        atoms: newSignalAssignment,
        js: [{
          coupling,
          atoms: signalAssignment
        }]
      };
    }

    /**
     * Create the xy object from an array of 1D signals.
     * @param signals Array of signals with assigned or unassigned couplings.
     */
    function hackSignalsToXY(signals, options = {}) {
      const newSignals = addDummySignals(signals);
      return signalsToXY(newSignals, options);
    }

    function checkForSignals(ranges) {
      for (const range of ranges) {
        if (!range.signals) throw new Error('range has not signals');
      }
    }
    const defaultFromTo = (nucleus = '') => {
      switch (nucleus.toUpperCase()) {
        case '13C':
          return {
            from: -5,
            to: 206
          };
        default:
          return {
            from: -0.5,
            to: 10.5
          };
      }
    };
    function rangesToXY(ranges, options = {}) {
      checkForSignals(ranges);
      const {
        frequency = 400,
        lineWidth = 1,
        nbPoints = 16 * 1024,
        shape = {
          kind: 'gaussian'
        }
      } = options;
      const addSpectrum = (a, b) => {
        for (let i = 0; i < nbPoints; i++) {
          a[i] += b[i];
        }
      };
      const {
        from,
        to
      } = getFromTo(ranges, options);
      const spectrumOptions = {
        to,
        from,
        nbPoints,
        shape,
        lineWidth,
        frequency
      };
      const spectrum = new Float64Array(nbPoints);
      for (const range of ranges) {
        const {
          integration,
          signals = []
        } = range;
        const {
          multiplicity: rangeMulplicity = ''
        } = range;
        if (rangeMulplicity === 'm' && signals.length < 1) {
          const {
            from,
            to
          } = range;
          signals.push({
            delta: (from + to) / 2,
            multiplicity: 'm'
          });
        }
        const rangeSpectrum = new Float64Array(nbPoints);
        for (const signal of signals) {
          const {
            multiplicity
          } = signal;
          const signalSpectrum = multiplicity === 'm' || multiplicity === 'b' || multiplicity === 'br s' ? broadPeakOrMultipletSpectrum([signal], spectrumOptions).y : hackSignalsToXY([signal], spectrumOptions).y;
          normalizeSpectrum(signalSpectrum, [signal]);
          addSpectrum(rangeSpectrum, signalSpectrum);
        }
        if (range.integration) {
          normalizeSpectrum(rangeSpectrum, signals, {
            integration
          });
        }
        addSpectrum(spectrum, rangeSpectrum);
      }
      return {
        x: xSequentialFill({
          from,
          to,
          size: nbPoints
        }),
        y: spectrum
      };
    }
    function broadPeakOrMultipletSpectrum(signals, options = {}) {
      const {
        lineWidth,
        frequency
      } = options;
      const spectrumGenerator = new SpectrumGenerator(options);
      const broadWidth = lineWidth * 3 / frequency;
      for (const signal of signals) {
        const {
          multiplicity,
          delta,
          integration = 1
        } = signal;
        if (multiplicity === 'b' || multiplicity === 'br s') {
          spectrumGenerator.addPeak({
            x: delta,
            y: integration,
            width: broadWidth
          });
        } else {
          const peaks = peaksOfMultiplet(delta, {
            lineWidth,
            frequency
          });
          spectrumGenerator.addPeaks(peaks);
        }
      }
      return spectrumGenerator.getSpectrum();
    }
    function peaksOfMultiplet(delta, options) {
      const {
        frequency,
        lineWidth,
        intensities = [1, 2, 5, 4, 5, 7, 5, 4, 5, 2, 1]
      } = options;
      const lineWidthPpm = lineWidth / frequency;
      const spaceBetweenPeaks = lineWidthPpm * 1.5;
      const peaks = [];
      const firstPeakPosition = delta - spaceBetweenPeaks * intensities.length / 2;
      for (let i = 0; i < intensities.length; i++) {
        peaks.push({
          x: firstPeakPosition + spaceBetweenPeaks * i,
          y: intensities[i],
          width: lineWidthPpm
        });
      }
      return peaks;
    }
    function normalizeSpectrum(spectrum, signals, options = {}) {
      const {
        integration = signals.reduce((acc, signal) => {
          const {
            integration = 1
          } = signal;
          return acc + integration;
        }, 0)
      } = options;
      const sum = spectrum.reduce((acc, element) => acc + element, 0);
      if (sum !== 0) {
        const norma = integration / sum * 1e6;
        for (let i = 0; i < spectrum.length; i++) {
          spectrum[i] *= norma;
        }
      }
    }
    function getFromTo(ranges, options) {
      const {
        from: defaultFrom,
        to: defaultTo
      } = defaultFromTo(options.nucleus);
      let rangesFrom = Number.MAX_SAFE_INTEGER;
      let rangesTo = Number.MIN_SAFE_INTEGER;
      for (const range of ranges) {
        for (const signal of range.signals || []) {
          if (rangesFrom > signal.delta) rangesFrom = signal.delta;
          if (rangesTo < signal.delta) rangesTo = signal.delta;
        }
      }
      const {
        from = Math.min(rangesFrom - 0.5, defaultFrom),
        to = Math.max(rangesTo + 0.5, defaultTo)
      } = options;
      return {
        from,
        to
      };
    }

    function directOptimization(data, sumOfShapes, options) {
      const {
        minValues,
        maxValues,
        directOptions = {}
      } = options;
      const objectiveFunction = getObjectiveFunction(data, sumOfShapes);
      const result = direct(objectiveFunction, minValues, maxValues, directOptions);
      const {
        optima
      } = result;
      return {
        error: result.minFunctionValue,
        iterations: result.iterations,
        parameterValues: optima[0]
      };
    }
    function getObjectiveFunction(data, sumOfShapes) {
      const {
        x,
        y
      } = data;
      const nbPoints = x.length;
      return parameters => {
        const fct = sumOfShapes(parameters);
        let error = 0;
        for (let i = 0; i < nbPoints; i++) {
          error += Math.pow(y[i] - fct(x[i]), 2);
        }
        return error;
      };
    }

    const defaultParameters = {
      delta: {
        init: data => data.signal.delta,
        min: data => data.signal.delta - data.shape.fwhm * 2,
        max: data => data.signal.delta + data.shape.fwhm * 2,
        gradientDifference: () => 2e-3
      },
      intensity: {
        init: data => data.signal.intensity || 1,
        min: () => 0,
        max: () => 1.5,
        gradientDifference: () => 1e-3
      },
      fwhm: {
        init: data => data.shape.fwhm,
        min: data => data.shape.fwhm * 0.25,
        max: data => data.shape.fwhm * 4,
        gradientDifference: data => data.shape.fwhm * 2e-3
      },
      mu: {
        init: data => data.shape.mu || 0.5,
        min: () => 0,
        max: () => 1,
        gradientDifference: () => 0.01
      },
      coupling: {
        init: data => data.jCoupling.coupling,
        min: data => data.jCoupling.coupling * 0.8,
        max: data => data.jCoupling.coupling * 1.2,
        gradientDifference: () => 0.01
      }
    };

    function signalsToPointXY(signals, options) {
      const {
        frequency,
        maxClusterSize = 1
      } = options;
      const completeSignalSet = addDummySignals(signals);
      const spinSystem = signalsToSpinSystem(completeSignalSet);
      spinSystem.clusters = splitSpinSystem(spinSystem, {
        frequency,
        maxClusterSize
      });
      const pointsXY = simulateXYPeaks(spinSystem, options).filter(point => point.x < 1000);
      const maxIntensity = pointsXY.reduce((max, peak) => peak.y > max ? peak.y : max, Number.MIN_SAFE_INTEGER);
      for (const point of pointsXY) {
        point.y /= maxIntensity;
      }
      return pointsXY;
    }

    const properties = ['init', 'min', 'max', 'gradientDifference'];
    function getInternalSignals(data, signals, minMaxY, options) {
      let index = 0;
      const internalSignals = [];
      const {
        shape: shapeAsOption = {
          kind: 'gaussian'
        },
        baseline: shiftValue = minMaxY.min,
        lineWidth = 1,
        frequency
      } = options;
      const normalizedSignals = normalizeSignals({
        signals,
        data,
        shiftValue,
        frequency,
        range: minMaxY.range
      });
      for (const signal of normalizedSignals) {
        const {
          shape = {
            ...shapeAsOption
          }
        } = signal;
        if (!shape.fwhm) {
          shape.fwhm = lineWidth;
        }
        shape.fwhm /= frequency;
        const shapeFct = getShape1D(shape);
        const parameters = ['delta', 'intensity', ...shapeFct.getParameters()];
        const propertiesValues = {
          min: [],
          max: [],
          init: [],
          gradientDifference: []
        };
        for (const parameter of parameters) {
          for (const property of properties) {
            propertiesValues[property].push(getPropertyValue({
              signal,
              frequency,
              parameter,
              property,
              minMaxY,
              shapeFct
            }));
          }
        }
        for (const jCoupling of signal.js || []) {
          for (const property of properties) {
            const parameter = 'coupling';
            const generalParameterValue = options?.parameters?.[parameter]?.[property];
            if (generalParameterValue) {
              if (typeof generalParameterValue === 'number') {
                propertiesValues[property].push(getNormalizedValue(generalParameterValue, parameter, property, minMaxY, frequency, options.baseline));
              } else {
                const value = generalParameterValue({
                  jCoupling
                });
                propertiesValues[property].push(getNormalizedValue(value, parameter, property, minMaxY, frequency, options.baseline));
              }
            }
            if (!defaultParameters[parameter]) {
              throw new Error(`No default parameter for ${parameter}`);
            }
            const defaultParameterValues = defaultParameters[parameter][property];
            propertiesValues[property].push(defaultParameterValues({
              jCoupling
            }));
          }
        }
        const fromIndex = index;
        const toIndex = fromIndex + parameters.length - 1;
        const fromIndexCoupling = toIndex + 1;
        const toIndexCoupling = fromIndexCoupling + signal.js.length - 1;
        index += toIndexCoupling - fromIndex + 1;
        internalSignals.push({
          shape: shape,
          shapeFct,
          signal,
          parameters,
          propertiesValues,
          fromIndex,
          toIndex,
          fromIndexCoupling,
          toIndexCoupling
        });
      }
      return internalSignals;
    }
    function normalizeSignals(options) {
      const {
        signals,
        data,
        frequency,
        shiftValue,
        range
      } = options;
      const getMaxIntensity = signal => {
        const peaks = signalsToPointXY([signal], {
          frequency,
          maxClusterSize: 1
        });
        const biggestPeak = peaks.reduce((biggest, peak) => peak.y > biggest.y ? peak : biggest, peaks[0]);
        const {
          y: maxIntensity
        } = xyFindClosestPoint(data, biggestPeak.x);
        return maxIntensity;
      };
      return signals.map(signal => {
        const {
          intensity = getMaxIntensity(signal)
        } = signal;
        return {
          ...signal,
          js: signal.js || [],
          intensity: (intensity - shiftValue) / range
        };
      });
    }
    function getPropertyValue(options) {
      const {
        signal,
        frequency,
        parameter,
        property,
        minMaxY,
        shapeFct
      } = options;
      // check if the property is specified in the signal
      const propertyValue = signal?.parameters?.[parameter]?.[property];
      if (propertyValue) {
        return getNormalizedValue(propertyValue, parameter, property, minMaxY, frequency, options.baseline);
      }
      // check if there are some global option, it could be a number or a callback
      const generalParameterValue = options?.parameters?.[parameter]?.[property];
      if (generalParameterValue) {
        if (typeof generalParameterValue === 'number') {
          return getNormalizedValue(generalParameterValue, parameter, property, minMaxY, frequency, options.baseline);
        } else {
          const value = generalParameterValue(signal);
          return getNormalizedValue(value, parameter, property, minMaxY, frequency, options.baseline);
        }
      }
      // we just need to take the default parameters
      if (!defaultParameters[parameter]) {
        throw new Error(`No default parameter for ${parameter}`);
      }
      const defaultParameterValues = defaultParameters[parameter][property];
      return defaultParameterValues({
        signal,
        shape: shapeFct
      });
    }
    function getNormalizedValue(value, parameter, property, minMaxY, frequency, baseline) {
      if (parameter === 'intensity') {
        if (property === 'gradientDifference') {
          return value;
        } else {
          return baseline !== undefined ? (value - baseline) / minMaxY.range : (value - minMaxY.min) / minMaxY.range;
        }
      } else if (parameter === 'fwhm') {
        return value / frequency;
      }
      return value;
    }

    const defaultSimulationOptions = {
      maxClusterSize: 1
    };
    function getSumOfShapes(internalSignals, options) {
      const simulationOptions = {
        ...defaultSimulationOptions,
        ...options
      };
      return function sumOfShapes(parameters) {
        const peaks = [];
        for (const internalSignal of internalSignals) {
          const delta = parameters[internalSignal.fromIndex];
          const intensity = parameters[internalSignal.fromIndex + 1];
          for (let i = 2; i < internalSignal.parameters.length; i++) {
            //@ts-expect-error Not simply to solve the issue
            internalSignal.shapeFct[internalSignal.parameters[i]] = parameters[internalSignal.fromIndex + i];
          }
          const couplings = internalSignal.signal.js;
          for (let i = 0; i < couplings.length; i++) {
            couplings[i].coupling = parameters[internalSignal.fromIndexCoupling + i];
          }
          const currentPeaks = signalsToPointXY([{
            delta,
            js: couplings
          }], simulationOptions);
          peaks.push(...currentPeaks.map(peak => {
            return {
              intensity,
              shape: internalSignal.shapeFct,
              ...peak
            };
          }));
        }
        return currentX => {
          let totalY = 0;
          for (const peak of peaks) {
            const {
              x,
              y,
              intensity,
              shape
            } = peak;
            totalY += y * intensity * shape.fct(currentX - x);
          }
          return totalY;
        };
      };
    }

    const defaultLMOptimizationOptions = {
      damping: 1.5,
      maxIterations: 100,
      errorTolerance: 1e-8
    };
    const defaultDirectOptimizationOptions = {
      iterations: 25
    };
    function optimizeSignals(data, signals, options) {
      const {
        optimization = {},
        simulation: simulationOptions,
        ...restOptions
      } = options;
      const temp = xMinMaxValues(data.y);
      const minMaxY = {
        ...temp,
        range: temp.max - temp.min
      };
      const internalSignals = getInternalSignals(data, signals, minMaxY, {
        frequency: simulationOptions.frequency,
        ...restOptions
      });
      const {
        baseline: shiftValue = minMaxY.min
      } = options;
      const normalizedY = new Float64Array(data.y.length);
      for (let i = 0; i < data.y.length; i++) {
        normalizedY[i] = (data.y[i] - shiftValue) / minMaxY.range;
      }
      const nbParams = internalSignals[internalSignals.length - 1].toIndexCoupling + 1;
      const minValues = new Float64Array(nbParams);
      const maxValues = new Float64Array(nbParams);
      const initialValues = new Float64Array(nbParams);
      const gradientDifferences = new Float64Array(nbParams);
      let index = 0;
      for (const internalSignal of internalSignals) {
        for (let i = 0; i < internalSignal.parameters.length; i++) {
          minValues[index] = internalSignal.propertiesValues.min[i];
          maxValues[index] = internalSignal.propertiesValues.max[i];
          initialValues[index] = internalSignal.propertiesValues.init[i];
          gradientDifferences[index++] = internalSignal.propertiesValues.gradientDifference[i];
        }
        for (let i = internalSignal.parameters.length; i < internalSignal.parameters.length + internalSignal.signal.js.length; i++) {
          minValues[index] = internalSignal.propertiesValues.min[i];
          maxValues[index] = internalSignal.propertiesValues.max[i];
          initialValues[index] = internalSignal.propertiesValues.init[i];
          gradientDifferences[index++] = internalSignal.propertiesValues.gradientDifference[i];
        }
      }
      const sumOfShapes = getSumOfShapes(internalSignals, simulationOptions);
      const {
        kind,
        options: optimizationOptions
      } = optimization;
      const [algorithm, defaultOptimizationOptions] = kind === 'direct' ? [directOptimization, defaultDirectOptimizationOptions] : [levenbergMarquardt, defaultLMOptimizationOptions];
      const fitted = algorithm({
        x: data.x,
        y: normalizedY
      }, sumOfShapes, {
        minValues,
        maxValues,
        initialValues,
        gradientDifference: gradientDifferences,
        ...defaultOptimizationOptions,
        ...optimizationOptions
      });
      const fittedValues = fitted.parameterValues;
      const newSignals = [];
      for (const internalSignal of internalSignals) {
        const {
          fromIndexCoupling
        } = internalSignal;
        const js = internalSignal.signal.js.map((jCoupling, i) => {
          jCoupling.coupling = fittedValues[fromIndexCoupling + i];
          return jCoupling;
        });
        const delta = fittedValues[internalSignal.fromIndex];
        const pointXYs = signalsToPointXY([{
          delta,
          js
        }], simulationOptions);
        const intensity = fittedValues[internalSignal.fromIndex + 1] * minMaxY.range + shiftValue;
        const newPeaks = [];
        for (const {
          x,
          y
        } of pointXYs) {
          const peak = {
            id: v4(),
            x,
            y: intensity * y,
            width: 0,
            shape: {
              ...internalSignal.shape
            }
          };
          for (let i = 2; i < internalSignal.parameters.length; i++) {
            //@ts-expect-error should be fixed once
            peak.shape[internalSignal.parameters[i]] = fittedValues[internalSignal.fromIndex + i];
          }
          peak.shape.fwhm *= simulationOptions.frequency;
          peak.width = getShape1D(peak.shape).fwhmToWidth(peak.shape.fwhm);
          newPeaks.push(peak);
        }
        newSignals.push({
          delta,
          js,
          shape: {
            ...newPeaks[0].shape
          },
          intensity,
          peaks: newPeaks
        });
      }
      return newSignals;
    }

    /**
     * pure function that applies a window function to the input data.
     */
    function applyWindow(data, options) {
      const dataLength = data.length;
      const {
        windowData,
        start = 0,
        length = dataLength,
        pointsToShift = 0,
        output = new Float64Array(data)
      } = options;
      const firstEndPoint = Math.min(start + length, dataLength - pointsToShift);
      for (let i = start, j = 0; i < firstEndPoint; i++) {
        output[i] *= windowData[j++];
      }
      for (let i = dataLength - 1, j = 0; i > dataLength - pointsToShift - 1; i--) {
        output[i] *= windowData[j++];
      }
      return output;
    }

    function exponential(options) {
      const {
        lb,
        dw
      } = options;
      const coefExp = -lb * Math.PI * dw;
      return i => Math.exp(coefExp * i);
    }

    function lorentzToGauss(options) {
      const {
        dw,
        length,
        gaussianHz = 0,
        exponentialHz = 0,
        center = 0
      } = options;
      if (center > 1 || center < 0) {
        throw new Error('The center of gaussian shape should be inside of the window function: 0 - 1');
      }
      const C5 = Math.pow(0.6 * Math.PI * gaussianHz * dw, 2);
      const C2 = center * (length - 1);
      const C6 = Math.PI * dw * exponentialHz;
      return i => Math.exp(i * C6 - Math.pow(C2 - i, 2) * C5);
    }

    function sineBell(options) {
      const {
        exponent = 1,
        offset = 0,
        end = 1,
        length
      } = options;
      if (length === undefined) {
        throw new Error('length options should exists for sineBell shape');
      }
      if (end < 0 || end > 1) {
        throw new Error(`the end parameter should be [0-1]`);
      }
      const c1 = Math.PI * offset;
      const c2 = Math.PI * (end - offset) / (length - 1);
      return i => Math.pow(Math.sin(c1 + c2 * i), exponent);
    }

    function getFunction(shape) {
      const {
        kind,
        options
      } = shape;
      switch (kind) {
        case 'exponential':
          return exponential(options);
        case 'lorentzToGauss':
          return lorentzToGauss(options);
        case 'sineBell':
          return sineBell(options);
        default:
          throw Error(`Unknown distribution ${kind}`);
      }
    }

    /**
     * generate and scale the data of a window function
     */
    function getData(func, length) {
      const data = new Float64Array(length);
      let max = Number.MIN_SAFE_INTEGER;
      for (let i = 0; i < length; i++) {
        const value = func(i);
        data[i] = value;
        if (value > max) max = value;
      }
      for (let i = 0; i < length; i++) data[i] /= max;
      return data;
    }

    function compose(shapeOptions) {
      const {
        length: dataLength,
        shapes = []
      } = shapeOptions;
      const data = new Float64Array(dataLength).fill(1);
      shapes.forEach(options => {
        const {
          shape,
          start = 0
        } = options;
        const {
          options: shapeOptions
        } = shape;
        const length = 'length' in shapeOptions ? shapeOptions.length : dataLength - start;
        const windowData = getData(getFunction(shape), length);
        applyWindow(data, {
          windowData,
          length,
          start,
          output: data
        });
      });
      return data;
    }

    function apodization$1(data, options = {}) {
      const {
        compose: composeOptions,
        pointsToShift,
        apply = true
      } = options;
      if (!composeOptions) {
        return {
          ...data,
          windowData: new Float64Array(data.re.length).fill(1)
        };
      }
      const windowData = compose(composeOptions);
      const applyWindowOptions = {
        windowData,
        pointsToShift
      };
      const re = apply ? applyWindow(data.re, applyWindowOptions) : data.re;
      const im = apply ? applyWindow(data.im, applyWindowOptions) : data.im;
      return {
        re,
        im,
        windowData
      };
    }

    const twoPi = Math.PI * 2;
    /**
     * it use the chemical shifts and the coupling between them in order to generate a FID.
     * It does not propagate the hamiltonian of the spin system, instead it simulate all the
     * frequencies in the spectrum (including second order) and add all the waves in a real and
     * imaginary parts, that is why the couplings should be assigned in order to generate the spin system.
     */
    function signalsToFID(signals, options) {
      const {
        lb = 1,
        from = 0,
        to = 10,
        nbPoints = 1024,
        frequency = 400,
        maxClusterSize = 8
      } = options;
      const spinSystem = signalsToSpinSystem(signals);
      spinSystem.clusters = splitSpinSystem(spinSystem, {
        frequency,
        maxClusterSize
      });
      const peaks = simulateXYPeaks(spinSystem, {
        frequency
      });
      const re = new Float64Array(nbPoints);
      const im = new Float64Array(nbPoints);
      const sw = Math.abs(to - from);
      const adquisitionTime = nbPoints / (sw * frequency);
      const time = xSequentialFill({
        size: nbPoints,
        from: 0,
        to: adquisitionTime
      });
      for (const peak of peaks) {
        const {
          x,
          y
        } = peak;
        const cs = x * frequency * twoPi;
        for (let i = 0; i < nbPoints; i++) {
          re[i] += y * Math.cos(cs * time[i]);
          im[i] += y * Math.sin(cs * time[i]);
        }
      }
      const {
        windowData,
        ...result
      } = apodization$1({
        re,
        im
      }, {
        pointsToShift: 0,
        compose: {
          length: nbPoints,
          shapes: [{
            start: 0,
            shape: {
              kind: 'exponential',
              options: {
                dw: adquisitionTime / (nbPoints - 1),
                lb
              }
            }
          }]
        }
      });
      return result;
    }

    let possiblePatterns = [];
    for (const {
      label,
      value,
      names
    } of MultiplicityPatterns) {
      possiblePatterns.push(label, value, ...names);
    }
    possiblePatterns = possiblePatterns.sort((a, b) => b.length - a.length);
    /**
     * split a composed pattern like dt or ddt into ['d','t'] and ['d','d','t']
     * @param multiplet - pattern or abbreviation.
     * @returns
     */
    function splitPatterns(multiplet) {
      if (/^\s*$/.exec(multiplet)) return [];
      const regexp = new RegExp(possiblePatterns.join('|'), 'g');
      const result = multiplet.match(regexp);
      if (result) return result.map(entry => entry.trim());
      return [multiplet];
    }

    function splitParenthesis(part) {
      const match = / *(?<before>[^(]*?) *\( *(?<inside>.*?) *\) *(?<after>.*?) */.exec(part);
      if (!match) {
        return {
          before: part,
          inside: '',
          after: ''
        };
      }
      return match.groups;
    }

    function resurrectRange(part, options = {}) {
      const {
        nucleus = '1h',
        frequency = 400
      } = options;
      const split = splitParenthesis(part);
      if (!split.before) return;
      const beforeMatch = /.*?(?<from>-?[0-9.]+)-?(?<to>-?[0-9.]*).*/.exec(split.before);
      if (!beforeMatch || !beforeMatch.groups) return;
      const isARange = beforeMatch.groups.to !== '';
      const from = Number(beforeMatch.groups.from);
      const to = isARange ? Number(beforeMatch.groups.to) : from;
      const insideParts = split.inside.split(/ *, */);
      const signal = {
        delta: NaN,
        js: []
      };
      let range = {
        from,
        to,
        signals: [signal]
      };
      const integrationParts = insideParts.filter(part => part.match(/^[0-9]+\s*H$/));
      if (integrationParts.length === 1) {
        range.integration = Number(integrationParts[0].replace('H', ''));
      }
      const multiplicityParts = insideParts.filter(part => part.match(/^([br ]*)\.*\s*[a-zA-Z]+\.*$/));
      if (multiplicityParts.length === 1) {
        const multiplicity = multiplicityParts[0];
        if (multiplicity === 'm') {
          if (isARange) {
            // a real range
            signal.delta = (from + to) / 2;
            signal.multiplicity = multiplicity;
          } else {
            // a complex signal
            signal.delta = from;
            signal.multiplicity = multiplicity;
          }
        } else if (!isARange) {
          // a complex signal
          signal.delta = from;
          signal.multiplicity = multiplicity;
        }
      }
      const jCouplings = insideParts.filter(part => part.match(/(Hz|J|^[0-9.]+$)/)).map(jCoupling => Number(jCoupling.replace(/J[0-9.]*\s*=/, 'J =').replace(/[^0-9.]/g, '')));
      const multiplicities = splitPatterns(signal.multiplicity || '');
      if (multiplicities.length === jCouplings.length) {
        for (let i = 0; i < multiplicities.length; i++) {
          if (!signal.js) {
            throw new Error('signal has not js');
          }
          signal.js.push({
            coupling: jCouplings[i],
            multiplicity: multiplicities[i]
          });
        }
      }
      if (range.from === range.to) {
        range = {
          ...range,
          ...rangeFromSignal(signal, {
            nucleus,
            frequency
          })
        };
      }
      return range;
    }

    function resurrect(acsString) {
      const data = {
        experiment: {},
        ranges: [],
        acsString,
        normalized: acsString.replace(/\.\s*$/, '').replace(/[\r\n\t]/g, ' ').replace(/[;:]/g, ',').replace(/\}/g, ')').replace(/\{/g, '(').replace(/[\u2011\u2012\u2013\u2014\u2015\u2212]/g, '-')
      };
      createParts(data);
      parseParts(data);
      return data;
    }
    function parseParts(data) {
      for (const part of data.parts) {
        if (part.toLowerCase().includes('nmr')) {
          processExperiment(data, part);
        } else {
          const {
            frequency,
            nucleus
          } = data.experiment;
          const range = resurrectRange(part, {
            frequency,
            nucleus
          });
          if (range) data.ranges.push(range);
        }
      }
    }
    function processExperiment(data, part) {
      const split = splitParenthesis(part);
      const before = split.before.replace(/[ -]*nmr[ -]*/i, '').replace(/[ -]/g, '');
      if (/^[0-9]+[A-Z][a-z]?$/.exec(before)) {
        // 36Cl, 1H, 13C, ...
        data.experiment.nucleus = before;
      }
      if (/^[A-Z][a-z]?[0-9]+$/.exec(before)) {
        // Cl35, H1, C13, ...
        data.experiment.nucleus = before.replace(/^([A-Z][a-z]?)([0-9]+)$/, '$2$1');
      }
      if (split.inside) {
        // some frequency and solvent ???
        const insideParts = split.inside.split(/[,]/);
        const frequencyParts = insideParts.filter(part => /[0-9]{2}/.exec(part));
        if (frequencyParts.length) {
          const frequency = frequencyParts[0].replace(/[^0-9]/g, '');
          if (frequency.length > 1) data.experiment.frequency = Number(frequency);
        }
        const solventParts = insideParts.filter(part => !part.match(/[0-9]{2}/));
        if (solventParts.length) {
          data.experiment.solvent = solventParts[0];
        }
      }
    }
    function createParts(data) {
      const parts = data.normalized.split(/\)(?![^()]*\))/).map(part => part.replace(/^\s*(.*?)\s*$/, '$1')).filter(part => part).map(part => `${part})`).map(part => part.replace(/\(([^()]+)\)(?!$)/g, '[$1]')).map(part => part.split(/[,;](?![^()]*\))/)).flat().map(part => part.replace(/^\s*(.*?)\s*$/, '$1')).filter(part => part);
      data.parts = parts;
    }

    const nucleusSymbolMap = {
      proton: '1H',
      hydrogen: 'H',
      helium: 'He',
      lithium: 'Li',
      carbon: 'C',
      nitrogen: 'N',
      oxigen: 'O',
      fluorine: 'F',
      sodium: 'Na',
      aluminum: 'Al',
      silicon: 'Si',
      phosphorus: 'P',
      iron: 'Fe',
      copper: 'Cu',
      zinc: 'Zn',
      xenon: 'Xe'
    };

    /**
     * The idea is to convert nucleus name like H1, proton or Fluorine19 to 1H and 19F.
     */
    function normalizeNucleus(name) {
      if (gyromagneticRatio[name]) {
        return name;
      }
      const massNumber = name.replace(/\D/g, '');
      const atomName = name.replace(/\d*\W*/g, '').toLowerCase();
      const nucleusName = atomName.length > 1 ? nucleusSymbolMap[atomName] : atomName.toUpperCase();
      const symbol = `${massNumber}${nucleusName}`;
      if (!gyromagneticRatio[symbol]) {
        return name;
      }
      return symbol;
    }

    function convertWidthToHz(peaks, options) {
      return convertWidth(peaks, {
        ...options,
        convertTo: 'hz'
      });
    }

    function xyAutoPeaksPicking(data, options) {
      const {
        from,
        to,
        frequency,
        noiseLevel,
        thresholdFactor = 3,
        minMaxRatio = 0.05,
        broadRatio = 0.00025,
        useSanPlot = false,
        smoothY = true,
        optimize = false,
        factorLimits = 4,
        realTopDetection = true,
        shape = {
          kind: 'gaussian'
        },
        optimization = {
          kind: 'lm'
        },
        broadWidth = 0.25,
        sgOptions = {},
        direction = 'positive'
      } = options;
      const pointsPerHz = 1 / frequency / (data.x[1] - data.x[0]);
      // we can consider a peak with of 0.5 Hz for the windowSize
      const ws = Math.max(Math.round(pointsPerHz / 2), 5);
      const {
        windowSize = ws - ws % 2 + 1,
        polynomial = 3
      } = sgOptions;
      // check falsy values including NaN
      if (!windowSize) {
        throw new XYAutoPeaksPickingWindowSizeError({
          windowSize,
          ws,
          pointsPerHz,
          frequency,
          x: data.x.slice(0, 2),
          sgOptions,
          autoWindowSizeFormula: 'ws - (ws % 2) + 1',
          pointsPerHzFormula: '1 / frequency / (x[1] - x[0])'
        });
      }
      if (data.x.length < windowSize) return [];
      if (from !== undefined && to !== undefined) {
        data = xyExtract(data, {
          zones: [{
            from,
            to
          }]
        });
      }
      const cutOff = getCutOff(data.y, {
        noiseLevel,
        useSanPlot,
        thresholdFactor
      });
      const getPeakOptions = {
        shape,
        frequency,
        broadWidth,
        optimize,
        factorLimits,
        maxCriteria: true,
        sgOptions: {
          windowSize,
          polynomial
        },
        minMaxRatio,
        broadRatio,
        noiseLevel: cutOff.positive,
        smoothY,
        optimization,
        realTopDetection
      };
      switch (direction) {
        case 'positive':
          {
            return getPeakList(data, getPeakOptions);
          }
        case 'negative':
          {
            return getNegativePeaks(data, cutOff, getPeakOptions);
          }
        case 'both':
          {
            return [...getPeakList(data, getPeakOptions), ...getNegativePeaks(data, cutOff, getPeakOptions)];
          }
        default:
          {
            const unHandled = direction;
            // eslint-disable-next-line @typescript-eslint/restrict-template-expressions
            throw Error(`Unknown distribution ${unHandled}`);
          }
      }
    }
    function getNegativePeaks(data, cutOff, getPeakOptions) {
      getPeakOptions.noiseLevel = cutOff.negative;
      getPeakOptions.maxCriteria = false;
      return getPeakList(data, getPeakOptions);
    }
    function getPeakList(data, options) {
      const {
        shape,
        frequency,
        broadWidth,
        optimize,
        maxCriteria,
        factorLimits,
        sgOptions,
        minMaxRatio,
        broadRatio,
        noiseLevel,
        smoothY,
        optimization,
        realTopDetection
      } = options;
      const peaks = gsd(data, {
        sgOptions,
        maxCriteria,
        minMaxRatio,
        noiseLevel,
        smoothY,
        realTopDetection
      });
      const peakList = setShape(peaks, {
        output: peaks,
        shape
      });
      const newPeaks = broadWidth ? joinBroadPeaks(peakList, {
        broadRatio,
        broadWidth,
        shape,
        optimization
      }).map(toNMRPeak1DStructure) : peakList.map(toNMRPeak1DStructure);
      return convertWidthToHz(optimize ? optimizePeaks(data, newPeaks, {
        shape,
        factorLimits,
        optimization
      }).map(toNMRPeak1DStructure) : newPeaks, {
        frequency
      });
    }
    function getCutOff(data, options) {
      const {
        noiseLevel,
        useSanPlot,
        thresholdFactor
      } = options;
      const formatResult = noiseLevel => typeof noiseLevel === 'number' ? {
        positive: noiseLevel,
        negative: -noiseLevel
      } : noiseLevel;
      if (noiseLevel) {
        return formatResult(noiseLevel);
      } else {
        return useSanPlot ? xNoiseSanPlot(data, {
          factorStd: thresholdFactor
        }) : formatResult(xAbsoluteMedian(data) * thresholdFactor);
      }
    }
    function toNMRPeak1DStructure(peak) {
      const {
        id,
        shape,
        x,
        y,
        width
      } = peak;
      return {
        id,
        x,
        y,
        width,
        shape
      };
    }
    /**
     * error for default windowSize computation
     * out in data, all data relative to the computation
     */
    class XYAutoPeaksPickingWindowSizeError extends Error {
      constructor(data) {
        super('windowSize must be an odd integer >= 5, check data property of this error for contextual information');
        this.data = data;
      }
    }

    function joinRanges(ranges) {
      ranges.sort((a, b) => a.from - b.from);
      for (let i = 0; i < ranges.length - 1; i++) {
        const currentRange = ranges[i];
        const nextRange = ranges[i + 1];
        if (currentRange.to > nextRange.from) {
          currentRange.id = v4();
          currentRange.to = Math.max(nextRange.to, currentRange.to);
          if (currentRange.signals && nextRange.signals) {
            currentRange.signals = currentRange.signals.concat(nextRange.signals);
          }
          if (currentRange.integration !== undefined) {
            currentRange.integration += nextRange.integration || 0;
          }
          ranges.splice(i + 1, 1);
          i--;
        }
      }
      return ranges;
    }

    /*
     * This library implements the J analyser described by Cobas et al in the paper:
     * A two-stage approach to automatic determination of 1H NMR coupling constants
     */
    const patterns = ['s', 'd', 't', 'q', 'quint', 'h', 'sept', 'o', 'n'];
    const symRatio = 1.5;
    const maxErrorIter1 = 2.5; // Hz
    const maxErrorIter2 = 1; // Hz
    const jAxisKeys = {
      jAxis: 'x',
      intensity: 'intensity'
    };
    function checkSignalForCompilePattern(signal) {
      if (!('symRank' in signal)) {
        throw new Error('Internal error, symRank was not calculated');
      }
      if (!('mask' in signal)) {
        throw new Error('Internal Error, mask was not added');
      }
    }
    var jAnalyzer = {
      /**
       * The compilation process implements at the first stage a normalization procedure described by Golotvin et al.
       * embedding in peak-component-counting method described by Hoyes et al.
       * @param {object} signal
       * @private
       */
      compilePattern: (inputSignal, options = {}) => {
        const {
          jAxisKey = jAxisKeys
        } = options;
        // checkForPeaks(inputSignal);
        // const signal = JSON.parse(JSON.stringify(inputSignal));
        // 1.1 symmetrize
        // It will add a set of peaks(signal.peaksComp) to the signal that will be used during
        // the compilation process. The unit of those peaks will be in Hz
        const signal = symmetrizeChoiseBest(inputSignal, {
          maxError: maxErrorIter1,
          iteration: 1,
          jAxisKey
        });
        signal.multiplicity = 'm';
        signal.asymmetric = true;
        //asserts the signal has all needed
        checkSignalForCompilePattern(signal);
        // Is the signal symmetric?
        if (signal.symRank >= 0.95 && signal.peaksComp.length < 32) {
          signal.asymmetric = false;
          let P1, n2, maxFlagged;
          let k = 1;
          let jCouplings = [];
          // Loop over the possible number of coupling contributing to the multiplet
          for (let n = 0; n < 9; n++) {
            // 1.2 Normalize. It makes a deep copy of the peaks before to modify them.
            const peaks = normalize(signal, n);
            let validPattern = false; // It will change to true, when we find the good patter
            // Lets check if the signal could be a singulet.
            if (peaks.length === 1 && n === 0) {
              validPattern = true;
            } else if (peaks.length <= 1) {
              continue;
            }
            // 1.3 Establish a range for the Heights Hi [peaks.intensity*0.85,peaks.intensity*1.15];
            const ranges = getRanges(peaks);
            n2 = Math.pow(2, n);
            // 1.4 Find a combination of integer heights Hi, one from each Si, that sums to 2^n.
            let heights = null;
            let counter = 1;
            while (!validPattern && (heights = getNextCombination(ranges, n2)) !== null && counter < 400) {
              // 2.1 Number the components of the multiplet consecutively from 1 to 2n,
              // starting at peak 1
              const numbering = new Array(heights.length);
              k = 1;
              for (let i = 0; i < heights.length; i++) {
                numbering[i] = new Array(heights[i]);
                for (let j = 0; j < heights[i]; j++) {
                  numbering[i][j] = k++;
                }
              }
              jCouplings = []; // The array to store the detected j-coupling
              // 2.2 Set j = 1; J1 = P2 - P1. Flag components 1 and 2 as accounted for.
              let j = 1;
              jCouplings.push(peaks[1].x - peaks[0].x);
              P1 = peaks[0].x;
              numbering[0].splice(0, 1); // Flagged
              numbering[1].splice(0, 1); // Flagged
              k = 1;
              let nFlagged = 2;
              maxFlagged = Math.pow(2, n) - 1;
              while (jCouplings.length < n && nFlagged < maxFlagged && k < peaks.length) {
                counter += 1;
                // 4.1. Increment j. Set k to the number of the first unflagged component.
                j++;
                while (k < peaks.length && numbering[k].length === 0) {
                  k++;
                }
                if (k < peaks.length) {
                  // 4.2 Jj = Pk - P1.
                  jCouplings.push(peaks[k].x - peaks[0].x);
                  // Flag component k and, for each sum of the...
                  numbering[k].splice(0, 1); // Flageed
                  nFlagged++;
                  // Flag the other components of the multiplet
                  for (let u = 2; u <= j; u++) {
                    let jSum = 0;
                    for (let i = 0; i < u; i++) {
                      jSum += jCouplings[i];
                    }
                    for (let i = 1; i < numbering.length; i++) {
                      // Maybe 0.25 Hz is too much?
                      if (Math.abs(peaks[i].x - (P1 + jSum)) < 0.25) {
                        numbering[i].splice(0, 1); // Flageed
                        nFlagged++;
                        break;
                      }
                    }
                  }
                }
              }
              // Calculate the ideal patter by using the extracted j-couplings
              const pattern = idealPattern(jCouplings);
              // Compare the ideal pattern with the proposed intensities.
              // All the intensities have to match to accept the multiplet
              validPattern = true;
              for (let i = 0; i < pattern.length; i++) {
                if (pattern[i].intensity !== heights[i]) {
                  validPattern = false;
                }
              }
            }
            // If we found a valid pattern we should inform about the pattern.
            if (validPattern) {
              updateSignal(signal, jCouplings);
            }
          }
        }
        // Before to return, change the units of peaksComp from Hz to PPM again
        for (const peakComp of signal.peaksComp) {
          peakComp.x /= signal.observe;
          peakComp.width *= signal.observe;
        }
        return signal;
      }
    };
    /**
     * @private
     * update the signal
     * @param {*} signal
     * @param {*} jCouplings
     */
    function updateSignal(signal, jCouplings) {
      // Update the limits of the signal
      const peaks = signal.peaksComp; // Always in Hz
      const nbPeaks = peaks.length;
      signal.startX = peaks[0].x / signal.observe - peaks[0].width;
      signal.stopX = peaks[nbPeaks - 1].x / signal.observe + peaks[nbPeaks - 1].width;
      signal.integralData.from = peaks[0].x / signal.observe - peaks[0].width * 3;
      signal.integralData.to = peaks[nbPeaks - 1].x / signal.observe + peaks[nbPeaks - 1].width * 3;
      // Compile the pattern and format the constant couplings
      signal.maskPattern = signal.mask2;
      signal.multiplicity = abstractPattern(signal, jCouplings);
      signal.pattern = signal.multiplicity; // Our library depends on this parameter, but it is old
    }
    /**
     * Returns the multiplet in the compact format
     * @param {object} signal
     * @param {object} jCouplings
     * @return {String}
     * @private
     */
    function abstractPattern(signal, jCouplings) {
      const tol = 0.05;
      let pattern = '';
      let cont = 1;
      const newNmrJs = [];
      if (jCouplings && jCouplings.length > 0) {
        jCouplings.sort((a, b) => {
          return b - a;
        });
        for (let i = 0; i < jCouplings.length - 1; i++) {
          if (Math.abs(jCouplings[i] - jCouplings[i + 1]) < tol) {
            cont++;
          } else {
            newNmrJs.push({
              coupling: Math.abs(jCouplings[i]),
              multiplicity: patterns[cont]
            });
            pattern += patterns[cont];
            cont = 1;
          }
        }
        const index = jCouplings.length - 1;
        newNmrJs.push({
          coupling: Math.abs(jCouplings[index]),
          multiplicity: patterns[cont]
        });
        pattern += patterns[cont];
        signal.nmrJs = newNmrJs;
      } else {
        pattern = 's';
        if (Math.abs(signal.startX - signal.stopX) * signal.observe > 16) {
          pattern = 'br s';
        }
      }
      return pattern;
    }
    /**
     * This function creates an ideal pattern from the given J-couplings
     * @private
     * @param {Array} jCouplings
     * @return {*[]}
     * @private
     */
    function idealPattern(jCouplings) {
      const hsum = Math.pow(2, jCouplings.length);
      const pattern = [{
        x: 0,
        intensity: hsum
      }];
      // To split the initial height
      for (const jc of jCouplings) {
        for (let j = pattern.length - 1; j >= 0; j--) {
          pattern.push({
            x: pattern[j].x + jc / 2,
            intensity: pattern[j].intensity / 2
          });
          pattern[j].x = pattern[j].x - jc / 2;
          pattern[j].intensity = pattern[j].intensity / 2;
        }
      }
      // To sum the heights in the same positions
      pattern.sort((a, b) => {
        return a.x - b.x;
      });
      for (let j = pattern.length - 2; j >= 0; j--) {
        if (Math.abs(pattern[j].x - pattern[j + 1].x) < 0.1) {
          pattern[j].intensity += pattern[j + 1].intensity;
          pattern.splice(j + 1, 1);
        }
      }
      return pattern;
    }
    /**
     * Find a combination of integer heights Hi, one from each Si, that sums to 2n.
     * @param {object} ranges
     * @param {Number} value
     * @return {*}
     * @private
     */
    function getNextCombination(ranges, value) {
      const half = Math.ceil(ranges.values.length * 0.5);
      const lng = ranges.values.length;
      let sum = 0;
      let ok;
      while (sum !== value) {
        // Update the indexes to point at the next possible combination
        ok = false;
        while (!ok) {
          ok = true;
          ranges.currentIndex[ranges.active]++;
          if (ranges.currentIndex[ranges.active] >= ranges.values[ranges.active].length) {
            // In this case, there is no more possible combinations
            if (ranges.active + 1 === half) {
              return null;
            } else {
              // If this happens we need to try the next active peak
              ranges.currentIndex[ranges.active] = 0;
              ok = false;
              ranges.active++;
            }
          } else {
            ranges.active = 0;
          }
        }
        // Sum the heights for this combination
        sum = 0;
        for (let i = 0; i < half; i++) {
          sum += ranges.values[i][ranges.currentIndex[i]] * 2;
        }
        if (ranges.values.length % 2 !== 0) {
          sum -= ranges.values[half - 1][ranges.currentIndex[half - 1]];
        }
      }
      // If the sum is equal to the expected value, fill the array to return
      if (sum === value) {
        const heights = new Array(lng);
        for (let i = 0; i < half; i++) {
          heights[i] = ranges.values[i][ranges.currentIndex[i]];
          heights[lng - i - 1] = ranges.values[i][ranges.currentIndex[i]];
        }
        return heights;
      }
      return null;
    }
    /**
     * This function generates the possible values that each peak can contribute
     * to the multiplet.
     * @param {Array} peaks Array of objects with peaks information {intensity}
     * @return {{values: Array, currentIndex: Array, active: number}}
     * @private
     */
    function getRanges(peaks) {
      const ranges = new Array(peaks.length);
      const currentIndex = new Array(peaks.length);
      let min, max;
      ranges[0] = [1];
      ranges[peaks.length - 1] = [1];
      currentIndex[0] = -1;
      currentIndex[peaks.length - 1] = 0;
      for (let i = 1; i < peaks.length - 1; i++) {
        min = Math.round(peaks[i].intensity * 0.85);
        max = Math.round(peaks[i].intensity * 1.15);
        ranges[i] = [];
        for (let j = min; j <= max; j++) {
          ranges[i].push(j);
        }
        currentIndex[i] = 0;
      }
      return {
        values: ranges,
        currentIndex,
        active: 0
      };
    }
    /**
     * Performs a symmetrization of the signal by using different aproximations to the center.
     * It will return the result of the symmetrization that removes less peaks from the signal
     * @param {object} signal
     * @param {Number} maxError
     * @param {Number} iteration
     * @return {*}
     * @private
     */
    function symmetrizeChoiseBest(signal, options) {
      const {
        maxError,
        iteration,
        jAxisKey = jAxisKeys
      } = options;
      //make a clone of signal
      const newSignal1 = symmetrize(signal, maxError, iteration, jAxisKey);
      if (!newSignal1.peaksComp) {
        throw new Error('internal peaksComp was not created');
      }
      const tmpPeaks = newSignal1.peaksComp;
      const tmpMask = newSignal1.mask;
      const cs = signal.delta;
      signal.delta = (signal.peaks[0].x + signal.peaks[signal.peaks.length - 1].x) / 2;
      const newSignal2 = symmetrize(signal, maxError, iteration, jAxisKey);
      if (!newSignal2.peaksComp) {
        throw new Error('internal peaksComp was not created');
      }
      if (newSignal2.peaksComp.length > tmpPeaks.length) {
        return newSignal2;
      }
      signal.delta = cs;
      signal.peaksComp = tmpPeaks;
      signal.mask = tmpMask;
      return newSignal1;
    }
    /**
     * This function will return a set of symmetric peaks that will
     * be the enter point for the patter compilation process.
     * @param {object} signal
     * @param {Number} maxError
     * @param {Number} iteration
     * @return {Number}
     * @private
     */
    function symmetrize(signalInput, maxError, iteration, key) {
      const {
        jAxis,
        intensity
      } = key;
      const newSignal = JSON.parse(JSON.stringify(signalInput));
      // Before to symmetrize we need to keep only the peaks that possibly conforms the multiplete
      let max, min, avg, ratio, avgWidth;
      const peaks = new Array(newSignal.peaks.length);
      // Make a deep copy of the peaks and convert PPM ot HZ
      for (let i = 0; i < peaks.length; i++) {
        const peak = newSignal.peaks[i];
        peaks[i] = {
          x: peak[jAxis] * newSignal.observe,
          intensity: peak[intensity],
          width: peak.width / newSignal.observe
        };
      }
      // Join the peaks that are closer than 0.25 Hz
      for (let i = peaks.length - 2; i >= 0; i--) {
        if (Math.abs(peaks[i].x - peaks[i + 1].x) < 0.25) {
          peaks[i].x = peaks[i].x * peaks[i].intensity + peaks[i + 1].x * peaks[i + 1].intensity;
          peaks[i].intensity = peaks[i].intensity + peaks[i + 1].intensity;
          peaks[i].x /= peaks[i].intensity;
          peaks[i].intensity /= 2;
          peaks[i].width += peaks[i + 1].width;
          peaks.splice(i + 1, 1);
        }
      }
      newSignal.peaksComp = peaks;
      const nbPeaks = peaks.length;
      const mask = new Array(nbPeaks);
      newSignal.mask = mask;
      let left = 0;
      let right = peaks.length - 1;
      let cs = newSignal.delta * newSignal.observe;
      let middle = [(peaks[0].x + peaks[nbPeaks - 1].x) / 2, 1];
      maxError = error(Math.abs(cs - middle[0]));
      let heightSum = 0;
      // We try to symmetrize the extreme peaks. We consider as candidates for symmetricing those which have
      // ratio smaller than 3
      for (let i = 0; i < nbPeaks; i++) {
        mask[i] = true;
        // heightSum += signal.peaks[i].intensity;
        heightSum += peaks[i].intensity;
      }
      while (left <= right) {
        mask[left] = true;
        mask[right] = true;
        if (left === right) {
          if (nbPeaks > 2 && Math.abs(peaks[left].x - cs) > maxError) {
            mask[left] = false;
          }
        } else {
          max = Math.max(peaks[left].intensity, peaks[right].intensity);
          min = Math.min(peaks[left].intensity, peaks[right].intensity);
          ratio = max / min;
          if (ratio > symRatio) {
            if (peaks[left].intensity === min) {
              mask[left] = false;
              right++;
            } else {
              mask[right] = false;
              left--;
            }
          } else {
            const diffL = Math.abs(peaks[left].x - cs);
            const diffR = Math.abs(peaks[right].x - cs);
            if (Math.abs(diffL - diffR) < maxError) {
              avg = Math.min(peaks[left].intensity, peaks[right].intensity);
              avgWidth = Math.min(peaks[left].width, peaks[right].width);
              peaks[left].intensity = avg;
              peaks[right].intensity = avg;
              peaks[left].width = avgWidth;
              peaks[right].width = avgWidth;
              middle = [middle[0] + (peaks[right].x + peaks[left].x) / 2, middle[1] + 1];
            } else if (Math.max(diffL, diffR) === diffR) {
              mask[right] = false;
              left--;
            } else {
              mask[left] = false;
              right++;
            }
          }
        }
        left++;
        right--;
        // Only alter cs if it is the first iteration of the sym process.
        if (iteration === 1) {
          cs = chemicalShift(peaks, mask);
          // There is not more available peaks
          if (isNaN(cs)) {
            newSignal.symRank = 0;
            return newSignal;
          }
        }
        maxError = error(Math.abs(cs - middle[0] / middle[1]));
      }
      // To remove the weak peaks and recalculate the cs
      for (let i = nbPeaks - 1; i >= 0; i--) {
        if (mask[i] === false) {
          peaks.splice(i, 1);
        }
      }
      cs = chemicalShift(peaks);
      if (isNaN(cs)) {
        newSignal.symRank = 0;
        return newSignal;
      }
      newSignal.delta = cs / newSignal.observe;
      // Now, the peak should be symmetric in heights, but we need to know if it is symmetric in x
      let symFactor = 0;
      let weight = 0;
      if (peaks.length > 1) {
        for (let i = Math.ceil(peaks.length / 2) - 1; i >= 0; i--) {
          symFactor += (3 + Math.min(Math.abs(peaks[i].x - cs), Math.abs(peaks[peaks.length - 1 - i].x - cs))) / (3 + Math.max(Math.abs(peaks[i].x - cs), Math.abs(peaks[peaks.length - 1 - i].x - cs))) * peaks[i].intensity;
          weight += peaks[i].intensity;
        }
        symFactor /= weight;
      } else if (peaks.length === 1) {
        symFactor = 1;
      }
      let newSumHeights = 0;
      for (const peak of peaks) {
        newSumHeights += peak.intensity;
      }
      symFactor -= (heightSum - newSumHeights) / heightSum * 0.12; // Removed peaks penalty
      // Sometimes we need a second opinion after the first symmetrization.
      if (symFactor > 0.8 && symFactor < 0.97 && iteration < 2) {
        return symmetrize(newSignal, maxErrorIter2, 2, key);
      } else if (peaks.length > 1) {
        // Center the given pattern at cs and symmetrize x
        let dxi;
        for (let i = Math.ceil(peaks.length / 2) - 1; i >= 0; i--) {
          dxi = (peaks[i].x - peaks[peaks.length - 1 - i].x) / 2.0;
          peaks[i].x = cs + dxi;
          peaks[peaks.length - 1 - i].x = cs - dxi;
        }
      }
      newSignal.symRank = symFactor;
      return newSignal;
    }
    /**
     * Error validator
     * @param {Number} value
     * @return {Number}
     * @private
     */
    function error(value) {
      let maxError = value * 2.5;
      if (maxError < 0.75) {
        maxError = 0.75;
      }
      if (maxError > 3) {
        maxError = 3;
      }
      return maxError;
    }
    /**
     * @private
     * 2 stages normalizarion of the peaks heights to Math.pow(2,n).
     * Creates a new mask with the peaks that could contribute to the multiplete
     * @param {object} signal
     * @param {Number} n
     * @return {*}
     */
    function normalize(signal, n) {
      // Perhaps this is slow
      const peaks = JSON.parse(JSON.stringify(signal.peaksComp));
      let norm = 0;
      let norm2 = 0;
      for (const peak of peaks) {
        norm += peak.intensity;
      }
      norm = Math.pow(2, n) / norm;
      signal.mask2 = signal.mask.slice();
      let index = signal.mask2.length - 1;
      for (let i = peaks.length - 1; i >= 0; i--) {
        peaks[i].intensity *= norm;
        while (index >= 0 && !signal.mask2[index]) {
          index--;
        }
        if (peaks[i].intensity < 0.75) {
          peaks.splice(i, 1);
          signal.mask2[index] = false;
        } else {
          norm2 += peaks[i].intensity;
        }
        index--;
      }
      norm2 = Math.pow(2, n) / norm2;
      for (let i = peaks.length - 1; i >= 0; i--) {
        peaks[i].intensity *= norm2;
      }
      return peaks;
    }
    /**
     * @private
     * Calculates the chemical shift as the weighted sum of the peaks
     * @param {Array} peaks
     * @param {Array} mask
     * @return {Number}
     */
    function chemicalShift(peaks, mask = []) {
      let sum = 0;
      let cs = 0;
      let area;
      if (mask.length > 0) {
        for (let i = 0; i < peaks.length; i++) {
          if (mask[i]) {
            area = getArea(peaks[i]);
            sum += area;
            cs += area * peaks[i].x;
          }
        }
      } else {
        for (const peak of peaks) {
          area = getArea(peak);
          sum += area;
          cs += area * peak.x;
        }
      }
      return cs / sum;
    }
    /**
     * Return the area of a Lorentzian function
     * @param {object} peak - object with peak information
     * @return {Number}
     * @private
     */
    function getArea(peak) {
      return Math.abs(peak.intensity * peak.width * 1.57); // 1.772453851);
    }

    function checkSignalAfterCompilePattern(signal) {
      // if (!signal.symRank)
      //   throw new Error('Internal error, symRank was not calculated');
      if (!signal.mask) throw new Error('Internal Error, mask was not added');
      if (!signal.mask2) throw new Error('Internal Error, mask2 was not added');
      if (!signal.maskPattern) {
        throw new Error('Internal Error, maskPattern was not added');
      }
    }
    const assignSignal = (peak, frequency, nucleus) => {
      return {
        id: v4(),
        delta: NaN,
        nbPeaks: 1,
        kind: 'signal',
        startX: peak.x - peak.width / frequency,
        stopX: peak.x + peak.width / frequency,
        observe: frequency,
        nucleus,
        integralData: {
          from: peak.x - peak.width * 3 / frequency,
          to: peak.x + peak.width * 3 / frequency,
          value: 0
        },
        peaks: [{
          x: peak.x,
          intensity: peak.y,
          width: peak.width
        }]
      };
    };
    /**
     * This function clustering peaks and calculate the integration value for each range from the peak list returned from extractPeaks function.
     */
    function peaksToRanges(data, peakList, options = {}) {
      const {
        integrationSum = 100,
        joinOverlapRanges = true,
        clean = 0.4,
        compile = true,
        integralType = 'sum',
        frequency = 400,
        frequencyCluster = 16,
        keepPeaks = false,
        nucleus = '1H'
      } = options;
      const signalOptions = {
        integrationSum,
        integralType,
        frequencyCluster,
        frequency,
        nucleus
      };
      if (peakList.length === 0) return [];
      if (data.x[0] > data.x[1]) {
        data.x = data.x.reverse();
        data.y = data.y.reverse();
      }
      const signals = detectSignals(data, peakList, signalOptions);
      if (clean) {
        for (let i = 0; i < signals.length; i++) {
          if (Math.abs(signals[i].integralData.value) < clean) {
            signals.splice(i, 1);
          }
        }
      }
      if (compile) {
        let nHi, sum;
        for (let i = 0; i < signals.length; i++) {
          signals[i] = jAnalyzer.compilePattern(signals[i]);
          const signal = signals[i];
          if (signal.maskPattern && signal.multiplicity !== 'm' && signal.multiplicity !== '') {
            checkSignalAfterCompilePattern(signal);
            // Create a new signal with the removed peaks
            nHi = 0;
            sum = 0;
            const peaksO = [];
            for (let j = signal.maskPattern.length - 1; j >= 0; j--) {
              sum += computeArea(signal.peaks[j], frequency);
              if (!signal.maskPattern[j]) {
                const peakR = signal.peaks.splice(j, 1)[0];
                peaksO.push({
                  x: peakR.x,
                  y: peakR.intensity,
                  width: peakR.width
                });
                signal.mask.splice(j, 1);
                signal.mask2.splice(j, 1);
                signal.maskPattern.splice(j, 1);
                signal.nbPeaks--;
                nHi += computeArea(peakR, frequency);
              }
            }
            if (peaksO.length > 0) {
              nHi = nHi * signal.integralData.value / sum;
              signal.integralData.value -= nHi;
              const peaks1 = [];
              for (let j = peaksO.length - 1; j >= 0; j--) {
                peaks1.push(peaksO[j]);
              }
              signalOptions.integrationSum = Math.abs(nHi);
              const ranges = detectSignals(data, peaks1, signalOptions);
              for (const range of ranges) {
                signals.push(range);
              }
            }
          }
        }
        // it was a updateIntegrals function.
        updateIntegration(signals, integrationSum);
      }
      signals.sort((a, b) => {
        return b.delta - a.delta;
      });
      if (clean) {
        for (let i = signals.length - 1; i >= 0; i--) {
          if (Math.abs(signals[i].integralData.value) < clean) {
            signals.splice(i, 1);
          }
        }
      }
      let ranges = [];
      for (let i = 0; i < signals.length; i++) {
        const signal = signals[i];
        const range = {
          id: v4(),
          from: signal.integralData.from,
          to: signal.integralData.to,
          integration: signal.integralData.value
        };
        const signalResult = {
          delta: signal.delta,
          kind: signal.kind || 'signal',
          multiplicity: signal.multiplicity
        };
        if (keepPeaks) {
          signalResult.peaks = signal.peaks.map(peak => {
            const newResult = {
              y: peak.intensity,
              ...peak
            };
            delete newResult.intensity;
            return newResult;
          });
        }
        if (signal.nmrJs) {
          signalResult.js = signal.nmrJs;
        }
        range.signals = [signalResult];
        ranges[i] = range;
      }
      if (joinOverlapRanges) ranges = joinRanges(ranges);
      return ranges;
    }
    /**
     * Extract the signals from the peakList and the given spectrum.
     * @param {object} data - spectra data
     * @param {array} peakList - nmr signals
     * @param {object} [options = {}]
     * @param {number} [options.integrationSum='100'] - Number of hydrogens or some number to normalize the integration data, If it's zero return the absolute integration value
     * @param {string} [options.integralType='sum'] - option to chose between approx area with peaks or the sum of the points of given range
     * @param {number} [options.frequencyCluster=16] - distance limit to clustering the peaks.
     * range = frequencyCluster / observeFrequency -> Peaks withing this range are considered to belongs to the same signal1D
     * @param {string} [options.nucleus='1H'] - - Nucleus
     * @param {String} [options.frequency = 400] - Observed frequency
     * @return {array} nmr signals
     * @private
     */
    function detectSignals(data, peakList, options = {}) {
      const {
        frequencyCluster = 16,
        integrationSum = 100,
        integralType = 'sum',
        frequency = 400,
        nucleus = '1H'
      } = options;
      let signal1D = assignSignal(peakList[0], frequency, nucleus);
      let peaks;
      const signals = [];
      let prevPeak = {
        x: Number.MIN_SAFE_INTEGER
      };
      const distantToClustering = frequencyCluster / frequency;
      for (const peak of peakList) {
        if (Math.abs(peak.x - prevPeak.x) > distantToClustering) {
          signal1D = assignSignal(peak, frequency, nucleus);
          if (peak.kind) signal1D.kind = peak.kind;
          signals.push(signal1D);
        } else {
          const tmp = peak.x + peak.width / frequency;
          signal1D.stopX = Math.max(signal1D.stopX, tmp);
          signal1D.startX = Math.min(signal1D.startX, tmp);
          signal1D.nbPeaks++;
          signal1D.peaks.push({
            x: peak.x,
            intensity: peak.y,
            width: peak.width
          });
          signal1D.integralData.from = Math.min(signal1D.integralData.from, peak.x - peak.width * 3 / frequency);
          signal1D.integralData.to = Math.max(signal1D.integralData.to, peak.x + peak.width * 3 / frequency);
          if (peak.kind) signal1D.kind = peak.kind;
        }
        prevPeak = peak;
      }
      for (const signal of signals) {
        peaks = signal.peaks;
        const integration = signal.integralData;
        let chemicalShift = 0;
        let integralPeaks = 0;
        for (const peak of peaks) {
          const area = computeArea(peak, frequency);
          chemicalShift += peak.x * area;
          integralPeaks += area;
        }
        signal.delta = chemicalShift / integralPeaks;
        if (integralType === 'sum') {
          integration.value = xyIntegration(data, {
            from: integration.from,
            to: integration.to
          });
        } else {
          integration.value = integralPeaks;
        }
      }
      if (integrationSum > 0) {
        updateIntegration(signals, integrationSum);
      }
      return signals;
    }
    /**
     * Return the area of a Lorentzian function
     * @private
     */
    function computeArea(peak, frequency) {
      return Math.abs(peak.intensity * peak.width / frequency * 1.57); // todo add an option with this value: 1.772453851
    }
    /**
     * update the integration Values
     * @param signals
     * @param integrationSum
     */
    function updateIntegration(signals, integrationSum) {
      let sumIntegral = 0;
      let sumObserved = 0;
      for (const signal of signals) {
        sumObserved += Math.abs(Math.round(signal.integralData.value));
      }
      if (sumObserved !== integrationSum) {
        sumIntegral = integrationSum / sumObserved;
        for (const signal of signals) {
          signal.integralData.value *= sumIntegral;
        }
      }
    }

    /**
     * Detect peaks, optimize parameters and compile multiplicity if required.
     */
    function xyAutoRangesPicking(data, options) {
      let peaks = xyAutoPeaksPicking(data, options.peakPicking);
      peaks = peaksFilterImpurities(peaks, options.impurities);
      return peaksToRanges(data, peaks, options.ranges);
    }

    function xyPeaksOptimization(data, peaks, options) {
      const {
        frequency
      } = options;
      const newPeaks = convertWidthToPPM(peaks, {
        frequency
      });
      const optimizedPeaks = optimizePeaks(data, newPeaks, options);
      const result = convertWidthToHz(optimizedPeaks, {
        frequency,
        output: optimizedPeaks
      });
      return result;
    }

    function formatZones(signals) {
      const zones = [];
      for (const signal of signals) {
        const minMax1 = [Number.MAX_VALUE, 0];
        const minMax2 = [Number.MAX_VALUE, 0];
        for (const peak of signal.peaks || []) {
          if (peak.minX < minMax1[0]) {
            minMax1[0] = peak.minX;
          }
          if (peak.maxX > minMax1[1]) {
            minMax1[1] = peak.maxX;
          }
          if (peak.minY < minMax2[0]) {
            minMax2[0] = peak.minY;
          }
          if (peak.maxY > minMax2[1]) {
            minMax2[1] = peak.maxY;
          }
        }
        zones.push({
          id: v4(),
          x: {
            from: minMax1[0],
            to: minMax1[1]
          },
          y: {
            from: minMax2[0],
            to: minMax2[1]
          },
          signals: [signal]
        });
      }
      return zones;
    }

    function xyzAutoZonesPicking(spectraData, options) {
      const signals = xyzAutoSignalsPicking(spectraData, options);
      return formatZones(signals);
    }

    var src = {};

    var fftlib = {};

    /**
     * Fast Fourier Transform module
     * 1D-FFT/IFFT, 2D-FFT/IFFT (radix-2)
     */
    (function (exports) {
      (function () {
        var FFT;
        {
          FFT = exports; // for CommonJS
        }
        var version = {
          release: '0.3.0',
          date: '2013-03'
        };
        FFT.toString = function () {
          return "version " + version.release + ", released " + version.date;
        };

        // core operations
        var _n = 0,
          // order
          _bitrev = null,
          // bit reversal table
          _cstb = null; // sin/cos table

        var core = {
          init: function (n) {
            if (n !== 0 && (n & n - 1) === 0) {
              _n = n;
              core._initArray();
              core._makeBitReversalTable();
              core._makeCosSinTable();
            } else {
              throw new Error("init: radix-2 required");
            }
          },
          // 1D-FFT
          fft1d: function (re, im) {
            core.fft(re, im, 1);
          },
          // 1D-IFFT
          ifft1d: function (re, im) {
            var n = 1 / _n;
            core.fft(re, im, -1);
            for (var i = 0; i < _n; i++) {
              re[i] *= n;
              im[i] *= n;
            }
          },
          // 1D-IFFT
          bt1d: function (re, im) {
            core.fft(re, im, -1);
          },
          // 2D-FFT Not very useful if the number of rows have to be equal to cols
          fft2d: function (re, im) {
            var tre = [],
              tim = [],
              i = 0;
            // x-axis
            for (var y = 0; y < _n; y++) {
              i = y * _n;
              for (var x1 = 0; x1 < _n; x1++) {
                tre[x1] = re[x1 + i];
                tim[x1] = im[x1 + i];
              }
              core.fft1d(tre, tim);
              for (var x2 = 0; x2 < _n; x2++) {
                re[x2 + i] = tre[x2];
                im[x2 + i] = tim[x2];
              }
            }
            // y-axis
            for (var x = 0; x < _n; x++) {
              for (var y1 = 0; y1 < _n; y1++) {
                i = x + y1 * _n;
                tre[y1] = re[i];
                tim[y1] = im[i];
              }
              core.fft1d(tre, tim);
              for (var y2 = 0; y2 < _n; y2++) {
                i = x + y2 * _n;
                re[i] = tre[y2];
                im[i] = tim[y2];
              }
            }
          },
          // 2D-IFFT
          ifft2d: function (re, im) {
            var tre = [],
              tim = [],
              i = 0;
            // x-axis
            for (var y = 0; y < _n; y++) {
              i = y * _n;
              for (var x1 = 0; x1 < _n; x1++) {
                tre[x1] = re[x1 + i];
                tim[x1] = im[x1 + i];
              }
              core.ifft1d(tre, tim);
              for (var x2 = 0; x2 < _n; x2++) {
                re[x2 + i] = tre[x2];
                im[x2 + i] = tim[x2];
              }
            }
            // y-axis
            for (var x = 0; x < _n; x++) {
              for (var y1 = 0; y1 < _n; y1++) {
                i = x + y1 * _n;
                tre[y1] = re[i];
                tim[y1] = im[i];
              }
              core.ifft1d(tre, tim);
              for (var y2 = 0; y2 < _n; y2++) {
                i = x + y2 * _n;
                re[i] = tre[y2];
                im[i] = tim[y2];
              }
            }
          },
          // core operation of FFT
          fft: function (re, im, inv) {
            var d,
              h,
              ik,
              m,
              tmp,
              wr,
              wi,
              xr,
              xi,
              n4 = _n >> 2;
            // bit reversal
            for (var l = 0; l < _n; l++) {
              m = _bitrev[l];
              if (l < m) {
                tmp = re[l];
                re[l] = re[m];
                re[m] = tmp;
                tmp = im[l];
                im[l] = im[m];
                im[m] = tmp;
              }
            }
            // butterfly operation
            for (var k = 1; k < _n; k <<= 1) {
              h = 0;
              d = _n / (k << 1);
              for (var j = 0; j < k; j++) {
                wr = _cstb[h + n4];
                wi = inv * _cstb[h];
                for (var i = j; i < _n; i += k << 1) {
                  ik = i + k;
                  xr = wr * re[ik] + wi * im[ik];
                  xi = wr * im[ik] - wi * re[ik];
                  re[ik] = re[i] - xr;
                  re[i] += xr;
                  im[ik] = im[i] - xi;
                  im[i] += xi;
                }
                h += d;
              }
            }
          },
          // initialize the array (supports TypedArray)
          _initArray: function () {
            if (typeof Uint32Array !== 'undefined') {
              _bitrev = new Uint32Array(_n);
            } else {
              _bitrev = [];
            }
            if (typeof Float64Array !== 'undefined') {
              _cstb = new Float64Array(_n * 1.25);
            } else {
              _cstb = [];
            }
          },
          // zero padding
          _paddingZero: function () {
            // TODO
          },
          // makes bit reversal table
          _makeBitReversalTable: function () {
            var i = 0,
              j = 0,
              k = 0;
            _bitrev[0] = 0;
            while (++i < _n) {
              k = _n >> 1;
              while (k <= j) {
                j -= k;
                k >>= 1;
              }
              j += k;
              _bitrev[i] = j;
            }
          },
          // makes trigonometiric function table
          _makeCosSinTable: function () {
            var n2 = _n >> 1,
              n4 = _n >> 2,
              n8 = _n >> 3,
              n2p4 = n2 + n4,
              t = Math.sin(Math.PI / _n),
              dc = 2 * t * t,
              ds = Math.sqrt(dc * (2 - dc)),
              c = _cstb[n4] = 1,
              s = _cstb[0] = 0;
            t = 2 * dc;
            for (var i = 1; i < n8; i++) {
              c -= dc;
              dc += t * c;
              s += ds;
              ds -= t * s;
              _cstb[i] = s;
              _cstb[n4 - i] = c;
            }
            if (n8 !== 0) {
              _cstb[n8] = Math.sqrt(0.5);
            }
            for (var j = 0; j < n4; j++) {
              _cstb[n2 - j] = _cstb[j];
            }
            for (var k = 0; k < n2p4; k++) {
              _cstb[k + n2] = -_cstb[k];
            }
          }
        };
        // aliases (public APIs)
        var apis = ['init', 'fft1d', 'ifft1d', 'fft2d', 'ifft2d'];
        for (var i = 0; i < apis.length; i++) {
          FFT[apis[i]] = core[apis[i]];
        }
        FFT.bt = core.bt1d;
        FFT.fft = core.fft1d;
        FFT.ifft = core.ifft1d;
        return FFT;
      }).call(commonjsGlobal);
    })(fftlib);

    var FFT = fftlib;
    var FFTUtils$1 = {
      DEBUG: false,
      /**
       * Calculates the inverse of a 2D Fourier transform
       *
       * @param ft
       * @param ftRows
       * @param ftCols
       * @return
       */
      ifft2DArray: function (ft, ftRows, ftCols) {
        var tempTransform = new Array(ftRows * ftCols);
        var nRows = ftRows / 2;
        var nCols = (ftCols - 1) * 2;
        // reverse transform columns
        FFT.init(nRows);
        var tmpCols = {
          re: new Array(nRows),
          im: new Array(nRows)
        };
        for (var iCol = 0; iCol < ftCols; iCol++) {
          for (var iRow = nRows - 1; iRow >= 0; iRow--) {
            tmpCols.re[iRow] = ft[iRow * 2 * ftCols + iCol];
            tmpCols.im[iRow] = ft[(iRow * 2 + 1) * ftCols + iCol];
          }
          //Unnormalized inverse transform
          FFT.bt(tmpCols.re, tmpCols.im);
          for (var iRow = nRows - 1; iRow >= 0; iRow--) {
            tempTransform[iRow * 2 * ftCols + iCol] = tmpCols.re[iRow];
            tempTransform[(iRow * 2 + 1) * ftCols + iCol] = tmpCols.im[iRow];
          }
        }

        // reverse row transform
        var finalTransform = new Array(nRows * nCols);
        FFT.init(nCols);
        var tmpRows = {
          re: new Array(nCols),
          im: new Array(nCols)
        };
        var scale = nCols * nRows;
        for (var iRow = 0; iRow < ftRows; iRow += 2) {
          tmpRows.re[0] = tempTransform[iRow * ftCols];
          tmpRows.im[0] = tempTransform[(iRow + 1) * ftCols];
          for (var iCol = 1; iCol < ftCols; iCol++) {
            tmpRows.re[iCol] = tempTransform[iRow * ftCols + iCol];
            tmpRows.im[iCol] = tempTransform[(iRow + 1) * ftCols + iCol];
            tmpRows.re[nCols - iCol] = tempTransform[iRow * ftCols + iCol];
            tmpRows.im[nCols - iCol] = -tempTransform[(iRow + 1) * ftCols + iCol];
          }
          //Unnormalized inverse transform
          FFT.bt(tmpRows.re, tmpRows.im);
          var indexB = iRow / 2 * nCols;
          for (var iCol = nCols - 1; iCol >= 0; iCol--) {
            finalTransform[indexB + iCol] = tmpRows.re[iCol] / scale;
          }
        }
        return finalTransform;
      },
      /**
       * Calculates the fourier transform of a matrix of size (nRows,nCols) It is
       * assumed that both nRows and nCols are a power of two
       *
       * On exit the matrix has dimensions (nRows * 2, nCols / 2 + 1) where the
       * even rows contain the real part and the odd rows the imaginary part of the
       * transform
       * @param data
       * @param nRows
       * @param nCols
       * @return
       */
      fft2DArray: function (data, nRows, nCols, opt) {
        Object.assign({}, {
          inplace: true
        });
        var ftCols = nCols / 2 + 1;
        var ftRows = nRows * 2;
        var tempTransform = new Array(ftRows * ftCols);
        FFT.init(nCols);
        // transform rows
        var tmpRows = {
          re: new Array(nCols),
          im: new Array(nCols)
        };
        var row1 = {
          re: new Array(nCols),
          im: new Array(nCols)
        };
        var row2 = {
          re: new Array(nCols),
          im: new Array(nCols)
        };
        var index, iRow0, iRow1, iRow2, iRow3;
        for (var iRow = 0; iRow < nRows / 2; iRow++) {
          index = iRow * 2 * nCols;
          tmpRows.re = data.slice(index, index + nCols);
          index = (iRow * 2 + 1) * nCols;
          tmpRows.im = data.slice(index, index + nCols);
          FFT.fft1d(tmpRows.re, tmpRows.im);
          this.reconstructTwoRealFFT(tmpRows, row1, row2);
          //Now lets put back the result into the output array
          iRow0 = iRow * 4 * ftCols;
          iRow1 = (iRow * 4 + 1) * ftCols;
          iRow2 = (iRow * 4 + 2) * ftCols;
          iRow3 = (iRow * 4 + 3) * ftCols;
          for (var k = ftCols - 1; k >= 0; k--) {
            tempTransform[iRow0 + k] = row1.re[k];
            tempTransform[iRow1 + k] = row1.im[k];
            tempTransform[iRow2 + k] = row2.re[k];
            tempTransform[iRow3 + k] = row2.im[k];
          }
        }

        //console.log(tempTransform);
        row1 = null;
        row2 = null;
        // transform columns
        var finalTransform = new Array(ftRows * ftCols);
        FFT.init(nRows);
        var tmpCols = {
          re: new Array(nRows),
          im: new Array(nRows)
        };
        for (var iCol = ftCols - 1; iCol >= 0; iCol--) {
          for (var iRow = nRows - 1; iRow >= 0; iRow--) {
            tmpCols.re[iRow] = tempTransform[iRow * 2 * ftCols + iCol];
            tmpCols.im[iRow] = tempTransform[(iRow * 2 + 1) * ftCols + iCol];
            //TODO Chech why this happens
            if (isNaN(tmpCols.re[iRow])) {
              tmpCols.re[iRow] = 0;
            }
            if (isNaN(tmpCols.im[iRow])) {
              tmpCols.im[iRow] = 0;
            }
          }
          FFT.fft1d(tmpCols.re, tmpCols.im);
          for (var iRow = nRows - 1; iRow >= 0; iRow--) {
            finalTransform[iRow * 2 * ftCols + iCol] = tmpCols.re[iRow];
            finalTransform[(iRow * 2 + 1) * ftCols + iCol] = tmpCols.im[iRow];
          }
        }

        //console.log(finalTransform);
        return finalTransform;
      },
      /**
       *
       * @param fourierTransform
       * @param realTransform1
       * @param realTransform2
       *
       * Reconstructs the individual Fourier transforms of two simultaneously
       * transformed series. Based on the Symmetry relationships (the asterisk
       * denotes the complex conjugate)
       *
       * F_{N-n} = F_n^{*} for a purely real f transformed to F
       *
       * G_{N-n} = G_n^{*} for a purely imaginary g transformed to G
       *
       */
      reconstructTwoRealFFT: function (fourierTransform, realTransform1, realTransform2) {
        var length = fourierTransform.re.length;

        // the components n=0 are trivial
        realTransform1.re[0] = fourierTransform.re[0];
        realTransform1.im[0] = 0.0;
        realTransform2.re[0] = fourierTransform.im[0];
        realTransform2.im[0] = 0.0;
        var rm, rp, im, ip, j;
        for (var i = length / 2; i > 0; i--) {
          j = length - i;
          rm = 0.5 * (fourierTransform.re[i] - fourierTransform.re[j]);
          rp = 0.5 * (fourierTransform.re[i] + fourierTransform.re[j]);
          im = 0.5 * (fourierTransform.im[i] - fourierTransform.im[j]);
          ip = 0.5 * (fourierTransform.im[i] + fourierTransform.im[j]);
          realTransform1.re[i] = rp;
          realTransform1.im[i] = im;
          realTransform1.re[j] = rp;
          realTransform1.im[j] = -im;
          realTransform2.re[i] = ip;
          realTransform2.im[i] = -rm;
          realTransform2.re[j] = ip;
          realTransform2.im[j] = rm;
        }
      },
      /**
       * In place version of convolute 2D
       *
       * @param ftSignal
       * @param ftFilter
       * @param ftRows
       * @param ftCols
       * @return
       */
      convolute2DI: function (ftSignal, ftFilter, ftRows, ftCols) {
        var re, im;
        for (var iRow = 0; iRow < ftRows / 2; iRow++) {
          for (var iCol = 0; iCol < ftCols; iCol++) {
            //
            re = ftSignal[iRow * 2 * ftCols + iCol] * ftFilter[iRow * 2 * ftCols + iCol] - ftSignal[(iRow * 2 + 1) * ftCols + iCol] * ftFilter[(iRow * 2 + 1) * ftCols + iCol];
            im = ftSignal[iRow * 2 * ftCols + iCol] * ftFilter[(iRow * 2 + 1) * ftCols + iCol] + ftSignal[(iRow * 2 + 1) * ftCols + iCol] * ftFilter[iRow * 2 * ftCols + iCol];
            //
            ftSignal[iRow * 2 * ftCols + iCol] = re;
            ftSignal[(iRow * 2 + 1) * ftCols + iCol] = im;
          }
        }
      },
      /**
       *
       * @param data
       * @param kernel
       * @param nRows
       * @param nCols
       * @returns {*}
       */
      convolute: function (data, kernel, nRows, nCols, opt) {
        var ftSpectrum = new Array(nCols * nRows);
        for (var i = 0; i < nRows * nCols; i++) {
          ftSpectrum[i] = data[i];
        }
        ftSpectrum = this.fft2DArray(ftSpectrum, nRows, nCols);
        var dimR = kernel.length;
        var dimC = kernel[0].length;
        var ftFilterData = new Array(nCols * nRows);
        for (var i = 0; i < nCols * nRows; i++) {
          ftFilterData[i] = 0;
        }
        var iRow, iCol;
        var shiftR = Math.floor((dimR - 1) / 2);
        var shiftC = Math.floor((dimC - 1) / 2);
        for (var ir = 0; ir < dimR; ir++) {
          iRow = (ir - shiftR + nRows) % nRows;
          for (var ic = 0; ic < dimC; ic++) {
            iCol = (ic - shiftC + nCols) % nCols;
            ftFilterData[iRow * nCols + iCol] = kernel[ir][ic];
          }
        }
        ftFilterData = this.fft2DArray(ftFilterData, nRows, nCols);
        var ftRows = nRows * 2;
        var ftCols = nCols / 2 + 1;
        this.convolute2DI(ftSpectrum, ftFilterData, ftRows, ftCols);
        return this.ifft2DArray(ftSpectrum, ftRows, ftCols);
      },
      toRadix2: function (data, nRows, nCols) {
        var i, j, irow, icol;
        var cols = nCols,
          rows = nRows;
        if (!(nCols !== 0 && (nCols & nCols - 1) === 0)) {
          //Then we have to make a pading to next radix2
          cols = 0;
          while (nCols >> ++cols != 0);
          cols = 1 << cols;
        }
        if (!(nRows !== 0 && (nRows & nRows - 1) === 0)) {
          //Then we have to make a pading to next radix2
          rows = 0;
          while (nRows >> ++rows != 0);
          rows = 1 << rows;
        }
        if (rows == nRows && cols == nCols)
          //Do nothing. Returns the same input!!! Be careful
          return {
            data: data,
            rows: nRows,
            cols: nCols
          };
        var output = new Array(rows * cols);
        var shiftR = Math.floor((rows - nRows) / 2) - nRows;
        var shiftC = Math.floor((cols - nCols) / 2) - nCols;
        for (i = 0; i < rows; i++) {
          irow = i * cols;
          icol = (i - shiftR) % nRows * nCols;
          for (j = 0; j < cols; j++) {
            output[irow + j] = data[icol + (j - shiftC) % nCols];
          }
        }
        return {
          data: output,
          rows: rows,
          cols: cols
        };
      },
      /**
       * Crop the given matrix to fit the corresponding number of rows and columns
       */
      crop: function (data, rows, cols, nRows, nCols, opt) {
        if (rows == nRows && cols == nCols)
          //Do nothing. Returns the same input!!! Be careful
          return data;
        Object.assign({}, opt);
        var output = new Array(nCols * nRows);
        var shiftR = Math.floor((rows - nRows) / 2);
        var shiftC = Math.floor((cols - nCols) / 2);
        var destinyRow, sourceRow, i, j;
        for (i = 0; i < nRows; i++) {
          destinyRow = i * nCols;
          sourceRow = (i + shiftR) * cols;
          for (j = 0; j < nCols; j++) {
            output[destinyRow + j] = data[sourceRow + (j + shiftC)];
          }
        }
        return output;
      }
    };
    var FFTUtils_1 = FFTUtils$1;

    var FFTUtils = src.FFTUtils = FFTUtils_1;
    src.FFT = fftlib;

    function matrix2Array(input) {
      let inputData = input;
      let nRows, nCols;
      if (typeof input[0] !== 'number') {
        nRows = input.length;
        nCols = input[0].length;
        inputData = new Array(nRows * nCols);
        for (let i = 0; i < nRows; i++) {
          for (let j = 0; j < nCols; j++) {
            inputData[i * nCols + j] = input[i][j];
          }
        }
      } else {
        let tmp = Math.sqrt(input.length);
        if (Number.isInteger(tmp)) {
          nRows = tmp;
          nCols = tmp;
        }
      }
      return {
        data: inputData,
        rows: nRows,
        cols: nCols
      };
    }

    function convolutionFFT(input, kernel, opt) {
      let tmp = matrix2Array(input);
      let inputData = tmp.data;
      let options = Object.assign({
        normalize: false,
        divisor: 1,
        rows: tmp.rows,
        cols: tmp.cols
      }, opt);
      let nRows, nCols;
      if (options.rows && options.cols) {
        nRows = options.rows;
        nCols = options.cols;
      } else {
        throw new Error(`Invalid number of rows or columns ${nRows} ${nCols}`);
      }
      let divisor = options.divisor;
      let kHeight = kernel.length;
      let kWidth = kernel[0].length;
      if (options.normalize) {
        divisor = 0;
        for (let i = 0; i < kHeight; i++) {
          for (let j = 0; j < kWidth; j++) divisor += kernel[i][j];
        }
      }
      if (divisor === 0) {
        throw new RangeError('convolution: The divisor is equal to zero');
      }
      let radix2Sized = FFTUtils.toRadix2(inputData, nRows, nCols);
      let conv = FFTUtils.convolute(radix2Sized.data, kernel, radix2Sized.rows, radix2Sized.cols);
      conv = FFTUtils.crop(conv, radix2Sized.rows, radix2Sized.cols, nRows, nCols);
      if (divisor !== 0 && divisor !== 1) {
        for (let i = 0; i < conv.length; i++) {
          conv[i] /= divisor;
        }
      }
      return conv;
    }

    function convolutionDirect(input, kernel, opt) {
      let tmp = matrix2Array(input);
      let inputData = tmp.data;
      let options = Object.assign({
        normalize: false,
        divisor: 1,
        rows: tmp.rows,
        cols: tmp.cols
      }, opt);
      let nRows, nCols;
      if (options.rows && options.cols) {
        nRows = options.rows;
        nCols = options.cols;
      } else {
        throw new Error(`Invalid number of rows or columns ${nRows} ${nCols}`);
      }
      let divisor = options.divisor;
      let kHeight = kernel.length;
      let kWidth = kernel[0].length;
      let index, sum, kVal, row, col;
      if (options.normalize) {
        divisor = 0;
        for (let i = 0; i < kHeight; i++) {
          for (let j = 0; j < kWidth; j++) divisor += kernel[i][j];
        }
      }
      if (divisor === 0) {
        throw new RangeError('convolution: The divisor is equal to zero');
      }
      let output = new Array(nRows * nCols);
      let hHeight = Math.floor(kHeight / 2);
      let hWidth = Math.floor(kWidth / 2);
      for (let y = 0; y < nRows; y++) {
        for (let x = 0; x < nCols; x++) {
          sum = 0;
          for (let j = 0; j < kHeight; j++) {
            for (let i = 0; i < kWidth; i++) {
              kVal = kernel[kHeight - j - 1][kWidth - i - 1];
              row = (y + j - hHeight + nRows) % nRows;
              col = (x + i - hWidth + nCols) % nCols;
              index = row * nCols + col;
              sum += inputData[index] * kVal;
            }
          }
          index = y * nCols + x;
          output[index] = sum / divisor;
        }
      }
      return output;
    }

    /**
     * @class DisjointSet
     */
    class DisjointSet {
      constructor() {
        this.nodes = new Map();
      }

      /**
       * Adds an element as a new set
       * @param {*} value
       * @return {DisjointSetNode} Object holding the element
       */
      add(value) {
        var node = this.nodes.get(value);
        if (!node) {
          node = new DisjointSetNode(value);
          this.nodes.set(value, node);
        }
        return node;
      }

      /**
       * Merges the sets that contain x and y
       * @param {DisjointSetNode} x
       * @param {DisjointSetNode} y
       */
      union(x, y) {
        const rootX = this.find(x);
        const rootY = this.find(y);
        if (rootX === rootY) {
          return;
        }
        if (rootX.rank < rootY.rank) {
          rootX.parent = rootY;
        } else if (rootX.rank > rootY.rank) {
          rootY.parent = rootX;
        } else {
          rootY.parent = rootX;
          rootX.rank++;
        }
      }

      /**
       * Finds and returns the root node of the set that contains node
       * @param {DisjointSetNode} node
       * @return {DisjointSetNode}
       */
      find(node) {
        var rootX = node;
        while (rootX.parent !== null) {
          rootX = rootX.parent;
        }
        var toUpdateX = node;
        while (toUpdateX.parent !== null) {
          var toUpdateParent = toUpdateX;
          toUpdateX = toUpdateX.parent;
          toUpdateParent.parent = rootX;
        }
        return rootX;
      }

      /**
       * Returns true if x and y belong to the same set
       * @param {DisjointSetNode} x
       * @param {DisjointSetNode} y
       */
      connected(x, y) {
        return this.find(x) === this.find(y);
      }
    }
    var DisjointSet_1 = DisjointSet;
    function DisjointSetNode(value) {
      this.value = value;
      this.parent = null;
      this.rank = 0;
    }
    var DisjointSet$1 = DisjointSet_1;

    const direction8X$2 = [-1, -1, 0, 1, -1, 0, 1, 1];
    const direction8Y$2 = [0, -1, -1, -1, 1, 1, 1, 0];
    const neighbours8$1 = [null, null, null, null, null, null, null, null];
    const direction4X$1 = [-1, 0, 1, 0];
    const direction4Y$1 = [0, -1, 0, 1];
    const neighbours4$1 = [null, null, null, null];
    function drainLabelling(data, mask, options = {}) {
      const {
        neighbours = 8,
        width,
        height
      } = options;
      let directionX;
      let directionY;
      let neighboursList;
      if (neighbours === 8) {
        directionX = direction8X$2;
        directionY = direction8Y$2;
        neighboursList = neighbours8$1;
      } else if (neighbours === 4) {
        directionX = direction4X$1;
        directionY = direction4Y$1;
        neighboursList = neighbours4$1;
      } else {
        throw new RangeError(`unsupported neighbours count: ${neighbours}`);
      }
      let sorted = new Array(height * width);
      for (let i = 0, index = 0; i < height; i++) {
        for (let j = 0; j < width; j++, index++) {
          sorted[index] = {
            value: data[index],
            row: i,
            col: j,
            mask: mask[index]
          };
        }
      }
      sorted.sort((a, b) => a.value - b.value);
      const size = mask.length;
      const labels = new Array(size);
      const pixels = new Int16Array(size);
      const linked = new DisjointSet$1();
      for (let i = 0, currentLabel = 1; i < mask.length; i++) {
        let element = sorted[i];
        if (!element.mask) continue;
        let {
          row,
          col,
          value
        } = element;
        let index = col + row * width;
        let label = labels[index];
        if (!label) {
          labels[index] = linked.add(currentLabel++);
        }
        for (let k = 0; k < neighboursList.length; k++) {
          let ii = col + directionX[k];
          let jj = row + directionY[k];
          if (ii >= 0 && jj >= 0 && ii < width && jj < height) {
            let neighbor = labels[ii + jj * width];
            if (!neighbor) {
              let neighborValue = data[ii + jj * width];
              if (value < neighborValue) {
                labels[ii + jj * width] = labels[index];
              }
            }
          }
        }
      }
      for (let j = 0; j < height; j++) {
        for (let i = 0; i < width; i++) {
          let index = i + j * width;
          if (mask[index]) {
            pixels[index] = linked.find(labels[index]).value;
          }
        }
      }
      return pixels;
    }

    const direction4X = [-1, 0];
    const direction4Y = [0, -1];
    const neighbours4 = [null, null];
    const direction8X$1 = [-1, -1, 0, 1];
    const direction8Y$1 = [0, -1, -1, -1];
    const neighbours8 = [null, null, null, null];
    function floodFillLabelling(mask, width, height, options) {
      options = options || {};
      const neighbours = options.neighbours || 8;
      let directionX;
      let directionY;
      let neighboursList;
      if (neighbours === 8) {
        directionX = direction8X$1;
        directionY = direction8Y$1;
        neighboursList = neighbours8;
      } else if (neighbours === 4) {
        directionX = direction4X;
        directionY = direction4Y;
        neighboursList = neighbours4;
      } else {
        throw new RangeError(`unsupported neighbours count: ${neighbours}`);
      }
      const size = mask.length;
      const labels = new Array(size);
      const pixels = new Int16Array(size);
      const linked = new DisjointSet$1();
      let index;
      let currentLabel = 1;
      for (let j = 0; j < height; j++) {
        for (let i = 0; i < width; i++) {
          // true means out of background
          let smallestNeighbor = null;
          index = i + j * width;
          if (mask[index]) {
            for (let k = 0; k < neighboursList.length; k++) {
              let ii = i + directionX[k];
              let jj = j + directionY[k];
              if (ii >= 0 && jj >= 0 && ii < width && jj < height) {
                let neighbor = labels[ii + jj * width];
                if (!neighbor) {
                  neighboursList[k] = null;
                } else {
                  neighboursList[k] = neighbor;
                  if (!smallestNeighbor || neighboursList[k].value < smallestNeighbor.value) {
                    smallestNeighbor = neighboursList[k];
                  }
                }
              }
            }
            if (!smallestNeighbor) {
              labels[index] = linked.add(currentLabel++);
            } else {
              labels[index] = smallestNeighbor;
              for (let k = 0; k < neighboursList.length; k++) {
                if (neighboursList[k] && neighboursList[k] !== smallestNeighbor) {
                  linked.union(smallestNeighbor, neighboursList[k]);
                }
              }
            }
          }
        }
      }
      for (let j = 0; j < height; j++) {
        for (let i = 0; i < width; i++) {
          index = i + j * width;
          if (mask[index]) {
            pixels[index] = linked.find(labels[index]).value;
          }
        }
      }
      return pixels;
    }

    const smallFilter$1 = [[0, 0, 1, 2, 2, 2, 1, 0, 0], [0, 1, 4, 7, 7, 7, 4, 1, 0], [1, 4, 5, 3, 0, 3, 5, 4, 1], [2, 7, 3, -12, -23, -12, 3, 7, 2], [2, 7, 0, -23, -40, -23, 0, 7, 2], [2, 7, 3, -12, -23, -12, 3, 7, 2], [1, 4, 5, 3, 0, 3, 5, 4, 1], [0, 1, 3, 7, 7, 7, 3, 1, 0], [0, 0, 1, 2, 2, 2, 1, 0, 0]];

    /**
     * Detects all the 2D-peaks in the given spectrum based on center of mass logic.
     * @param {Array<Array>} input - matrix to get the local maxima
     * @param {Object} [options = {}] - options of the method.
     * @param {Array<Array>} [options.nStdDev = 3] - number of times of the standard deviations for the noise level.Float64Array
     * @param {Array<Array>} [options.kernel] - kernel to the convolution step.
     * @param {string} [options.labelling = 'drain'] - select the labelling algorithm to assign pixels.
     * @param {Array<Array>} [options.originalData] - original data useful when the original matrix has values and the input matrix has absolute ones
     * @param {Array<Array>} [options.filteredData] - convoluted data, if it is defined the convolution step is skipped
     */
    function findPeaks2DRegion(input, options = {}) {
      let {
        nStdDev = 3,
        kernel = smallFilter$1,
        originalData = matrix2Array(input).data,
        filteredData,
        rows: nRows,
        cols: nCols,
        labelling = 'drain'
      } = options;
      let flatten = matrix2Array(input);
      let data = flatten.data;
      if (!nRows || !nCols) {
        nRows = flatten.rows;
        nCols = flatten.cols;
      }
      if (!nRows || !nCols) {
        throw new Error(`Invalid number of rows or columns ${nRows} ${nCols}`);
      }
      let cs = filteredData;
      if (!cs) cs = convolutionFFT(data, kernel, options);
      let threshold = 0;
      for (let i = nCols * nRows - 2; i >= 0; i--) {
        threshold += Math.pow(cs[i] - cs[i + 1], 2);
      }
      threshold = -Math.sqrt(threshold) * nStdDev / nRows;
      let bitmask = new Uint16Array(nCols * nRows);
      for (let i = cs.length - 1; i >= 0; i--) {
        if (cs[i] < threshold) {
          bitmask[i] = 1;
        }
      }
      let pixels;
      switch (labelling.toLowerCase()) {
        case 'drain':
          pixels = drainLabelling(cs, bitmask, {
            neighbours: 8,
            width: nCols,
            height: nRows
          });
          break;
        case 'floodfill':
          pixels = floodFillLabelling(bitmask, nCols, nRows, {
            neighbours: 8
          });
          break;
        default:
          throw new Error(`labelling ${labelling} does not support`);
      }
      return extractPeaks(pixels, {
        data,
        nCols,
        originalData
      });
    }
    function extractPeaks(pixels, options) {
      const {
        data,
        nCols,
        originalData
      } = options;
      //How many different groups we have?
      let labels = {};
      let row, col, tmp;
      for (let i = 0; i < pixels.length; i++) {
        if (pixels[i] !== 0) {
          col = i % nCols;
          row = (i - col) / nCols;
          if (labels[pixels[i]]) {
            tmp = labels[pixels[i]];
            tmp.x += col * data[i];
            tmp.y += row * data[i];
            tmp.z += originalData[i];
            if (col < tmp.minX) tmp.minX = col;
            if (col > tmp.maxX) tmp.maxX = col;
            if (row < tmp.minY) tmp.minY = row;
            if (row > tmp.maxY) tmp.maxY = row;
          } else {
            labels[pixels[i]] = {
              x: col * data[i],
              y: row * data[i],
              z: originalData[i],
              minX: col,
              maxX: col,
              minY: row,
              maxY: row
            };
          }
        }
      }
      let keys = Object.keys(labels);
      let peakList = new Array(keys.length);
      for (let i = 0; i < keys.length; i++) {
        peakList[i] = labels[keys[i]];
        let zValue = Math.abs(peakList[i].z);
        peakList[i].x /= zValue;
        peakList[i].y /= zValue;
      }
      return peakList;
    }

    const {
      parse: parse$2,
      stringify: stringify$2
    } = JSON;
    function addMissingIDs(peaks, options = {}) {
      const {
        output = parse$2(stringify$2(peaks))
      } = options;
      for (const peak of output) {
        if (!('id' in peak)) {
          peak.id = v4();
        }
      }
      return output;
    }

    const direction8X = [-1, -1, -1, 0, 0, 1, 1, 1];
    const direction8Y = [-1, 0, 1, -1, 1, -1, 0, 1];
    const direction16X = [-2, -2, -2, -2, -2, -1, -1, 0, 0, 1, 1, 2, 2, 2, 2, 2];
    const direction16Y = [-2, -1, 0, 1, 2, -2, 2, -2, 2, -2, 2, -2, -1, 0, 1, 2];
    function determineRealTop(peaks, options) {
      const {
        nCols,
        absoluteData,
        originalData,
        minX,
        maxX,
        minY,
        maxY
      } = options;
      for (let i = 0; i < peaks.length; i++) {
        const xIndex = Math.round(peaks[i].x);
        const yIndex = Math.round(peaks[i].y);
        let currentIndex = xIndex + yIndex * nCols;
        const {
          index,
          isMax
        } = determineMax(absoluteData, {
          xIndex,
          yIndex,
          nCols,
          shell: 1
        });
        currentIndex = isMax ? index : determineMax(absoluteData, {
          xIndex,
          yIndex,
          nCols,
          shell: 2
        }).index;
        const realTopCoordinates = fitGaussian(originalData, {
          nCols,
          index: currentIndex,
          minY,
          maxY,
          minX,
          maxX
        });
        peaks[i] = Object.assign(peaks[i], realTopCoordinates);
      }
      return peaks;
    }
    function determineMax(data, options) {
      const {
        xIndex,
        yIndex,
        shell,
        nCols
      } = options;
      let currentIndex = xIndex + yIndex * nCols;
      const [directionX, directionY] = shell > 1 ? [direction16X, direction16Y] : [direction8X, direction8Y];
      let isMax = false;
      for (let i = 0; i < directionX.length; i++) {
        const c = xIndex + directionX[i];
        const r = yIndex + directionY[i];
        if (data[c + r * nCols] >= data[currentIndex]) {
          isMax = true;
          const candidateIndex = c + r * nCols;
          for (let k = 0; k < direction8Y.length; k++) {
            const nc = c + direction8X[k];
            const nr = r + direction8Y[k];
            if (data[nc + nr * nCols] > data[candidateIndex]) {
              isMax = false;
              break;
            }
          }
          if (isMax) {
            currentIndex = candidateIndex;
          }
        }
      }
      return {
        index: currentIndex,
        isMax
      };
    }
    function fitGaussian(data, options) {
      const {
        nCols,
        index,
        minY,
        maxY,
        minX,
        maxX
      } = options;
      const nRows = data.length / nCols;
      const intervalX = (maxX - minX) / (nCols - 1);
      const intervalY = (maxY - minY) / (nRows - 1);
      const col = index % nCols;
      const row = (index - col) / nCols;
      const newCol = 1;
      const newRow = 1;
      let max = Number.MIN_SAFE_INTEGER;
      const z = new Array(direction8X.length + 1);
      const xAxis = new Array(direction8X.length + 1);
      for (let i = -1, xi = 0; i < 2; i++) {
        for (let j = -1; j < 2; j++, xAxis[xi] = xi++) {
          const value = data[col + j + (row + i) * nCols];
          if (max < value) max = value;
          z[newCol + j + (newRow + i) * 3] = value;
        }
      }
      for (let i = 0; i < z.length; i++) z[i] /= max;
      const maxValues = [newCol + 1, newRow + 1, 1.5, 1, 1];
      const minValues = [newCol - 1, newRow - 1, -1.5, 0.001, 0.001];
      const initialValues = [newCol, newRow, z[newCol + newRow * 3], 0.2, 0.2];
      const gradientDifference = [1e-4, 1e-4, 1e-3, 1e-3, 1e-3];
      const func = paramGaussian2D(intervalX, intervalY, 3);
      const pFit = levenbergMarquardt({
        x: xAxis,
        y: z
      }, func, {
        damping: 1.5,
        maxIterations: 100,
        errorTolerance: 1e-8,
        initialValues,
        gradientDifference,
        maxValues,
        minValues
      }).parameterValues;
      return {
        x: pFit[0] + col - 1,
        y: pFit[1] + row - 1,
        z: pFit[2] * max
      };
    }
    function paramGaussian2D(intervalX, intervalY, nCols) {
      const gaussian2D = new Gaussian2D();
      return p => {
        return t => {
          const nL = p.length / 5;
          let result = 0;
          const xIndex = t % nCols;
          const yIndex = (t - xIndex) / nCols;
          for (let i = 0; i < nL; i++) {
            gaussian2D.fwhm = {
              x: p[i + 3 * nL],
              y: p[i + 4 * nL]
            };
            result += p[i + 2 * nL] * gaussian2D.fct((xIndex - p[i]) * intervalX, (yIndex - p[i + nL]) * intervalY);
          }
          return result;
        };
      };
    }

    function getKernel(options = {}) {
      const {
        sigma = 1.4,
        xLength = 9,
        yLength = 9
      } = options;
      const factor = -40 / laplacianOfGaussian(0, 0, sigma);
      const xCenter = (xLength - 1) / 2;
      const yCenter = (yLength - 1) / 2;
      const matrix = new Array(xLength);
      for (let x = 0; x < xLength; x++) {
        matrix[x] = new Array(yLength);
        for (let y = 0; y < yLength; y++) {
          matrix[x][y] = laplacianOfGaussian(x - xCenter, y - yCenter, sigma) * factor;
        }
      }
      return matrix;
    }
    const laplacianOfGaussian = (x, y, sigma) => {
      const factor = -(Math.pow(x, 2) + Math.pow(y, 2)) / 2 / Math.pow(sigma, 2);
      return -(1 / Math.PI / Math.pow(sigma, 4)) * (1 + factor) * Math.exp(factor);
    };

    const diagonalError = 0.05;
    const tolerance = 0.05;
    function clean(peaks, threshold) {
      let max = Number.NEGATIVE_INFINITY;
      // double min = Double.MAX_VALUE;
      for (let i = peaks.length - 1; i >= 0; i--) {
        if (Math.abs(peaks[i].z) > max) {
          max = Math.abs(peaks[i].z);
        }
      }
      max *= threshold;
      for (let i = peaks.length - 1; i >= 0; i--) {
        if (Math.abs(peaks[i].z) < max) {
          peaks.splice(i, 1);
        }
      }
      return peaks;
    }
    function enhanceSymmetry(signals) {
      const properties = initializeProperties(signals);
      const output = signals.slice();
      // First step of the optimization: Symmetry validation
      let hits;
      for (let i = output.length - 1; i >= 0; i--) {
        const signal = output[i];
        if (signal.peaks && signal.peaks.length > 1) {
          properties[i][1]++;
        }
        if (properties[i][0] === 1) {
          const index = exist(output, properties, signal, -1, true);
          if (index >= 0) {
            properties[i][1] += 2;
            properties[index][1] += 2;
          }
        }
      }
      // Second step of the optimization: Diagonal image existence
      for (let i = output.length - 1; i >= 0; i--) {
        const signal = output[i];
        if (properties[i][0] === 0) {
          hits = checkCrossPeaks(output, properties, signal, true);
          properties[i][1] += hits;
          // checkCrossPeaks(output, properties, signal, false);
        }
      }
      // Now, each peak have a score between 0 and 4, we can complete the patterns which
      // contains peaks with high scores, and finally, we can remove peaks with scores 0 and 1
      let count = 0;
      for (let i = output.length - 1; i >= 0; i--) {
        if (properties[i][0] !== 0 && properties[i][1] > 2) {
          count++;
          count += completeMissingIfNeeded(output, properties, output[i], properties[i]);
        }
        if (properties[i][1] >= 2 && properties[i][0] === 0) {
          count++;
        }
      }
      const toReturn = new Array(count);
      count--;
      for (let i = output.length - 1; i >= 0; i--) {
        if (properties[i][0] !== 0 && properties[i][1] > 2 || properties[i][0] === 0 && properties[i][1] > 1) {
          toReturn[count--] = output[i];
        }
      }
      return toReturn;
    }
    // export function alignDimensions(signals2D, references) {
    //   // For each reference dimension
    //   for (let i = 0; i < references.length; i++) {
    //     let ref = references[i];
    //     if (ref) {
    //       alignSingleDimension(signals2D, ref);
    //     }
    //   }
    // }
    function completeMissingIfNeeded(output, properties, thisSignal, thisProp) {
      // Check for symmetry
      const index = exist(output, properties, thisSignal, -thisProp[0], true);
      let addedPeaks = 0;
      let newSignal;
      let tmpProp = null;
      if (index < 0) {
        // If this signal have no a symmetry image, we have to include it
        newSignal = {
          x: {
            nucleus: thisSignal.x.nucleus,
            resolution: thisSignal.x.resolution,
            delta: thisSignal.y.delta
          },
          y: {
            nucleus: thisSignal.y.nucleus,
            resolution: thisSignal.y.resolution,
            delta: thisSignal.x.delta
          },
          peaks: [{
            x: thisSignal.y.delta,
            y: thisSignal.x.delta,
            z: 1
          }]
        };
        output.push(newSignal);
        tmpProp = [-thisProp[0], thisProp[1]];
        properties.push(tmpProp);
        addedPeaks++;
      }
      // Check for diagonal peaks
      let signal;
      let diagX = false;
      let diagY = false;
      for (let j = output.length - 1; j >= 0; j--) {
        signal = output[j];
        if (properties[j][0] === 0) {
          if (Math.abs(signal.x.delta - thisSignal.x.delta) < diagonalError) {
            diagX = true;
          }
          if (Math.abs(signal.y.delta - thisSignal.y.delta) < diagonalError) {
            diagY = true;
          }
        }
      }
      if (!diagX) {
        newSignal = {
          x: {
            delta: thisSignal.y.delta,
            nucleus: thisSignal.x.nucleus,
            resolution: thisSignal.x.resolution
          },
          y: {
            delta: thisSignal.x.delta,
            nucleus: thisSignal.y.nucleus,
            resolution: thisSignal.y.resolution
          },
          peaks: [{
            x: thisSignal.x.delta,
            y: thisSignal.x.delta,
            z: 1
          }]
        };
        newSignal.peaks = [{
          x: thisSignal.x.delta,
          y: thisSignal.x.delta,
          z: 1
        }];
        output.push(newSignal);
        tmpProp = [0, thisProp[1]];
        properties.push(tmpProp);
        addedPeaks++;
      }
      if (!diagY) {
        newSignal = {
          x: {
            delta: thisSignal.y.delta,
            nucleus: thisSignal.x.nucleus,
            resolution: thisSignal.x.resolution
          },
          y: {
            delta: thisSignal.y.delta,
            nucleus: thisSignal.y.nucleus,
            resolution: thisSignal.y.resolution
          },
          peaks: [{
            x: thisSignal.y.delta,
            y: thisSignal.y.delta,
            z: 1
          }]
        };
        output.push(newSignal);
        tmpProp = [0, thisProp[1]];
        properties.push(tmpProp);
        addedPeaks++;
      }
      return addedPeaks;
    }
    // Check for any diagonal peak that match this cross peak
    function checkCrossPeaks(output, properties, signal, updateProperties) {
      let hits = 0;
      let shift = signal.x.delta * 4;
      const crossPeaksX = [];
      const crossPeaksY = [];
      let cross;
      for (let i = output.length - 1; i >= 0; i--) {
        cross = output[i];
        if (properties[i][0] !== 0) {
          if (Math.abs(cross.x.delta - signal.x.delta) < diagonalError) {
            hits++;
            if (updateProperties) {
              properties[i][1]++;
            }
            crossPeaksX.push(i);
            shift += cross.x.delta;
          } else if (Math.abs(cross.y.delta - signal.y.delta) < diagonalError) {
            hits++;
            if (updateProperties) {
              properties[i][1]++;
            }
            crossPeaksY.push(i);
            shift += cross.y.delta;
          }
        }
      }
      // Update found crossPeaks and diagonal peak
      shift /= crossPeaksX.length + crossPeaksY.length + 4;
      if (crossPeaksX.length > 0) {
        for (let i = crossPeaksX.length - 1; i >= 0; i--) {
          output[crossPeaksX[i]].x.delta = shift;
        }
      }
      if (crossPeaksY.length > 0) {
        for (let i = crossPeaksY.length - 1; i >= 0; i--) {
          output[crossPeaksY[i]].y.delta = shift;
        }
      }
      signal.x.delta = shift;
      signal.y.delta = shift;
      return hits;
    }
    function exist(output, properties, signal, type, symmetricSearch) {
      for (let i = output.length - 1; i >= 0; i--) {
        if (properties[i][0] === type) {
          if (distanceTo(signal, output[i], symmetricSearch) < tolerance) {
            if (!symmetricSearch) {
              const shiftX = (output[i].x.delta + signal.x.delta) / 2.0;
              const shiftY = (output[i].y.delta + signal.y.delta) / 2.0;
              output[i].x.delta = shiftX;
              output[i].y.delta = shiftY;
              signal.x.delta = shiftX;
              signal.y.delta = shiftY;
            } else {
              const shiftX = signal.x.delta;
              const shiftY = output[i].x.delta;
              output[i].y.delta = shiftX;
              signal.y.delta = shiftY;
            }
            return i;
          }
        }
      }
      return -1;
    }
    /**
     * Try to determine the position of each signal within the spectrum matrix.
     * Peaks could be of 3 types: upper diagonal, diagonal or under diagonal 1,0,-1
     * respectively.
     * @param {Array} signals
     * @return {*} A matrix containing the properties of each signal
     * @private
     */
    function initializeProperties(signals) {
      const signalsProperties = new Array(signals.length);
      for (let i = signals.length - 1; i >= 0; i--) {
        signalsProperties[i] = [0, 0];
        // We check if it is a diagonal peak
        if (Math.abs(signals[i].x.delta - signals[i].y.delta) <= diagonalError) {
          signalsProperties[i][1] = 1;
          const shift = (signals[i].x.delta * 2 + signals[i].y.delta) / 3.0;
          signals[i].x.delta = shift;
          signals[i].y.delta = shift;
        } else if (signals[i].x.delta - signals[i].y.delta > 0) {
          signalsProperties[i][0] = 1;
        } else {
          signalsProperties[i][0] = -1;
        }
      }
      return signalsProperties;
    }
    /**
     * This function calculates the distance between 2 nmr signals . If toImage is true,
     * it will interchange x by y in the distance calculation for the second signal.
     * @param {object} a
     * @param {object} b
     * @param {boolean} toImage
     * @return {number}
     * @private
     */
    function distanceTo(a, b, toImage) {
      if (!toImage) {
        return Math.sqrt(Math.pow(a.x.delta - b.x.delta, 2) + Math.pow(a.y.delta - b.y.delta, 2));
      } else {
        return Math.sqrt(Math.pow(a.x.delta - b.y.delta, 2) + Math.pow(a.y.delta - b.x.delta, 2));
      }
    }
    // type Signal2DWithHighlight = MakeMandatory<NMRSignal2D, '_highlight'>;
    // function checkHighlight(signals2D: NMRSignal2D[]): asserts signals2D is Signal2DwithHighlight[] {
    //   for (let signal of signals2D) {
    //     if (!signal._highlight) throw new Error('there is not _hightlight property');
    //     if (!signal.nucleusX) throw new Error('there is not nucleusX property');
    //     if (!signal.nucleusY) throw new Error('there is not nucleusY property')
    //   }
    // }
    // function alignSingleDimension(signals2D: NMRSignal2D[], references) {
    //   // For each 2D signal
    //   for (let i = 0; i < signals2D.length; i++) {
    //     let signal2D = signals2D[i];
    //     for (let j = 0; j < references.length; j++) {
    //       const center = (references[j].startX + references[j].stopX) / 2;
    //       const width = Math.abs(references[j].startX - references[j].stopX) / 2;
    //       if (signal2D.nucleusX === references[j].nucleus) {
    //         // The 2D peak overlaps with the 1D signal
    //         if (Math.abs(signal2D.x.delta - center) <= width) {
    //           signal2D._highlight.push(references[j]._highlight[0]);
    //         }
    //       }
    //       if (signal2D.nucleusY === references[j].nucleus) {
    //         if (Math.abs(signal2D.y.delta - center) <= width) {
    //           signal2D._highlight.push(references[j]._highlight[0]);
    //         }
    //       }
    //     }
    //   }
    // }

    function padData(spectraData, options) {
      const {
        minX,
        maxX,
        minY,
        maxY
      } = spectraData;
      const width = options.width;
      const nbPoints = spectraData.z[0].length;
      const nbSubSpectra = spectraData.z.length;
      const yInterval = (maxY - minY) / (nbSubSpectra - 1);
      const xInterval = (maxX - minX) / (nbPoints - 1);
      let yDiff = width - nbSubSpectra;
      let xDiff = Math.max(width - nbPoints, 0);
      if (xDiff % 2) xDiff++;
      if (yDiff % 2) yDiff++;
      const xOffset = xDiff / 2;
      const yOffset = yDiff / 2;
      const newMatrix = [];
      for (let i = 0; i < nbSubSpectra + yDiff; i++) {
        newMatrix.push(new Float64Array(nbPoints + xDiff));
      }
      for (let i = 0; i < nbSubSpectra; i++) {
        for (let j = 0; j < nbPoints; j++) {
          newMatrix[i + yOffset].set(spectraData.z[i], xOffset);
        }
      }
      return {
        ...spectraData,
        z: newMatrix,
        minX: minX - xOffset * xInterval,
        maxX: maxX + xOffset * xInterval,
        minY: minY - yOffset * yInterval,
        maxY: maxY + yOffset * yInterval
      };
    }

    const smallFilter = [[0, 0, 1, 2, 2, 2, 1, 0, 0], [0, 1, 4, 7, 7, 7, 4, 1, 0], [1, 4, 5, 3, 0, 3, 5, 4, 1], [2, 7, 3, -12, -23, -12, 3, 7, 2], [2, 7, 0, -23, -40, -23, 0, 7, 2], [2, 7, 3, -12, -23, -12, 3, 7, 2], [1, 4, 5, 3, 0, 3, 5, 4, 1], [0, 1, 3, 7, 7, 7, 3, 1, 0], [0, 0, 1, 2, 2, 2, 1, 0, 0]];
    function xyzAutoSignalsPicking(spectraData, options) {
      const {
        sizeToPad = 14,
        realTopDetection = true,
        nuclei = ['1H', '1H'],
        observedFrequencies,
        enhanceSymmetry: enhanceSymmetry$1 = false,
        clean: clean$1 = true,
        maxPercentCutOff = 0.03,
        tolerances = [24, 24],
        convolutionByFFT = true,
        kernel: kernelOptions
      } = options;
      let {
        thresholdFactor = 0.5
      } = options;
      if (!Array.isArray(observedFrequencies) && !ArrayBuffer.isView(observedFrequencies)) {
        throw new Error('observedFrequencies is mandatory');
      }
      thresholdFactor = thresholdFactor === 0 ? 1 : Math.abs(thresholdFactor);
      let nbPoints = spectraData.z[0].length;
      let nbSubSpectra = spectraData.z.length;
      if (nbSubSpectra < sizeToPad) {
        spectraData = padData(spectraData, {
          width: sizeToPad
        });
        nbPoints = spectraData.z[0].length;
        nbSubSpectra = spectraData.z.length;
      }
      const absoluteData = new Float64Array(nbPoints * nbSubSpectra);
      const originalData = new Float64Array(nbPoints * nbSubSpectra);
      for (let iSubSpectra = 0; iSubSpectra < nbSubSpectra; iSubSpectra++) {
        const spectrum = spectraData.z[iSubSpectra];
        for (let iCol = 0; iCol < nbPoints; iCol++) {
          const index = iSubSpectra * nbPoints + iCol;
          absoluteData[index] = Math.abs(spectrum[iCol]);
          originalData[index] = spectrum[iCol]; //@todo pensar si se puede evitar originalData
        }
      }

      const kernel = kernelOptions ? getKernel(kernelOptions) : smallFilter;
      const convolutedSpectrum = convolutionByFFT ? convolutionFFT(absoluteData, kernel, {
        rows: nbSubSpectra,
        cols: nbPoints
      }) : convolutionDirect(absoluteData, kernel, {
        rows: nbSubSpectra,
        cols: nbPoints
      });
      let peaksMC1 = findPeaks2DRegion(absoluteData, {
        originalData,
        filteredData: convolutedSpectrum,
        rows: nbSubSpectra,
        cols: nbPoints,
        nStdDev: thresholdFactor
      });
      if (clean$1) {
        // Remove peaks with less than x% of the intensity of the highest peak
        peaksMC1 = clean(peaksMC1, maxPercentCutOff);
      }
      let signals = createSignals2D$1(addMissingIDs(peaksMC1), {
        nRows: nbSubSpectra,
        nCols: nbPoints,
        minX: spectraData.minX,
        maxX: spectraData.maxX,
        minY: spectraData.minY,
        maxY: spectraData.maxY,
        absoluteData,
        originalData,
        tolerances,
        nuclei,
        observedFrequencies,
        realTopDetection
      });
      if (enhanceSymmetry$1) {
        signals = enhanceSymmetry(signals);
      }
      return signals;
    }
    function createSignals2D$1(peaks, options) {
      const {
        nCols,
        nRows,
        absoluteData,
        originalData,
        observedFrequencies,
        tolerances,
        nuclei,
        realTopDetection,
        minY,
        maxY,
        minX,
        maxX
      } = options;
      const [nucleusX, nucleusY] = nuclei;
      const [toleranceX, toleranceY] = tolerances;
      const [observeFrequencyX, observeFrequencyY] = observedFrequencies;
      const dy = (maxY - minY) / (nRows - 1);
      const dx = (maxX - minX) / (nCols - 1);
      if (realTopDetection) {
        peaks = determineRealTop(peaks, {
          nCols,
          absoluteData,
          originalData,
          minX,
          maxX,
          minY,
          maxY
        });
      }
      for (let i = peaks.length - 1; i >= 0; i--) {
        const {
          x,
          y
        } = peaks[i];
        peaks[i].x = minX + dx * x;
        peaks[i].y = minY + dy * y;
        peaks[i].minX = minX + dx * peaks[i].minX;
        peaks[i].minY = minY + dy * peaks[i].minY;
        peaks[i].maxX = minX + dx * peaks[i].maxX;
        peaks[i].maxY = minY + dy * peaks[i].maxY;
        // Still having problems to correctly detect peaks on those areas. So I'm removing everything there.
      }
      // The connectivity matrix is an square and symmetric matrix, so we'll only store the upper diagonal in an
      // array like form
      const connectivity = [];
      for (let i = 0; i < peaks.length; i++) {
        for (let j = i; j < peaks.length; j++) {
          if (Math.abs(peaks[i].x - peaks[j].x) * observeFrequencyX < toleranceX && Math.abs(peaks[i].y - peaks[j].y) * observeFrequencyY < toleranceY) {
            // 24*24Hz We cannot distinguish peaks with less than 20 Hz of separation
            connectivity.push(1);
          } else {
            connectivity.push(0);
          }
        }
      }
      const clusters = simpleClustering(connectivity);
      const signals = [];
      if (clusters) {
        for (const cluster of clusters) {
          const signal = {
            id: v4(),
            x: {
              delta: 0,
              nucleus: nucleusX,
              resolution: dx
            },
            y: {
              delta: 0,
              nucleus: nucleusY,
              resolution: dy
            }
          };
          const peaks2D = [];
          let sumZ = 0;
          for (let jPeak = 0; jPeak < cluster.length; jPeak++) {
            if (cluster[jPeak] === 1) {
              peaks2D.push(peaks[jPeak]);
              signal.x.delta += peaks[jPeak].x * peaks[jPeak].z;
              signal.y.delta += peaks[jPeak].y * peaks[jPeak].z;
              sumZ += peaks[jPeak].z;
            }
          }
          signal.x.delta /= sumZ;
          signal.y.delta /= sumZ;
          signal.peaks = peaks2D;
          signals.push(signal);
        }
      }
      return signals;
    }

    const {
      parse: parse$1,
      stringify: stringify$1
    } = JSON;
    function xyzJResAnalyzer(signals, options = {}) {
      const {
        reference = 0,
        getZones = false,
        referenceMaxShiftError = 0.08,
        tolerances = [10, 100],
        nuclei = ['1H', '1H'],
        observedFrequencies = [400, 400],
        jAnalyzer = {
          jAxisKey: {
            jAxis: 'y',
            intensity: 'z'
          }
        }
      } = options;
      const temporalSignals = compilePattern(signals, {
        observedFrequencies,
        tolerances,
        nuclei,
        jAnalyzer
      });
      //check if the signal are symmetric around the reference
      let signals2D = [];
      for (const tempSignal of temporalSignals) {
        const delta = tempSignal.y.delta;
        if (Math.abs(delta - reference) > referenceMaxShiftError) continue;
        signals2D.push(tempSignal);
      }
      signals2D = addMissingIDs(signals2D);
      return getZones ? formatZones(signals2D) : signals2D;
    }
    function compilePattern(signals, options) {
      const {
        observedFrequencies,
        tolerances,
        nuclei,
        jAnalyzer: jAnalyzerOptions
      } = options;
      const signalOptions = {
        observedFrequencies,
        tolerances,
        nuclei,
        dx: signals[0].x.resolution,
        dy: signals[0].y.resolution
      };
      const defaultWidth = 0.02 * observedFrequencies[0];
      const newSignals = [];
      //adapt to 1D jAnalyzer
      for (const signal of signals) {
        const peaks = [];
        for (const peak of signal.peaks || []) {
          peaks.push({
            ...peak,
            width: defaultWidth
          });
        }
        peaks.sort((a, b) => a.y - b.y);
        newSignals.push({
          nbPeaks: peaks.length,
          multiplicity: '',
          pattern: '',
          startX: peaks[0].y - defaultWidth,
          stopX: peaks[peaks.length - 1].y + defaultWidth,
          delta: signal.y.delta,
          observe: observedFrequencies[1],
          integralData: {
            value: -1,
            from: Number.MAX_SAFE_INTEGER,
            to: Number.MIN_SAFE_INTEGER
          },
          peaks
        });
      }
      for (let i = 0; i < signals.length; i++) {
        const signal = jAnalyzer.compilePattern(newSignals[i], jAnalyzerOptions);
        if (signal.maskPattern && signal.multiplicity !== 'm' && signal.multiplicity !== '') {
          // Create a new signal with the removed peaks
          const peaksO = [];
          for (let j = signal.maskPattern.length - 1; j >= 0; j--) {
            if (!signal.maskPattern[j]) {
              const peakR = signal.peaks.splice(j, 1)[0];
              peaksO.push(peakR);
              signal.mask.splice(j, 1);
              signal.mask2?.splice(j, 1);
              signal.maskPattern.splice(j, 1);
              signal.nbPeaks--;
            }
          }
          if (peaksO.length > 0) {
            peaksO.reverse();
            const signals2D = createSignals2D(peaksO, signalOptions);
            for (const signal of signals2D) {
              newSignals.push(signal);
            }
          }
        }
      }
      signals.sort((a, b) => {
        return b.x.delta - a.x.delta;
      });
      return signals;
    }
    function createSignals2D(peaksInput, options) {
      const {
        observedFrequencies,
        tolerances,
        nuclei,
        dx,
        dy
      } = options;
      const peaks = parse$1(stringify$1(peaksInput));
      const [nucleusX, nucleusY] = nuclei;
      const [toleranceX, toleranceY] = tolerances;
      const [observeFrequencyX, observeFrequencyY] = observedFrequencies;
      // The connectivity matrix is an square and symmetric matrix, so we'll only store the upper diagonal in an
      // array like form
      const connectivity = [];
      for (let i = 0; i < peaks.length; i++) {
        for (let j = i; j < peaks.length; j++) {
          if (Math.abs(peaks[i].x - peaks[j].x) * observeFrequencyX < toleranceX && Math.abs(peaks[i].y - peaks[j].y) * observeFrequencyY < toleranceY) {
            // 24*24Hz We cannot distinguish peaks with less than 20 Hz of separation
            connectivity.push(1);
          } else {
            connectivity.push(0);
          }
        }
      }
      const clusters = simpleClustering(connectivity);
      const signals = [];
      if (clusters) {
        for (const cluster of clusters) {
          const signal = {
            id: v4(),
            nucleusX,
            nucleusY,
            integralData: {
              from: Number.MAX_SAFE_INTEGER,
              to: Number.MIN_SAFE_INTEGER
            },
            nbPeaks: 0,
            multiplicity: '',
            pattern: '',
            observe: observeFrequencyY,
            resolutionX: dx,
            resolutionY: dy,
            shiftX: 0,
            shiftY: 0
          };
          const peaks2D = [];
          const minMax1 = [Number.MAX_VALUE, 0];
          const minMax2 = [Number.MAX_VALUE, 0];
          let sumZ = 0;
          for (let jPeak = cluster.length - 1; jPeak >= 0; jPeak--) {
            if (cluster[jPeak] === 1) {
              signal.nbPeaks++;
              if (!peaks[jPeak].width) peaks[jPeak].width = 0.02;
              peaks2D.push(peaks[jPeak]);
              signal.shiftX += peaks[jPeak].x * peaks[jPeak].z;
              signal.shiftY += peaks[jPeak].y * peaks[jPeak].z;
              sumZ += peaks[jPeak].z;
              if (peaks[jPeak].minX < minMax1[0]) {
                minMax1[0] = peaks[jPeak].minX;
              }
              if (peaks[jPeak].maxX > minMax1[1]) {
                minMax1[1] = peaks[jPeak].maxX;
              }
              if (peaks[jPeak].minY < minMax2[0]) {
                minMax2[0] = peaks[jPeak].minY;
              }
              if (peaks[jPeak].maxY > minMax2[1]) {
                minMax2[1] = peaks[jPeak].maxY;
              }
            }
          }
          signal.fromTo = [{
            from: minMax1[0],
            to: minMax1[1]
          }, {
            from: minMax2[0],
            to: minMax2[1]
          }];
          signal.shiftX /= sumZ;
          signal.shiftY /= sumZ;
          signal.delta1 = signal.shiftY;
          signal.peaks = peaks2D;
          signals.push(signal);
        }
      }
      return signals;
    }

    function checkData2DFid(data) {
      if (!('re' in data && 'im' in data)) {
        throw new Error('imaginary (im) data should exists');
      }
    }

    function quadrature(data, options) {
      checkData2DFid(data);
      const {
        fnMode
      } = options;
      switch (fnMode.toLowerCase()) {
        case 'ttpi':
          return ttpiQuadrature(data);
        case 'states':
          return shrQuadrature(data);
        default:
          return {
            re: data.re.z,
            im: data.im.z
          };
      }
    }
    function shrQuadrature(data) {
      const {
        re,
        im
      } = data;
      return {
        re: re.z,
        im: im.z
      };
    }
    function ttpiQuadrature(data) {
      const newRe = [];
      const newIm = [];
      const {
        re: {
          z: reMatrix
        },
        im: {
          z: imMatrix
        }
      } = data;
      for (let i = 0; i < reMatrix.length; i++) {
        newRe.push(i % 2 ? reMatrix[i].slice() : xMultiply(reMatrix[i], -1));
        newIm.push(i % 2 ? imMatrix[i].slice() : xMultiply(imMatrix[i], -1));
      }
      return {
        re: newRe,
        im: newIm
      };
    }

    function digitalFilter$1(data, options) {
      const {
        digitalFilterValue = 0
      } = options;
      const re = new Float64Array(data.re);
      const im = new Float64Array(data.im);
      let pointsToShift = Math.floor(digitalFilterValue);
      const skip = 0;
      pointsToShift += 0;
      const newRe = new Float64Array(re.length);
      const newIm = new Float64Array(im.length);
      newRe.set(re.slice(pointsToShift));
      newRe.set(re.slice(skip, pointsToShift), re.length - pointsToShift);
      newIm.set(im.slice(pointsToShift));
      newIm.set(im.slice(skip, pointsToShift), im.length - pointsToShift);
      return {
        re: newRe,
        im: newIm
      };
    }

    function removeDCOffset$1(data, options) {
      const {
        digitalFilterValue = 0
      } = options;
      const nbPoints = data.re.length;
      const newRe = new Float64Array(data.re);
      const newIm = new Float64Array(data.im);
      const averageRe = xMean(data.re.slice(nbPoints * 0.75 >> 0, nbPoints - digitalFilterValue));
      const averageIm = xMean(data.im.slice(nbPoints * 0.75 >> 0, nbPoints - digitalFilterValue));
      for (let i = digitalFilterValue; i < nbPoints - digitalFilterValue; i++) {
        newRe[i] -= averageRe;
        newIm[i] -= averageIm;
      }
      return {
        re: newRe,
        im: newIm
      };
    }

    function zeroFilling$1(data, options) {
      const {
        factor = 2,
        digitalFilterValue: grpdly = 0
      } = options;
      const nbPoints = options.nbPoints || 2 ** Math.round(Math.log2(data.re.length * factor));
      let pointsToShift;
      if (grpdly > 0) {
        pointsToShift = Math.floor(grpdly);
      } else {
        pointsToShift = 0;
      }
      const {
        re,
        im
      } = data;
      const newRE = new Float64Array(nbPoints);
      const newIM = new Float64Array(nbPoints);
      const length = Math.min(nbPoints, re.length);
      newRE.set(re.slice(0, length - pointsToShift));
      newIM.set(im.slice(0, length - pointsToShift));
      if (pointsToShift > 0 && pointsToShift < nbPoints) {
        newRE.set(re.slice(re.length - pointsToShift), nbPoints - pointsToShift);
        newIM.set(im.slice(re.length - pointsToShift), nbPoints - pointsToShift);
      }
      return {
        re: newRE,
        im: newIM
      };
    }

    function fftDirectDimension(data, options = {}) {
      const {
        re: reData,
        im: imData
      } = data;
      const nbRows = reData.length;
      const nbColumns = reData[0].length;
      const {
        zeroFilling: zeroFillingF2,
        digitalFilterValue = 0
      } = options;
      const {
        factor: f2Factor = 2
      } = zeroFillingF2 || {};
      const nbPointsF2 = zeroFillingF2?.nbPoints || 2 ** Math.round(Math.log2(nbColumns * f2Factor));
      const reFFT = new Matrix(nbRows, nbPointsF2);
      const imFFT = new Matrix(nbRows, nbPointsF2);
      const {
        apodization: apodizationF2,
        phaseCorrection = {}
      } = options;
      const {
        ph0: ph0F2,
        ph1: ph1F2,
        mode: pcModeF2 = 'no'
      } = phaseCorrection;
      for (let i = 0; i < nbRows; i++) {
        const newData = digitalFilter$1({
          re: reData[i],
          im: imData[i]
        }, {
          digitalFilterValue
        });
        Object.assign(newData, removeDCOffset$1(newData, {
          digitalFilterValue
        }));
        if (apodizationF2) {
          const {
            re: aRe,
            im: aIm
          } = apodization$1(newData, apodizationF2);
          Object.assign(newData, {
            re: aRe,
            im: aIm
          });
        }
        Object.assign(newData, zeroFilling$1(newData, {
          digitalFilterValue,
          nbPoints: nbPointsF2
        }));
        Object.assign(newData, reimFFT(newData, {
          applyZeroShift: true
        }));
        const dfResidual = (digitalFilterValue - Math.round(digitalFilterValue)) * Math.PI * 2;
        if (dfResidual > 0) {
          Object.assign(newData, reimPhaseCorrection(newData, 0, -dfResidual));
        }
        if (pcModeF2 === 'pk' && ph0F2 !== undefined && ph1F2 !== undefined) {
          Object.assign(newData, reimPhaseCorrection(newData, ph0F2 * Math.PI / 180, ph1F2 * Math.PI / 180));
        }
        reFFT.setRow(i, newData.re);
        imFFT.setRow(i, newData.im);
      }
      return {
        re: reFFT,
        im: imFFT
      };
    }

    function fftIndirectDimension(data, options) {
      const newData = {
        re: Matrix.checkMatrix(data.re),
        im: Matrix.checkMatrix(data.im)
      };
      const nbRows = newData.re.rows;
      const nbColumns = newData.re.columns;
      const {
        zeroFilling: zeroFillingF1,
        reverse
      } = options;
      const {
        factor: f1Factor = 2
      } = zeroFillingF1 || {};
      const nbPoints = zeroFillingF1?.nbPoints || 2 ** Math.round(Math.log2(nbRows * f1Factor));
      const {
        fnMode
      } = options;
      const newOptions = {
        ...options,
        zeroFilling: {
          ...zeroFillingF1,
          nbPoints
        }
      };
      const quadrants = {};
      if (fnMode === 'QF') {
        const reResult = new Matrix(nbPoints, nbColumns);
        const imResult = new Matrix(nbPoints, nbColumns);
        for (let i = 0; i < nbColumns; i++) {
          const data = {
            re: newData.re.getColumn(i),
            im: reverse ? newData.im.getColumn(i) : xMultiply(newData.im.getColumn(i), -1)
          };
          const {
            re,
            im
          } = applyFFT(data, newOptions);
          reResult.setColumn(i, re);
          imResult.setColumn(i, im);
        }
        quadrants.rr = reResult;
        quadrants.ir = imResult;
      } else {
        // four quadrant processing
        for (const current in newData) {
          const reResult = new Matrix(nbPoints, nbColumns);
          const imResult = new Matrix(nbPoints, nbColumns);
          for (let i = 0; i < nbColumns; i++) {
            const data = getColumnFrom(newData[current], {
              fnMode,
              index: i
            });
            const {
              re,
              im
            } = applyFFT(data, newOptions);
            reResult.setColumn(i, re);
            imResult.setColumn(i, im);
          }
          if (current === 're') {
            quadrants.rr = reResult;
            quadrants.ir = imResult;
          } else {
            quadrants.ri = reResult;
            quadrants.ii = imResult;
          }
        }
      }
      return quadrants;
    }
    function applyFFT(data, options) {
      const {
        fnMode,
        apodization: apodizationF1,
        zeroFilling: zeroFillingF1 = {},
        phaseCorrection = {}
      } = options;
      const {
        re,
        im
      } = apodization$1(data, apodizationF1);
      const {
        nbPoints = 0
      } = zeroFillingF1;
      const transformed = reimFFT(zeroFilling$1({
        re,
        im
      }, {
        nbPoints
      }), {
        applyZeroShift: !['SEQ', 'TPPI'].some(k => fnMode === k)
      });
      if (options.phaseCorrection?.mode === 'mc') {
        Object.assign(transformed, {
          re: reimAbsolute(transformed),
          im: new Float64Array(nbPoints)
        });
      }
      const {
        mode: pcModeF1,
        ph0: ph0F1,
        ph1: ph1F1
      } = phaseCorrection;
      if (pcModeF1 === 'pk' && ph0F1 !== undefined && ph1F1 !== undefined) {
        Object.assign(transformed, reimPhaseCorrection(transformed, ph0F1 * Math.PI / 180, ph1F1 * Math.PI / 180));
      }
      return transformed;
    }
    function getColumnFrom(data, info) {
      const {
        fnMode,
        index
      } = info;
      const reverse = info.reverse || fnMode === 'QF';
      const nbPoints = data.rows / 2;
      const re = new Float64Array(nbPoints);
      const im = new Float64Array(nbPoints);
      switch (fnMode?.toLocaleLowerCase()) {
        // case 'states':
        // case 'echo-antiecho':
        case 'seq':
        case 'tppi':
          return {
            re: data.getColumn(index),
            im: new Float64Array(data.rows)
          };
        default:
          if (reverse) {
            for (let i = 0; i < nbPoints; i++) {
              re[i] = data.get(2 * i, index);
              im[i] = -data.get(2 * i + 1, index);
            }
          } else {
            for (let i = 0; i < nbPoints; i++) {
              re[i] = data.get(2 * i, index);
              im[i] = data.get(2 * i + 1, index);
            }
          }
      }
      if (fnMode === 'states-ttpi') {
        //it is to convert to the equivalent of states
        for (let i = 0; i < nbPoints; i += 2) {
          re[i] *= -1;
          im[i] *= -1;
        }
      }
      return {
        re,
        im
      };
    }

    function getMinMaxXY(info) {
      const min = [];
      const max = [];
      for (let i = 0; i < info.baseFrequency.length; i++) {
        const baseFrequency = info.baseFrequency[i];
        const frequencyOffset = info.frequencyOffset[i];
        const spectralWidth = info.spectralWidth[i];
        const offset = frequencyOffset / baseFrequency;
        min.push(offset - spectralWidth * 0.5);
        max.push(offset + spectralWidth * 0.5);
      }
      return {
        minX: min[0],
        maxX: max[0],
        minY: min[1],
        maxY: max[1]
      };
    }

    function xyzBidimensionalFFT(data, options) {
      ensureData2DFid(data);
      const {
        info,
        fnMode,
        digitalFilterValue = 0,
        zeroFilling: zeroFillingGeneral
      } = options;
      const orderedData = quadrature(data, {
        fnMode
      });
      const {
        re: reData
      } = orderedData;
      const nbRows = reData.length;
      const nbColumns = reData[0].length;
      const {
        direct = {},
        indirect = {}
      } = options;
      const {
        zeroFilling: zeroFillingF2 = zeroFillingGeneral
      } = direct;
      const {
        factor: f2Factor = 2
      } = zeroFillingF2 || {};
      const nbPointsF2 = zeroFillingF2?.nbPoints || 2 ** Math.round(Math.log2(nbColumns * f2Factor));
      const transformed = fftDirectDimension(orderedData, {
        ...direct,
        fnMode,
        digitalFilterValue,
        zeroFilling: {
          nbPoints: nbPointsF2
        }
      });
      const {
        zeroFilling: zeroFillingF1
      } = options;
      const {
        factor: f1Factor = 2
      } = zeroFillingF1 || {};
      const nbPointsF1 = zeroFillingF1?.nbPoints || 2 ** Math.round(Math.log2(nbRows * f1Factor));
      const {
        re: reResult,
        im: imResult
      } = fftIndirectDimension(transformed, {
        ...indirect,
        fnMode,
        zeroFilling: {
          nbPoints: nbPointsF1
        }
      });
      const minMaxXY = getMinMaxXY(info);
      return {
        re: {
          z: convertToDoubleArray(reResult),
          maxZ: reResult.max(),
          minZ: reResult.min(),
          ...minMaxXY
        },
        im: {
          z: convertToDoubleArray(imResult),
          maxZ: imResult.max(),
          minZ: imResult.min(),
          ...minMaxXY
        }
      };
    }
    function ensureData2DFid(data) {
      if (!('re' in data)) {
        throw new Error('bidimensional FFT only can be applied to raw data');
      }
    }
    function convertToDoubleArray(data) {
      const result = new Array(data.rows);
      for (let i = 0; i < data.rows; i++) {
        result[i] = new Float64Array(data.getRow(i));
      }
      return result;
    }

    async function getDatabase(url = 'https://docs.google.com/spreadsheets/d/1uwyq_L38PMRWCcT4If_EhPbHKyY3q_2tpjV8vr5_zh0/edit?usp=sharing', options = {}) {
      const {
        format = 'tsv'
      } = options;
      if (url.includes('google.com')) {
        url = `https://googledocs.cheminfo.org/spreadsheets/d/${extractGoogleUUID(url)}/export?format=tsv`;
      }
      let parsed;
      const response = await browserPonyfill.exports.fetch(url);
      switch (format) {
        case 'tsv':
          parsed = parseData(await response.text());
          break;
        case 'json':
          parsed = await response.json();
          break;
        default:
          throw new Error('unknown database format');
      }
      const results = [];
      for (const line of parsed.data) {
        const result = {
          smiles: '',
          solvent: '',
          nucleus: '',
          jcampURL: '',
          names: [],
          meta: {},
          ranges: []
        };
        for (let i = 0; i < line.length; i++) {
          switch (parsed.kinds[i]) {
            case 'smiles':
              result.smiles = line[i];
              break;
            case 'solvent':
              result.solvent = line[i];
              break;
            case 'nucleus':
              result.nucleus = line[i];
              break;
            case 'jcampURL':
              result.jcampURL = line[i];
              break;
            case 'name':
              if (result.names) result.names.push(line[i]);
              break;
            case 'meta':
              if (result.meta) result.meta[parsed.labels[i]] = line[i];
              break;
            case 'signal':
              {
                const range = resurrectRange(line[i]);
                if (range) result.ranges.push(range);
                break;
              }
            default:
              throw new Error(`Unknown column kind: ${parsed.kinds[i]}`);
          }
        }
        results.push(result);
      }
      return results;
    }
    function extractGoogleUUID(url) {
      return url.replace(/.*\/(?<uuid>[^/]{20,50}).*/, '$<uuid>');
    }
    function parseData(text) {
      const matrix = text.split(/\r?\n/).filter(line => !/^\s*$/.exec(line)).map(line => line.split('\t').map(cell => cell.trim()));
      const labels = matrix[0];
      const kinds = matrix[1];
      const data = matrix.slice(2);
      return {
        labels,
        kinds,
        data
      };
    }

    const carbonImpurities = [{
      names: ['acetic acid'],
      smiles: 'CC(=O)O',
      ranges: [{
        signals: [{
          delta: 171.69,
          assignment: 'CO'
        }],
        from: 171.685,
        to: 171.695
      }, {
        signals: [{
          delta: 20.13,
          assignment: 'CH₃'
        }],
        from: 20.125,
        to: 20.134999999999998
      }],
      nucleus: '13C',
      solvent: 'THF-d₈'
    }, {
      names: ['acetone'],
      smiles: 'CC(=O)C',
      ranges: [{
        signals: [{
          delta: 204.19,
          assignment: 'CO'
        }],
        from: 204.185,
        to: 204.195
      }, {
        signals: [{
          delta: 30.17,
          assignment: 'CH₃'
        }],
        from: 30.165000000000003,
        to: 30.175
      }],
      nucleus: '13C',
      solvent: 'THF-d₈'
    }, {
      names: ['acetonitrile'],
      smiles: 'CC#N',
      ranges: [{
        signals: [{
          delta: 116.79,
          assignment: 'CN'
        }],
        from: 116.78500000000001,
        to: 116.795
      }, {
        signals: [{
          delta: 0.45,
          assignment: 'CH₃'
        }],
        from: 0.445,
        to: 0.455
      }],
      nucleus: '13C',
      solvent: 'THF-d₈'
    }, {
      names: ['benzene'],
      smiles: 'c1ccccc1',
      ranges: [{
        signals: [{
          delta: 128.84,
          assignment: 'CH'
        }],
        from: 128.835,
        to: 128.845
      }],
      nucleus: '13C',
      solvent: 'THF-d₈'
    }, {
      names: ['tert -butyl alcohol'],
      smiles: 'CC(C)(C)O',
      ranges: [{
        signals: [{
          delta: 67.5,
          assignment: '(CH₃)₃C'
        }],
        from: 67.495,
        to: 67.505
      }, {
        signals: [{
          delta: 30.57,
          assignment: '(CH₃)₃C'
        }],
        from: 30.565,
        to: 30.575
      }],
      nucleus: '13C',
      solvent: 'THF-d₈'
    }, {
      names: ['carbon dioxide'],
      smiles: 'O=C=O',
      ranges: [{
        signals: [{
          delta: 125.69,
          assignment: 'CO₂'
        }],
        from: 125.685,
        to: 125.695
      }],
      nucleus: '13C',
      solvent: 'THF-d₈'
    }, {
      names: ['carbon disulfide'],
      smiles: 'S=C=S',
      ranges: [{
        signals: [{
          delta: 193.37,
          assignment: 'CS₂'
        }],
        from: 193.365,
        to: 193.375
      }],
      nucleus: '13C',
      solvent: 'THF-d₈'
    }, {
      names: ['carbon tetrachloride'],
      smiles: 'ClC(Cl)(Cl)Cl',
      ranges: [{
        signals: [{
          delta: 96.89,
          assignment: 'CCl₄'
        }],
        from: 96.885,
        to: 96.895
      }],
      nucleus: '13C',
      solvent: 'THF-d₈'
    }, {
      names: ['chloroform'],
      smiles: 'ClC(Cl)Cl',
      ranges: [{
        signals: [{
          delta: 79.24,
          assignment: 'CH'
        }],
        from: 79.235,
        to: 79.24499999999999
      }],
      nucleus: '13C',
      solvent: 'THF-d₈'
    }, {
      names: ['18-crown-6'],
      smiles: 'C1COCCOCCOCCOCCOCCO1',
      ranges: [{
        signals: [{
          delta: 71.34,
          assignment: 'CH₂'
        }],
        from: 71.33500000000001,
        to: 71.345
      }],
      nucleus: '13C',
      solvent: 'THF-d₈'
    }, {
      names: ['cyclohexane'],
      smiles: 'C1CCCCC1',
      ranges: [{
        signals: [{
          delta: 27.58,
          assignment: 'CH₂'
        }],
        from: 27.575,
        to: 27.584999999999997
      }],
      nucleus: '13C',
      solvent: 'THF-d₈'
    }, {
      names: ['1,2-dichloroethane'],
      smiles: 'ClCCCl',
      ranges: [{
        signals: [{
          delta: 44.64,
          assignment: 'CH₂'
        }],
        from: 44.635,
        to: 44.645
      }],
      nucleus: '13C',
      solvent: 'THF-d₈'
    }, {
      names: ['dichloromethane'],
      smiles: 'ClCCl',
      ranges: [{
        signals: [{
          delta: 54.67,
          assignment: 'CH₂'
        }],
        from: 54.665,
        to: 54.675000000000004
      }],
      nucleus: '13C',
      solvent: 'THF-d₈'
    }, {
      names: ['diethyl ether'],
      smiles: 'CCOCC',
      ranges: [{
        signals: [{
          delta: 15.49,
          assignment: 'CH₃'
        }],
        from: 15.485,
        to: 15.495000000000001
      }, {
        signals: [{
          delta: 66.14,
          assignment: 'CH₂'
        }],
        from: 66.135,
        to: 66.145
      }],
      nucleus: '13C',
      solvent: 'THF-d₈'
    }, {
      names: ['diglyme'],
      smiles: 'COCCOCCOC',
      ranges: [{
        signals: [{
          delta: 58.72,
          assignment: 'CH₃'
        }],
        from: 58.714999999999996,
        to: 58.725
      }, {
        signals: [{
          delta: 71.17,
          assignment: 'CH₂'
        }],
        from: 71.165,
        to: 71.175
      }, {
        signals: [{
          delta: 72.72,
          assignment: 'CH₂'
        }],
        from: 72.715,
        to: 72.725
      }],
      nucleus: '13C',
      solvent: 'THF-d₈'
    }, {
      names: ['dimethylformamide'],
      smiles: 'C(=O)N(C)C',
      ranges: [{
        signals: [{
          delta: 161.96,
          assignment: 'CH'
        }],
        from: 161.955,
        to: 161.965
      }, {
        signals: [{
          delta: 35.65,
          assignment: 'CH₃'
        }],
        from: 35.644999999999996,
        to: 35.655
      }, {
        signals: [{
          delta: 30.7,
          assignment: 'CH₃'
        }],
        from: 30.695,
        to: 30.705
      }],
      nucleus: '13C',
      solvent: 'THF-d₈'
    }, {
      names: ['1,4-dioxane'],
      smiles: 'C1COCCOC1',
      ranges: [{
        signals: [{
          delta: 67.65,
          assignment: 'CH₂'
        }],
        from: 67.64500000000001,
        to: 67.655
      }],
      nucleus: '13C',
      solvent: 'THF-d₈'
    }, {
      names: ['DME', 'diethyl ether'],
      smiles: 'CCOCC',
      ranges: [{
        signals: [{
          delta: 58.72,
          assignment: 'CH₃'
        }],
        from: 58.714999999999996,
        to: 58.725
      }, {
        signals: [{
          delta: 72.58,
          assignment: 'CH₂'
        }],
        from: 72.575,
        to: 72.585
      }],
      nucleus: '13C',
      solvent: 'THF-d₈'
    }, {
      names: ['ethane'],
      smiles: 'CC',
      ranges: [{
        signals: [{
          delta: 6.79,
          assignment: 'CH₃'
        }],
        from: 6.785,
        to: 6.795
      }],
      nucleus: '13C',
      solvent: 'THF-d₈'
    }, {
      names: ['ethanol'],
      smiles: 'CCO',
      ranges: [{
        signals: [{
          delta: 18.9,
          assignment: 'CH₃'
        }],
        from: 18.895,
        to: 18.904999999999998
      }, {
        signals: [{
          delta: 57.6,
          assignment: 'CH₂'
        }],
        from: 57.595,
        to: 57.605000000000004
      }],
      nucleus: '13C',
      solvent: 'THF-d₈'
    }, {
      names: ['ethyl acetate'],
      smiles: 'CC(=O)CC',
      ranges: [{
        signals: [{
          delta: 20.45,
          assignment: 'CH₃CO'
        }],
        from: 20.445,
        to: 20.455
      }, {
        signals: [{
          delta: 170.32,
          assignment: 'CO'
        }],
        from: 170.315,
        to: 170.325
      }, {
        signals: [{
          delta: 60.3,
          assignment: 'CH₂'
        }],
        from: 60.294999999999995,
        to: 60.305
      }, {
        signals: [{
          delta: 14.37,
          assignment: 'CH₃'
        }],
        from: 14.364999999999998,
        to: 14.375
      }],
      nucleus: '13C',
      solvent: 'THF-d₈'
    }, {
      names: ['ethylene'],
      smiles: 'C=C',
      ranges: [{
        signals: [{
          delta: 123.09,
          assignment: 'CH₂'
        }],
        from: 123.08500000000001,
        to: 123.095
      }],
      nucleus: '13C',
      solvent: 'THF-d₈'
    }, {
      names: ['ethylene glycol'],
      smiles: 'OCCO',
      ranges: [{
        signals: [{
          delta: 64.35,
          assignment: 'CH₂'
        }],
        from: 64.345,
        to: 64.35499999999999
      }],
      nucleus: '13C',
      solvent: 'THF-d₈'
    }, {
      names: ['H grease'],
      smiles: '',
      ranges: [{
        signals: [{
          delta: 30.45,
          assignment: 'CH₂'
        }],
        from: 30.445,
        to: 30.455
      }],
      nucleus: '13C',
      solvent: 'THF-d₈'
    }, {
      names: ['hexamethylbenzene'],
      smiles: 'CC1=C(C(=C(C(=C1C)C)C)C)C',
      ranges: [{
        signals: [{
          delta: 131.88,
          assignment: 'C'
        }],
        from: 131.875,
        to: 131.885
      }, {
        signals: [{
          delta: 16.71,
          assignment: 'CH₃'
        }],
        from: 16.705000000000002,
        to: 16.715
      }],
      nucleus: '13C',
      solvent: 'THF-d₈'
    }, {
      names: ['n-hexane'],
      smiles: 'CCCCCC',
      ranges: [{
        signals: [{
          delta: 14.22,
          assignment: 'CH₃'
        }],
        from: 14.215,
        to: 14.225000000000001
      }, {
        signals: [{
          delta: 23.33,
          assignment: 'CH₂(2,5)'
        }],
        from: 23.325,
        to: 23.334999999999997
      }, {
        signals: [{
          delta: 32.34,
          assignment: 'CH₂(3,4)'
        }],
        from: 32.335,
        to: 32.345000000000006
      }],
      nucleus: '13C',
      solvent: 'THF-d₈'
    }, {
      names: ['HMDSO'],
      smiles: 'C[Si](C)(C)O[Si](C)(C)C',
      ranges: [{
        signals: [{
          delta: 1.83,
          assignment: 'CH₃'
        }],
        from: 1.8250000000000002,
        to: 1.835
      }],
      nucleus: '13C',
      solvent: 'THF-d₈'
    }, {
      names: ['HMPA'],
      smiles: 'CN(C)P(=O)(N(C)C)N(C)C',
      ranges: [{
        signals: [{
          delta: 36.89,
          assignment: 'CH₃'
        }],
        from: 36.885,
        to: 36.895
      }],
      nucleus: '13C',
      solvent: 'THF-d₈'
    }, {
      names: ['imidazole'],
      smiles: 'C1=CN=CN1',
      ranges: [{
        signals: [{
          delta: 135.72,
          assignment: 'CH(2)'
        }],
        from: 135.715,
        to: 135.725
      }, {
        signals: [{
          delta: 122.2,
          assignment: 'CH(4,5)'
        }],
        from: 122.19500000000001,
        to: 122.205
      }],
      nucleus: '13C',
      solvent: 'THF-d₈'
    }, {
      names: ['methane'],
      smiles: 'C',
      ranges: [{
        signals: [{
          delta: -4.9,
          assignment: 'CH₄'
        }],
        from: -4.905,
        to: -4.8950000000000005
      }],
      nucleus: '13C',
      solvent: 'THF-d₈'
    }, {
      names: ['methanol'],
      smiles: 'CO',
      ranges: [{
        signals: [{
          delta: 49.64,
          assignment: 'CH₃'
        }],
        from: 49.635,
        to: 49.645
      }],
      nucleus: '13C',
      solvent: 'THF-d₈'
    }, {
      names: ['nitromethane'],
      smiles: 'C[N+](=O)[O-]',
      ranges: [{
        signals: [{
          delta: 62.49,
          assignment: 'CH₃'
        }],
        from: 62.485,
        to: 62.495000000000005
      }],
      nucleus: '13C',
      solvent: 'THF-d₈'
    }, {
      names: ['n-pentane'],
      smiles: 'CCCCC',
      ranges: [{
        signals: [{
          delta: 14.18,
          assignment: 'CH₃'
        }],
        from: 14.174999999999999,
        to: 14.185
      }, {
        signals: [{
          delta: 23,
          assignment: 'CH₂(2,4)'
        }],
        from: 22.995,
        to: 23.005
      }, {
        signals: [{
          delta: 34.87,
          assignment: 'CH₂(3)'
        }],
        from: 34.864999999999995,
        to: 34.875
      }],
      nucleus: '13C',
      solvent: 'THF-d₈'
    }, {
      names: ['propane'],
      smiles: 'CCC',
      ranges: [{
        signals: [{
          delta: 16.6,
          assignment: 'CH₃'
        }],
        from: 16.595000000000002,
        to: 16.605
      }, {
        signals: [{
          delta: 16.82,
          assignment: 'CH₂'
        }],
        from: 16.815,
        to: 16.825
      }],
      nucleus: '13C',
      solvent: 'THF-d₈'
    }, {
      names: ['2-propanol'],
      smiles: 'CC(O)C',
      ranges: [{
        signals: [{
          delta: 25.7,
          assignment: 'CH₃'
        }],
        from: 25.695,
        to: 25.705
      }, {
        signals: [{
          delta: 66.14,
          assignment: 'CH'
        }],
        from: 66.135,
        to: 66.145
      }],
      nucleus: '13C',
      solvent: 'THF-d₈'
    }, {
      names: ['propylene'],
      smiles: 'CC=C',
      ranges: [{
        signals: [{
          delta: 19.27,
          assignment: 'CH₃'
        }],
        from: 19.265,
        to: 19.275
      }, {
        signals: [{
          delta: 115.74,
          assignment: 'CH₂'
        }],
        from: 115.735,
        to: 115.74499999999999
      }, {
        signals: [{
          delta: 134.02,
          assignment: 'CH'
        }],
        from: 134.01500000000001,
        to: 134.025
      }],
      nucleus: '13C',
      solvent: 'THF-d₈'
    }, {
      names: ['pyridine'],
      smiles: 'C1=CC=NC=C1',
      ranges: [{
        signals: [{
          delta: 150.57,
          assignment: 'CH(2,6)'
        }],
        from: 150.565,
        to: 150.575
      }, {
        signals: [{
          delta: 124.08,
          assignment: 'CH(3,5)'
        }],
        from: 124.075,
        to: 124.085
      }, {
        signals: [{
          delta: 135.99,
          assignment: 'CH(4)'
        }],
        from: 135.985,
        to: 135.995
      }],
      nucleus: '13C',
      solvent: 'THF-d₈'
    }, {
      names: ['pyrrole'],
      smiles: 'C1=CNC=C1',
      ranges: [{
        signals: [{
          delta: 118.03,
          assignment: 'CH(2,5)'
        }],
        from: 118.025,
        to: 118.035
      }, {
        signals: [{
          delta: 107.74,
          assignment: 'CH(3,4)'
        }],
        from: 107.735,
        to: 107.74499999999999
      }],
      nucleus: '13C',
      solvent: 'THF-d₈'
    }, {
      names: ['pyrrolidine'],
      smiles: 'C1CCNC1',
      ranges: [{
        signals: [{
          delta: 45.82,
          assignment: 'CH₂(2,5)'
        }],
        from: 45.815,
        to: 45.825
      }, {
        signals: [{
          delta: 26.17,
          assignment: 'CH₂(3,4)'
        }],
        from: 26.165000000000003,
        to: 26.175
      }],
      nucleus: '13C',
      solvent: 'THF-d₈'
    }, {
      names: ['silicone grease'],
      smiles: '',
      ranges: [{
        signals: [{
          delta: 1.2,
          assignment: 'CH₃'
        }],
        from: 1.195,
        to: 1.2049999999999998
      }],
      nucleus: '13C',
      solvent: 'THF-d₈'
    }, {
      names: ['tetrahydrofuran'],
      smiles: 'C1COCC1',
      ranges: [{
        signals: [{
          delta: 68.03,
          assignment: 'CH₂(2,5)'
        }],
        from: 68.025,
        to: 68.035
      }, {
        signals: [{
          delta: 26.19,
          assignment: 'CH₂(3,4)'
        }],
        from: 26.185000000000002,
        to: 26.195
      }],
      nucleus: '13C',
      solvent: 'THF-d₈'
    }, {
      names: ['toluene'],
      smiles: 'Cc1ccccc1',
      ranges: [{
        signals: [{
          delta: 21.29,
          assignment: 'CH₃'
        }],
        from: 21.285,
        to: 21.294999999999998
      }, {
        signals: [{
          delta: 138.24,
          assignment: 'C(1)'
        }],
        from: 138.235,
        to: 138.245
      }, {
        signals: [{
          delta: 129.47,
          assignment: 'CH(2,6)'
        }],
        from: 129.465,
        to: 129.475
      }, {
        signals: [{
          delta: 128.71,
          assignment: 'CH(3,5)'
        }],
        from: 128.705,
        to: 128.715
      }, {
        signals: [{
          delta: 125.84,
          assignment: 'CH(4)'
        }],
        from: 125.83500000000001,
        to: 125.845
      }],
      nucleus: '13C',
      solvent: 'THF-d₈'
    }, {
      names: ['triethylamine'],
      smiles: 'CCN(CC)CC',
      ranges: [{
        signals: [{
          delta: 12.51,
          assignment: 'CH₃'
        }],
        from: 12.504999999999999,
        to: 12.515
      }, {
        signals: [{
          delta: 47.18,
          assignment: 'CH₂'
        }],
        from: 47.175,
        to: 47.185
      }],
      nucleus: '13C',
      solvent: 'THF-d₈'
    }, {
      names: ['acetic acid'],
      smiles: 'CC(=O)O',
      ranges: [{
        signals: [{
          delta: 175.85,
          assignment: 'CO'
        }],
        from: 175.845,
        to: 175.855
      }, {
        signals: [{
          delta: 20.91,
          assignment: 'CH₃'
        }],
        from: 20.905,
        to: 20.915
      }],
      nucleus: '13C',
      solvent: 'CD₂Cl₂'
    }, {
      names: ['acetone'],
      smiles: 'CC(=O)C',
      ranges: [{
        signals: [{
          delta: 206.78,
          assignment: 'CO'
        }],
        from: 206.775,
        to: 206.785
      }, {
        signals: [{
          delta: 31,
          assignment: 'CH₃'
        }],
        from: 30.995,
        to: 31.005
      }],
      nucleus: '13C',
      solvent: 'CD₂Cl₂'
    }, {
      names: ['acetonitrile'],
      smiles: 'CC#N',
      ranges: [{
        signals: [{
          delta: 116.92,
          assignment: 'CN'
        }],
        from: 116.915,
        to: 116.925
      }, {
        signals: [{
          delta: 2.03,
          assignment: 'CH₃'
        }],
        from: 2.025,
        to: 2.0349999999999997
      }],
      nucleus: '13C',
      solvent: 'CD₂Cl₂'
    }, {
      names: ['benzene'],
      smiles: 'c1ccccc1',
      ranges: [{
        signals: [{
          delta: 128.68,
          assignment: 'CH'
        }],
        from: 128.675,
        to: 128.685
      }],
      nucleus: '13C',
      solvent: 'CD₂Cl₂'
    }, {
      names: ['tert -butyl alcohol'],
      smiles: 'CC(C)(C)O',
      ranges: [{
        signals: [{
          delta: 69.11,
          assignment: '(CH₃)₃C'
        }],
        from: 69.105,
        to: 69.115
      }, {
        signals: [{
          delta: 31.46,
          assignment: '(CH₃)₃C'
        }],
        from: 31.455000000000002,
        to: 31.465
      }],
      nucleus: '13C',
      solvent: 'CD₂Cl₂'
    }, {
      names: ['carbon dioxide'],
      smiles: 'O=C=O',
      ranges: [{
        signals: [{
          delta: 125.26,
          assignment: 'CO₂'
        }],
        from: 125.25500000000001,
        to: 125.265
      }],
      nucleus: '13C',
      solvent: 'CD₂Cl₂'
    }, {
      names: ['carbon disulfide'],
      smiles: 'S=C=S',
      ranges: [{
        signals: [{
          delta: 192.95,
          assignment: 'CS₂'
        }],
        from: 192.945,
        to: 192.95499999999998
      }],
      nucleus: '13C',
      solvent: 'CD₂Cl₂'
    }, {
      names: ['carbon tetrachloride'],
      smiles: 'ClC(Cl)(Cl)Cl',
      ranges: [{
        signals: [{
          delta: 96.52,
          assignment: 'CCl₄'
        }],
        from: 96.515,
        to: 96.52499999999999
      }],
      nucleus: '13C',
      solvent: 'CD₂Cl₂'
    }, {
      names: ['chloroform'],
      smiles: 'ClC(Cl)Cl',
      ranges: [{
        signals: [{
          delta: 77.99,
          assignment: 'CH'
        }],
        from: 77.985,
        to: 77.99499999999999
      }],
      nucleus: '13C',
      solvent: 'CD₂Cl₂'
    }, {
      names: ['18-crown-6'],
      smiles: 'C1COCCOCCOCCOCCOCCO1',
      ranges: [{
        signals: [{
          delta: 70.47,
          assignment: 'CH₂'
        }],
        from: 70.465,
        to: 70.475
      }],
      nucleus: '13C',
      solvent: 'CD₂Cl₂'
    }, {
      names: ['cyclohexane'],
      smiles: 'C1CCCCC1',
      ranges: [{
        signals: [{
          delta: 27.38,
          assignment: 'CH₂'
        }],
        from: 27.375,
        to: 27.384999999999998
      }],
      nucleus: '13C',
      solvent: 'CD₂Cl₂'
    }, {
      names: ['1,2-dichloroethane'],
      smiles: 'ClCCCl',
      ranges: [{
        signals: [{
          delta: 44.35,
          assignment: 'CH₂'
        }],
        from: 44.345,
        to: 44.355000000000004
      }],
      nucleus: '13C',
      solvent: 'CD₂Cl₂'
    }, {
      names: ['dichloromethane'],
      smiles: 'ClCCl',
      ranges: [{
        signals: [{
          delta: 54.24,
          assignment: 'CH₂'
        }],
        from: 54.235,
        to: 54.245000000000005
      }],
      nucleus: '13C',
      solvent: 'CD₂Cl₂'
    }, {
      names: ['diethyl ether'],
      smiles: 'CCOCC',
      ranges: [{
        signals: [{
          delta: 15.44,
          assignment: 'CH₃'
        }],
        from: 15.434999999999999,
        to: 15.445
      }, {
        signals: [{
          delta: 66.11,
          assignment: 'CH₂'
        }],
        from: 66.105,
        to: 66.115
      }],
      nucleus: '13C',
      solvent: 'CD₂Cl₂'
    }, {
      names: ['diglyme'],
      smiles: 'COCCOCCOC',
      ranges: [{
        signals: [{
          delta: 58.95,
          assignment: 'CH₃'
        }],
        from: 58.945,
        to: 58.955000000000005
      }, {
        signals: [{
          delta: 70.7,
          assignment: 'CH₂'
        }],
        from: 70.69500000000001,
        to: 70.705
      }, {
        signals: [{
          delta: 72.25,
          assignment: 'CH₂'
        }],
        from: 72.245,
        to: 72.255
      }],
      nucleus: '13C',
      solvent: 'CD₂Cl₂'
    }, {
      names: ['dimethylformamide'],
      smiles: 'C(=O)N(C)C',
      ranges: [{
        signals: [{
          delta: 162.57,
          assignment: 'CH'
        }],
        from: 162.565,
        to: 162.575
      }, {
        signals: [{
          delta: 36.56,
          assignment: 'CH₃'
        }],
        from: 36.555,
        to: 36.565000000000005
      }, {
        signals: [{
          delta: 31.39,
          assignment: 'CH₃'
        }],
        from: 31.385,
        to: 31.395
      }],
      nucleus: '13C',
      solvent: 'CD₂Cl₂'
    }, {
      names: ['1,4-dioxane'],
      smiles: 'C1COCCOC1',
      ranges: [{
        signals: [{
          delta: 67.47,
          assignment: 'CH₂'
        }],
        from: 67.465,
        to: 67.475
      }],
      nucleus: '13C',
      solvent: 'CD₂Cl₂'
    }, {
      names: ['DME', 'diethyl ether'],
      smiles: 'CCOCC',
      ranges: [{
        signals: [{
          delta: 59.02,
          assignment: 'CH₃'
        }],
        from: 59.015,
        to: 59.025000000000006
      }, {
        signals: [{
          delta: 72.24,
          assignment: 'CH₂'
        }],
        from: 72.235,
        to: 72.24499999999999
      }],
      nucleus: '13C',
      solvent: 'CD₂Cl₂'
    }, {
      names: ['ethane'],
      smiles: 'CC',
      ranges: [{
        signals: [{
          delta: 6.91,
          assignment: 'CH₃'
        }],
        from: 6.905,
        to: 6.915
      }],
      nucleus: '13C',
      solvent: 'CD₂Cl₂'
    }, {
      names: ['ethanol'],
      smiles: 'CCO',
      ranges: [{
        signals: [{
          delta: 18.69,
          assignment: 'CH₃'
        }],
        from: 18.685000000000002,
        to: 18.695
      }, {
        signals: [{
          delta: 58.57,
          assignment: 'CH₂'
        }],
        from: 58.565,
        to: 58.575
      }],
      nucleus: '13C',
      solvent: 'CD₂Cl₂'
    }, {
      names: ['ethyl acetate'],
      smiles: 'CC(=O)CC',
      ranges: [{
        signals: [{
          delta: 21.15,
          assignment: 'CH₃CO'
        }],
        from: 21.145,
        to: 21.154999999999998
      }, {
        signals: [{
          delta: 171.24,
          assignment: 'CO'
        }],
        from: 171.235,
        to: 171.245
      }, {
        signals: [{
          delta: 60.63,
          assignment: 'CH₂'
        }],
        from: 60.625,
        to: 60.635000000000005
      }, {
        signals: [{
          delta: 14.37,
          assignment: 'CH₃'
        }],
        from: 14.364999999999998,
        to: 14.375
      }],
      nucleus: '13C',
      solvent: 'CD₂Cl₂'
    }, {
      names: ['ethylene'],
      smiles: 'C=C',
      ranges: [{
        signals: [{
          delta: 123.2,
          assignment: 'CH₂'
        }],
        from: 123.19500000000001,
        to: 123.205
      }],
      nucleus: '13C',
      solvent: 'CD₂Cl₂'
    }, {
      names: ['ethylene glycol'],
      smiles: 'OCCO',
      ranges: [{
        signals: [{
          delta: 64.08,
          assignment: 'CH₂'
        }],
        from: 64.075,
        to: 64.085
      }],
      nucleus: '13C',
      solvent: 'CD₂Cl₂'
    }, {
      names: ['H grease'],
      smiles: '',
      ranges: [{
        signals: [{
          delta: 30.14,
          assignment: 'CH₂'
        }],
        from: 30.135,
        to: 30.145
      }],
      nucleus: '13C',
      solvent: 'CD₂Cl₂'
    }, {
      names: ['hexamethylbenzene'],
      smiles: 'CC1=C(C(=C(C(=C1C)C)C)C)C',
      ranges: [{
        signals: [{
          delta: 132.09,
          assignment: 'C'
        }],
        from: 132.085,
        to: 132.095
      }, {
        signals: [{
          delta: 16.93,
          assignment: 'CH₃'
        }],
        from: 16.925,
        to: 16.935
      }],
      nucleus: '13C',
      solvent: 'CD₂Cl₂'
    }, {
      names: ['n-hexane'],
      smiles: 'CCCCCC',
      ranges: [{
        signals: [{
          delta: 14.28,
          assignment: 'CH₃'
        }],
        from: 14.274999999999999,
        to: 14.285
      }, {
        signals: [{
          delta: 23.07,
          assignment: 'CH₂(2,5)'
        }],
        from: 23.065,
        to: 23.075
      }, {
        signals: [{
          delta: 32.01,
          assignment: 'CH₂(3,4)'
        }],
        from: 32.004999999999995,
        to: 32.015
      }],
      nucleus: '13C',
      solvent: 'CD₂Cl₂'
    }, {
      names: ['HMDSO'],
      smiles: 'C[Si](C)(C)O[Si](C)(C)C',
      ranges: [{
        signals: [{
          delta: 1.96,
          assignment: 'CH₃'
        }],
        from: 1.955,
        to: 1.9649999999999999
      }],
      nucleus: '13C',
      solvent: 'CD₂Cl₂'
    }, {
      names: ['HMPA'],
      smiles: 'CN(C)P(=O)(N(C)C)N(C)C',
      ranges: [{
        signals: [{
          delta: 36.99,
          assignment: 'CH₃'
        }],
        from: 36.985,
        to: 36.995000000000005
      }],
      nucleus: '13C',
      solvent: 'CD₂Cl₂'
    }, {
      names: ['imidazole'],
      smiles: 'C1=CN=CN1',
      ranges: [{
        signals: [{
          delta: 135.76,
          assignment: 'CH(2)'
        }],
        from: 135.755,
        to: 135.765
      }, {
        signals: [{
          delta: 122.16,
          assignment: 'CH(4,5)'
        }],
        from: 122.155,
        to: 122.16499999999999
      }],
      nucleus: '13C',
      solvent: 'CD₂Cl₂'
    }, {
      names: ['methane'],
      smiles: 'C',
      ranges: [{
        signals: [{
          delta: -4.33,
          assignment: 'CH₄'
        }],
        from: -4.335,
        to: -4.325
      }],
      nucleus: '13C',
      solvent: 'CD₂Cl₂'
    }, {
      names: ['methanol'],
      smiles: 'CO',
      ranges: [{
        signals: [{
          delta: 50.45,
          assignment: 'CH₃'
        }],
        from: 50.445,
        to: 50.455000000000005
      }],
      nucleus: '13C',
      solvent: 'CD₂Cl₂'
    }, {
      names: ['nitromethane'],
      smiles: 'C[N+](=O)[O-]',
      ranges: [{
        signals: [{
          delta: 63.03,
          assignment: 'CH₃'
        }],
        from: 63.025,
        to: 63.035000000000004
      }],
      nucleus: '13C',
      solvent: 'CD₂Cl₂'
    }, {
      names: ['n-pentane'],
      smiles: 'CCCCC',
      ranges: [{
        signals: [{
          delta: 14.24,
          assignment: 'CH₃'
        }],
        from: 14.235,
        to: 14.245000000000001
      }, {
        signals: [{
          delta: 22.77,
          assignment: 'CH₂(2,4)'
        }],
        from: 22.765,
        to: 22.775
      }, {
        signals: [{
          delta: 34.57,
          assignment: 'CH₂(3)'
        }],
        from: 34.565,
        to: 34.575
      }],
      nucleus: '13C',
      solvent: 'CD₂Cl₂'
    }, {
      names: ['propane'],
      smiles: 'CCC',
      ranges: [{
        signals: [{
          delta: 16.63,
          assignment: 'CH₃'
        }],
        from: 16.625,
        to: 16.634999999999998
      }, {
        signals: [{
          delta: 16.63,
          assignment: 'CH₂'
        }],
        from: 16.625,
        to: 16.634999999999998
      }],
      nucleus: '13C',
      solvent: 'CD₂Cl₂'
    }, {
      names: ['2-propanol'],
      smiles: 'CC(O)C',
      ranges: [{
        signals: [{
          delta: 25.43,
          assignment: 'CH₃'
        }],
        from: 25.425,
        to: 25.435
      }, {
        signals: [{
          delta: 64.67,
          assignment: 'CH'
        }],
        from: 64.665,
        to: 64.675
      }],
      nucleus: '13C',
      solvent: 'CD₂Cl₂'
    }, {
      names: ['propylene'],
      smiles: 'CC=C',
      ranges: [{
        signals: [{
          delta: 19.47,
          assignment: 'CH₃'
        }],
        from: 19.465,
        to: 19.474999999999998
      }, {
        signals: [{
          delta: 115.7,
          assignment: 'CH₂'
        }],
        from: 115.69500000000001,
        to: 115.705
      }, {
        signals: [{
          delta: 134.21,
          assignment: 'CH'
        }],
        from: 134.205,
        to: 134.215
      }],
      nucleus: '13C',
      solvent: 'CD₂Cl₂'
    }, {
      names: ['pyridine'],
      smiles: 'C1=CC=NC=C1',
      ranges: [{
        signals: [{
          delta: 150.27,
          assignment: 'CH(2,6)'
        }],
        from: 150.26500000000001,
        to: 150.275
      }, {
        signals: [{
          delta: 124.06,
          assignment: 'CH(3,5)'
        }],
        from: 124.055,
        to: 124.065
      }, {
        signals: [{
          delta: 136.16,
          assignment: 'CH(4)'
        }],
        from: 136.155,
        to: 136.165
      }],
      nucleus: '13C',
      solvent: 'CD₂Cl₂'
    }, {
      names: ['pyrrole'],
      smiles: 'C1=CNC=C1',
      ranges: [{
        signals: [{
          delta: 117.93,
          assignment: 'CH(2,5)'
        }],
        from: 117.92500000000001,
        to: 117.935
      }, {
        signals: [{
          delta: 108.02,
          assignment: 'CH(3,4)'
        }],
        from: 108.015,
        to: 108.02499999999999
      }],
      nucleus: '13C',
      solvent: 'CD₂Cl₂'
    }, {
      names: ['pyrrolidine'],
      smiles: 'C1CCNC1',
      ranges: [{
        signals: [{
          delta: 47.02,
          assignment: 'CH₂(2,5)'
        }],
        from: 47.015,
        to: 47.025000000000006
      }, {
        signals: [{
          delta: 25.83,
          assignment: 'CH₂(3,4)'
        }],
        from: 25.825,
        to: 25.834999999999997
      }],
      nucleus: '13C',
      solvent: 'CD₂Cl₂'
    }, {
      names: ['silicone grease'],
      smiles: '',
      ranges: [{
        signals: [{
          delta: 1.22,
          assignment: 'CH₃'
        }],
        from: 1.215,
        to: 1.2249999999999999
      }],
      nucleus: '13C',
      solvent: 'CD₂Cl₂'
    }, {
      names: ['tetrahydrofuran'],
      smiles: 'C1COCC1',
      ranges: [{
        signals: [{
          delta: 68.16,
          assignment: 'CH₂(2,5)'
        }],
        from: 68.155,
        to: 68.16499999999999
      }, {
        signals: [{
          delta: 25.98,
          assignment: 'CH₂(3,4)'
        }],
        from: 25.975,
        to: 25.985
      }],
      nucleus: '13C',
      solvent: 'CD₂Cl₂'
    }, {
      names: ['toluene'],
      smiles: 'Cc1ccccc1',
      ranges: [{
        signals: [{
          delta: 21.53,
          assignment: 'CH₃'
        }],
        from: 21.525000000000002,
        to: 21.535
      }, {
        signals: [{
          delta: 138.36,
          assignment: 'C(1)'
        }],
        from: 138.35500000000002,
        to: 138.365
      }, {
        signals: [{
          delta: 129.35,
          assignment: 'CH(2,6)'
        }],
        from: 129.345,
        to: 129.355
      }, {
        signals: [{
          delta: 128.54,
          assignment: 'CH(3,5)'
        }],
        from: 128.535,
        to: 128.545
      }, {
        signals: [{
          delta: 125.62,
          assignment: 'CH(4)'
        }],
        from: 125.61500000000001,
        to: 125.625
      }],
      nucleus: '13C',
      solvent: 'CD₂Cl₂'
    }, {
      names: ['triethylamine'],
      smiles: 'CCN(CC)CC',
      ranges: [{
        signals: [{
          delta: 12.12,
          assignment: 'CH₃'
        }],
        from: 12.114999999999998,
        to: 12.125
      }, {
        signals: [{
          delta: 46.75,
          assignment: 'CH₂'
        }],
        from: 46.745,
        to: 46.755
      }],
      nucleus: '13C',
      solvent: 'CD₂Cl₂'
    }, {
      names: ['acetic acid'],
      smiles: 'CC(=O)O',
      ranges: [{
        signals: [{
          delta: 175.99,
          assignment: 'CO'
        }],
        from: 175.985,
        to: 175.995
      }, {
        signals: [{
          delta: 20.81,
          assignment: 'CH₃'
        }],
        from: 20.805,
        to: 20.814999999999998
      }],
      nucleus: '13C',
      solvent: 'CDCl₃'
    }, {
      names: ['acetone'],
      smiles: 'CC(=O)C',
      ranges: [{
        signals: [{
          delta: 207.07,
          assignment: 'CO'
        }],
        from: 207.065,
        to: 207.075
      }, {
        signals: [{
          delta: 30.92,
          assignment: 'CH₃'
        }],
        from: 30.915000000000003,
        to: 30.925
      }],
      nucleus: '13C',
      solvent: 'CDCl₃'
    }, {
      names: ['acetonitrile'],
      smiles: 'CC#N',
      ranges: [{
        signals: [{
          delta: 116.43,
          assignment: 'CN'
        }],
        from: 116.42500000000001,
        to: 116.435
      }, {
        signals: [{
          delta: 1.89,
          assignment: 'CH₃'
        }],
        from: 1.885,
        to: 1.8949999999999998
      }],
      nucleus: '13C',
      solvent: 'CDCl₃'
    }, {
      names: ['benzene'],
      smiles: 'c1ccccc1',
      ranges: [{
        signals: [{
          delta: 128.37,
          assignment: 'CH'
        }],
        from: 128.365,
        to: 128.375
      }],
      nucleus: '13C',
      solvent: 'CDCl₃'
    }, {
      names: ['tert -butyl alcohol'],
      smiles: 'CC(C)(C)O',
      ranges: [{
        signals: [{
          delta: 69.15,
          assignment: '(CH₃)₃C'
        }],
        from: 69.14500000000001,
        to: 69.155
      }, {
        signals: [{
          delta: 31.25,
          assignment: '(CH₃)₃C'
        }],
        from: 31.245,
        to: 31.255
      }],
      nucleus: '13C',
      solvent: 'CDCl₃'
    }, {
      names: ['carbon dioxide'],
      smiles: 'O=C=O',
      ranges: [{
        signals: [{
          delta: 124.99,
          assignment: 'CO₂'
        }],
        from: 124.985,
        to: 124.99499999999999
      }],
      nucleus: '13C',
      solvent: 'CDCl₃'
    }, {
      names: ['carbon disulfide'],
      smiles: 'S=C=S',
      ranges: [{
        signals: [{
          delta: 192.83,
          assignment: 'CS₂'
        }],
        from: 192.82500000000002,
        to: 192.835
      }],
      nucleus: '13C',
      solvent: 'CDCl₃'
    }, {
      names: ['carbon tetrachloride'],
      smiles: 'ClC(Cl)(Cl)Cl',
      ranges: [{
        signals: [{
          delta: 96.34,
          assignment: 'CCl₄'
        }],
        from: 96.33500000000001,
        to: 96.345
      }],
      nucleus: '13C',
      solvent: 'CDCl₃'
    }, {
      names: ['chloroform'],
      smiles: 'ClC(Cl)Cl',
      ranges: [{
        signals: [{
          delta: 77.36,
          assignment: 'CH'
        }],
        from: 77.355,
        to: 77.365
      }],
      nucleus: '13C',
      solvent: 'CDCl₃'
    }, {
      names: ['18-crown-6'],
      smiles: 'C1COCCOCCOCCOCCOCCO1',
      ranges: [{
        signals: [{
          delta: 70.55,
          assignment: 'CH₂'
        }],
        from: 70.545,
        to: 70.55499999999999
      }],
      nucleus: '13C',
      solvent: 'CDCl₃'
    }, {
      names: ['cyclohexane'],
      smiles: 'C1CCCCC1',
      ranges: [{
        signals: [{
          delta: 26.94,
          assignment: 'CH₂'
        }],
        from: 26.935000000000002,
        to: 26.945
      }],
      nucleus: '13C',
      solvent: 'CDCl₃'
    }, {
      names: ['1,2-dichloroethane'],
      smiles: 'ClCCCl',
      ranges: [{
        signals: [{
          delta: 43.5,
          assignment: 'CH₂'
        }],
        from: 43.495,
        to: 43.505
      }],
      nucleus: '13C',
      solvent: 'CDCl₃'
    }, {
      names: ['dichloromethane'],
      smiles: 'ClCCl',
      ranges: [{
        signals: [{
          delta: 53.52,
          assignment: 'CH₂'
        }],
        from: 53.515,
        to: 53.525000000000006
      }],
      nucleus: '13C',
      solvent: 'CDCl₃'
    }, {
      names: ['diethyl ether'],
      smiles: 'CCOCC',
      ranges: [{
        signals: [{
          delta: 15.2,
          assignment: 'CH₃'
        }],
        from: 15.194999999999999,
        to: 15.205
      }, {
        signals: [{
          delta: 65.91,
          assignment: 'CH₂'
        }],
        from: 65.905,
        to: 65.91499999999999
      }],
      nucleus: '13C',
      solvent: 'CDCl₃'
    }, {
      names: ['diglyme'],
      smiles: 'COCCOCCOC',
      ranges: [{
        signals: [{
          delta: 59.01,
          assignment: 'CH₃'
        }],
        from: 59.004999999999995,
        to: 59.015
      }, {
        signals: [{
          delta: 70.51,
          assignment: 'CH₂'
        }],
        from: 70.50500000000001,
        to: 70.515
      }, {
        signals: [{
          delta: 71.9,
          assignment: 'CH₂'
        }],
        from: 71.89500000000001,
        to: 71.905
      }],
      nucleus: '13C',
      solvent: 'CDCl₃'
    }, {
      names: ['dimethylformamide'],
      smiles: 'C(=O)N(C)C',
      ranges: [{
        signals: [{
          delta: 162.62,
          assignment: 'CH'
        }],
        from: 162.615,
        to: 162.625
      }, {
        signals: [{
          delta: 36.5,
          assignment: 'CH₃'
        }],
        from: 36.495,
        to: 36.505
      }, {
        signals: [{
          delta: 31.45,
          assignment: 'CH₃'
        }],
        from: 31.445,
        to: 31.455
      }],
      nucleus: '13C',
      solvent: 'CDCl₃'
    }, {
      names: ['1,4-dioxane'],
      smiles: 'C1COCCOC1',
      ranges: [{
        signals: [{
          delta: 67.14,
          assignment: 'CH₂'
        }],
        from: 67.135,
        to: 67.145
      }],
      nucleus: '13C',
      solvent: 'CDCl₃'
    }, {
      names: ['DME', 'diethyl ether'],
      smiles: 'CCOCC',
      ranges: [{
        signals: [{
          delta: 59.08,
          assignment: 'CH₃'
        }],
        from: 59.074999999999996,
        to: 59.085
      }, {
        signals: [{
          delta: 71.84,
          assignment: 'CH₂'
        }],
        from: 71.83500000000001,
        to: 71.845
      }],
      nucleus: '13C',
      solvent: 'CDCl₃'
    }, {
      names: ['ethane'],
      smiles: 'CC',
      ranges: [{
        signals: [{
          delta: 6.89,
          assignment: 'CH₃'
        }],
        from: 6.885,
        to: 6.895
      }],
      nucleus: '13C',
      solvent: 'CDCl₃'
    }, {
      names: ['ethanol'],
      smiles: 'CCO',
      ranges: [{
        signals: [{
          delta: 18.41,
          assignment: 'CH₃'
        }],
        from: 18.405,
        to: 18.415
      }, {
        signals: [{
          delta: 58.28,
          assignment: 'CH₂'
        }],
        from: 58.275,
        to: 58.285000000000004
      }],
      nucleus: '13C',
      solvent: 'CDCl₃'
    }, {
      names: ['ethyl acetate'],
      smiles: 'CC(=O)CC',
      ranges: [{
        signals: [{
          delta: 21.04,
          assignment: 'CH₃CO'
        }],
        from: 21.035,
        to: 21.044999999999998
      }, {
        signals: [{
          delta: 171.36,
          assignment: 'CO'
        }],
        from: 171.35500000000002,
        to: 171.365
      }, {
        signals: [{
          delta: 60.49,
          assignment: 'CH₂'
        }],
        from: 60.485,
        to: 60.495000000000005
      }, {
        signals: [{
          delta: 14.19,
          assignment: 'CH₃'
        }],
        from: 14.184999999999999,
        to: 14.195
      }],
      nucleus: '13C',
      solvent: 'CDCl₃'
    }, {
      names: ['ethylene'],
      smiles: 'C=C',
      ranges: [{
        signals: [{
          delta: 123.13,
          assignment: 'CH₂'
        }],
        from: 123.125,
        to: 123.13499999999999
      }],
      nucleus: '13C',
      solvent: 'CDCl₃'
    }, {
      names: ['ethylene glycol'],
      smiles: 'OCCO',
      ranges: [{
        signals: [{
          delta: 63.79,
          assignment: 'CH₂'
        }],
        from: 63.785,
        to: 63.795
      }],
      nucleus: '13C',
      solvent: 'CDCl₃'
    }, {
      names: ['H grease'],
      smiles: '',
      ranges: [{
        signals: [{
          delta: 29.71,
          assignment: 'CH₂'
        }],
        from: 29.705000000000002,
        to: 29.715
      }],
      nucleus: '13C',
      solvent: 'CDCl₃'
    }, {
      names: ['hexamethylbenzene'],
      smiles: 'CC1=C(C(=C(C(=C1C)C)C)C)C',
      ranges: [{
        signals: [{
          delta: 132.21,
          assignment: 'C'
        }],
        from: 132.205,
        to: 132.215
      }, {
        signals: [{
          delta: 16.98,
          assignment: 'CH₃'
        }],
        from: 16.975,
        to: 16.985
      }],
      nucleus: '13C',
      solvent: 'CDCl₃'
    }, {
      names: ['n-hexane'],
      smiles: 'CCCCCC',
      ranges: [{
        signals: [{
          delta: 14.14,
          assignment: 'CH₃'
        }],
        from: 14.135,
        to: 14.145000000000001
      }, {
        signals: [{
          delta: 22.7,
          assignment: 'CH₂(2,5)'
        }],
        from: 22.695,
        to: 22.705
      }, {
        signals: [{
          delta: 31.64,
          assignment: 'CH₂(3,4)'
        }],
        from: 31.635,
        to: 31.645
      }],
      nucleus: '13C',
      solvent: 'CDCl₃'
    }, {
      names: ['HMDSO'],
      smiles: 'C[Si](C)(C)O[Si](C)(C)C',
      ranges: [{
        signals: [{
          delta: 1.97,
          assignment: 'CH₃'
        }],
        from: 1.965,
        to: 1.9749999999999999
      }],
      nucleus: '13C',
      solvent: 'CDCl₃'
    }, {
      names: ['HMPA'],
      smiles: 'CN(C)P(=O)(N(C)C)N(C)C',
      ranges: [{
        signals: [{
          delta: 36.87,
          assignment: 'CH₃'
        }],
        from: 36.864999999999995,
        to: 36.875
      }],
      nucleus: '13C',
      solvent: 'CDCl₃'
    }, {
      names: ['imidazole'],
      smiles: 'C1=CN=CN1',
      ranges: [{
        signals: [{
          delta: 135.38,
          assignment: 'CH(2)'
        }],
        from: 135.375,
        to: 135.385
      }, {
        signals: [{
          delta: 122,
          assignment: 'CH(4,5)'
        }],
        from: 121.995,
        to: 122.005
      }],
      nucleus: '13C',
      solvent: 'CDCl₃'
    }, {
      names: ['methane'],
      smiles: 'C',
      ranges: [{
        signals: [{
          delta: -4.63,
          assignment: 'CH₄'
        }],
        from: -4.635,
        to: -4.625
      }],
      nucleus: '13C',
      solvent: 'CDCl₃'
    }, {
      names: ['methanol'],
      smiles: 'CO',
      ranges: [{
        signals: [{
          delta: 50.41,
          assignment: 'CH₃'
        }],
        from: 50.404999999999994,
        to: 50.415
      }],
      nucleus: '13C',
      solvent: 'CDCl₃'
    }, {
      names: ['nitromethane'],
      smiles: 'C[N+](=O)[O-]',
      ranges: [{
        signals: [{
          delta: 62.5,
          assignment: 'CH₃'
        }],
        from: 62.495,
        to: 62.505
      }],
      nucleus: '13C',
      solvent: 'CDCl₃'
    }, {
      names: ['n-pentane'],
      smiles: 'CCCCC',
      ranges: [{
        signals: [{
          delta: 14.08,
          assignment: 'CH₃'
        }],
        from: 14.075,
        to: 14.085
      }, {
        signals: [{
          delta: 22.38,
          assignment: 'CH₂(2,4)'
        }],
        from: 22.375,
        to: 22.384999999999998
      }, {
        signals: [{
          delta: 34.16,
          assignment: 'CH₂(3)'
        }],
        from: 34.154999999999994,
        to: 34.165
      }],
      nucleus: '13C',
      solvent: 'CDCl₃'
    }, {
      names: ['propane'],
      smiles: 'CCC',
      ranges: [{
        signals: [{
          delta: 16.63,
          assignment: 'CH₃'
        }],
        from: 16.625,
        to: 16.634999999999998
      }, {
        signals: [{
          delta: 16.37,
          assignment: 'CH₂'
        }],
        from: 16.365000000000002,
        to: 16.375
      }],
      nucleus: '13C',
      solvent: 'CDCl₃'
    }, {
      names: ['2-propanol'],
      smiles: 'CC(O)C',
      ranges: [{
        signals: [{
          delta: 25.14,
          assignment: 'CH₃'
        }],
        from: 25.135,
        to: 25.145
      }, {
        signals: [{
          delta: 64.5,
          assignment: 'CH'
        }],
        from: 64.495,
        to: 64.505
      }],
      nucleus: '13C',
      solvent: 'CDCl₃'
    }, {
      names: ['propylene'],
      smiles: 'CC=C',
      ranges: [{
        signals: [{
          delta: 19.5,
          assignment: 'CH₃'
        }],
        from: 19.495,
        to: 19.505
      }, {
        signals: [{
          delta: 115.74,
          assignment: 'CH₂'
        }],
        from: 115.735,
        to: 115.74499999999999
      }, {
        signals: [{
          delta: 133.91,
          assignment: 'CH'
        }],
        from: 133.905,
        to: 133.915
      }],
      nucleus: '13C',
      solvent: 'CDCl₃'
    }, {
      names: ['pyridine'],
      smiles: 'C1=CC=NC=C1',
      ranges: [{
        signals: [{
          delta: 149.9,
          assignment: 'CH(2,6)'
        }],
        from: 149.895,
        to: 149.905
      }, {
        signals: [{
          delta: 123.75,
          assignment: 'CH(3,5)'
        }],
        from: 123.745,
        to: 123.755
      }, {
        signals: [{
          delta: 135.96,
          assignment: 'CH(4)'
        }],
        from: 135.955,
        to: 135.965
      }],
      nucleus: '13C',
      solvent: 'CDCl₃'
    }, {
      names: ['pyrrole'],
      smiles: 'C1=CNC=C1',
      ranges: [{
        signals: [{
          delta: 117.77,
          assignment: 'CH(2,5)'
        }],
        from: 117.765,
        to: 117.77499999999999
      }, {
        signals: [{
          delta: 107.98,
          assignment: 'CH(3,4)'
        }],
        from: 107.97500000000001,
        to: 107.985
      }],
      nucleus: '13C',
      solvent: 'CDCl₃'
    }, {
      names: ['pyrrolidine'],
      smiles: 'C1CCNC1',
      ranges: [{
        signals: [{
          delta: 46.93,
          assignment: 'CH₂(2,5)'
        }],
        from: 46.925,
        to: 46.935
      }, {
        signals: [{
          delta: 25.56,
          assignment: 'CH₂(3,4)'
        }],
        from: 25.555,
        to: 25.564999999999998
      }],
      nucleus: '13C',
      solvent: 'CDCl₃'
    }, {
      names: ['silicone grease'],
      smiles: '',
      ranges: [{
        signals: [{
          delta: 1.19,
          assignment: 'CH₃'
        }],
        from: 1.185,
        to: 1.1949999999999998
      }],
      nucleus: '13C',
      solvent: 'CDCl₃'
    }, {
      names: ['tetrahydrofuran'],
      smiles: 'C1COCC1',
      ranges: [{
        signals: [{
          delta: 67.97,
          assignment: 'CH₂(2,5)'
        }],
        from: 67.965,
        to: 67.975
      }, {
        signals: [{
          delta: 25.62,
          assignment: 'CH₂(3,4)'
        }],
        from: 25.615000000000002,
        to: 25.625
      }],
      nucleus: '13C',
      solvent: 'CDCl₃'
    }, {
      names: ['toluene'],
      smiles: 'Cc1ccccc1',
      ranges: [{
        signals: [{
          delta: 21.46,
          assignment: 'CH₃'
        }],
        from: 21.455000000000002,
        to: 21.465
      }, {
        signals: [{
          delta: 137.89,
          assignment: 'C(1)'
        }],
        from: 137.885,
        to: 137.89499999999998
      }, {
        signals: [{
          delta: 129.07,
          assignment: 'CH(2,6)'
        }],
        from: 129.065,
        to: 129.075
      }, {
        signals: [{
          delta: 128.26,
          assignment: 'CH(3,5)'
        }],
        from: 128.255,
        to: 128.265
      }, {
        signals: [{
          delta: 125.33,
          assignment: 'CH(4)'
        }],
        from: 125.325,
        to: 125.335
      }],
      nucleus: '13C',
      solvent: 'CDCl₃'
    }, {
      names: ['triethylamine'],
      smiles: 'CCN(CC)CC',
      ranges: [{
        signals: [{
          delta: 11.61,
          assignment: 'CH₃'
        }],
        from: 11.604999999999999,
        to: 11.615
      }, {
        signals: [{
          delta: 46.25,
          assignment: 'CH₂'
        }],
        from: 46.245,
        to: 46.255
      }],
      nucleus: '13C',
      solvent: 'CDCl₃'
    }, {
      names: ['acetic acid'],
      smiles: 'CC(=O)O',
      ranges: [{
        signals: [{
          delta: 175.3,
          assignment: 'CO'
        }],
        from: 175.29500000000002,
        to: 175.305
      }, {
        signals: [{
          delta: 20.27,
          assignment: 'CH₃'
        }],
        from: 20.265,
        to: 20.275
      }],
      nucleus: '13C',
      solvent: 'toluene- d₈'
    }, {
      names: ['acetone'],
      smiles: 'CC(=O)C',
      ranges: [{
        signals: [{
          delta: 204,
          assignment: 'CO'
        }],
        from: 203.995,
        to: 204.005
      }, {
        signals: [{
          delta: 30.03,
          assignment: 'CH₃'
        }],
        from: 30.025000000000002,
        to: 30.035
      }],
      nucleus: '13C',
      solvent: 'toluene- d₈'
    }, {
      names: ['acetonitrile'],
      smiles: 'CC#N',
      ranges: [{
        signals: [{
          delta: 115.76,
          assignment: 'CN'
        }],
        from: 115.75500000000001,
        to: 115.765
      }, {
        signals: [{
          delta: 0.03,
          assignment: 'CH₃'
        }],
        from: 0.024999999999999998,
        to: 0.034999999999999996
      }],
      nucleus: '13C',
      solvent: 'toluene- d₈'
    }, {
      names: ['benzene'],
      smiles: 'c1ccccc1',
      ranges: [{
        signals: [{
          delta: 128.57,
          assignment: 'CH'
        }],
        from: 128.565,
        to: 128.575
      }],
      nucleus: '13C',
      solvent: 'toluene- d₈'
    }, {
      names: ['tert -butyl alcohol'],
      smiles: 'CC(C)(C)O',
      ranges: [{
        signals: [{
          delta: 68.12,
          assignment: '(CH₃)₃C'
        }],
        from: 68.11500000000001,
        to: 68.125
      }, {
        signals: [{
          delta: 30.49,
          assignment: '(CH₃)₃C'
        }],
        from: 30.485,
        to: 30.494999999999997
      }],
      nucleus: '13C',
      solvent: 'toluene- d₈'
    }, {
      names: ['carbon dioxide'],
      smiles: 'O=C=O',
      ranges: [{
        signals: [{
          delta: 124.86,
          assignment: 'CO₂'
        }],
        from: 124.855,
        to: 124.865
      }],
      nucleus: '13C',
      solvent: 'toluene- d₈'
    }, {
      names: ['carbon disulfide'],
      smiles: 'S=C=S',
      ranges: [{
        signals: [{
          delta: 192.71,
          assignment: 'CS₂'
        }],
        from: 192.705,
        to: 192.715
      }],
      nucleus: '13C',
      solvent: 'toluene- d₈'
    }, {
      names: ['carbon tetrachloride'],
      smiles: 'ClC(Cl)(Cl)Cl',
      ranges: [{
        signals: [{
          delta: 96.57,
          assignment: 'CCl₄'
        }],
        from: 96.565,
        to: 96.57499999999999
      }],
      nucleus: '13C',
      solvent: 'toluene- d₈'
    }, {
      names: ['chloroform'],
      smiles: 'ClC(Cl)Cl',
      ranges: [{
        signals: [{
          delta: 77.89,
          assignment: 'CH'
        }],
        from: 77.885,
        to: 77.895
      }],
      nucleus: '13C',
      solvent: 'toluene- d₈'
    }, {
      names: ['18-crown-6'],
      smiles: 'C1COCCOCCOCCOCCOCCO1',
      ranges: [{
        signals: [{
          delta: 70.86,
          assignment: 'CH₂'
        }],
        from: 70.855,
        to: 70.865
      }],
      nucleus: '13C',
      solvent: 'toluene- d₈'
    }, {
      names: ['cyclohexane'],
      smiles: 'C1CCCCC1',
      ranges: [{
        signals: [{
          delta: 27.31,
          assignment: 'CH₂'
        }],
        from: 27.305,
        to: 27.314999999999998
      }],
      nucleus: '13C',
      solvent: 'toluene- d₈'
    }, {
      names: ['1,2-dichloroethane'],
      smiles: 'ClCCCl',
      ranges: [{
        signals: [{
          delta: 43.4,
          assignment: 'CH₂'
        }],
        from: 43.394999999999996,
        to: 43.405
      }],
      nucleus: '13C',
      solvent: 'toluene- d₈'
    }, {
      names: ['dichloromethane'],
      smiles: 'ClCCl',
      ranges: [{
        signals: [{
          delta: 53.47,
          assignment: 'CH₂'
        }],
        from: 53.464999999999996,
        to: 53.475
      }],
      nucleus: '13C',
      solvent: 'toluene- d₈'
    }, {
      names: ['diethyl ether'],
      smiles: 'CCOCC',
      ranges: [{
        signals: [{
          delta: 15.47,
          assignment: 'CH₃'
        }],
        from: 15.465,
        to: 15.475000000000001
      }, {
        signals: [{
          delta: 65.94,
          assignment: 'CH₂'
        }],
        from: 65.935,
        to: 65.945
      }],
      nucleus: '13C',
      solvent: 'toluene- d₈'
    }, {
      names: ['diglyme'],
      smiles: 'COCCOCCOC',
      ranges: [{
        signals: [{
          delta: 58.62,
          assignment: 'CH₃'
        }],
        from: 58.614999999999995,
        to: 58.625
      }, {
        signals: [{
          delta: 70.92,
          assignment: 'CH₂'
        }],
        from: 70.915,
        to: 70.925
      }, {
        signals: [{
          delta: 72.39,
          assignment: 'CH₂'
        }],
        from: 72.385,
        to: 72.395
      }],
      nucleus: '13C',
      solvent: 'toluene- d₈'
    }, {
      names: ['dimethylformamide'],
      smiles: 'C(=O)N(C)C',
      ranges: [{
        signals: [{
          delta: 161.93,
          assignment: 'CH'
        }],
        from: 161.925,
        to: 161.935
      }, {
        signals: [{
          delta: 35.22,
          assignment: 'CH₃'
        }],
        from: 35.214999999999996,
        to: 35.225
      }, {
        signals: [{
          delta: 30.64,
          assignment: 'CH₃'
        }],
        from: 30.635,
        to: 30.645
      }],
      nucleus: '13C',
      solvent: 'toluene- d₈'
    }, {
      names: ['1,4-dioxane'],
      smiles: 'C1COCCOC1',
      ranges: [{
        signals: [{
          delta: 67.17,
          assignment: 'CH₂'
        }],
        from: 67.165,
        to: 67.175
      }],
      nucleus: '13C',
      solvent: 'toluene- d₈'
    }, {
      names: ['DME', 'diethyl ether'],
      smiles: 'CCOCC',
      ranges: [{
        signals: [{
          delta: 58.63,
          assignment: 'CH₃'
        }],
        from: 58.625,
        to: 58.635000000000005
      }, {
        signals: [{
          delta: 72.25,
          assignment: 'CH₂'
        }],
        from: 72.245,
        to: 72.255
      }],
      nucleus: '13C',
      solvent: 'toluene- d₈'
    }, {
      names: ['ethane'],
      smiles: 'CC',
      ranges: [{
        signals: [{
          delta: 6.94,
          assignment: 'CH₃'
        }],
        from: 6.9350000000000005,
        to: 6.945
      }],
      nucleus: '13C',
      solvent: 'toluene- d₈'
    }, {
      names: ['ethanol'],
      smiles: 'CCO',
      ranges: [{
        signals: [{
          delta: 18.78,
          assignment: 'CH₃'
        }],
        from: 18.775000000000002,
        to: 18.785
      }, {
        signals: [{
          delta: 57.81,
          assignment: 'CH₂'
        }],
        from: 57.805,
        to: 57.815000000000005
      }],
      nucleus: '13C',
      solvent: 'toluene- d₈'
    }, {
      names: ['ethyl acetate'],
      smiles: 'CC(=O)CC',
      ranges: [{
        signals: [{
          delta: 20.46,
          assignment: 'CH₃CO'
        }],
        from: 20.455000000000002,
        to: 20.465
      }, {
        signals: [{
          delta: 170.02,
          assignment: 'CO'
        }],
        from: 170.01500000000001,
        to: 170.025
      }, {
        signals: [{
          delta: 60.08,
          assignment: 'CH₂'
        }],
        from: 60.074999999999996,
        to: 60.085
      }, {
        signals: [{
          delta: 14.23,
          assignment: 'CH₃'
        }],
        from: 14.225,
        to: 14.235000000000001
      }],
      nucleus: '13C',
      solvent: 'toluene- d₈'
    }, {
      names: ['ethylene'],
      smiles: 'C=C',
      ranges: [{
        signals: [{
          delta: 122.92,
          assignment: 'CH₂'
        }],
        from: 122.915,
        to: 122.925
      }],
      nucleus: '13C',
      solvent: 'toluene- d₈'
    }, {
      names: ['ethylene glycol'],
      smiles: 'OCCO',
      ranges: [{
        signals: [{
          delta: 64.29,
          assignment: 'CH₂'
        }],
        from: 64.28500000000001,
        to: 64.295
      }],
      nucleus: '13C',
      solvent: 'toluene- d₈'
    }, {
      names: ['H grease'],
      smiles: '',
      ranges: [{
        signals: [{
          delta: 30.31,
          assignment: 'CH₂'
        }],
        from: 30.305,
        to: 30.314999999999998
      }],
      nucleus: '13C',
      solvent: 'toluene- d₈'
    }, {
      names: ['hexamethylbenzene'],
      smiles: 'CC1=C(C(=C(C(=C1C)C)C)C)C',
      ranges: [{
        signals: [{
          delta: 131.72,
          assignment: 'C'
        }],
        from: 131.715,
        to: 131.725
      }, {
        signals: [{
          delta: 16.84,
          assignment: 'CH₃'
        }],
        from: 16.835,
        to: 16.845
      }],
      nucleus: '13C',
      solvent: 'toluene- d₈'
    }, {
      names: ['n-hexane'],
      smiles: 'CCCCCC',
      ranges: [{
        signals: [{
          delta: 14.34,
          assignment: 'CH₃'
        }],
        from: 14.334999999999999,
        to: 14.345
      }, {
        signals: [{
          delta: 23.12,
          assignment: 'CH₂(2,5)'
        }],
        from: 23.115000000000002,
        to: 23.125
      }, {
        signals: [{
          delta: 32.06,
          assignment: 'CH₂(3,4)'
        }],
        from: 32.055,
        to: 32.065000000000005
      }],
      nucleus: '13C',
      solvent: 'toluene- d₈'
    }, {
      names: ['HMDSO'],
      smiles: 'C[Si](C)(C)O[Si](C)(C)C',
      ranges: [{
        signals: [{
          delta: 1.99,
          assignment: 'CH₃'
        }],
        from: 1.985,
        to: 1.9949999999999999
      }],
      nucleus: '13C',
      solvent: 'toluene- d₈'
    }, {
      names: ['HMPA'],
      smiles: 'CN(C)P(=O)(N(C)C)N(C)C',
      ranges: [{
        signals: [{
          delta: 36.8,
          assignment: 'CH₃'
        }],
        from: 36.794999999999995,
        to: 36.805
      }],
      nucleus: '13C',
      solvent: 'toluene- d₈'
    }, {
      names: ['imidazole'],
      smiles: 'C1=CN=CN1',
      ranges: [{
        signals: [{
          delta: 135.57,
          assignment: 'CH(2)'
        }],
        from: 135.565,
        to: 135.575
      }, {
        signals: [{
          delta: 122.13,
          assignment: 'CH(4,5)'
        }],
        from: 122.125,
        to: 122.13499999999999
      }],
      nucleus: '13C',
      solvent: 'toluene- d₈'
    }, {
      names: ['methane'],
      smiles: 'C',
      ranges: [{
        signals: [{
          delta: -4.34,
          assignment: 'CH₄'
        }],
        from: -4.345,
        to: -4.335
      }],
      nucleus: '13C',
      solvent: 'toluene- d₈'
    }, {
      names: ['methanol'],
      smiles: 'CO',
      ranges: [{
        signals: [{
          delta: 49.9,
          assignment: 'CH₃'
        }],
        from: 49.894999999999996,
        to: 49.905
      }],
      nucleus: '13C',
      solvent: 'toluene- d₈'
    }, {
      names: ['nitromethane'],
      smiles: 'C[N+](=O)[O-]',
      ranges: [{
        signals: [{
          delta: 61.14,
          assignment: 'CH₃'
        }],
        from: 61.135,
        to: 61.145
      }],
      nucleus: '13C',
      solvent: 'toluene- d₈'
    }, {
      names: ['n-pentane'],
      smiles: 'CCCCC',
      ranges: [{
        signals: [{
          delta: 14.27,
          assignment: 'CH₃'
        }],
        from: 14.264999999999999,
        to: 14.275
      }, {
        signals: [{
          delta: 22.79,
          assignment: 'CH₂(2,4)'
        }],
        from: 22.785,
        to: 22.794999999999998
      }, {
        signals: [{
          delta: 34.54,
          assignment: 'CH₂(3)'
        }],
        from: 34.535,
        to: 34.545
      }],
      nucleus: '13C',
      solvent: 'toluene- d₈'
    }, {
      names: ['propane'],
      smiles: 'CCC',
      ranges: [{
        signals: [{
          delta: 16.65,
          assignment: 'CH₃'
        }],
        from: 16.645,
        to: 16.654999999999998
      }, {
        signals: [{
          delta: 16.63,
          assignment: 'CH₂'
        }],
        from: 16.625,
        to: 16.634999999999998
      }],
      nucleus: '13C',
      solvent: 'toluene- d₈'
    }, {
      names: ['2-propanol'],
      smiles: 'CC(O)C',
      ranges: [{
        signals: [{
          delta: 25.24,
          assignment: 'CH₃'
        }],
        from: 25.235,
        to: 25.244999999999997
      }, {
        signals: [{
          delta: 64.12,
          assignment: 'CH'
        }],
        from: 64.11500000000001,
        to: 64.125
      }],
      nucleus: '13C',
      solvent: 'toluene- d₈'
    }, {
      names: ['propylene'],
      smiles: 'CC=C',
      ranges: [{
        signals: [{
          delta: 19.32,
          assignment: 'CH₃'
        }],
        from: 19.315,
        to: 19.325
      }, {
        signals: [{
          delta: 115.89,
          assignment: 'CH₂'
        }],
        from: 115.885,
        to: 115.895
      }, {
        signals: [{
          delta: 133.61,
          assignment: 'CH'
        }],
        from: 133.60500000000002,
        to: 133.615
      }],
      nucleus: '13C',
      solvent: 'toluene- d₈'
    }, {
      names: ['pyridine'],
      smiles: 'C1=CC=NC=C1',
      ranges: [{
        signals: [{
          delta: 150.25,
          assignment: 'CH(2,6)'
        }],
        from: 150.245,
        to: 150.255
      }, {
        signals: [{
          delta: 123.46,
          assignment: 'CH(3,5)'
        }],
        from: 123.455,
        to: 123.46499999999999
      }, {
        signals: [{
          delta: 135.17,
          assignment: 'CH(4)'
        }],
        from: 135.165,
        to: 135.17499999999998
      }],
      nucleus: '13C',
      solvent: 'toluene- d₈'
    }, {
      names: ['pyrrole'],
      smiles: 'C1=CNC=C1',
      ranges: [{
        signals: [{
          delta: 117.61,
          assignment: 'CH(2,5)'
        }],
        from: 117.605,
        to: 117.615
      }, {
        signals: [{
          delta: 108.15,
          assignment: 'CH(3,4)'
        }],
        from: 108.14500000000001,
        to: 108.155
      }],
      nucleus: '13C',
      solvent: 'toluene- d₈'
    }, {
      names: ['pyrrolidine'],
      smiles: 'C1CCNC1',
      ranges: [{
        signals: [{
          delta: 47.12,
          assignment: 'CH₂(2,5)'
        }],
        from: 47.114999999999995,
        to: 47.125
      }, {
        signals: [{
          delta: 25.75,
          assignment: 'CH₂(3,4)'
        }],
        from: 25.745,
        to: 25.755
      }],
      nucleus: '13C',
      solvent: 'toluene- d₈'
    }, {
      names: ['silicone grease'],
      smiles: '',
      ranges: [{
        signals: [{
          delta: 1.37,
          assignment: 'CH₃'
        }],
        from: 1.3650000000000002,
        to: 1.375
      }],
      nucleus: '13C',
      solvent: 'toluene- d₈'
    }, {
      names: ['tetrahydrofuran'],
      smiles: 'C1COCC1',
      ranges: [{
        signals: [{
          delta: 67.75,
          assignment: 'CH₂(2,5)'
        }],
        from: 67.745,
        to: 67.755
      }, {
        signals: [{
          delta: 25.79,
          assignment: 'CH₂(3,4)'
        }],
        from: 25.785,
        to: 25.794999999999998
      }],
      nucleus: '13C',
      solvent: 'toluene- d₈'
    }, {
      names: ['toluene'],
      smiles: 'Cc1ccccc1',
      ranges: [{
        signals: [{
          delta: 21.37,
          assignment: 'CH₃'
        }],
        from: 21.365000000000002,
        to: 21.375
      }, {
        signals: [{
          delta: 137.84,
          assignment: 'C(1)'
        }],
        from: 137.835,
        to: 137.845
      }, {
        signals: [{
          delta: 129.33,
          assignment: 'CH(2,6)'
        }],
        from: 129.32500000000002,
        to: 129.335
      }, {
        signals: [{
          delta: 128.51,
          assignment: 'CH(3,5)'
        }],
        from: 128.505,
        to: 128.515
      }, {
        signals: [{
          delta: 125.66,
          assignment: 'CH(4)'
        }],
        from: 125.655,
        to: 125.66499999999999
      }],
      nucleus: '13C',
      solvent: 'toluene- d₈'
    }, {
      names: ['triethylamine'],
      smiles: 'CCN(CC)CC',
      ranges: [{
        signals: [{
          delta: 12.39,
          assignment: 'CH₃'
        }],
        from: 12.385,
        to: 12.395000000000001
      }, {
        signals: [{
          delta: 46.82,
          assignment: 'CH₂'
        }],
        from: 46.815,
        to: 46.825
      }],
      nucleus: '13C',
      solvent: 'toluene- d₈'
    }, {
      names: ['acetic acid'],
      smiles: 'CC(=O)O',
      ranges: [{
        signals: [{
          delta: 175.82,
          assignment: 'CO'
        }],
        from: 175.815,
        to: 175.825
      }, {
        signals: [{
          delta: 20.37,
          assignment: 'CH₃'
        }],
        from: 20.365000000000002,
        to: 20.375
      }],
      nucleus: '13C',
      solvent: 'C₆D₆'
    }, {
      names: ['acetone'],
      smiles: 'CC(=O)C',
      ranges: [{
        signals: [{
          delta: 204.43,
          assignment: 'CO'
        }],
        from: 204.425,
        to: 204.435
      }, {
        signals: [{
          delta: 30.14,
          assignment: 'CH₃'
        }],
        from: 30.135,
        to: 30.145
      }],
      nucleus: '13C',
      solvent: 'C₆D₆'
    }, {
      names: ['acetonitrile'],
      smiles: 'CC#N',
      ranges: [{
        signals: [{
          delta: 116.02,
          assignment: 'CN'
        }],
        from: 116.015,
        to: 116.02499999999999
      }, {
        signals: [{
          delta: 0.2,
          assignment: 'CH₃'
        }],
        from: 0.195,
        to: 0.20500000000000002
      }],
      nucleus: '13C',
      solvent: 'C₆D₆'
    }, {
      names: ['benzene'],
      smiles: 'c1ccccc1',
      ranges: [{
        signals: [{
          delta: 128.62,
          assignment: 'CH'
        }],
        from: 128.615,
        to: 128.625
      }],
      nucleus: '13C',
      solvent: 'C₆D₆'
    }, {
      names: ['tert -butyl alcohol'],
      smiles: 'CC(C)(C)O',
      ranges: [{
        signals: [{
          delta: 68.19,
          assignment: '(CH₃)₃C'
        }],
        from: 68.185,
        to: 68.195
      }, {
        signals: [{
          delta: 30.47,
          assignment: '(CH₃)₃C'
        }],
        from: 30.465,
        to: 30.474999999999998
      }],
      nucleus: '13C',
      solvent: 'C₆D₆'
    }, {
      names: ['carbon dioxide'],
      smiles: 'O=C=O',
      ranges: [{
        signals: [{
          delta: 124.76,
          assignment: 'CO₂'
        }],
        from: 124.75500000000001,
        to: 124.765
      }],
      nucleus: '13C',
      solvent: 'C₆D₆'
    }, {
      names: ['carbon disulfide'],
      smiles: 'S=C=S',
      ranges: [{
        signals: [{
          delta: 192.69,
          assignment: 'CS₂'
        }],
        from: 192.685,
        to: 192.695
      }],
      nucleus: '13C',
      solvent: 'C₆D₆'
    }, {
      names: ['carbon tetrachloride'],
      smiles: 'ClC(Cl)(Cl)Cl',
      ranges: [{
        signals: [{
          delta: 96.44,
          assignment: 'CCl₄'
        }],
        from: 96.435,
        to: 96.445
      }],
      nucleus: '13C',
      solvent: 'C₆D₆'
    }, {
      names: ['chloroform'],
      smiles: 'ClC(Cl)Cl',
      ranges: [{
        signals: [{
          delta: 77.79,
          assignment: 'CH'
        }],
        from: 77.78500000000001,
        to: 77.795
      }],
      nucleus: '13C',
      solvent: 'C₆D₆'
    }, {
      names: ['18-crown-6'],
      smiles: 'C1COCCOCCOCCOCCOCCO1',
      ranges: [{
        signals: [{
          delta: 70.59,
          assignment: 'CH₂'
        }],
        from: 70.58500000000001,
        to: 70.595
      }],
      nucleus: '13C',
      solvent: 'C₆D₆'
    }, {
      names: ['cyclohexane'],
      smiles: 'C1CCCCC1',
      ranges: [{
        signals: [{
          delta: 27.23,
          assignment: 'CH₂'
        }],
        from: 27.225,
        to: 27.235
      }],
      nucleus: '13C',
      solvent: 'C₆D₆'
    }, {
      names: ['1,2-dichloroethane'],
      smiles: 'ClCCCl',
      ranges: [{
        signals: [{
          delta: 43.59,
          assignment: 'CH₂'
        }],
        from: 43.585,
        to: 43.595000000000006
      }],
      nucleus: '13C',
      solvent: 'C₆D₆'
    }, {
      names: ['dichloromethane'],
      smiles: 'ClCCl',
      ranges: [{
        signals: [{
          delta: 53.46,
          assignment: 'CH₂'
        }],
        from: 53.455,
        to: 53.465
      }],
      nucleus: '13C',
      solvent: 'C₆D₆'
    }, {
      names: ['diethyl ether'],
      smiles: 'CCOCC',
      ranges: [{
        signals: [{
          delta: 15.46,
          assignment: 'CH₃'
        }],
        from: 15.455,
        to: 15.465000000000002
      }, {
        signals: [{
          delta: 65.94,
          assignment: 'CH₂'
        }],
        from: 65.935,
        to: 65.945
      }],
      nucleus: '13C',
      solvent: 'C₆D₆'
    }, {
      names: ['diglyme'],
      smiles: 'COCCOCCOC',
      ranges: [{
        signals: [{
          delta: 58.66,
          assignment: 'CH₃'
        }],
        from: 58.654999999999994,
        to: 58.665
      }, {
        signals: [{
          delta: 70.87,
          assignment: 'CH₂'
        }],
        from: 70.86500000000001,
        to: 70.875
      }, {
        signals: [{
          delta: 72.35,
          assignment: 'CH₂'
        }],
        from: 72.345,
        to: 72.35499999999999
      }],
      nucleus: '13C',
      solvent: 'C₆D₆'
    }, {
      names: ['dimethylformamide'],
      smiles: 'C(=O)N(C)C',
      ranges: [{
        signals: [{
          delta: 162.13,
          assignment: 'CH'
        }],
        from: 162.125,
        to: 162.135
      }, {
        signals: [{
          delta: 35.25,
          assignment: 'CH₃'
        }],
        from: 35.245,
        to: 35.255
      }, {
        signals: [{
          delta: 30.72,
          assignment: 'CH₃'
        }],
        from: 30.715,
        to: 30.724999999999998
      }],
      nucleus: '13C',
      solvent: 'C₆D₆'
    }, {
      names: ['1,4-dioxane'],
      smiles: 'C1COCCOC1',
      ranges: [{
        signals: [{
          delta: 67.16,
          assignment: 'CH₂'
        }],
        from: 67.155,
        to: 67.16499999999999
      }],
      nucleus: '13C',
      solvent: 'C₆D₆'
    }, {
      names: ['DME', 'diethyl ether'],
      smiles: 'CCOCC',
      ranges: [{
        signals: [{
          delta: 58.68,
          assignment: 'CH₃'
        }],
        from: 58.675,
        to: 58.685
      }, {
        signals: [{
          delta: 72.21,
          assignment: 'CH₂'
        }],
        from: 72.205,
        to: 72.21499999999999
      }],
      nucleus: '13C',
      solvent: 'C₆D₆'
    }, {
      names: ['ethane'],
      smiles: 'CC',
      ranges: [{
        signals: [{
          delta: 6.96,
          assignment: 'CH₃'
        }],
        from: 6.955,
        to: 6.965
      }],
      nucleus: '13C',
      solvent: 'C₆D₆'
    }, {
      names: ['ethanol'],
      smiles: 'CCO',
      ranges: [{
        signals: [{
          delta: 18.72,
          assignment: 'CH₃'
        }],
        from: 18.715,
        to: 18.724999999999998
      }, {
        signals: [{
          delta: 57.86,
          assignment: 'CH₂'
        }],
        from: 57.855,
        to: 57.865
      }],
      nucleus: '13C',
      solvent: 'C₆D₆'
    }, {
      names: ['ethyl acetate'],
      smiles: 'CC(=O)CC',
      ranges: [{
        signals: [{
          delta: 20.56,
          assignment: 'CH₃CO'
        }],
        from: 20.555,
        to: 20.564999999999998
      }, {
        signals: [{
          delta: 170.44,
          assignment: 'CO'
        }],
        from: 170.435,
        to: 170.445
      }, {
        signals: [{
          delta: 60.21,
          assignment: 'CH₂'
        }],
        from: 60.205,
        to: 60.215
      }, {
        signals: [{
          delta: 14.19,
          assignment: 'CH₃'
        }],
        from: 14.184999999999999,
        to: 14.195
      }],
      nucleus: '13C',
      solvent: 'C₆D₆'
    }, {
      names: ['ethylene'],
      smiles: 'C=C',
      ranges: [{
        signals: [{
          delta: 122.96,
          assignment: 'CH₂'
        }],
        from: 122.955,
        to: 122.96499999999999
      }],
      nucleus: '13C',
      solvent: 'C₆D₆'
    }, {
      names: ['ethylene glycol'],
      smiles: 'OCCO',
      ranges: [{
        signals: [{
          delta: 64.34,
          assignment: 'CH₂'
        }],
        from: 64.33500000000001,
        to: 64.345
      }],
      nucleus: '13C',
      solvent: 'C₆D₆'
    }, {
      names: ['H grease'],
      smiles: '',
      ranges: [{
        signals: [{
          delta: 30.22,
          assignment: 'CH₂'
        }],
        from: 30.215,
        to: 30.224999999999998
      }],
      nucleus: '13C',
      solvent: 'C₆D₆'
    }, {
      names: ['hexamethylbenzene'],
      smiles: 'CC1=C(C(=C(C(=C1C)C)C)C)C',
      ranges: [{
        signals: [{
          delta: 131.79,
          assignment: 'C'
        }],
        from: 131.785,
        to: 131.795
      }, {
        signals: [{
          delta: 16.95,
          assignment: 'CH₃'
        }],
        from: 16.945,
        to: 16.955
      }],
      nucleus: '13C',
      solvent: 'C₆D₆'
    }, {
      names: ['n-hexane'],
      smiles: 'CCCCCC',
      ranges: [{
        signals: [{
          delta: 14.32,
          assignment: 'CH₃'
        }],
        from: 14.315,
        to: 14.325000000000001
      }, {
        signals: [{
          delta: 23.04,
          assignment: 'CH₂(2,5)'
        }],
        from: 23.035,
        to: 23.044999999999998
      }, {
        signals: [{
          delta: 31.96,
          assignment: 'CH₂(3,4)'
        }],
        from: 31.955000000000002,
        to: 31.965
      }],
      nucleus: '13C',
      solvent: 'C₆D₆'
    }, {
      names: ['HMDSO'],
      smiles: 'C[Si](C)(C)O[Si](C)(C)C',
      ranges: [{
        signals: [{
          delta: 2.05,
          assignment: 'CH₃'
        }],
        from: 2.045,
        to: 2.0549999999999997
      }],
      nucleus: '13C',
      solvent: 'C₆D₆'
    }, {
      names: ['HMPA'],
      smiles: 'CN(C)P(=O)(N(C)C)N(C)C',
      ranges: [{
        signals: [{
          delta: 36.88,
          assignment: 'CH₃'
        }],
        from: 36.875,
        to: 36.885000000000005
      }],
      nucleus: '13C',
      solvent: 'C₆D₆'
    }, {
      names: ['imidazole'],
      smiles: 'C1=CN=CN1',
      ranges: [{
        signals: [{
          delta: 135.76,
          assignment: 'CH(2)'
        }],
        from: 135.755,
        to: 135.765
      }, {
        signals: [{
          delta: 122.16,
          assignment: 'CH(4,5)'
        }],
        from: 122.155,
        to: 122.16499999999999
      }],
      nucleus: '13C',
      solvent: 'C₆D₆'
    }, {
      names: ['methane'],
      smiles: 'C',
      ranges: [{
        signals: [{
          delta: -4.29,
          assignment: 'CH₄'
        }],
        from: -4.295,
        to: -4.285
      }],
      nucleus: '13C',
      solvent: 'C₆D₆'
    }, {
      names: ['methanol'],
      smiles: 'CO',
      ranges: [{
        signals: [{
          delta: 49.97,
          assignment: 'CH₃'
        }],
        from: 49.964999999999996,
        to: 49.975
      }],
      nucleus: '13C',
      solvent: 'C₆D₆'
    }, {
      names: ['nitromethane'],
      smiles: 'C[N+](=O)[O-]',
      ranges: [{
        signals: [{
          delta: 61.16,
          assignment: 'CH₃'
        }],
        from: 61.154999999999994,
        to: 61.165
      }],
      nucleus: '13C',
      solvent: 'C₆D₆'
    }, {
      names: ['n-pentane'],
      smiles: 'CCCCC',
      ranges: [{
        signals: [{
          delta: 14.25,
          assignment: 'CH₃'
        }],
        from: 14.245,
        to: 14.255
      }, {
        signals: [{
          delta: 22.72,
          assignment: 'CH₂(2,4)'
        }],
        from: 22.715,
        to: 22.724999999999998
      }, {
        signals: [{
          delta: 34.45,
          assignment: 'CH₂(3)'
        }],
        from: 34.445,
        to: 34.455000000000005
      }],
      nucleus: '13C',
      solvent: 'C₆D₆'
    }, {
      names: ['propane'],
      smiles: 'CCC',
      ranges: [{
        signals: [{
          delta: 16.66,
          assignment: 'CH₃'
        }],
        from: 16.655,
        to: 16.665
      }, {
        signals: [{
          delta: 16.6,
          assignment: 'CH₂'
        }],
        from: 16.595000000000002,
        to: 16.605
      }],
      nucleus: '13C',
      solvent: 'C₆D₆'
    }, {
      names: ['2-propanol'],
      smiles: 'CC(O)C',
      ranges: [{
        signals: [{
          delta: 25.18,
          assignment: 'CH₃'
        }],
        from: 25.175,
        to: 25.185
      }, {
        signals: [{
          delta: 64.23,
          assignment: 'CH'
        }],
        from: 64.22500000000001,
        to: 64.235
      }],
      nucleus: '13C',
      solvent: 'C₆D₆'
    }, {
      names: ['propylene'],
      smiles: 'CC=C',
      ranges: [{
        signals: [{
          delta: 19.38,
          assignment: 'CH₃'
        }],
        from: 19.375,
        to: 19.384999999999998
      }, {
        signals: [{
          delta: 115.92,
          assignment: 'CH₂'
        }],
        from: 115.915,
        to: 115.925
      }, {
        signals: [{
          delta: 133.69,
          assignment: 'CH'
        }],
        from: 133.685,
        to: 133.695
      }],
      nucleus: '13C',
      solvent: 'C₆D₆'
    }, {
      names: ['pyridine'],
      smiles: 'C1=CC=NC=C1',
      ranges: [{
        signals: [{
          delta: 150.27,
          assignment: 'CH(2,6)'
        }],
        from: 150.26500000000001,
        to: 150.275
      }, {
        signals: [{
          delta: 123.58,
          assignment: 'CH(3,5)'
        }],
        from: 123.575,
        to: 123.585
      }, {
        signals: [{
          delta: 135.28,
          assignment: 'CH(4)'
        }],
        from: 135.275,
        to: 135.285
      }],
      nucleus: '13C',
      solvent: 'C₆D₆'
    }, {
      names: ['pyrrole'],
      smiles: 'C1=CNC=C1',
      ranges: [{
        signals: [{
          delta: 117.78,
          assignment: 'CH(2,5)'
        }],
        from: 117.775,
        to: 117.785
      }, {
        signals: [{
          delta: 108.21,
          assignment: 'CH(3,4)'
        }],
        from: 108.205,
        to: 108.21499999999999
      }],
      nucleus: '13C',
      solvent: 'C₆D₆'
    }, {
      names: ['pyrrolidine'],
      smiles: 'C1CCNC1',
      ranges: [{
        signals: [{
          delta: 46.86,
          assignment: 'CH₂(2,5)'
        }],
        from: 46.855,
        to: 46.865
      }, {
        signals: [{
          delta: 25.65,
          assignment: 'CH₂(3,4)'
        }],
        from: 25.645,
        to: 25.654999999999998
      }],
      nucleus: '13C',
      solvent: 'C₆D₆'
    }, {
      names: ['silicone grease'],
      smiles: '',
      ranges: [{
        signals: [{
          delta: 1.38,
          assignment: 'CH₃'
        }],
        from: 1.375,
        to: 1.3849999999999998
      }],
      nucleus: '13C',
      solvent: 'C₆D₆'
    }, {
      names: ['tetrahydrofuran'],
      smiles: 'C1COCC1',
      ranges: [{
        signals: [{
          delta: 67.8,
          assignment: 'CH₂(2,5)'
        }],
        from: 67.795,
        to: 67.80499999999999
      }, {
        signals: [{
          delta: 25.72,
          assignment: 'CH₂(3,4)'
        }],
        from: 25.715,
        to: 25.724999999999998
      }],
      nucleus: '13C',
      solvent: 'C₆D₆'
    }, {
      names: ['toluene'],
      smiles: 'Cc1ccccc1',
      ranges: [{
        signals: [{
          delta: 21.1,
          assignment: 'CH₃'
        }],
        from: 21.095000000000002,
        to: 21.105
      }, {
        signals: [{
          delta: 137.91,
          assignment: 'C(1)'
        }],
        from: 137.905,
        to: 137.915
      }, {
        signals: [{
          delta: 129.33,
          assignment: 'CH(2,6)'
        }],
        from: 129.32500000000002,
        to: 129.335
      }, {
        signals: [{
          delta: 128.56,
          assignment: 'CH(3,5)'
        }],
        from: 128.555,
        to: 128.565
      }, {
        signals: [{
          delta: 125.68,
          assignment: 'CH(4)'
        }],
        from: 125.67500000000001,
        to: 125.685
      }],
      nucleus: '13C',
      solvent: 'C₆D₆'
    }, {
      names: ['triethylamine'],
      smiles: 'CCN(CC)CC',
      ranges: [{
        signals: [{
          delta: 12.35,
          assignment: 'CH₃'
        }],
        from: 12.344999999999999,
        to: 12.355
      }, {
        signals: [{
          delta: 46.77,
          assignment: 'CH₂'
        }],
        from: 46.765,
        to: 46.775000000000006
      }],
      nucleus: '13C',
      solvent: 'C₆D₆'
    }, {
      names: ['acetic acid'],
      smiles: 'CC(=O)O',
      ranges: [{
        signals: [{
          delta: 175.67,
          assignment: 'CO'
        }],
        from: 175.665,
        to: 175.67499999999998
      }, {
        signals: [{
          delta: 20.4,
          assignment: 'CH₃'
        }],
        from: 20.395,
        to: 20.404999999999998
      }],
      nucleus: '13C',
      solvent: 'C₆D₅Cl'
    }, {
      names: ['acetone'],
      smiles: 'CC(=O)C',
      ranges: [{
        signals: [{
          delta: 204.83,
          assignment: 'CO'
        }],
        from: 204.82500000000002,
        to: 204.835
      }, {
        signals: [{
          delta: 30.12,
          assignment: 'CH₃'
        }],
        from: 30.115000000000002,
        to: 30.125
      }],
      nucleus: '13C',
      solvent: 'C₆D₅Cl'
    }, {
      names: ['acetonitrile'],
      smiles: 'CC#N',
      ranges: [{
        signals: [{
          delta: 115.93,
          assignment: 'CN'
        }],
        from: 115.92500000000001,
        to: 115.935
      }, {
        signals: [{
          delta: 0.63,
          assignment: 'CH₃'
        }],
        from: 0.625,
        to: 0.635
      }],
      nucleus: '13C',
      solvent: 'C₆D₅Cl'
    }, {
      names: ['benzene'],
      smiles: 'c1ccccc1',
      ranges: [{
        signals: [{
          delta: 128.38,
          assignment: 'CH'
        }],
        from: 128.375,
        to: 128.385
      }],
      nucleus: '13C',
      solvent: 'C₆D₅Cl'
    }, {
      names: ['tert -butyl alcohol'],
      smiles: 'CC(C)(C)O',
      ranges: [{
        signals: [{
          delta: 68.19,
          assignment: '(CH₃)₃C'
        }],
        from: 68.185,
        to: 68.195
      }, {
        signals: [{
          delta: 31.13,
          assignment: '(CH₃)₃C'
        }],
        from: 31.125,
        to: 31.134999999999998
      }],
      nucleus: '13C',
      solvent: 'C₆D₅Cl'
    }, {
      names: ['carbon dioxide'],
      smiles: 'O=C=O',
      ranges: [{
        signals: [{
          delta: 126.08,
          assignment: 'CO₂'
        }],
        from: 126.075,
        to: 126.085
      }],
      nucleus: '13C',
      solvent: 'C₆D₅Cl'
    }, {
      names: ['carbon disulfide'],
      smiles: 'S=C=S',
      ranges: [{
        signals: [{
          delta: 192.49,
          assignment: 'CS₂'
        }],
        from: 192.485,
        to: 192.495
      }],
      nucleus: '13C',
      solvent: 'C₆D₅Cl'
    }, {
      names: ['carbon tetrachloride'],
      smiles: 'ClC(Cl)(Cl)Cl',
      ranges: [{
        signals: [{
          delta: 96.38,
          assignment: 'CCl₄'
        }],
        from: 96.375,
        to: 96.38499999999999
      }],
      nucleus: '13C',
      solvent: 'C₆D₅Cl'
    }, {
      names: ['chloroform'],
      smiles: 'ClC(Cl)Cl',
      ranges: [{
        signals: [{
          delta: 77.67,
          assignment: 'CH'
        }],
        from: 77.665,
        to: 77.675
      }],
      nucleus: '13C',
      solvent: 'C₆D₅Cl'
    }, {
      names: ['18-crown-6'],
      smiles: 'C1COCCOCCOCCOCCOCCO1',
      ranges: [{
        signals: [{
          delta: 70.55,
          assignment: 'CH₂'
        }],
        from: 70.545,
        to: 70.55499999999999
      }],
      nucleus: '13C',
      solvent: 'C₆D₅Cl'
    }, {
      names: ['cyclohexane'],
      smiles: 'C1CCCCC1',
      ranges: [{
        signals: [{
          delta: 26.99,
          assignment: 'CH₂'
        }],
        from: 26.985,
        to: 26.994999999999997
      }],
      nucleus: '13C',
      solvent: 'C₆D₅Cl'
    }, {
      names: ['1,2-dichloroethane'],
      smiles: 'ClCCCl',
      ranges: [{
        signals: [{
          delta: 43.6,
          assignment: 'CH₂'
        }],
        from: 43.595,
        to: 43.605000000000004
      }],
      nucleus: '13C',
      solvent: 'C₆D₅Cl'
    }, {
      names: ['dichloromethane'],
      smiles: 'ClCCl',
      ranges: [{
        signals: [{
          delta: 53.54,
          assignment: 'CH₂'
        }],
        from: 53.535,
        to: 53.545
      }],
      nucleus: '13C',
      solvent: 'C₆D₅Cl'
    }, {
      names: ['diethyl ether'],
      smiles: 'CCOCC',
      ranges: [{
        signals: [{
          delta: 15.35,
          assignment: 'CH₃'
        }],
        from: 15.344999999999999,
        to: 15.355
      }, {
        signals: [{
          delta: 65.79,
          assignment: 'CH₂'
        }],
        from: 65.78500000000001,
        to: 65.795
      }],
      nucleus: '13C',
      solvent: 'C₆D₅Cl'
    }, {
      names: ['diglyme'],
      smiles: 'COCCOCCOC',
      ranges: [{
        signals: [{
          delta: 58.42,
          assignment: 'CH₃'
        }],
        from: 58.415,
        to: 58.425000000000004
      }, {
        signals: [{
          delta: 70.56,
          assignment: 'CH₂'
        }],
        from: 70.555,
        to: 70.565
      }, {
        signals: [{
          delta: 72.07,
          assignment: 'CH₂'
        }],
        from: 72.065,
        to: 72.07499999999999
      }],
      nucleus: '13C',
      solvent: 'C₆D₅Cl'
    }, {
      names: ['dimethylformamide'],
      smiles: 'C(=O)N(C)C',
      ranges: [{
        signals: [{
          delta: 162.01,
          assignment: 'CH'
        }],
        from: 162.005,
        to: 162.015
      }, {
        signals: [{
          delta: 35.45,
          assignment: 'CH₃'
        }],
        from: 35.445,
        to: 35.455000000000005
      }, {
        signals: [{
          delta: 30.71,
          assignment: 'CH₃'
        }],
        from: 30.705000000000002,
        to: 30.715
      }],
      nucleus: '13C',
      solvent: 'C₆D₅Cl'
    }, {
      names: ['1,4-dioxane'],
      smiles: 'C1COCCOC1',
      ranges: [{
        signals: [{
          delta: 66.95,
          assignment: 'CH₂'
        }],
        from: 66.94500000000001,
        to: 66.955
      }],
      nucleus: '13C',
      solvent: 'C₆D₅Cl'
    }, {
      names: ['DME', 'diethyl ether'],
      smiles: 'CCOCC',
      ranges: [{
        signals: [{
          delta: 58.31,
          assignment: 'CH₃'
        }],
        from: 58.305,
        to: 58.315000000000005
      }, {
        signals: [{
          delta: 71.81,
          assignment: 'CH₂'
        }],
        from: 71.805,
        to: 71.815
      }],
      nucleus: '13C',
      solvent: 'C₆D₅Cl'
    }, {
      names: ['ethane'],
      smiles: 'CC',
      ranges: [{
        signals: [{
          delta: 6.91,
          assignment: 'CH₃'
        }],
        from: 6.905,
        to: 6.915
      }],
      nucleus: '13C',
      solvent: 'C₆D₅Cl'
    }, {
      names: ['ethanol'],
      smiles: 'CCO',
      ranges: [{
        signals: [{
          delta: 18.55,
          assignment: 'CH₃'
        }],
        from: 18.545,
        to: 18.555
      }, {
        signals: [{
          delta: 57.63,
          assignment: 'CH₂'
        }],
        from: 57.625,
        to: 57.635000000000005
      }],
      nucleus: '13C',
      solvent: 'C₆D₅Cl'
    }, {
      names: ['ethyl acetate'],
      smiles: 'CC(=O)CC',
      ranges: [{
        signals: [{
          delta: 20.5,
          assignment: 'CH₃CO'
        }],
        from: 20.495,
        to: 20.505
      }, {
        signals: [{
          delta: 170.2,
          assignment: 'CO'
        }],
        from: 170.195,
        to: 170.20499999999998
      }, {
        signals: [{
          delta: 60.06,
          assignment: 'CH₂'
        }],
        from: 60.055,
        to: 60.065000000000005
      }, {
        signals: [{
          delta: 14.07,
          assignment: 'CH₃'
        }],
        from: 14.065,
        to: 14.075000000000001
      }],
      nucleus: '13C',
      solvent: 'C₆D₅Cl'
    }, {
      names: ['ethylene'],
      smiles: 'C=C',
      ranges: [{
        signals: [{
          delta: 122.95,
          assignment: 'CH₂'
        }],
        from: 122.94500000000001,
        to: 122.955
      }],
      nucleus: '13C',
      solvent: 'C₆D₅Cl'
    }, {
      names: ['ethylene glycol'],
      smiles: 'OCCO',
      ranges: [{
        signals: [{
          delta: 64.03,
          assignment: 'CH₂'
        }],
        from: 64.025,
        to: 64.035
      }],
      nucleus: '13C',
      solvent: 'C₆D₅Cl'
    }, {
      names: ['H grease'],
      smiles: '',
      ranges: [{
        signals: [{
          delta: 30.11,
          assignment: 'CH₂'
        }],
        from: 30.105,
        to: 30.115
      }],
      nucleus: '13C',
      solvent: 'C₆D₅Cl'
    }, {
      names: ['hexamethylbenzene'],
      smiles: 'CC1=C(C(=C(C(=C1C)C)C)C)C',
      ranges: [{
        signals: [{
          delta: 131.54,
          assignment: 'C'
        }],
        from: 131.535,
        to: 131.545
      }, {
        signals: [{
          delta: 16.68,
          assignment: 'CH₃'
        }],
        from: 16.675,
        to: 16.685
      }],
      nucleus: '13C',
      solvent: 'C₆D₅Cl'
    }, {
      names: ['n-hexane'],
      smiles: 'CCCCCC',
      ranges: [{
        signals: [{
          delta: 14.18,
          assignment: 'CH₃'
        }],
        from: 14.174999999999999,
        to: 14.185
      }, {
        signals: [{
          delta: 22.86,
          assignment: 'CH₂(2,5)'
        }],
        from: 22.855,
        to: 22.865
      }, {
        signals: [{
          delta: 31.77,
          assignment: 'CH₂(3,4)'
        }],
        from: 31.765,
        to: 31.775
      }],
      nucleus: '13C',
      solvent: 'C₆D₅Cl'
    }, {
      names: ['HMDSO'],
      smiles: 'C[Si](C)(C)O[Si](C)(C)C',
      ranges: [{
        signals: [{
          delta: 1.92,
          assignment: 'CH₃'
        }],
        from: 1.915,
        to: 1.9249999999999998
      }],
      nucleus: '13C',
      solvent: 'C₆D₅Cl'
    }, {
      names: ['HMPA'],
      smiles: 'CN(C)P(=O)(N(C)C)N(C)C',
      ranges: [{
        signals: [{
          delta: 36.64,
          assignment: 'CH₃'
        }],
        from: 36.635,
        to: 36.645
      }],
      nucleus: '13C',
      solvent: 'C₆D₅Cl'
    }, {
      names: ['imidazole'],
      smiles: 'C1=CN=CN1',
      ranges: [{
        signals: [{
          delta: 135.5,
          assignment: 'CH(2)'
        }],
        from: 135.495,
        to: 135.505
      }, {
        signals: [{
          delta: 121.96,
          assignment: 'CH(4,5)'
        }],
        from: 121.955,
        to: 121.96499999999999
      }],
      nucleus: '13C',
      solvent: 'C₆D₅Cl'
    }, {
      names: ['methane'],
      smiles: 'C',
      ranges: [{
        signals: [{
          delta: -4.33,
          assignment: 'CH₄'
        }],
        from: -4.335,
        to: -4.325
      }],
      nucleus: '13C',
      solvent: 'C₆D₅Cl'
    }, {
      names: ['methanol'],
      smiles: 'CO',
      ranges: [{
        signals: [{
          delta: 49.66,
          assignment: 'CH₃'
        }],
        from: 49.654999999999994,
        to: 49.665
      }],
      nucleus: '13C',
      solvent: 'C₆D₅Cl'
    }, {
      names: ['nitromethane'],
      smiles: 'C[N+](=O)[O-]',
      ranges: [{
        signals: [{
          delta: 61.68,
          assignment: 'CH₃'
        }],
        from: 61.675,
        to: 61.685
      }],
      nucleus: '13C',
      solvent: 'C₆D₅Cl'
    }, {
      names: ['n-pentane'],
      smiles: 'CCCCC',
      ranges: [{
        signals: [{
          delta: 14.1,
          assignment: 'CH₃'
        }],
        from: 14.094999999999999,
        to: 14.105
      }, {
        signals: [{
          delta: 22.54,
          assignment: 'CH₂(2,4)'
        }],
        from: 22.535,
        to: 22.544999999999998
      }, {
        signals: [{
          delta: 34.26,
          assignment: 'CH₂(3)'
        }],
        from: 34.254999999999995,
        to: 34.265
      }],
      nucleus: '13C',
      solvent: 'C₆D₅Cl'
    }, {
      names: ['propane'],
      smiles: 'CCC',
      ranges: [{
        signals: [{
          delta: 16.56,
          assignment: 'CH₃'
        }],
        from: 16.555,
        to: 16.564999999999998
      }, {
        signals: [{
          delta: 16.48,
          assignment: 'CH₂'
        }],
        from: 16.475,
        to: 16.485
      }],
      nucleus: '13C',
      solvent: 'C₆D₅Cl'
    }, {
      names: ['2-propanol'],
      smiles: 'CC(O)C',
      ranges: [{
        signals: [{
          delta: 25.14,
          assignment: 'CH₃'
        }],
        from: 25.135,
        to: 25.145
      }, {
        signals: [{
          delta: 64.18,
          assignment: 'CH'
        }],
        from: 64.17500000000001,
        to: 64.185
      }],
      nucleus: '13C',
      solvent: 'C₆D₅Cl'
    }, {
      names: ['propylene'],
      smiles: 'CC=C',
      ranges: [{
        signals: [{
          delta: 19.32,
          assignment: 'CH₃'
        }],
        from: 19.315,
        to: 19.325
      }, {
        signals: [{
          delta: 115.86,
          assignment: 'CH₂'
        }],
        from: 115.855,
        to: 115.865
      }, {
        signals: [{
          delta: 133.57,
          assignment: 'CH'
        }],
        from: 133.565,
        to: 133.575
      }],
      nucleus: '13C',
      solvent: 'C₆D₅Cl'
    }, {
      names: ['pyridine'],
      smiles: 'C1=CC=NC=C1',
      ranges: [{
        signals: [{
          delta: 149.93,
          assignment: 'CH(2,6)'
        }],
        from: 149.925,
        to: 149.935
      }, {
        signals: [{
          delta: 123.49,
          assignment: 'CH(3,5)'
        }],
        from: 123.485,
        to: 123.49499999999999
      }, {
        signals: [{
          delta: 135.32,
          assignment: 'CH(4)'
        }],
        from: 135.315,
        to: 135.325
      }],
      nucleus: '13C',
      solvent: 'C₆D₅Cl'
    }, {
      names: ['pyrrole'],
      smiles: 'C1=CNC=C1',
      ranges: [{
        signals: [{
          delta: 117.65,
          assignment: 'CH(2,5)'
        }],
        from: 117.64500000000001,
        to: 117.655
      }, {
        signals: [{
          delta: 108.03,
          assignment: 'CH(3,4)'
        }],
        from: 108.025,
        to: 108.035
      }],
      nucleus: '13C',
      solvent: 'C₆D₅Cl'
    }, {
      names: ['pyrrolidine'],
      smiles: 'C1CCNC1',
      ranges: [{
        signals: [{
          delta: 46.75,
          assignment: 'CH₂(2,5)'
        }],
        from: 46.745,
        to: 46.755
      }, {
        signals: [{
          delta: 25.59,
          assignment: 'CH₂(3,4)'
        }],
        from: 25.585,
        to: 25.595
      }],
      nucleus: '13C',
      solvent: 'C₆D₅Cl'
    }, {
      names: ['silicone grease'],
      smiles: '',
      ranges: [{
        signals: [{
          delta: 1.09,
          assignment: 'CH₃'
        }],
        from: 1.0850000000000002,
        to: 1.095
      }],
      nucleus: '13C',
      solvent: 'C₆D₅Cl'
    }, {
      names: ['tetrahydrofuran'],
      smiles: 'C1COCC1',
      ranges: [{
        signals: [{
          delta: 67.64,
          assignment: 'CH₂(2,5)'
        }],
        from: 67.635,
        to: 67.645
      }, {
        signals: [{
          delta: 25.68,
          assignment: 'CH₂(3,4)'
        }],
        from: 25.675,
        to: 25.685
      }],
      nucleus: '13C',
      solvent: 'C₆D₅Cl'
    }, {
      names: ['toluene'],
      smiles: 'Cc1ccccc1',
      ranges: [{
        signals: [{
          delta: 21.23,
          assignment: 'CH₃'
        }],
        from: 21.225,
        to: 21.235
      }, {
        signals: [{
          delta: 137.65,
          assignment: 'C(1)'
        }],
        from: 137.645,
        to: 137.655
      }, {
        signals: [{
          delta: 129.12,
          assignment: 'CH(2,6)'
        }],
        from: 129.115,
        to: 129.125
      }, {
        signals: [{
          delta: 128.31,
          assignment: 'CH(3,5)'
        }],
        from: 128.305,
        to: 128.315
      }, {
        signals: [{
          delta: 125.43,
          assignment: 'CH(4)'
        }],
        from: 125.42500000000001,
        to: 125.435
      }],
      nucleus: '13C',
      solvent: 'C₆D₅Cl'
    }, {
      names: ['triethylamine'],
      smiles: 'CCN(CC)CC',
      ranges: [{
        signals: [{
          delta: 11.87,
          assignment: 'CH₃'
        }],
        from: 11.864999999999998,
        to: 11.875
      }, {
        signals: [{
          delta: 46.36,
          assignment: 'CH₂'
        }],
        from: 46.355,
        to: 46.365
      }],
      nucleus: '13C',
      solvent: 'C₆D₅Cl'
    }, {
      names: ['acetic acid'],
      smiles: 'CC(=O)O',
      ranges: [{
        signals: [{
          delta: 172.31,
          assignment: 'CO'
        }],
        from: 172.305,
        to: 172.315
      }, {
        signals: [{
          delta: 20.51,
          assignment: 'CH₃'
        }],
        from: 20.505000000000003,
        to: 20.515
      }],
      nucleus: '13C',
      solvent: '(CD₃)₂CO'
    }, {
      names: ['acetone'],
      smiles: 'CC(=O)C',
      ranges: [{
        signals: [{
          delta: 205.87,
          assignment: 'CO'
        }],
        from: 205.865,
        to: 205.875
      }, {
        signals: [{
          delta: 30.6,
          assignment: 'CH₃'
        }],
        from: 30.595000000000002,
        to: 30.605
      }],
      nucleus: '13C',
      solvent: '(CD₃)₂CO'
    }, {
      names: ['acetonitrile'],
      smiles: 'CC#N',
      ranges: [{
        signals: [{
          delta: 117.6,
          assignment: 'CN'
        }],
        from: 117.595,
        to: 117.60499999999999
      }, {
        signals: [{
          delta: 1.12,
          assignment: 'CH₃'
        }],
        from: 1.1150000000000002,
        to: 1.125
      }],
      nucleus: '13C',
      solvent: '(CD₃)₂CO'
    }, {
      names: ['benzene'],
      smiles: 'c1ccccc1',
      ranges: [{
        signals: [{
          delta: 129.15,
          assignment: 'CH'
        }],
        from: 129.145,
        to: 129.155
      }],
      nucleus: '13C',
      solvent: '(CD₃)₂CO'
    }, {
      names: ['tert -butyl alcohol'],
      smiles: 'CC(C)(C)O',
      ranges: [{
        signals: [{
          delta: 68.13,
          assignment: '(CH₃)₃C'
        }],
        from: 68.125,
        to: 68.13499999999999
      }, {
        signals: [{
          delta: 30.72,
          assignment: '(CH₃)₃C'
        }],
        from: 30.715,
        to: 30.724999999999998
      }],
      nucleus: '13C',
      solvent: '(CD₃)₂CO'
    }, {
      names: ['carbon dioxide'],
      smiles: 'O=C=O',
      ranges: [{
        signals: [{
          delta: 125.81,
          assignment: 'CO₂'
        }],
        from: 125.805,
        to: 125.815
      }],
      nucleus: '13C',
      solvent: '(CD₃)₂CO'
    }, {
      names: ['carbon disulfide'],
      smiles: 'S=C=S',
      ranges: [{
        signals: [{
          delta: 193.58,
          assignment: 'CS₂'
        }],
        from: 193.57500000000002,
        to: 193.585
      }],
      nucleus: '13C',
      solvent: '(CD₃)₂CO'
    }, {
      names: ['carbon tetrachloride'],
      smiles: 'ClC(Cl)(Cl)Cl',
      ranges: [{
        signals: [{
          delta: 96.65,
          assignment: 'CCl₄'
        }],
        from: 96.64500000000001,
        to: 96.655
      }],
      nucleus: '13C',
      solvent: '(CD₃)₂CO'
    }, {
      names: ['chloroform'],
      smiles: 'ClC(Cl)Cl',
      ranges: [{
        signals: [{
          delta: 79.19,
          assignment: 'CH'
        }],
        from: 79.185,
        to: 79.195
      }],
      nucleus: '13C',
      solvent: '(CD₃)₂CO'
    }, {
      names: ['18-crown-6'],
      smiles: 'C1COCCOCCOCCOCCOCCO1',
      ranges: [{
        signals: [{
          delta: 71.25,
          assignment: 'CH₂'
        }],
        from: 71.245,
        to: 71.255
      }],
      nucleus: '13C',
      solvent: '(CD₃)₂CO'
    }, {
      names: ['cyclohexane'],
      smiles: 'C1CCCCC1',
      ranges: [{
        signals: [{
          delta: 27.51,
          assignment: 'CH₂'
        }],
        from: 27.505000000000003,
        to: 27.515
      }],
      nucleus: '13C',
      solvent: '(CD₃)₂CO'
    }, {
      names: ['1,2-dichloroethane'],
      smiles: 'ClCCCl',
      ranges: [{
        signals: [{
          delta: 45.25,
          assignment: 'CH₂'
        }],
        from: 45.245,
        to: 45.255
      }],
      nucleus: '13C',
      solvent: '(CD₃)₂CO'
    }, {
      names: ['dichloromethane'],
      smiles: 'ClCCl',
      ranges: [{
        signals: [{
          delta: 54.95,
          assignment: 'CH₂'
        }],
        from: 54.945,
        to: 54.955000000000005
      }],
      nucleus: '13C',
      solvent: '(CD₃)₂CO'
    }, {
      names: ['diethyl ether'],
      smiles: 'CCOCC',
      ranges: [{
        signals: [{
          delta: 15.78,
          assignment: 'CH₃'
        }],
        from: 15.774999999999999,
        to: 15.785
      }, {
        signals: [{
          delta: 66.12,
          assignment: 'CH₂'
        }],
        from: 66.11500000000001,
        to: 66.125
      }],
      nucleus: '13C',
      solvent: '(CD₃)₂CO'
    }, {
      names: ['diglyme'],
      smiles: 'COCCOCCOC',
      ranges: [{
        signals: [{
          delta: 58.77,
          assignment: 'CH₃'
        }],
        from: 58.765,
        to: 58.775000000000006
      }, {
        signals: [{
          delta: 71.03,
          assignment: 'CH₂'
        }],
        from: 71.025,
        to: 71.035
      }, {
        signals: [{
          delta: 72.63,
          assignment: 'CH₂'
        }],
        from: 72.625,
        to: 72.63499999999999
      }],
      nucleus: '13C',
      solvent: '(CD₃)₂CO'
    }, {
      names: ['dimethylformamide'],
      smiles: 'C(=O)N(C)C',
      ranges: [{
        signals: [{
          delta: 162.79,
          assignment: 'CH'
        }],
        from: 162.785,
        to: 162.795
      }, {
        signals: [{
          delta: 36.15,
          assignment: 'CH₃'
        }],
        from: 36.144999999999996,
        to: 36.155
      }, {
        signals: [{
          delta: 31.03,
          assignment: 'CH₃'
        }],
        from: 31.025000000000002,
        to: 31.035
      }],
      nucleus: '13C',
      solvent: '(CD₃)₂CO'
    }, {
      names: ['1,4-dioxane'],
      smiles: 'C1COCCOC1',
      ranges: [{
        signals: [{
          delta: 67.6,
          assignment: 'CH₂'
        }],
        from: 67.595,
        to: 67.60499999999999
      }],
      nucleus: '13C',
      solvent: '(CD₃)₂CO'
    }, {
      names: ['DME', 'diethyl ether'],
      smiles: 'CCOCC',
      ranges: [{
        signals: [{
          delta: 58.45,
          assignment: 'CH₃'
        }],
        from: 58.445,
        to: 58.455000000000005
      }, {
        signals: [{
          delta: 72.47,
          assignment: 'CH₂'
        }],
        from: 72.465,
        to: 72.475
      }],
      nucleus: '13C',
      solvent: '(CD₃)₂CO'
    }, {
      names: ['ethane'],
      smiles: 'CC',
      ranges: [{
        signals: [{
          delta: 6.88,
          assignment: 'CH₃'
        }],
        from: 6.875,
        to: 6.885
      }],
      nucleus: '13C',
      solvent: '(CD₃)₂CO'
    }, {
      names: ['ethanol'],
      smiles: 'CCO',
      ranges: [{
        signals: [{
          delta: 18.89,
          assignment: 'CH₃'
        }],
        from: 18.885,
        to: 18.895
      }, {
        signals: [{
          delta: 57.72,
          assignment: 'CH₂'
        }],
        from: 57.714999999999996,
        to: 57.725
      }],
      nucleus: '13C',
      solvent: '(CD₃)₂CO'
    }, {
      names: ['ethyl acetate'],
      smiles: 'CC(=O)CC',
      ranges: [{
        signals: [{
          delta: 20.83,
          assignment: 'CH₃CO'
        }],
        from: 20.825,
        to: 20.834999999999997
      }, {
        signals: [{
          delta: 170.96,
          assignment: 'CO'
        }],
        from: 170.955,
        to: 170.965
      }, {
        signals: [{
          delta: 60.56,
          assignment: 'CH₂'
        }],
        from: 60.555,
        to: 60.565000000000005
      }, {
        signals: [{
          delta: 14.5,
          assignment: 'CH₃'
        }],
        from: 14.495,
        to: 14.505
      }],
      nucleus: '13C',
      solvent: '(CD₃)₂CO'
    }, {
      names: ['ethylene'],
      smiles: 'C=C',
      ranges: [{
        signals: [{
          delta: 123.47,
          assignment: 'CH₂'
        }],
        from: 123.465,
        to: 123.475
      }],
      nucleus: '13C',
      solvent: '(CD₃)₂CO'
    }, {
      names: ['ethylene glycol'],
      smiles: 'OCCO',
      ranges: [{
        signals: [{
          delta: 64.26,
          assignment: 'CH₂'
        }],
        from: 64.25500000000001,
        to: 64.265
      }],
      nucleus: '13C',
      solvent: '(CD₃)₂CO'
    }, {
      names: ['H grease'],
      smiles: '',
      ranges: [],
      nucleus: '13C',
      solvent: '(CD₃)₂CO'
    }, {
      names: ['hexamethylbenzene'],
      smiles: 'CC1=C(C(=C(C(=C1C)C)C)C)C',
      ranges: [{
        signals: [{
          delta: 132.22,
          assignment: 'C'
        }],
        from: 132.215,
        to: 132.225
      }, {
        signals: [{
          delta: 16.86,
          assignment: 'CH₃'
        }],
        from: 16.855,
        to: 16.865
      }],
      nucleus: '13C',
      solvent: '(CD₃)₂CO'
    }, {
      names: ['n-hexane'],
      smiles: 'CCCCCC',
      ranges: [{
        signals: [{
          delta: 14.34,
          assignment: 'CH₃'
        }],
        from: 14.334999999999999,
        to: 14.345
      }, {
        signals: [{
          delta: 23.28,
          assignment: 'CH₂(2,5)'
        }],
        from: 23.275000000000002,
        to: 23.285
      }, {
        signals: [{
          delta: 32.3,
          assignment: 'CH₂(3,4)'
        }],
        from: 32.294999999999995,
        to: 32.305
      }],
      nucleus: '13C',
      solvent: '(CD₃)₂CO'
    }, {
      names: ['HMDSO'],
      smiles: 'C[Si](C)(C)O[Si](C)(C)C',
      ranges: [{
        signals: [{
          delta: 2.01,
          assignment: 'CH₃'
        }],
        from: 2.005,
        to: 2.0149999999999997
      }],
      nucleus: '13C',
      solvent: '(CD₃)₂CO'
    }, {
      names: ['HMPA'],
      smiles: 'CN(C)P(=O)(N(C)C)N(C)C',
      ranges: [{
        signals: [{
          delta: 37.04,
          assignment: 'CH₃'
        }],
        from: 37.035,
        to: 37.045
      }],
      nucleus: '13C',
      solvent: '(CD₃)₂CO'
    }, {
      names: ['imidazole'],
      smiles: 'C1=CN=CN1',
      ranges: [{
        signals: [{
          delta: 135.89,
          assignment: 'CH(2)'
        }],
        from: 135.885,
        to: 135.89499999999998
      }, {
        signals: [{
          delta: 122.31,
          assignment: 'CH(4,5)'
        }],
        from: 122.305,
        to: 122.315
      }],
      nucleus: '13C',
      solvent: '(CD₃)₂CO'
    }, {
      names: ['methane'],
      smiles: 'C',
      ranges: [{
        signals: [{
          delta: -5.33,
          assignment: 'CH₄'
        }],
        from: -5.335,
        to: -5.325
      }],
      nucleus: '13C',
      solvent: '(CD₃)₂CO'
    }, {
      names: ['methanol'],
      smiles: 'CO',
      ranges: [{
        signals: [{
          delta: 49.77,
          assignment: 'CH₃'
        }],
        from: 49.765,
        to: 49.775000000000006
      }],
      nucleus: '13C',
      solvent: '(CD₃)₂CO'
    }, {
      names: ['nitromethane'],
      smiles: 'C[N+](=O)[O-]',
      ranges: [{
        signals: [{
          delta: 63.21,
          assignment: 'CH₃'
        }],
        from: 63.205,
        to: 63.215
      }],
      nucleus: '13C',
      solvent: '(CD₃)₂CO'
    }, {
      names: ['n-pentane'],
      smiles: 'CCCCC',
      ranges: [{
        signals: [{
          delta: 14.29,
          assignment: 'CH₃'
        }],
        from: 14.284999999999998,
        to: 14.295
      }, {
        signals: [{
          delta: 22.98,
          assignment: 'CH₂(2,4)'
        }],
        from: 22.975,
        to: 22.985
      }, {
        signals: [{
          delta: 34.83,
          assignment: 'CH₂(3)'
        }],
        from: 34.824999999999996,
        to: 34.835
      }],
      nucleus: '13C',
      solvent: '(CD₃)₂CO'
    }, {
      names: ['propane'],
      smiles: 'CCC',
      ranges: [{
        signals: [{
          delta: 16.68,
          assignment: 'CH₃'
        }],
        from: 16.675,
        to: 16.685
      }, {
        signals: [{
          delta: 16.78,
          assignment: 'CH₂'
        }],
        from: 16.775000000000002,
        to: 16.785
      }],
      nucleus: '13C',
      solvent: '(CD₃)₂CO'
    }, {
      names: ['2-propanol'],
      smiles: 'CC(O)C',
      ranges: [{
        signals: [{
          delta: 25.67,
          assignment: 'CH₃'
        }],
        from: 25.665000000000003,
        to: 25.675
      }, {
        signals: [{
          delta: 63.85,
          assignment: 'CH'
        }],
        from: 63.845,
        to: 63.855000000000004
      }],
      nucleus: '13C',
      solvent: '(CD₃)₂CO'
    }, {
      names: ['propylene'],
      smiles: 'CC=C',
      ranges: [{
        signals: [{
          delta: 19.42,
          assignment: 'CH₃'
        }],
        from: 19.415000000000003,
        to: 19.425
      }, {
        signals: [{
          delta: 116.03,
          assignment: 'CH₂'
        }],
        from: 116.025,
        to: 116.035
      }, {
        signals: [{
          delta: 134.34,
          assignment: 'CH'
        }],
        from: 134.335,
        to: 134.345
      }],
      nucleus: '13C',
      solvent: '(CD₃)₂CO'
    }, {
      names: ['pyridine'],
      smiles: 'C1=CC=NC=C1',
      ranges: [{
        signals: [{
          delta: 150.67,
          assignment: 'CH(2,6)'
        }],
        from: 150.665,
        to: 150.67499999999998
      }, {
        signals: [{
          delta: 124.57,
          assignment: 'CH(3,5)'
        }],
        from: 124.565,
        to: 124.57499999999999
      }, {
        signals: [{
          delta: 136.56,
          assignment: 'CH(4)'
        }],
        from: 136.555,
        to: 136.565
      }],
      nucleus: '13C',
      solvent: '(CD₃)₂CO'
    }, {
      names: ['pyrrole'],
      smiles: 'C1=CNC=C1',
      ranges: [{
        signals: [{
          delta: 117.98,
          assignment: 'CH(2,5)'
        }],
        from: 117.97500000000001,
        to: 117.985
      }, {
        signals: [{
          delta: 108.04,
          assignment: 'CH(3,4)'
        }],
        from: 108.03500000000001,
        to: 108.045
      }],
      nucleus: '13C',
      solvent: '(CD₃)₂CO'
    }, {
      names: ['pyrrolidine'],
      smiles: 'C1CCNC1',
      ranges: [],
      nucleus: '13C',
      solvent: '(CD₃)₂CO'
    }, {
      names: ['silicone grease'],
      smiles: '',
      ranges: [{
        signals: [{
          delta: 1.4,
          assignment: 'CH₃'
        }],
        from: 1.395,
        to: 1.4049999999999998
      }],
      nucleus: '13C',
      solvent: '(CD₃)₂CO'
    }, {
      names: ['tetrahydrofuran'],
      smiles: 'C1COCC1',
      ranges: [{
        signals: [{
          delta: 68.07,
          assignment: 'CH₂(2,5)'
        }],
        from: 68.065,
        to: 68.07499999999999
      }, {
        signals: [{
          delta: 26.15,
          assignment: 'CH₂(3,4)'
        }],
        from: 26.145,
        to: 26.154999999999998
      }],
      nucleus: '13C',
      solvent: '(CD₃)₂CO'
    }, {
      names: ['toluene'],
      smiles: 'Cc1ccccc1',
      ranges: [{
        signals: [{
          delta: 21.46,
          assignment: 'CH₃'
        }],
        from: 21.455000000000002,
        to: 21.465
      }, {
        signals: [{
          delta: 138.48,
          assignment: 'C(1)'
        }],
        from: 138.475,
        to: 138.48499999999999
      }, {
        signals: [{
          delta: 129.76,
          assignment: 'CH(2,6)'
        }],
        from: 129.755,
        to: 129.765
      }, {
        signals: [{
          delta: 129.03,
          assignment: 'CH(3,5)'
        }],
        from: 129.025,
        to: 129.035
      }, {
        signals: [{
          delta: 126.12,
          assignment: 'CH(4)'
        }],
        from: 126.11500000000001,
        to: 126.125
      }],
      nucleus: '13C',
      solvent: '(CD₃)₂CO'
    }, {
      names: ['triethylamine'],
      smiles: 'CCN(CC)CC',
      ranges: [{
        signals: [{
          delta: 12.49,
          assignment: 'CH₃'
        }],
        from: 12.485,
        to: 12.495000000000001
      }, {
        signals: [{
          delta: 47.07,
          assignment: 'CH₂'
        }],
        from: 47.065,
        to: 47.075
      }],
      nucleus: '13C',
      solvent: '(CD₃)₂CO'
    }, {
      names: ['acetic acid'],
      smiles: 'CC(=O)O',
      ranges: [{
        signals: [{
          delta: 171.93,
          assignment: 'CO'
        }],
        from: 171.925,
        to: 171.935
      }, {
        signals: [{
          delta: 20.95,
          assignment: 'CH₃'
        }],
        from: 20.945,
        to: 20.955
      }],
      nucleus: '13C',
      solvent: '(CD₃)₂SO'
    }, {
      names: ['acetone'],
      smiles: 'CC(=O)C',
      ranges: [{
        signals: [{
          delta: 206.31,
          assignment: 'CO'
        }],
        from: 206.305,
        to: 206.315
      }, {
        signals: [{
          delta: 30.56,
          assignment: 'CH₃'
        }],
        from: 30.555,
        to: 30.564999999999998
      }],
      nucleus: '13C',
      solvent: '(CD₃)₂SO'
    }, {
      names: ['acetonitrile'],
      smiles: 'CC#N',
      ranges: [{
        signals: [{
          delta: 117.91,
          assignment: 'CN'
        }],
        from: 117.905,
        to: 117.91499999999999
      }, {
        signals: [{
          delta: 1.03,
          assignment: 'CH₃'
        }],
        from: 1.0250000000000001,
        to: 1.035
      }],
      nucleus: '13C',
      solvent: '(CD₃)₂SO'
    }, {
      names: ['benzene'],
      smiles: 'c1ccccc1',
      ranges: [{
        signals: [{
          delta: 128.3,
          assignment: 'CH'
        }],
        from: 128.29500000000002,
        to: 128.305
      }],
      nucleus: '13C',
      solvent: '(CD₃)₂SO'
    }, {
      names: ['tert -butyl alcohol'],
      smiles: 'CC(C)(C)O',
      ranges: [{
        signals: [{
          delta: 66.88,
          assignment: '(CH₃)₃C'
        }],
        from: 66.875,
        to: 66.88499999999999
      }, {
        signals: [{
          delta: 30.38,
          assignment: '(CH₃)₃C'
        }],
        from: 30.375,
        to: 30.384999999999998
      }],
      nucleus: '13C',
      solvent: '(CD₃)₂SO'
    }, {
      names: ['carbon dioxide'],
      smiles: 'O=C=O',
      ranges: [{
        signals: [{
          delta: 124.21,
          assignment: 'CO₂'
        }],
        from: 124.205,
        to: 124.21499999999999
      }],
      nucleus: '13C',
      solvent: '(CD₃)₂SO'
    }, {
      names: ['carbon disulfide'],
      smiles: 'S=C=S',
      ranges: [{
        signals: [{
          delta: 192.63,
          assignment: 'CS₂'
        }],
        from: 192.625,
        to: 192.635
      }],
      nucleus: '13C',
      solvent: '(CD₃)₂SO'
    }, {
      names: ['carbon tetrachloride'],
      smiles: 'ClC(Cl)(Cl)Cl',
      ranges: [{
        signals: [{
          delta: 95.44,
          assignment: 'CCl₄'
        }],
        from: 95.435,
        to: 95.445
      }],
      nucleus: '13C',
      solvent: '(CD₃)₂SO'
    }, {
      names: ['chloroform'],
      smiles: 'ClC(Cl)Cl',
      ranges: [{
        signals: [{
          delta: 79.16,
          assignment: 'CH'
        }],
        from: 79.155,
        to: 79.16499999999999
      }],
      nucleus: '13C',
      solvent: '(CD₃)₂SO'
    }, {
      names: ['18-crown-6'],
      smiles: 'C1COCCOCCOCCOCCOCCO1',
      ranges: [{
        signals: [{
          delta: 69.85,
          assignment: 'CH₂'
        }],
        from: 69.845,
        to: 69.85499999999999
      }],
      nucleus: '13C',
      solvent: '(CD₃)₂SO'
    }, {
      names: ['cyclohexane'],
      smiles: 'C1CCCCC1',
      ranges: [{
        signals: [{
          delta: 26.33,
          assignment: 'CH₂'
        }],
        from: 26.325,
        to: 26.334999999999997
      }],
      nucleus: '13C',
      solvent: '(CD₃)₂SO'
    }, {
      names: ['1,2-dichloroethane'],
      smiles: 'ClCCCl',
      ranges: [{
        signals: [{
          delta: 45.02,
          assignment: 'CH₂'
        }],
        from: 45.015,
        to: 45.025000000000006
      }],
      nucleus: '13C',
      solvent: '(CD₃)₂SO'
    }, {
      names: ['dichloromethane'],
      smiles: 'ClCCl',
      ranges: [{
        signals: [{
          delta: 54.84,
          assignment: 'CH₂'
        }],
        from: 54.835,
        to: 54.845000000000006
      }],
      nucleus: '13C',
      solvent: '(CD₃)₂SO'
    }, {
      names: ['diethyl ether'],
      smiles: 'CCOCC',
      ranges: [{
        signals: [{
          delta: 15.12,
          assignment: 'CH₃'
        }],
        from: 15.114999999999998,
        to: 15.125
      }, {
        signals: [{
          delta: 62.05,
          assignment: 'CH₂'
        }],
        from: 62.044999999999995,
        to: 62.055
      }],
      nucleus: '13C',
      solvent: '(CD₃)₂SO'
    }, {
      names: ['diglyme'],
      smiles: 'COCCOCCOC',
      ranges: [{
        signals: [{
          delta: 57.98,
          assignment: 'CH₃'
        }],
        from: 57.974999999999994,
        to: 57.985
      }, {
        signals: [{
          delta: 69.54,
          assignment: 'CH₂'
        }],
        from: 69.53500000000001,
        to: 69.545
      }, {
        signals: [{
          delta: 71.25,
          assignment: 'CH₂'
        }],
        from: 71.245,
        to: 71.255
      }],
      nucleus: '13C',
      solvent: '(CD₃)₂SO'
    }, {
      names: ['dimethylformamide'],
      smiles: 'C(=O)N(C)C',
      ranges: [{
        signals: [{
          delta: 162.29,
          assignment: 'CH'
        }],
        from: 162.285,
        to: 162.295
      }, {
        signals: [{
          delta: 35.73,
          assignment: 'CH₃'
        }],
        from: 35.724999999999994,
        to: 35.735
      }, {
        signals: [{
          delta: 30.73,
          assignment: 'CH₃'
        }],
        from: 30.725,
        to: 30.735
      }],
      nucleus: '13C',
      solvent: '(CD₃)₂SO'
    }, {
      names: ['1,4-dioxane'],
      smiles: 'C1COCCOC1',
      ranges: [{
        signals: [{
          delta: 66.36,
          assignment: 'CH₂'
        }],
        from: 66.355,
        to: 66.365
      }],
      nucleus: '13C',
      solvent: '(CD₃)₂SO'
    }, {
      names: ['DME', 'diethyl ether'],
      smiles: 'CCOCC',
      ranges: [{
        signals: [{
          delta: 58.03,
          assignment: 'CH₃'
        }],
        from: 58.025,
        to: 58.035000000000004
      }, {
        signals: [{
          delta: 71.17,
          assignment: 'CH₂'
        }],
        from: 71.165,
        to: 71.175
      }],
      nucleus: '13C',
      solvent: '(CD₃)₂SO'
    }, {
      names: ['ethane'],
      smiles: 'CC',
      ranges: [{
        signals: [{
          delta: 6.61,
          assignment: 'CH₃'
        }],
        from: 6.605,
        to: 6.615
      }],
      nucleus: '13C',
      solvent: '(CD₃)₂SO'
    }, {
      names: ['ethanol'],
      smiles: 'CCO',
      ranges: [{
        signals: [{
          delta: 18.51,
          assignment: 'CH₃'
        }],
        from: 18.505000000000003,
        to: 18.515
      }, {
        signals: [{
          delta: 56.07,
          assignment: 'CH₂'
        }],
        from: 56.065,
        to: 56.075
      }],
      nucleus: '13C',
      solvent: '(CD₃)₂SO'
    }, {
      names: ['ethyl acetate'],
      smiles: 'CC(=O)CC',
      ranges: [{
        signals: [{
          delta: 20.68,
          assignment: 'CH₃CO'
        }],
        from: 20.675,
        to: 20.685
      }, {
        signals: [{
          delta: 170.31,
          assignment: 'CO'
        }],
        from: 170.305,
        to: 170.315
      }, {
        signals: [{
          delta: 59.74,
          assignment: 'CH₂'
        }],
        from: 59.735,
        to: 59.745000000000005
      }, {
        signals: [{
          delta: 14.4,
          assignment: 'CH₃'
        }],
        from: 14.395,
        to: 14.405000000000001
      }],
      nucleus: '13C',
      solvent: '(CD₃)₂SO'
    }, {
      names: ['ethylene'],
      smiles: 'C=C',
      ranges: [{
        signals: [{
          delta: 123.52,
          assignment: 'CH₂'
        }],
        from: 123.515,
        to: 123.52499999999999
      }],
      nucleus: '13C',
      solvent: '(CD₃)₂SO'
    }, {
      names: ['ethylene glycol'],
      smiles: 'OCCO',
      ranges: [{
        signals: [{
          delta: 62.76,
          assignment: 'CH₂'
        }],
        from: 62.754999999999995,
        to: 62.765
      }],
      nucleus: '13C',
      solvent: '(CD₃)₂SO'
    }, {
      names: ['H grease'],
      smiles: '',
      ranges: [],
      nucleus: '13C',
      solvent: '(CD₃)₂SO'
    }, {
      names: ['hexamethylbenzene'],
      smiles: 'CC1=C(C(=C(C(=C1C)C)C)C)C',
      ranges: [{
        signals: [{
          delta: 131.1,
          assignment: 'C'
        }],
        from: 131.095,
        to: 131.105
      }, {
        signals: [{
          delta: 16.6,
          assignment: 'CH₃'
        }],
        from: 16.595000000000002,
        to: 16.605
      }],
      nucleus: '13C',
      solvent: '(CD₃)₂SO'
    }, {
      names: ['n-hexane'],
      smiles: 'CCCCCC',
      ranges: [{
        signals: [{
          delta: 13.88,
          assignment: 'CH₃'
        }],
        from: 13.875,
        to: 13.885000000000002
      }, {
        signals: [{
          delta: 22.05,
          assignment: 'CH₂(2,5)'
        }],
        from: 22.045,
        to: 22.055
      }, {
        signals: [{
          delta: 30.95,
          assignment: 'CH₂(3,4)'
        }],
        from: 30.945,
        to: 30.955
      }],
      nucleus: '13C',
      solvent: '(CD₃)₂SO'
    }, {
      names: ['HMDSO'],
      smiles: 'C[Si](C)(C)O[Si](C)(C)C',
      ranges: [{
        signals: [{
          delta: 1.96,
          assignment: 'CH₃'
        }],
        from: 1.955,
        to: 1.9649999999999999
      }],
      nucleus: '13C',
      solvent: '(CD₃)₂SO'
    }, {
      names: ['HMPA'],
      smiles: 'CN(C)P(=O)(N(C)C)N(C)C',
      ranges: [{
        signals: [{
          delta: 36.42,
          assignment: 'CH₃'
        }],
        from: 36.415,
        to: 36.425000000000004
      }],
      nucleus: '13C',
      solvent: '(CD₃)₂SO'
    }, {
      names: ['imidazole'],
      smiles: 'C1=CN=CN1',
      ranges: [{
        signals: [{
          delta: 135.15,
          assignment: 'CH(2)'
        }],
        from: 135.145,
        to: 135.155
      }, {
        signals: [{
          delta: 121.55,
          assignment: 'CH(4,5)'
        }],
        from: 121.545,
        to: 121.55499999999999
      }],
      nucleus: '13C',
      solvent: '(CD₃)₂SO'
    }, {
      names: ['methane'],
      smiles: 'C',
      ranges: [{
        signals: [{
          delta: -4.01,
          assignment: 'CH₄'
        }],
        from: -4.015,
        to: -4.005
      }],
      nucleus: '13C',
      solvent: '(CD₃)₂SO'
    }, {
      names: ['methanol'],
      smiles: 'CO',
      ranges: [{
        signals: [{
          delta: 48.59,
          assignment: 'CH₃'
        }],
        from: 48.585,
        to: 48.595000000000006
      }],
      nucleus: '13C',
      solvent: '(CD₃)₂SO'
    }, {
      names: ['nitromethane'],
      smiles: 'C[N+](=O)[O-]',
      ranges: [{
        signals: [{
          delta: 63.28,
          assignment: 'CH₃'
        }],
        from: 63.275,
        to: 63.285000000000004
      }],
      nucleus: '13C',
      solvent: '(CD₃)₂SO'
    }, {
      names: ['n-pentane'],
      smiles: 'CCCCC',
      ranges: [{
        signals: [{
          delta: 13.28,
          assignment: 'CH₃'
        }],
        from: 13.274999999999999,
        to: 13.285
      }, {
        signals: [{
          delta: 21.7,
          assignment: 'CH₂(2,4)'
        }],
        from: 21.695,
        to: 21.705
      }, {
        signals: [{
          delta: 33.48,
          assignment: 'CH₂(3)'
        }],
        from: 33.474999999999994,
        to: 33.485
      }],
      nucleus: '13C',
      solvent: '(CD₃)₂SO'
    }, {
      names: ['propane'],
      smiles: 'CCC',
      ranges: [{
        signals: [{
          delta: 16.34,
          assignment: 'CH₃'
        }],
        from: 16.335,
        to: 16.345
      }, {
        signals: [{
          delta: 15.67,
          assignment: 'CH₂'
        }],
        from: 15.665,
        to: 15.675
      }],
      nucleus: '13C',
      solvent: '(CD₃)₂SO'
    }, {
      names: ['2-propanol'],
      smiles: 'CC(O)C',
      ranges: [{
        signals: [{
          delta: 25.43,
          assignment: 'CH₃'
        }],
        from: 25.425,
        to: 25.435
      }, {
        signals: [{
          delta: 64.92,
          assignment: 'CH'
        }],
        from: 64.915,
        to: 64.925
      }],
      nucleus: '13C',
      solvent: '(CD₃)₂SO'
    }, {
      names: ['propylene'],
      smiles: 'CC=C',
      ranges: [{
        signals: [{
          delta: 19.2,
          assignment: 'CH₃'
        }],
        from: 19.195,
        to: 19.205
      }, {
        signals: [{
          delta: 116.07,
          assignment: 'CH₂'
        }],
        from: 116.065,
        to: 116.07499999999999
      }, {
        signals: [{
          delta: 133.55,
          assignment: 'CH'
        }],
        from: 133.54500000000002,
        to: 133.555
      }],
      nucleus: '13C',
      solvent: '(CD₃)₂SO'
    }, {
      names: ['pyridine'],
      smiles: 'C1=CC=NC=C1',
      ranges: [{
        signals: [{
          delta: 149.58,
          assignment: 'CH(2,6)'
        }],
        from: 149.57500000000002,
        to: 149.585
      }, {
        signals: [{
          delta: 123.84,
          assignment: 'CH(3,5)'
        }],
        from: 123.83500000000001,
        to: 123.845
      }, {
        signals: [{
          delta: 136.05,
          assignment: 'CH(4)'
        }],
        from: 136.04500000000002,
        to: 136.055
      }],
      nucleus: '13C',
      solvent: '(CD₃)₂SO'
    }, {
      names: ['pyrrole'],
      smiles: 'C1=CNC=C1',
      ranges: [{
        signals: [{
          delta: 117.32,
          assignment: 'CH(2,5)'
        }],
        from: 117.315,
        to: 117.32499999999999
      }, {
        signals: [{
          delta: 107.07,
          assignment: 'CH(3,4)'
        }],
        from: 107.065,
        to: 107.07499999999999
      }],
      nucleus: '13C',
      solvent: '(CD₃)₂SO'
    }, {
      names: ['pyrrolidine'],
      smiles: 'C1CCNC1',
      ranges: [{
        signals: [{
          delta: 46.51,
          assignment: 'CH₂(2,5)'
        }],
        from: 46.504999999999995,
        to: 46.515
      }, {
        signals: [{
          delta: 25.26,
          assignment: 'CH₂(3,4)'
        }],
        from: 25.255000000000003,
        to: 25.265
      }],
      nucleus: '13C',
      solvent: '(CD₃)₂SO'
    }, {
      names: ['silicone grease'],
      smiles: '',
      ranges: [],
      nucleus: '13C',
      solvent: '(CD₃)₂SO'
    }, {
      names: ['tetrahydrofuran'],
      smiles: 'C1COCC1',
      ranges: [{
        signals: [{
          delta: 67.03,
          assignment: 'CH₂(2,5)'
        }],
        from: 67.025,
        to: 67.035
      }, {
        signals: [{
          delta: 25.14,
          assignment: 'CH₂(3,4)'
        }],
        from: 25.135,
        to: 25.145
      }],
      nucleus: '13C',
      solvent: '(CD₃)₂SO'
    }, {
      names: ['toluene'],
      smiles: 'Cc1ccccc1',
      ranges: [{
        signals: [{
          delta: 20.99,
          assignment: 'CH₃'
        }],
        from: 20.985,
        to: 20.994999999999997
      }, {
        signals: [{
          delta: 137.35,
          assignment: 'C(1)'
        }],
        from: 137.345,
        to: 137.355
      }, {
        signals: [{
          delta: 128.88,
          assignment: 'CH(2,6)'
        }],
        from: 128.875,
        to: 128.885
      }, {
        signals: [{
          delta: 128.18,
          assignment: 'CH(3,5)'
        }],
        from: 128.175,
        to: 128.185
      }, {
        signals: [{
          delta: 125.29,
          assignment: 'CH(4)'
        }],
        from: 125.28500000000001,
        to: 125.295
      }],
      nucleus: '13C',
      solvent: '(CD₃)₂SO'
    }, {
      names: ['triethylamine'],
      smiles: 'CCN(CC)CC',
      ranges: [{
        signals: [{
          delta: 11.74,
          assignment: 'CH₃'
        }],
        from: 11.735,
        to: 11.745000000000001
      }, {
        signals: [{
          delta: 45.74,
          assignment: 'CH₂'
        }],
        from: 45.735,
        to: 45.745000000000005
      }],
      nucleus: '13C',
      solvent: '(CD₃)₂SO'
    }, {
      names: ['acetic acid'],
      smiles: 'CC(=O)O',
      ranges: [{
        signals: [{
          delta: 173.21,
          assignment: 'CO'
        }],
        from: 173.205,
        to: 173.215
      }, {
        signals: [{
          delta: 20.73,
          assignment: 'CH₃'
        }],
        from: 20.725,
        to: 20.735
      }],
      nucleus: '13C',
      solvent: 'CD₃CN'
    }, {
      names: ['acetone'],
      smiles: 'CC(=O)C',
      ranges: [{
        signals: [{
          delta: 207.43,
          assignment: 'CO'
        }],
        from: 207.425,
        to: 207.435
      }, {
        signals: [{
          delta: 30.91,
          assignment: 'CH₃'
        }],
        from: 30.905,
        to: 30.915
      }],
      nucleus: '13C',
      solvent: 'CD₃CN'
    }, {
      names: ['acetonitrile'],
      smiles: 'CC#N',
      ranges: [{
        signals: [{
          delta: 118.26,
          assignment: 'CN'
        }],
        from: 118.25500000000001,
        to: 118.265
      }, {
        signals: [{
          delta: 1.79,
          assignment: 'CH₃'
        }],
        from: 1.7850000000000001,
        to: 1.795
      }],
      nucleus: '13C',
      solvent: 'CD₃CN'
    }, {
      names: ['benzene'],
      smiles: 'c1ccccc1',
      ranges: [{
        signals: [{
          delta: 129.32,
          assignment: 'CH'
        }],
        from: 129.315,
        to: 129.325
      }],
      nucleus: '13C',
      solvent: 'CD₃CN'
    }, {
      names: ['tert -butyl alcohol'],
      smiles: 'CC(C)(C)O',
      ranges: [{
        signals: [{
          delta: 68.74,
          assignment: '(CH₃)₃C'
        }],
        from: 68.735,
        to: 68.74499999999999
      }, {
        signals: [{
          delta: 30.68,
          assignment: '(CH₃)₃C'
        }],
        from: 30.675,
        to: 30.685
      }],
      nucleus: '13C',
      solvent: 'CD₃CN'
    }, {
      names: ['carbon dioxide'],
      smiles: 'O=C=O',
      ranges: [{
        signals: [{
          delta: 125.89,
          assignment: 'CO₂'
        }],
        from: 125.885,
        to: 125.895
      }],
      nucleus: '13C',
      solvent: 'CD₃CN'
    }, {
      names: ['carbon disulfide'],
      smiles: 'S=C=S',
      ranges: [{
        signals: [{
          delta: 193.6,
          assignment: 'CS₂'
        }],
        from: 193.595,
        to: 193.605
      }],
      nucleus: '13C',
      solvent: 'CD₃CN'
    }, {
      names: ['carbon tetrachloride'],
      smiles: 'ClC(Cl)(Cl)Cl',
      ranges: [{
        signals: [{
          delta: 96.68,
          assignment: 'CCl₄'
        }],
        from: 96.67500000000001,
        to: 96.685
      }],
      nucleus: '13C',
      solvent: 'CD₃CN'
    }, {
      names: ['chloroform'],
      smiles: 'ClC(Cl)Cl',
      ranges: [{
        signals: [{
          delta: 79.17,
          assignment: 'CH'
        }],
        from: 79.165,
        to: 79.175
      }],
      nucleus: '13C',
      solvent: 'CD₃CN'
    }, {
      names: ['18-crown-6'],
      smiles: 'C1COCCOCCOCCOCCOCCO1',
      ranges: [{
        signals: [{
          delta: 71.22,
          assignment: 'CH₂'
        }],
        from: 71.215,
        to: 71.225
      }],
      nucleus: '13C',
      solvent: 'CD₃CN'
    }, {
      names: ['cyclohexane'],
      smiles: 'C1CCCCC1',
      ranges: [{
        signals: [{
          delta: 27.63,
          assignment: 'CH₂'
        }],
        from: 27.625,
        to: 27.634999999999998
      }],
      nucleus: '13C',
      solvent: 'CD₃CN'
    }, {
      names: ['1,2-dichloroethane'],
      smiles: 'ClCCCl',
      ranges: [{
        signals: [{
          delta: 45.54,
          assignment: 'CH₂'
        }],
        from: 45.535,
        to: 45.545
      }],
      nucleus: '13C',
      solvent: 'CD₃CN'
    }, {
      names: ['dichloromethane'],
      smiles: 'ClCCl',
      ranges: [{
        signals: [{
          delta: 55.32,
          assignment: 'CH₂'
        }],
        from: 55.315,
        to: 55.325
      }],
      nucleus: '13C',
      solvent: 'CD₃CN'
    }, {
      names: ['diethyl ether'],
      smiles: 'CCOCC',
      ranges: [{
        signals: [{
          delta: 15.63,
          assignment: 'CH₃'
        }],
        from: 15.625,
        to: 15.635000000000002
      }, {
        signals: [{
          delta: 66.32,
          assignment: 'CH₂'
        }],
        from: 66.315,
        to: 66.32499999999999
      }],
      nucleus: '13C',
      solvent: 'CD₃CN'
    }, {
      names: ['diglyme'],
      smiles: 'COCCOCCOC',
      ranges: [{
        signals: [{
          delta: 58.9,
          assignment: 'CH₃'
        }],
        from: 58.894999999999996,
        to: 58.905
      }, {
        signals: [{
          delta: 70.99,
          assignment: 'CH₂'
        }],
        from: 70.985,
        to: 70.99499999999999
      }, {
        signals: [{
          delta: 72.63,
          assignment: 'CH₂'
        }],
        from: 72.625,
        to: 72.63499999999999
      }],
      nucleus: '13C',
      solvent: 'CD₃CN'
    }, {
      names: ['dimethylformamide'],
      smiles: 'C(=O)N(C)C',
      ranges: [{
        signals: [{
          delta: 163.31,
          assignment: 'CH'
        }],
        from: 163.305,
        to: 163.315
      }, {
        signals: [{
          delta: 36.57,
          assignment: 'CH₃'
        }],
        from: 36.565,
        to: 36.575
      }, {
        signals: [{
          delta: 31.32,
          assignment: 'CH₃'
        }],
        from: 31.315,
        to: 31.325
      }],
      nucleus: '13C',
      solvent: 'CD₃CN'
    }, {
      names: ['1,4-dioxane'],
      smiles: 'C1COCCOC1',
      ranges: [{
        signals: [{
          delta: 67.72,
          assignment: 'CH₂'
        }],
        from: 67.715,
        to: 67.725
      }],
      nucleus: '13C',
      solvent: 'CD₃CN'
    }, {
      names: ['DME', 'diethyl ether'],
      smiles: 'CCOCC',
      ranges: [{
        signals: [{
          delta: 58.89,
          assignment: 'CH₃'
        }],
        from: 58.885,
        to: 58.895
      }, {
        signals: [{
          delta: 72.47,
          assignment: 'CH₂'
        }],
        from: 72.465,
        to: 72.475
      }],
      nucleus: '13C',
      solvent: 'CD₃CN'
    }, {
      names: ['ethane'],
      smiles: 'CC',
      ranges: [{
        signals: [{
          delta: 6.99,
          assignment: 'CH₃'
        }],
        from: 6.985,
        to: 6.995
      }],
      nucleus: '13C',
      solvent: 'CD₃CN'
    }, {
      names: ['ethanol'],
      smiles: 'CCO',
      ranges: [{
        signals: [{
          delta: 18.8,
          assignment: 'CH₃'
        }],
        from: 18.795,
        to: 18.805
      }, {
        signals: [{
          delta: 57.96,
          assignment: 'CH₂'
        }],
        from: 57.955,
        to: 57.965
      }],
      nucleus: '13C',
      solvent: 'CD₃CN'
    }, {
      names: ['ethyl acetate'],
      smiles: 'CC(=O)CC',
      ranges: [{
        signals: [{
          delta: 21.16,
          assignment: 'CH₃CO'
        }],
        from: 21.155,
        to: 21.165
      }, {
        signals: [{
          delta: 171.68,
          assignment: 'CO'
        }],
        from: 171.675,
        to: 171.685
      }, {
        signals: [{
          delta: 60.98,
          assignment: 'CH₂'
        }],
        from: 60.974999999999994,
        to: 60.985
      }, {
        signals: [{
          delta: 14.54,
          assignment: 'CH₃'
        }],
        from: 14.534999999999998,
        to: 14.545
      }],
      nucleus: '13C',
      solvent: 'CD₃CN'
    }, {
      names: ['ethylene'],
      smiles: 'C=C',
      ranges: [{
        signals: [{
          delta: 123.69,
          assignment: 'CH₂'
        }],
        from: 123.685,
        to: 123.695
      }],
      nucleus: '13C',
      solvent: 'CD₃CN'
    }, {
      names: ['ethylene glycol'],
      smiles: 'OCCO',
      ranges: [{
        signals: [{
          delta: 64.22,
          assignment: 'CH₂'
        }],
        from: 64.215,
        to: 64.225
      }],
      nucleus: '13C',
      solvent: 'CD₃CN'
    }, {
      names: ['H grease'],
      smiles: '',
      ranges: [],
      nucleus: '13C',
      solvent: 'CD₃CN'
    }, {
      names: ['hexamethylbenzene'],
      smiles: 'CC1=C(C(=C(C(=C1C)C)C)C)C',
      ranges: [{
        signals: [{
          delta: 132.61,
          assignment: 'C'
        }],
        from: 132.60500000000002,
        to: 132.615
      }, {
        signals: [{
          delta: 16.94,
          assignment: 'CH₃'
        }],
        from: 16.935000000000002,
        to: 16.945
      }],
      nucleus: '13C',
      solvent: 'CD₃CN'
    }, {
      names: ['n-hexane'],
      smiles: 'CCCCCC',
      ranges: [{
        signals: [{
          delta: 14.43,
          assignment: 'CH₃'
        }],
        from: 14.424999999999999,
        to: 14.435
      }, {
        signals: [{
          delta: 23.4,
          assignment: 'CH₂(2,5)'
        }],
        from: 23.395,
        to: 23.404999999999998
      }, {
        signals: [{
          delta: 32.36,
          assignment: 'CH₂(3,4)'
        }],
        from: 32.355,
        to: 32.365
      }],
      nucleus: '13C',
      solvent: 'CD₃CN'
    }, {
      names: ['HMDSO'],
      smiles: 'C[Si](C)(C)O[Si](C)(C)C',
      ranges: [{
        signals: [{
          delta: 2.07,
          assignment: 'CH₃'
        }],
        from: 2.065,
        to: 2.0749999999999997
      }],
      nucleus: '13C',
      solvent: 'CD₃CN'
    }, {
      names: ['HMPA'],
      smiles: 'CN(C)P(=O)(N(C)C)N(C)C',
      ranges: [{
        signals: [{
          delta: 37.1,
          assignment: 'CH₃'
        }],
        from: 37.095,
        to: 37.105000000000004
      }],
      nucleus: '13C',
      solvent: 'CD₃CN'
    }, {
      names: ['imidazole'],
      smiles: 'C1=CN=CN1',
      ranges: [{
        signals: [{
          delta: 136.33,
          assignment: 'CH(2)'
        }],
        from: 136.32500000000002,
        to: 136.335
      }, {
        signals: [{
          delta: 122.78,
          assignment: 'CH(4,5)'
        }],
        from: 122.775,
        to: 122.785
      }],
      nucleus: '13C',
      solvent: 'CD₃CN'
    }, {
      names: ['methane'],
      smiles: 'C',
      ranges: [{
        signals: [{
          delta: -4.61,
          assignment: 'CH₄'
        }],
        from: -4.615,
        to: -4.605
      }],
      nucleus: '13C',
      solvent: 'CD₃CN'
    }, {
      names: ['methanol'],
      smiles: 'CO',
      ranges: [{
        signals: [{
          delta: 49.9,
          assignment: 'CH₃'
        }],
        from: 49.894999999999996,
        to: 49.905
      }],
      nucleus: '13C',
      solvent: 'CD₃CN'
    }, {
      names: ['nitromethane'],
      smiles: 'C[N+](=O)[O-]',
      ranges: [{
        signals: [{
          delta: 63.66,
          assignment: 'CH₃'
        }],
        from: 63.654999999999994,
        to: 63.665
      }],
      nucleus: '13C',
      solvent: 'CD₃CN'
    }, {
      names: ['n-pentane'],
      smiles: 'CCCCC',
      ranges: [{
        signals: [{
          delta: 14.37,
          assignment: 'CH₃'
        }],
        from: 14.364999999999998,
        to: 14.375
      }, {
        signals: [{
          delta: 23.08,
          assignment: 'CH₂(2,4)'
        }],
        from: 23.075,
        to: 23.084999999999997
      }, {
        signals: [{
          delta: 34.89,
          assignment: 'CH₂(3)'
        }],
        from: 34.885,
        to: 34.895
      }],
      nucleus: '13C',
      solvent: 'CD₃CN'
    }, {
      names: ['propane'],
      smiles: 'CCC',
      ranges: [{
        signals: [{
          delta: 16.73,
          assignment: 'CH₃'
        }],
        from: 16.725,
        to: 16.735
      }, {
        signals: [{
          delta: 16.91,
          assignment: 'CH₂'
        }],
        from: 16.905,
        to: 16.915
      }],
      nucleus: '13C',
      solvent: 'CD₃CN'
    }, {
      names: ['2-propanol'],
      smiles: 'CC(O)C',
      ranges: [{
        signals: [{
          delta: 25.55,
          assignment: 'CH₃'
        }],
        from: 25.545,
        to: 25.555
      }, {
        signals: [{
          delta: 64.3,
          assignment: 'CH'
        }],
        from: 64.295,
        to: 64.30499999999999
      }],
      nucleus: '13C',
      solvent: 'CD₃CN'
    }, {
      names: ['propylene'],
      smiles: 'CC=C',
      ranges: [{
        signals: [{
          delta: 19.48,
          assignment: 'CH₃'
        }],
        from: 19.475,
        to: 19.485
      }, {
        signals: [{
          delta: 116.12,
          assignment: 'CH₂'
        }],
        from: 116.11500000000001,
        to: 116.125
      }, {
        signals: [{
          delta: 134.78,
          assignment: 'CH'
        }],
        from: 134.775,
        to: 134.785
      }],
      nucleus: '13C',
      solvent: 'CD₃CN'
    }, {
      names: ['pyridine'],
      smiles: 'C1=CC=NC=C1',
      ranges: [{
        signals: [{
          delta: 150.76,
          assignment: 'CH(2,6)'
        }],
        from: 150.755,
        to: 150.765
      }, {
        signals: [{
          delta: 127.76,
          assignment: 'CH(3,5)'
        }],
        from: 127.75500000000001,
        to: 127.765
      }, {
        signals: [{
          delta: 136.89,
          assignment: 'CH(4)'
        }],
        from: 136.885,
        to: 136.89499999999998
      }],
      nucleus: '13C',
      solvent: 'CD₃CN'
    }, {
      names: ['pyrrole'],
      smiles: 'C1=CNC=C1',
      ranges: [{
        signals: [{
          delta: 118.47,
          assignment: 'CH(2,5)'
        }],
        from: 118.465,
        to: 118.475
      }, {
        signals: [{
          delta: 108.31,
          assignment: 'CH(3,4)'
        }],
        from: 108.305,
        to: 108.315
      }],
      nucleus: '13C',
      solvent: 'CD₃CN'
    }, {
      names: ['pyrrolidine'],
      smiles: 'C1CCNC1',
      ranges: [{
        signals: [{
          delta: 47.57,
          assignment: 'CH₂(2,5)'
        }],
        from: 47.565,
        to: 47.575
      }, {
        signals: [{
          delta: 26.34,
          assignment: 'CH₂(3,4)'
        }],
        from: 26.335,
        to: 26.345
      }],
      nucleus: '13C',
      solvent: 'CD₃CN'
    }, {
      names: ['silicone grease'],
      smiles: '',
      ranges: [],
      nucleus: '13C',
      solvent: 'CD₃CN'
    }, {
      names: ['tetrahydrofuran'],
      smiles: 'C1COCC1',
      ranges: [{
        signals: [{
          delta: 68.33,
          assignment: 'CH₂(2,5)'
        }],
        from: 68.325,
        to: 68.335
      }, {
        signals: [{
          delta: 26.27,
          assignment: 'CH₂(3,4)'
        }],
        from: 26.265,
        to: 26.275
      }],
      nucleus: '13C',
      solvent: 'CD₃CN'
    }, {
      names: ['toluene'],
      smiles: 'Cc1ccccc1',
      ranges: [{
        signals: [{
          delta: 21.5,
          assignment: 'CH₃'
        }],
        from: 21.495,
        to: 21.505
      }, {
        signals: [{
          delta: 138.9,
          assignment: 'C(1)'
        }],
        from: 138.895,
        to: 138.905
      }, {
        signals: [{
          delta: 129.94,
          assignment: 'CH(2,6)'
        }],
        from: 129.935,
        to: 129.945
      }, {
        signals: [{
          delta: 129.23,
          assignment: 'CH(3,5)'
        }],
        from: 129.225,
        to: 129.23499999999999
      }, {
        signals: [{
          delta: 126.28,
          assignment: 'CH(4)'
        }],
        from: 126.275,
        to: 126.285
      }],
      nucleus: '13C',
      solvent: 'CD₃CN'
    }, {
      names: ['triethylamine'],
      smiles: 'CCN(CC)CC',
      ranges: [{
        signals: [{
          delta: 12.38,
          assignment: 'CH₃'
        }],
        from: 12.375,
        to: 12.385000000000002
      }, {
        signals: [{
          delta: 47.1,
          assignment: 'CH₂'
        }],
        from: 47.095,
        to: 47.105000000000004
      }],
      nucleus: '13C',
      solvent: 'CD₃CN'
    }, {
      names: ['acetic acid'],
      smiles: 'CC(=O)O',
      ranges: [{
        signals: [{
          delta: 177.96,
          assignment: 'CO'
        }],
        from: 177.955,
        to: 177.965
      }, {
        signals: [{
          delta: 20.91,
          assignment: 'CH₃'
        }],
        from: 20.905,
        to: 20.915
      }],
      nucleus: '13C',
      solvent: 'TFE-d₃'
    }, {
      names: ['acetone'],
      smiles: 'CC(=O)C',
      ranges: [{
        signals: [{
          delta: 32.35,
          assignment: 'CO'
        }],
        from: 32.345,
        to: 32.355000000000004
      }, {
        signals: [{
          delta: 214.98,
          assignment: 'CH₃'
        }],
        from: 214.975,
        to: 214.98499999999999
      }],
      nucleus: '13C',
      solvent: 'TFE-d₃'
    }, {
      names: ['acetonitrile'],
      smiles: 'CC#N',
      ranges: [{
        signals: [{
          delta: 118.95,
          assignment: 'CN'
        }],
        from: 118.94500000000001,
        to: 118.955
      }, {
        signals: [{
          delta: 1,
          assignment: 'CH₃'
        }],
        from: 0.995,
        to: 1.005
      }],
      nucleus: '13C',
      solvent: 'TFE-d₃'
    }, {
      names: ['benzene'],
      smiles: 'c1ccccc1',
      ranges: [{
        signals: [{
          delta: 129.84,
          assignment: 'CH'
        }],
        from: 129.835,
        to: 129.845
      }],
      nucleus: '13C',
      solvent: 'TFE-d₃'
    }, {
      names: ['tert -butyl alcohol'],
      smiles: 'CC(C)(C)O',
      ranges: [{
        signals: [{
          delta: 72.35,
          assignment: '(CH₃)₃C'
        }],
        from: 72.345,
        to: 72.35499999999999
      }, {
        signals: [{
          delta: 31.07,
          assignment: '(CH₃)₃C'
        }],
        from: 31.065,
        to: 31.075
      }],
      nucleus: '13C',
      solvent: 'TFE-d₃'
    }, {
      names: ['carbon dioxide'],
      smiles: 'O=C=O',
      ranges: [{
        signals: [{
          delta: 126.92,
          assignment: 'CO₂'
        }],
        from: 126.915,
        to: 126.925
      }],
      nucleus: '13C',
      solvent: 'TFE-d₃'
    }, {
      names: ['carbon disulfide'],
      smiles: 'S=C=S',
      ranges: [{
        signals: [{
          delta: 196.26,
          assignment: 'CS₂'
        }],
        from: 196.255,
        to: 196.265
      }],
      nucleus: '13C',
      solvent: 'TFE-d₃'
    }, {
      names: ['carbon tetrachloride'],
      smiles: 'ClC(Cl)(Cl)Cl',
      ranges: [{
        signals: [{
          delta: 97.74,
          assignment: 'CCl₄'
        }],
        from: 97.735,
        to: 97.74499999999999
      }],
      nucleus: '13C',
      solvent: 'TFE-d₃'
    }, {
      names: ['chloroform'],
      smiles: 'ClC(Cl)Cl',
      ranges: [{
        signals: [{
          delta: 78.83,
          assignment: 'CH'
        }],
        from: 78.825,
        to: 78.835
      }],
      nucleus: '13C',
      solvent: 'TFE-d₃'
    }, {
      names: ['18-crown-6'],
      smiles: 'C1COCCOCCOCCOCCOCCO1',
      ranges: [{
        signals: [{
          delta: 70.8,
          assignment: 'CH₂'
        }],
        from: 70.795,
        to: 70.80499999999999
      }],
      nucleus: '13C',
      solvent: 'TFE-d₃'
    }, {
      names: ['cyclohexane'],
      smiles: 'C1CCCCC1',
      ranges: [{
        signals: [{
          delta: 28.34,
          assignment: 'CH₂'
        }],
        from: 28.335,
        to: 28.345
      }],
      nucleus: '13C',
      solvent: 'TFE-d₃'
    }, {
      names: ['1,2-dichloroethane'],
      smiles: 'ClCCCl',
      ranges: [{
        signals: [{
          delta: 45.28,
          assignment: 'CH₂'
        }],
        from: 45.275,
        to: 45.285000000000004
      }],
      nucleus: '13C',
      solvent: 'TFE-d₃'
    }, {
      names: ['dichloromethane'],
      smiles: 'ClCCl',
      ranges: [{
        signals: [{
          delta: 54.46,
          assignment: 'CH₂'
        }],
        from: 54.455,
        to: 54.465
      }],
      nucleus: '13C',
      solvent: 'TFE-d₃'
    }, {
      names: ['diethyl ether'],
      smiles: 'CCOCC',
      ranges: [{
        signals: [{
          delta: 15.33,
          assignment: 'CH₃'
        }],
        from: 15.325,
        to: 15.335
      }, {
        signals: [{
          delta: 67.55,
          assignment: 'CH₂'
        }],
        from: 67.545,
        to: 67.55499999999999
      }],
      nucleus: '13C',
      solvent: 'TFE-d₃'
    }, {
      names: ['diglyme'],
      smiles: 'COCCOCCOC',
      ranges: [{
        signals: [{
          delta: 59.4,
          assignment: 'CH₃'
        }],
        from: 59.394999999999996,
        to: 59.405
      }, {
        signals: [{
          delta: 73.05,
          assignment: 'CH₂'
        }],
        from: 73.045,
        to: 73.05499999999999
      }, {
        signals: [{
          delta: 71.33,
          assignment: 'CH₂'
        }],
        from: 71.325,
        to: 71.335
      }],
      nucleus: '13C',
      solvent: 'TFE-d₃'
    }, {
      names: ['dimethylformamide'],
      smiles: 'C(=O)N(C)C',
      ranges: [{
        signals: [{
          delta: 166.01,
          assignment: 'CH'
        }],
        from: 166.005,
        to: 166.015
      }, {
        signals: [{
          delta: 37.76,
          assignment: 'CH₃'
        }],
        from: 37.754999999999995,
        to: 37.765
      }, {
        signals: [{
          delta: 30.96,
          assignment: 'CH₃'
        }],
        from: 30.955000000000002,
        to: 30.965
      }],
      nucleus: '13C',
      solvent: 'TFE-d₃'
    }, {
      names: ['1,4-dioxane'],
      smiles: 'C1COCCOC1',
      ranges: [{
        signals: [{
          delta: 68.52,
          assignment: 'CH₂'
        }],
        from: 68.515,
        to: 68.52499999999999
      }],
      nucleus: '13C',
      solvent: 'TFE-d₃'
    }, {
      names: ['DME', 'diethyl ether'],
      smiles: 'CCOCC',
      ranges: [{
        signals: [{
          delta: 59.52,
          assignment: 'CH₃'
        }],
        from: 59.515,
        to: 59.525000000000006
      }, {
        signals: [{
          delta: 72.87,
          assignment: 'CH₂'
        }],
        from: 72.86500000000001,
        to: 72.875
      }],
      nucleus: '13C',
      solvent: 'TFE-d₃'
    }, {
      names: ['ethane'],
      smiles: 'CC',
      ranges: [{
        signals: [{
          delta: 7.01,
          assignment: 'CH₃'
        }],
        from: 7.005,
        to: 7.015
      }],
      nucleus: '13C',
      solvent: 'TFE-d₃'
    }, {
      names: ['ethanol'],
      smiles: 'CCO',
      ranges: [{
        signals: [{
          delta: 18.11,
          assignment: 'CH₃'
        }],
        from: 18.105,
        to: 18.115
      }, {
        signals: [{
          delta: 59.68,
          assignment: 'CH₂'
        }],
        from: 59.675,
        to: 59.685
      }],
      nucleus: '13C',
      solvent: 'TFE-d₃'
    }, {
      names: ['ethyl acetate'],
      smiles: 'CC(=O)CC',
      ranges: [{
        signals: [{
          delta: 21.18,
          assignment: 'CH₃CO'
        }],
        from: 21.175,
        to: 21.185
      }, {
        signals: [{
          delta: 175.55,
          assignment: 'CO'
        }],
        from: 175.54500000000002,
        to: 175.555
      }, {
        signals: [{
          delta: 62.7,
          assignment: 'CH₂'
        }],
        from: 62.695,
        to: 62.705000000000005
      }, {
        signals: [{
          delta: 14.36,
          assignment: 'CH₃'
        }],
        from: 14.354999999999999,
        to: 14.365
      }],
      nucleus: '13C',
      solvent: 'TFE-d₃'
    }, {
      names: ['ethylene'],
      smiles: 'C=C',
      ranges: [{
        signals: [{
          delta: 124.08,
          assignment: 'CH₂'
        }],
        from: 124.075,
        to: 124.085
      }],
      nucleus: '13C',
      solvent: 'TFE-d₃'
    }, {
      names: ['ethylene glycol'],
      smiles: 'OCCO',
      ranges: [{
        signals: [{
          delta: 64.87,
          assignment: 'CH₂'
        }],
        from: 64.86500000000001,
        to: 64.875
      }],
      nucleus: '13C',
      solvent: 'TFE-d₃'
    }, {
      names: ['H grease'],
      smiles: '',
      ranges: [],
      nucleus: '13C',
      solvent: 'TFE-d₃'
    }, {
      names: ['hexamethylbenzene'],
      smiles: 'CC1=C(C(=C(C(=C1C)C)C)C)C',
      ranges: [{
        signals: [{
          delta: 134.04,
          assignment: 'C'
        }],
        from: 134.035,
        to: 134.045
      }, {
        signals: [{
          delta: 17.04,
          assignment: 'CH₃'
        }],
        from: 17.035,
        to: 17.044999999999998
      }],
      nucleus: '13C',
      solvent: 'TFE-d₃'
    }, {
      names: ['n-hexane'],
      smiles: 'CCCCCC',
      ranges: [{
        signals: [{
          delta: 14.63,
          assignment: 'CH₃'
        }],
        from: 14.625,
        to: 14.635000000000002
      }, {
        signals: [{
          delta: 24.06,
          assignment: 'CH₂(2,5)'
        }],
        from: 24.055,
        to: 24.064999999999998
      }, {
        signals: [{
          delta: 33.17,
          assignment: 'CH₂(3,4)'
        }],
        from: 33.165,
        to: 33.175000000000004
      }],
      nucleus: '13C',
      solvent: 'TFE-d₃'
    }, {
      names: ['HMDSO'],
      smiles: 'C[Si](C)(C)O[Si](C)(C)C',
      ranges: [{
        signals: [{
          delta: 2.09,
          assignment: 'CH₃'
        }],
        from: 2.085,
        to: 2.0949999999999998
      }],
      nucleus: '13C',
      solvent: 'TFE-d₃'
    }, {
      names: ['HMPA'],
      smiles: 'CN(C)P(=O)(N(C)C)N(C)C',
      ranges: [{
        signals: [{
          delta: 37.21,
          assignment: 'CH₃'
        }],
        from: 37.205,
        to: 37.215
      }],
      nucleus: '13C',
      solvent: 'TFE-d₃'
    }, {
      names: ['imidazole'],
      smiles: 'C1=CN=CN1',
      ranges: [{
        signals: [{
          delta: 136.58,
          assignment: 'CH(2)'
        }],
        from: 136.57500000000002,
        to: 136.585
      }, {
        signals: [{
          delta: 122.93,
          assignment: 'CH(4,5)'
        }],
        from: 122.92500000000001,
        to: 122.935
      }],
      nucleus: '13C',
      solvent: 'TFE-d₃'
    }, {
      names: ['methane'],
      smiles: 'C',
      ranges: [{
        signals: [{
          delta: -5.88,
          assignment: 'CH₄'
        }],
        from: -5.885,
        to: -5.875
      }],
      nucleus: '13C',
      solvent: 'TFE-d₃'
    }, {
      names: ['methanol'],
      smiles: 'CO',
      ranges: [{
        signals: [{
          delta: 50.67,
          assignment: 'CH₃'
        }],
        from: 50.665,
        to: 50.675000000000004
      }],
      nucleus: '13C',
      solvent: 'TFE-d₃'
    }, {
      names: ['nitromethane'],
      smiles: 'C[N+](=O)[O-]',
      ranges: [{
        signals: [{
          delta: 63.17,
          assignment: 'CH₃'
        }],
        from: 63.165,
        to: 63.175000000000004
      }],
      nucleus: '13C',
      solvent: 'TFE-d₃'
    }, {
      names: ['n-pentane'],
      smiles: 'CCCCC',
      ranges: [{
        signals: [{
          delta: 14.54,
          assignment: 'CH₃'
        }],
        from: 14.534999999999998,
        to: 14.545
      }, {
        signals: [{
          delta: 23.75,
          assignment: 'CH₂(2,4)'
        }],
        from: 23.745,
        to: 23.755
      }, {
        signals: [{
          delta: 35.76,
          assignment: 'CH₂(3)'
        }],
        from: 35.754999999999995,
        to: 35.765
      }],
      nucleus: '13C',
      solvent: 'TFE-d₃'
    }, {
      names: ['propane'],
      smiles: 'CCC',
      ranges: [{
        signals: [{
          delta: 16.93,
          assignment: 'CH₃'
        }],
        from: 16.925,
        to: 16.935
      }, {
        signals: [{
          delta: 17.46,
          assignment: 'CH₂'
        }],
        from: 17.455000000000002,
        to: 17.465
      }],
      nucleus: '13C',
      solvent: 'TFE-d₃'
    }, {
      names: ['2-propanol'],
      smiles: 'CC(O)C',
      ranges: [{
        signals: [{
          delta: 25.21,
          assignment: 'CH₃'
        }],
        from: 25.205000000000002,
        to: 25.215
      }, {
        signals: [{
          delta: 66.69,
          assignment: 'CH'
        }],
        from: 66.685,
        to: 66.695
      }],
      nucleus: '13C',
      solvent: 'TFE-d₃'
    }, {
      names: ['propylene'],
      smiles: 'CC=C',
      ranges: [{
        signals: [{
          delta: 19.63,
          assignment: 'CH₃'
        }],
        from: 19.625,
        to: 19.634999999999998
      }, {
        signals: [{
          delta: 116.38,
          assignment: 'CH₂'
        }],
        from: 116.375,
        to: 116.38499999999999
      }, {
        signals: [{
          delta: 136,
          assignment: 'CH'
        }],
        from: 135.995,
        to: 136.005
      }],
      nucleus: '13C',
      solvent: 'TFE-d₃'
    }, {
      names: ['pyridine'],
      smiles: 'C1=CC=NC=C1',
      ranges: [{
        signals: [{
          delta: 149.76,
          assignment: 'CH(2,6)'
        }],
        from: 149.755,
        to: 149.765
      }, {
        signals: [{
          delta: 126.27,
          assignment: 'CH(3,5)'
        }],
        from: 126.265,
        to: 126.27499999999999
      }, {
        signals: [{
          delta: 139.62,
          assignment: 'CH(4)'
        }],
        from: 139.615,
        to: 139.625
      }],
      nucleus: '13C',
      solvent: 'TFE-d₃'
    }, {
      names: ['pyrrole'],
      smiles: 'C1=CNC=C1',
      ranges: [{
        signals: [{
          delta: 119.61,
          assignment: 'CH(2,5)'
        }],
        from: 119.605,
        to: 119.615
      }, {
        signals: [{
          delta: 108.85,
          assignment: 'CH(3,4)'
        }],
        from: 108.845,
        to: 108.85499999999999
      }],
      nucleus: '13C',
      solvent: 'TFE-d₃'
    }, {
      names: ['pyrrolidine'],
      smiles: 'C1CCNC1',
      ranges: [{
        signals: [{
          delta: 47.43,
          assignment: 'CH₂(2,5)'
        }],
        from: 47.425,
        to: 47.435
      }, {
        signals: [{
          delta: 25.73,
          assignment: 'CH₂(3,4)'
        }],
        from: 25.725,
        to: 25.735
      }],
      nucleus: '13C',
      solvent: 'TFE-d₃'
    }, {
      names: ['silicone grease'],
      smiles: '',
      ranges: [{
        signals: [{
          delta: 2.87,
          assignment: 'CH₃'
        }],
        from: 2.865,
        to: 2.875
      }],
      nucleus: '13C',
      solvent: 'TFE-d₃'
    }, {
      names: ['tetrahydrofuran'],
      smiles: 'C1COCC1',
      ranges: [{
        signals: [{
          delta: 69.53,
          assignment: 'CH₂(2,5)'
        }],
        from: 69.525,
        to: 69.535
      }, {
        signals: [{
          delta: 26.69,
          assignment: 'CH₂(3,4)'
        }],
        from: 26.685000000000002,
        to: 26.695
      }],
      nucleus: '13C',
      solvent: 'TFE-d₃'
    }, {
      names: ['toluene'],
      smiles: 'Cc1ccccc1',
      ranges: [{
        signals: [{
          delta: 21.62,
          assignment: 'CH₃'
        }],
        from: 21.615000000000002,
        to: 21.625
      }, {
        signals: [{
          delta: 139.92,
          assignment: 'C(1)'
        }],
        from: 139.915,
        to: 139.92499999999998
      }, {
        signals: [{
          delta: 130.58,
          assignment: 'CH(2,6)'
        }],
        from: 130.57500000000002,
        to: 130.585
      }, {
        signals: [{
          delta: 129.79,
          assignment: 'CH(3,5)'
        }],
        from: 129.785,
        to: 129.795
      }, {
        signals: [{
          delta: 126.82,
          assignment: 'CH(4)'
        }],
        from: 126.815,
        to: 126.82499999999999
      }],
      nucleus: '13C',
      solvent: 'TFE-d₃'
    }, {
      names: ['triethylamine'],
      smiles: 'CCN(CC)CC',
      ranges: [{
        signals: [{
          delta: 9.51,
          assignment: 'CH₃'
        }],
        from: 9.504999999999999,
        to: 9.515
      }, {
        signals: [{
          delta: 48.45,
          assignment: 'CH₂'
        }],
        from: 48.445,
        to: 48.455000000000005
      }],
      nucleus: '13C',
      solvent: 'TFE-d₃'
    }, {
      names: ['acetic acid'],
      smiles: 'CC(=O)O',
      ranges: [{
        signals: [{
          delta: 175.11,
          assignment: 'CO'
        }],
        from: 175.10500000000002,
        to: 175.115
      }, {
        signals: [{
          delta: 20.56,
          assignment: 'CH₃'
        }],
        from: 20.555,
        to: 20.564999999999998
      }],
      nucleus: '13C',
      solvent: 'CD₃OD'
    }, {
      names: ['acetone'],
      smiles: 'CC(=O)C',
      ranges: [{
        signals: [{
          delta: 209.67,
          assignment: 'CO'
        }],
        from: 209.665,
        to: 209.67499999999998
      }, {
        signals: [{
          delta: 30.67,
          assignment: 'CH₃'
        }],
        from: 30.665000000000003,
        to: 30.675
      }],
      nucleus: '13C',
      solvent: 'CD₃OD'
    }, {
      names: ['acetonitrile'],
      smiles: 'CC#N',
      ranges: [{
        signals: [{
          delta: 118.06,
          assignment: 'CN'
        }],
        from: 118.055,
        to: 118.065
      }, {
        signals: [{
          delta: 0.85,
          assignment: 'CH₃'
        }],
        from: 0.845,
        to: 0.855
      }],
      nucleus: '13C',
      solvent: 'CD₃OD'
    }, {
      names: ['benzene'],
      smiles: 'c1ccccc1',
      ranges: [{
        signals: [{
          delta: 129.34,
          assignment: 'CH'
        }],
        from: 129.335,
        to: 129.345
      }],
      nucleus: '13C',
      solvent: 'CD₃OD'
    }, {
      names: ['tert -butyl alcohol'],
      smiles: 'CC(C)(C)O',
      ranges: [{
        signals: [{
          delta: 69.4,
          assignment: '(CH₃)₃C'
        }],
        from: 69.39500000000001,
        to: 69.405
      }, {
        signals: [{
          delta: 30.91,
          assignment: '(CH₃)₃C'
        }],
        from: 30.905,
        to: 30.915
      }],
      nucleus: '13C',
      solvent: 'CD₃OD'
    }, {
      names: ['carbon dioxide'],
      smiles: 'O=C=O',
      ranges: [{
        signals: [{
          delta: 126.31,
          assignment: 'CO₂'
        }],
        from: 126.305,
        to: 126.315
      }],
      nucleus: '13C',
      solvent: 'CD₃OD'
    }, {
      names: ['carbon disulfide'],
      smiles: 'S=C=S',
      ranges: [{
        signals: [{
          delta: 193.82,
          assignment: 'CS₂'
        }],
        from: 193.815,
        to: 193.825
      }],
      nucleus: '13C',
      solvent: 'CD₃OD'
    }, {
      names: ['carbon tetrachloride'],
      smiles: 'ClC(Cl)(Cl)Cl',
      ranges: [{
        signals: [{
          delta: 97.21,
          assignment: 'CCl₄'
        }],
        from: 97.205,
        to: 97.21499999999999
      }],
      nucleus: '13C',
      solvent: 'CD₃OD'
    }, {
      names: ['chloroform'],
      smiles: 'ClC(Cl)Cl',
      ranges: [{
        signals: [{
          delta: 79.44,
          assignment: 'CH'
        }],
        from: 79.435,
        to: 79.445
      }],
      nucleus: '13C',
      solvent: 'CD₃OD'
    }, {
      names: ['18-crown-6'],
      smiles: 'C1COCCOCCOCCOCCOCCO1',
      ranges: [{
        signals: [{
          delta: 71.47,
          assignment: 'CH₂'
        }],
        from: 71.465,
        to: 71.475
      }],
      nucleus: '13C',
      solvent: 'CD₃OD'
    }, {
      names: ['cyclohexane'],
      smiles: 'C1CCCCC1',
      ranges: [{
        signals: [{
          delta: 27.96,
          assignment: 'CH₂'
        }],
        from: 27.955000000000002,
        to: 27.965
      }],
      nucleus: '13C',
      solvent: 'CD₃OD'
    }, {
      names: ['1,2-dichloroethane'],
      smiles: 'ClCCCl',
      ranges: [{
        signals: [{
          delta: 45.11,
          assignment: 'CH₂'
        }],
        from: 45.105,
        to: 45.115
      }],
      nucleus: '13C',
      solvent: 'CD₃OD'
    }, {
      names: ['dichloromethane'],
      smiles: 'ClCCl',
      ranges: [{
        signals: [{
          delta: 54.78,
          assignment: 'CH₂'
        }],
        from: 54.775,
        to: 54.785000000000004
      }],
      nucleus: '13C',
      solvent: 'CD₃OD'
    }, {
      names: ['diethyl ether'],
      smiles: 'CCOCC',
      ranges: [{
        signals: [{
          delta: 15.46,
          assignment: 'CH₃'
        }],
        from: 15.455,
        to: 15.465000000000002
      }, {
        signals: [{
          delta: 66.88,
          assignment: 'CH₂'
        }],
        from: 66.875,
        to: 66.88499999999999
      }],
      nucleus: '13C',
      solvent: 'CD₃OD'
    }, {
      names: ['diglyme'],
      smiles: 'COCCOCCOC',
      ranges: [{
        signals: [{
          delta: 59.06,
          assignment: 'CH₃'
        }],
        from: 59.055,
        to: 59.065000000000005
      }, {
        signals: [{
          delta: 71.33,
          assignment: 'CH₂'
        }],
        from: 71.325,
        to: 71.335
      }, {
        signals: [{
          delta: 72.92,
          assignment: 'CH₂'
        }],
        from: 72.915,
        to: 72.925
      }],
      nucleus: '13C',
      solvent: 'CD₃OD'
    }, {
      names: ['dimethylformamide'],
      smiles: 'C(=O)N(C)C',
      ranges: [{
        signals: [{
          delta: 164.73,
          assignment: 'CH'
        }],
        from: 164.725,
        to: 164.73499999999999
      }, {
        signals: [{
          delta: 36.89,
          assignment: 'CH₃'
        }],
        from: 36.885,
        to: 36.895
      }, {
        signals: [{
          delta: 31.61,
          assignment: 'CH₃'
        }],
        from: 31.605,
        to: 31.615
      }],
      nucleus: '13C',
      solvent: 'CD₃OD'
    }, {
      names: ['1,4-dioxane'],
      smiles: 'C1COCCOC1',
      ranges: [{
        signals: [{
          delta: 68.11,
          assignment: 'CH₂'
        }],
        from: 68.105,
        to: 68.115
      }],
      nucleus: '13C',
      solvent: 'CD₃OD'
    }, {
      names: ['DME', 'diethyl ether'],
      smiles: 'CCOCC',
      ranges: [{
        signals: [{
          delta: 59.06,
          assignment: 'CH₃'
        }],
        from: 59.055,
        to: 59.065000000000005
      }, {
        signals: [{
          delta: 72.72,
          assignment: 'CH₂'
        }],
        from: 72.715,
        to: 72.725
      }],
      nucleus: '13C',
      solvent: 'CD₃OD'
    }, {
      names: ['ethane'],
      smiles: 'CC',
      ranges: [{
        signals: [{
          delta: 6.98,
          assignment: 'CH₃'
        }],
        from: 6.9750000000000005,
        to: 6.985
      }],
      nucleus: '13C',
      solvent: 'CD₃OD'
    }, {
      names: ['ethanol'],
      smiles: 'CCO',
      ranges: [{
        signals: [{
          delta: 18.4,
          assignment: 'CH₃'
        }],
        from: 18.395,
        to: 18.404999999999998
      }, {
        signals: [{
          delta: 58.26,
          assignment: 'CH₂'
        }],
        from: 58.254999999999995,
        to: 58.265
      }],
      nucleus: '13C',
      solvent: 'CD₃OD'
    }, {
      names: ['ethyl acetate'],
      smiles: 'CC(=O)CC',
      ranges: [{
        signals: [{
          delta: 20.88,
          assignment: 'CH₃CO'
        }],
        from: 20.875,
        to: 20.884999999999998
      }, {
        signals: [{
          delta: 172.89,
          assignment: 'CO'
        }],
        from: 172.885,
        to: 172.89499999999998
      }, {
        signals: [{
          delta: 61.5,
          assignment: 'CH₂'
        }],
        from: 61.495,
        to: 61.505
      }, {
        signals: [{
          delta: 14.49,
          assignment: 'CH₃'
        }],
        from: 14.485,
        to: 14.495000000000001
      }],
      nucleus: '13C',
      solvent: 'CD₃OD'
    }, {
      names: ['ethylene'],
      smiles: 'C=C',
      ranges: [{
        signals: [{
          delta: 123.46,
          assignment: 'CH₂'
        }],
        from: 123.455,
        to: 123.46499999999999
      }],
      nucleus: '13C',
      solvent: 'CD₃OD'
    }, {
      names: ['ethylene glycol'],
      smiles: 'OCCO',
      ranges: [{
        signals: [{
          delta: 64.3,
          assignment: 'CH₂'
        }],
        from: 64.295,
        to: 64.30499999999999
      }],
      nucleus: '13C',
      solvent: 'CD₃OD'
    }, {
      names: ['H grease'],
      smiles: '',
      ranges: [],
      nucleus: '13C',
      solvent: 'CD₃OD'
    }, {
      names: ['hexamethylbenzene'],
      smiles: 'CC1=C(C(=C(C(=C1C)C)C)C)C',
      ranges: [{
        signals: [{
          delta: 132.53,
          assignment: 'C'
        }],
        from: 132.525,
        to: 132.535
      }, {
        signals: [{
          delta: 16.9,
          assignment: 'CH₃'
        }],
        from: 16.895,
        to: 16.904999999999998
      }],
      nucleus: '13C',
      solvent: 'CD₃OD'
    }, {
      names: ['n-hexane'],
      smiles: 'CCCCCC',
      ranges: [{
        signals: [{
          delta: 14.45,
          assignment: 'CH₃'
        }],
        from: 14.444999999999999,
        to: 14.455
      }, {
        signals: [{
          delta: 23.68,
          assignment: 'CH₂(2,5)'
        }],
        from: 23.675,
        to: 23.685
      }, {
        signals: [{
          delta: 32.73,
          assignment: 'CH₂(3,4)'
        }],
        from: 32.724999999999994,
        to: 32.735
      }],
      nucleus: '13C',
      solvent: 'CD₃OD'
    }, {
      names: ['HMDSO'],
      smiles: 'C[Si](C)(C)O[Si](C)(C)C',
      ranges: [{
        signals: [{
          delta: 1.99,
          assignment: 'CH₃'
        }],
        from: 1.985,
        to: 1.9949999999999999
      }],
      nucleus: '13C',
      solvent: 'CD₃OD'
    }, {
      names: ['HMPA'],
      smiles: 'CN(C)P(=O)(N(C)C)N(C)C',
      ranges: [{
        signals: [{
          delta: 37,
          assignment: 'CH₃'
        }],
        from: 36.995,
        to: 37.005
      }],
      nucleus: '13C',
      solvent: 'CD₃OD'
    }, {
      names: ['imidazole'],
      smiles: 'C1=CN=CN1',
      ranges: [{
        signals: [{
          delta: 136.31,
          assignment: 'CH(2)'
        }],
        from: 136.305,
        to: 136.315
      }, {
        signals: [{
          delta: 122.6,
          assignment: 'CH(4,5)'
        }],
        from: 122.595,
        to: 122.60499999999999
      }],
      nucleus: '13C',
      solvent: 'CD₃OD'
    }, {
      names: ['methane'],
      smiles: 'C',
      ranges: [{
        signals: [{
          delta: -4.9,
          assignment: 'CH₄'
        }],
        from: -4.905,
        to: -4.8950000000000005
      }],
      nucleus: '13C',
      solvent: 'CD₃OD'
    }, {
      names: ['methanol'],
      smiles: 'CO',
      ranges: [{
        signals: [{
          delta: 49.86,
          assignment: 'CH₃'
        }],
        from: 49.855,
        to: 49.865
      }],
      nucleus: '13C',
      solvent: 'CD₃OD'
    }, {
      names: ['nitromethane'],
      smiles: 'C[N+](=O)[O-]',
      ranges: [{
        signals: [{
          delta: 63.08,
          assignment: 'CH₃'
        }],
        from: 63.074999999999996,
        to: 63.085
      }],
      nucleus: '13C',
      solvent: 'CD₃OD'
    }, {
      names: ['n-pentane'],
      smiles: 'CCCCC',
      ranges: [{
        signals: [{
          delta: 14.39,
          assignment: 'CH₃'
        }],
        from: 14.385,
        to: 14.395000000000001
      }, {
        signals: [{
          delta: 23.38,
          assignment: 'CH₂(2,4)'
        }],
        from: 23.375,
        to: 23.384999999999998
      }, {
        signals: [{
          delta: 35.3,
          assignment: 'CH₂(3)'
        }],
        from: 35.294999999999995,
        to: 35.305
      }],
      nucleus: '13C',
      solvent: 'CD₃OD'
    }, {
      names: ['propane'],
      smiles: 'CCC',
      ranges: [{
        signals: [{
          delta: 16.8,
          assignment: 'CH₃'
        }],
        from: 16.795,
        to: 16.805
      }, {
        signals: [{
          delta: 17.19,
          assignment: 'CH₂'
        }],
        from: 17.185000000000002,
        to: 17.195
      }],
      nucleus: '13C',
      solvent: 'CD₃OD'
    }, {
      names: ['2-propanol'],
      smiles: 'CC(O)C',
      ranges: [{
        signals: [{
          delta: 25.27,
          assignment: 'CH₃'
        }],
        from: 25.265,
        to: 25.275
      }, {
        signals: [{
          delta: 64.71,
          assignment: 'CH'
        }],
        from: 64.705,
        to: 64.71499999999999
      }],
      nucleus: '13C',
      solvent: 'CD₃OD'
    }, {
      names: ['propylene'],
      smiles: 'CC=C',
      ranges: [{
        signals: [{
          delta: 19.5,
          assignment: 'CH₃'
        }],
        from: 19.495,
        to: 19.505
      }, {
        signals: [{
          delta: 116.04,
          assignment: 'CH₂'
        }],
        from: 116.03500000000001,
        to: 116.045
      }, {
        signals: [{
          delta: 134.61,
          assignment: 'CH'
        }],
        from: 134.60500000000002,
        to: 134.615
      }],
      nucleus: '13C',
      solvent: 'CD₃OD'
    }, {
      names: ['pyridine'],
      smiles: 'C1=CC=NC=C1',
      ranges: [{
        signals: [{
          delta: 150.07,
          assignment: 'CH(2,6)'
        }],
        from: 150.065,
        to: 150.075
      }, {
        signals: [{
          delta: 125.53,
          assignment: 'CH(3,5)'
        }],
        from: 125.525,
        to: 125.535
      }, {
        signals: [{
          delta: 138.35,
          assignment: 'CH(4)'
        }],
        from: 138.345,
        to: 138.355
      }],
      nucleus: '13C',
      solvent: 'CD₃OD'
    }, {
      names: ['pyrrole'],
      smiles: 'C1=CNC=C1',
      ranges: [{
        signals: [{
          delta: 118.28,
          assignment: 'CH(2,5)'
        }],
        from: 118.275,
        to: 118.285
      }, {
        signals: [{
          delta: 108.11,
          assignment: 'CH(3,4)'
        }],
        from: 108.105,
        to: 108.115
      }],
      nucleus: '13C',
      solvent: 'CD₃OD'
    }, {
      names: ['pyrrolidine'],
      smiles: 'C1CCNC1',
      ranges: [{
        signals: [{
          delta: 47.23,
          assignment: 'CH₂(2,5)'
        }],
        from: 47.224999999999994,
        to: 47.235
      }, {
        signals: [{
          delta: 26.29,
          assignment: 'CH₂(3,4)'
        }],
        from: 26.285,
        to: 26.294999999999998
      }],
      nucleus: '13C',
      solvent: 'CD₃OD'
    }, {
      names: ['silicone grease'],
      smiles: '',
      ranges: [{
        signals: [{
          delta: 2.1,
          assignment: 'CH₃'
        }],
        from: 2.095,
        to: 2.105
      }],
      nucleus: '13C',
      solvent: 'CD₃OD'
    }, {
      names: ['tetrahydrofuran'],
      smiles: 'C1COCC1',
      ranges: [{
        signals: [{
          delta: 68.83,
          assignment: 'CH₂(2,5)'
        }],
        from: 68.825,
        to: 68.835
      }, {
        signals: [{
          delta: 26.48,
          assignment: 'CH₂(3,4)'
        }],
        from: 26.475,
        to: 26.485
      }],
      nucleus: '13C',
      solvent: 'CD₃OD'
    }, {
      names: ['toluene'],
      smiles: 'Cc1ccccc1',
      ranges: [{
        signals: [{
          delta: 21.5,
          assignment: 'CH₃'
        }],
        from: 21.495,
        to: 21.505
      }, {
        signals: [{
          delta: 138.85,
          assignment: 'C(1)'
        }],
        from: 138.845,
        to: 138.855
      }, {
        signals: [{
          delta: 129.91,
          assignment: 'CH(2,6)'
        }],
        from: 129.905,
        to: 129.915
      }, {
        signals: [{
          delta: 129.2,
          assignment: 'CH(3,5)'
        }],
        from: 129.195,
        to: 129.20499999999998
      }, {
        signals: [{
          delta: 126.29,
          assignment: 'CH(4)'
        }],
        from: 126.28500000000001,
        to: 126.295
      }],
      nucleus: '13C',
      solvent: 'CD₃OD'
    }, {
      names: ['triethylamine'],
      smiles: 'CCN(CC)CC',
      ranges: [{
        signals: [{
          delta: 11.09,
          assignment: 'CH₃'
        }],
        from: 11.084999999999999,
        to: 11.095
      }, {
        signals: [{
          delta: 46.96,
          assignment: 'CH₂'
        }],
        from: 46.955,
        to: 46.965
      }],
      nucleus: '13C',
      solvent: 'CD₃OD'
    }, {
      names: ['acetic acid'],
      smiles: 'CC(=O)O',
      ranges: [{
        signals: [{
          delta: 177.21,
          assignment: 'CO'
        }],
        from: 177.205,
        to: 177.215
      }, {
        signals: [{
          delta: 21.03,
          assignment: 'CH₃'
        }],
        from: 21.025000000000002,
        to: 21.035
      }],
      nucleus: '13C',
      solvent: 'D₂O'
    }, {
      names: ['acetone'],
      smiles: 'CC(=O)C',
      ranges: [{
        signals: [{
          delta: 215.94,
          assignment: 'CO'
        }],
        from: 215.935,
        to: 215.945
      }, {
        signals: [{
          delta: 30.89,
          assignment: 'CH₃'
        }],
        from: 30.885,
        to: 30.895
      }],
      nucleus: '13C',
      solvent: 'D₂O'
    }, {
      names: ['acetonitrile'],
      smiles: 'CC#N',
      ranges: [{
        signals: [{
          delta: 119.68,
          assignment: 'CN'
        }],
        from: 119.67500000000001,
        to: 119.685
      }, {
        signals: [{
          delta: 1.47,
          assignment: 'CH₃'
        }],
        from: 1.465,
        to: 1.4749999999999999
      }],
      nucleus: '13C',
      solvent: 'D₂O'
    }, {
      names: ['benzene'],
      smiles: 'c1ccccc1',
      ranges: [],
      nucleus: '13C',
      solvent: 'D₂O'
    }, {
      names: ['tert -butyl alcohol'],
      smiles: 'CC(C)(C)O',
      ranges: [{
        signals: [{
          delta: 70.36,
          assignment: '(CH₃)₃C'
        }],
        from: 70.355,
        to: 70.365
      }, {
        signals: [{
          delta: 30.29,
          assignment: '(CH₃)₃C'
        }],
        from: 30.285,
        to: 30.294999999999998
      }],
      nucleus: '13C',
      solvent: 'D₂O'
    }, {
      names: ['carbon dioxide'],
      smiles: 'O=C=O',
      ranges: [],
      nucleus: '13C',
      solvent: 'D₂O'
    }, {
      names: ['carbon disulfide'],
      smiles: 'S=C=S',
      ranges: [{
        signals: [{
          delta: 197.25,
          assignment: 'CS₂'
        }],
        from: 197.245,
        to: 197.255
      }],
      nucleus: '13C',
      solvent: 'D₂O'
    }, {
      names: ['carbon tetrachloride'],
      smiles: 'ClC(Cl)(Cl)Cl',
      ranges: [{
        signals: [{
          delta: 96.73,
          assignment: 'CCl₄'
        }],
        from: 96.72500000000001,
        to: 96.735
      }],
      nucleus: '13C',
      solvent: 'D₂O'
    }, {
      names: ['chloroform'],
      smiles: 'ClC(Cl)Cl',
      ranges: [],
      nucleus: '13C',
      solvent: 'D₂O'
    }, {
      names: ['18-crown-6'],
      smiles: 'C1COCCOCCOCCOCCOCCO1',
      ranges: [{
        signals: [{
          delta: 70.14,
          assignment: 'CH₂'
        }],
        from: 70.135,
        to: 70.145
      }],
      nucleus: '13C',
      solvent: 'D₂O'
    }, {
      names: ['cyclohexane'],
      smiles: 'C1CCCCC1',
      ranges: [],
      nucleus: '13C',
      solvent: 'D₂O'
    }, {
      names: ['1,2-dichloroethane'],
      smiles: 'ClCCCl',
      ranges: [],
      nucleus: '13C',
      solvent: 'D₂O'
    }, {
      names: ['dichloromethane'],
      smiles: 'ClCCl',
      ranges: [],
      nucleus: '13C',
      solvent: 'D₂O'
    }, {
      names: ['diethyl ether'],
      smiles: 'CCOCC',
      ranges: [{
        signals: [{
          delta: 14.77,
          assignment: 'CH₃'
        }],
        from: 14.764999999999999,
        to: 14.775
      }, {
        signals: [{
          delta: 66.42,
          assignment: 'CH₂'
        }],
        from: 66.415,
        to: 66.425
      }],
      nucleus: '13C',
      solvent: 'D₂O'
    }, {
      names: ['diglyme'],
      smiles: 'COCCOCCOC',
      ranges: [{
        signals: [{
          delta: 58.67,
          assignment: 'CH₃'
        }],
        from: 58.665,
        to: 58.675000000000004
      }, {
        signals: [{
          delta: 70.05,
          assignment: 'CH₂'
        }],
        from: 70.045,
        to: 70.05499999999999
      }, {
        signals: [{
          delta: 71.63,
          assignment: 'CH₂'
        }],
        from: 71.625,
        to: 71.63499999999999
      }],
      nucleus: '13C',
      solvent: 'D₂O'
    }, {
      names: ['dimethylformamide'],
      smiles: 'C(=O)N(C)C',
      ranges: [{
        signals: [{
          delta: 165.53,
          assignment: 'CH'
        }],
        from: 165.525,
        to: 165.535
      }, {
        signals: [{
          delta: 37.54,
          assignment: 'CH₃'
        }],
        from: 37.535,
        to: 37.545
      }, {
        signals: [{
          delta: 32.03,
          assignment: 'CH₃'
        }],
        from: 32.025,
        to: 32.035000000000004
      }],
      nucleus: '13C',
      solvent: 'D₂O'
    }, {
      names: ['1,4-dioxane'],
      smiles: 'C1COCCOC1',
      ranges: [{
        signals: [{
          delta: 67.19,
          assignment: 'CH₂'
        }],
        from: 67.185,
        to: 67.195
      }],
      nucleus: '13C',
      solvent: 'D₂O'
    }, {
      names: ['DME', 'diethyl ether'],
      smiles: 'CCOCC',
      ranges: [{
        signals: [{
          delta: 58.67,
          assignment: 'CH₃'
        }],
        from: 58.665,
        to: 58.675000000000004
      }, {
        signals: [{
          delta: 71.49,
          assignment: 'CH₂'
        }],
        from: 71.485,
        to: 71.49499999999999
      }],
      nucleus: '13C',
      solvent: 'D₂O'
    }, {
      names: ['ethane'],
      smiles: 'CC',
      ranges: [],
      nucleus: '13C',
      solvent: 'D₂O'
    }, {
      names: ['ethanol'],
      smiles: 'CCO',
      ranges: [{
        signals: [{
          delta: 17.47,
          assignment: 'CH₃'
        }],
        from: 17.465,
        to: 17.474999999999998
      }, {
        signals: [{
          delta: 58.05,
          assignment: 'CH₂'
        }],
        from: 58.044999999999995,
        to: 58.055
      }],
      nucleus: '13C',
      solvent: 'D₂O'
    }, {
      names: ['ethyl acetate'],
      smiles: 'CC(=O)CC',
      ranges: [{
        signals: [{
          delta: 21.15,
          assignment: 'CH₃CO'
        }],
        from: 21.145,
        to: 21.154999999999998
      }, {
        signals: [{
          delta: 175.26,
          assignment: 'CO'
        }],
        from: 175.255,
        to: 175.265
      }, {
        signals: [{
          delta: 62.32,
          assignment: 'CH₂'
        }],
        from: 62.315,
        to: 62.325
      }, {
        signals: [{
          delta: 13.92,
          assignment: 'CH₃'
        }],
        from: 13.915,
        to: 13.925
      }],
      nucleus: '13C',
      solvent: 'D₂O'
    }, {
      names: ['ethylene'],
      smiles: 'C=C',
      ranges: [],
      nucleus: '13C',
      solvent: 'D₂O'
    }, {
      names: ['ethylene glycol'],
      smiles: 'OCCO',
      ranges: [{
        signals: [{
          delta: 63.17,
          assignment: 'CH₂'
        }],
        from: 63.165,
        to: 63.175000000000004
      }],
      nucleus: '13C',
      solvent: 'D₂O'
    }, {
      names: ['H grease'],
      smiles: '',
      ranges: [],
      nucleus: '13C',
      solvent: 'D₂O'
    }, {
      names: ['hexamethylbenzene'],
      smiles: 'CC1=C(C(=C(C(=C1C)C)C)C)C',
      ranges: [],
      nucleus: '13C',
      solvent: 'D₂O'
    }, {
      names: ['n-hexane'],
      smiles: 'CCCCCC',
      ranges: [],
      nucleus: '13C',
      solvent: 'D₂O'
    }, {
      names: ['HMDSO'],
      smiles: 'C[Si](C)(C)O[Si](C)(C)C',
      ranges: [{
        signals: [{
          delta: 2.31,
          assignment: 'CH₃'
        }],
        from: 2.305,
        to: 2.315
      }],
      nucleus: '13C',
      solvent: 'D₂O'
    }, {
      names: ['HMPA'],
      smiles: 'CN(C)P(=O)(N(C)C)N(C)C',
      ranges: [{
        signals: [{
          delta: 36.46,
          assignment: 'CH₃'
        }],
        from: 36.455,
        to: 36.465
      }],
      nucleus: '13C',
      solvent: 'D₂O'
    }, {
      names: ['imidazole'],
      smiles: 'C1=CN=CN1',
      ranges: [{
        signals: [{
          delta: 136.65,
          assignment: 'CH(2)'
        }],
        from: 136.645,
        to: 136.655
      }, {
        signals: [{
          delta: 122.43,
          assignment: 'CH(4,5)'
        }],
        from: 122.42500000000001,
        to: 122.435
      }],
      nucleus: '13C',
      solvent: 'D₂O'
    }, {
      names: ['methane'],
      smiles: 'C',
      ranges: [],
      nucleus: '13C',
      solvent: 'D₂O'
    }, {
      names: ['methanol'],
      smiles: 'CO',
      ranges: [{
        signals: [{
          delta: 49.5,
          assignment: 'CH₃'
        }],
        from: 49.495,
        to: 49.505
      }],
      nucleus: '13C',
      solvent: 'D₂O'
    }, {
      names: ['nitromethane'],
      smiles: 'C[N+](=O)[O-]',
      ranges: [{
        signals: [{
          delta: 63.22,
          assignment: 'CH₃'
        }],
        from: 63.214999999999996,
        to: 63.225
      }],
      nucleus: '13C',
      solvent: 'D₂O'
    }, {
      names: ['n-pentane'],
      smiles: 'CCCCC',
      ranges: [],
      nucleus: '13C',
      solvent: 'D₂O'
    }, {
      names: ['propane'],
      smiles: 'CCC',
      ranges: [],
      nucleus: '13C',
      solvent: 'D₂O'
    }, {
      names: ['2-propanol'],
      smiles: 'CC(O)C',
      ranges: [{
        signals: [{
          delta: 24.38,
          assignment: 'CH₃'
        }],
        from: 24.375,
        to: 24.384999999999998
      }, {
        signals: [{
          delta: 64.88,
          assignment: 'CH'
        }],
        from: 64.875,
        to: 64.88499999999999
      }],
      nucleus: '13C',
      solvent: 'D₂O'
    }, {
      names: ['propylene'],
      smiles: 'CC=C',
      ranges: [],
      nucleus: '13C',
      solvent: 'D₂O'
    }, {
      names: ['pyridine'],
      smiles: 'C1=CC=NC=C1',
      ranges: [{
        signals: [{
          delta: 149.18,
          assignment: 'CH(2,6)'
        }],
        from: 149.175,
        to: 149.185
      }, {
        signals: [{
          delta: 125.12,
          assignment: 'CH(3,5)'
        }],
        from: 125.11500000000001,
        to: 125.125
      }, {
        signals: [{
          delta: 138.27,
          assignment: 'CH(4)'
        }],
        from: 138.26500000000001,
        to: 138.275
      }],
      nucleus: '13C',
      solvent: 'D₂O'
    }, {
      names: ['pyrrole'],
      smiles: 'C1=CNC=C1',
      ranges: [{
        signals: [{
          delta: 119.06,
          assignment: 'CH(2,5)'
        }],
        from: 119.055,
        to: 119.065
      }, {
        signals: [{
          delta: 107.83,
          assignment: 'CH(3,4)'
        }],
        from: 107.825,
        to: 107.835
      }],
      nucleus: '13C',
      solvent: 'D₂O'
    }, {
      names: ['pyrrolidine'],
      smiles: 'C1CCNC1',
      ranges: [{
        signals: [{
          delta: 46.83,
          assignment: 'CH₂(2,5)'
        }],
        from: 46.824999999999996,
        to: 46.835
      }, {
        signals: [{
          delta: 25.86,
          assignment: 'CH₂(3,4)'
        }],
        from: 25.855,
        to: 25.865
      }],
      nucleus: '13C',
      solvent: 'D₂O'
    }, {
      names: ['silicone grease'],
      smiles: '',
      ranges: [],
      nucleus: '13C',
      solvent: 'D₂O'
    }, {
      names: ['tetrahydrofuran'],
      smiles: 'C1COCC1',
      ranges: [{
        signals: [{
          delta: 68.68,
          assignment: 'CH₂(2,5)'
        }],
        from: 68.67500000000001,
        to: 68.685
      }, {
        signals: [{
          delta: 25.67,
          assignment: 'CH₂(3,4)'
        }],
        from: 25.665000000000003,
        to: 25.675
      }],
      nucleus: '13C',
      solvent: 'D₂O'
    }, {
      names: ['toluene'],
      smiles: 'Cc1ccccc1',
      ranges: [],
      nucleus: '13C',
      solvent: 'D₂O'
    }, {
      names: ['triethylamine'],
      smiles: 'CCN(CC)CC',
      ranges: [{
        signals: [{
          delta: 9.07,
          assignment: 'CH₃'
        }],
        from: 9.065,
        to: 9.075000000000001
      }, {
        signals: [{
          delta: 47.19,
          assignment: 'CH₂'
        }],
        from: 47.184999999999995,
        to: 47.195
      }],
      nucleus: '13C',
      solvent: 'D₂O'
    }];

    const protonImpurities = [{
      names: ['water'],
      smiles: 'O',
      ranges: [{
        signals: [{
          js: [],
          delta: 2.46,
          multiplicity: 's',
          assignment: 'OH'
        }],
        integration: 2,
        from: 2.455,
        to: 2.465
      }],
      nucleus: '1H',
      solvent: 'THF-d₈'
    }, {
      names: ['acetic acid'],
      smiles: 'CC(=O)O',
      ranges: [{
        signals: [{
          js: [],
          delta: 1.89,
          multiplicity: 's',
          assignment: 'CH₃'
        }],
        integration: 3,
        from: 1.885,
        to: 1.8949999999999998
      }],
      nucleus: '1H',
      solvent: 'THF-d₈'
    }, {
      names: ['acetone'],
      smiles: 'CC(=O)C',
      ranges: [{
        signals: [{
          js: [],
          delta: 2.05,
          multiplicity: 's',
          assignment: 'CH₃'
        }],
        integration: 6,
        from: 2.045,
        to: 2.0549999999999997
      }],
      nucleus: '1H',
      solvent: 'THF-d₈'
    }, {
      names: ['acetonitrile'],
      smiles: 'CC#N',
      ranges: [{
        signals: [{
          js: [],
          delta: 1.95,
          multiplicity: 's',
          assignment: 'CH₃'
        }],
        integration: 3,
        from: 1.945,
        to: 1.9549999999999998
      }],
      nucleus: '1H',
      solvent: 'THF-d₈'
    }, {
      names: ['benzene'],
      smiles: 'c1ccccc1',
      ranges: [{
        signals: [{
          js: [],
          delta: 7.31,
          multiplicity: 's',
          assignment: 'CH'
        }],
        integration: 6,
        from: 7.305,
        to: 7.3149999999999995
      }],
      nucleus: '1H',
      solvent: 'THF-d₈'
    }, {
      names: ['tert -butyl alcohol'],
      smiles: 'CC(C)(C)O',
      ranges: [{
        signals: [{
          js: [],
          delta: 1.15,
          multiplicity: 's',
          assignment: 'CH₃'
        }],
        integration: 9,
        from: 1.145,
        to: 1.1549999999999998
      }, {
        signals: [{
          js: [],
          delta: 3.16,
          multiplicity: 's',
          assignment: 'OH'
        }],
        integration: 1,
        from: 3.1550000000000002,
        to: 3.165
      }],
      nucleus: '1H',
      solvent: 'THF-d₈'
    }, {
      names: ['chloroform'],
      smiles: 'ClC(Cl)Cl',
      ranges: [{
        signals: [{
          js: [],
          delta: 7.89,
          multiplicity: 's',
          assignment: 'CH'
        }],
        integration: 1,
        from: 7.885,
        to: 7.895
      }],
      nucleus: '1H',
      solvent: 'THF-d₈'
    }, {
      names: ['18-crown-6'],
      smiles: 'C1COCCOCCOCCOCCOCCO1',
      ranges: [{
        signals: [{
          js: [],
          delta: 3.57,
          multiplicity: 's',
          assignment: 'CH₂'
        }],
        integration: 24,
        from: 3.565,
        to: 3.5749999999999997
      }],
      nucleus: '1H',
      solvent: 'THF-d₈'
    }, {
      names: ['cyclohexane'],
      smiles: 'C1CCCCC1',
      ranges: [{
        signals: [{
          js: [],
          delta: 1.44,
          multiplicity: 's',
          assignment: 'CH₂'
        }],
        integration: 12,
        from: 1.435,
        to: 1.4449999999999998
      }],
      nucleus: '1H',
      solvent: 'THF-d₈'
    }, {
      names: ['1,2-dichloroethane'],
      smiles: 'ClCCCl',
      ranges: [{
        signals: [{
          js: [],
          delta: 3.77,
          multiplicity: 's',
          assignment: 'CH₂'
        }],
        integration: 4,
        from: 3.765,
        to: 3.775
      }],
      nucleus: '1H',
      solvent: 'THF-d₈'
    }, {
      names: ['dichloromethane'],
      smiles: 'ClCCl',
      ranges: [{
        signals: [{
          js: [],
          delta: 5.51,
          multiplicity: 's',
          assignment: 'CH₂'
        }],
        integration: 2,
        from: 5.505,
        to: 5.515
      }],
      nucleus: '1H',
      solvent: 'THF-d₈'
    }, {
      names: ['diethyl ether'],
      smiles: 'CCOCC',
      ranges: [{
        signals: [{
          js: [{
            coupling: 7,
            multiplicity: 't'
          }],
          delta: 1.12,
          assignment: 'CH₃'
        }],
        integration: 6,
        from: 1.0975000000000001,
        to: 1.1425
      }, {
        signals: [{
          js: [{
            coupling: 7,
            multiplicity: 'q'
          }],
          delta: 3.38,
          assignment: 'CH₂'
        }],
        integration: 4,
        from: 3.34875,
        to: 3.41125
      }],
      nucleus: '1H',
      solvent: 'THF-d₈'
    }, {
      names: ['diglyme'],
      smiles: 'COCCOCCOC',
      ranges: [{
        signals: [{
          js: [],
          delta: 3.43,
          assignment: 'CH₂',
          multiplicity: 'm'
        }],
        integration: 4,
        from: 3.4050000000000002,
        to: 3.455
      }, {
        signals: [{
          js: [],
          delta: 3.53,
          assignment: 'CH₂',
          multiplicity: 'm'
        }],
        integration: 4,
        from: 3.505,
        to: 3.5549999999999997
      }, {
        signals: [{
          js: [],
          delta: 3.28,
          multiplicity: 's',
          assignment: 'OCH₃'
        }],
        integration: 6,
        from: 3.275,
        to: 3.2849999999999997
      }],
      nucleus: '1H',
      solvent: 'THF-d₈'
    }, {
      names: ['dimethylformamide'],
      smiles: 'C(=O)N(C)C',
      ranges: [{
        signals: [{
          js: [],
          delta: 7.91,
          multiplicity: 's',
          assignment: 'CH'
        }],
        integration: 1,
        from: 7.905,
        to: 7.915
      }, {
        signals: [{
          js: [],
          delta: 2.88,
          multiplicity: 's',
          assignment: 'CH₃'
        }],
        integration: 3,
        from: 2.875,
        to: 2.885
      }, {
        signals: [{
          js: [],
          delta: 2.76,
          multiplicity: 's',
          assignment: 'CH₃'
        }],
        integration: 3,
        from: 2.755,
        to: 2.7649999999999997
      }],
      nucleus: '1H',
      solvent: 'THF-d₈'
    }, {
      names: ['1,4-dioxane'],
      smiles: 'C1COCCOC1',
      ranges: [{
        signals: [{
          js: [],
          delta: 3.56,
          multiplicity: 's',
          assignment: 'CH₂'
        }],
        integration: 8,
        from: 3.555,
        to: 3.565
      }],
      nucleus: '1H',
      solvent: 'THF-d₈'
    }, {
      names: ['DME', 'diethyl ether'],
      smiles: 'CCOCC',
      ranges: [{
        signals: [{
          js: [],
          delta: 3.28,
          multiplicity: 's',
          assignment: 'CH₃'
        }],
        integration: 6,
        from: 3.275,
        to: 3.2849999999999997
      }, {
        signals: [{
          js: [],
          delta: 3.43,
          multiplicity: 's',
          assignment: 'CH₂'
        }],
        integration: 4,
        from: 3.4250000000000003,
        to: 3.435
      }],
      nucleus: '1H',
      solvent: 'THF-d₈'
    }, {
      names: ['ethane'],
      smiles: 'CC',
      ranges: [{
        signals: [{
          js: [],
          delta: 0.85,
          multiplicity: 's',
          assignment: 'CH₃'
        }],
        integration: 6,
        from: 0.845,
        to: 0.855
      }],
      nucleus: '1H',
      solvent: 'THF-d₈'
    }, {
      names: ['ethanol'],
      smiles: 'CCO',
      ranges: [{
        signals: [{
          js: [{
            coupling: 7,
            multiplicity: 't'
          }],
          delta: 1.1,
          assignment: 'CH₃'
        }],
        integration: 3,
        from: 1.0775000000000001,
        to: 1.1225
      }, {
        signals: [{
          js: [{
            coupling: 7,
            multiplicity: 'q'
          }],
          delta: 3.51,
          assignment: 'CH₂'
        }],
        integration: 2,
        from: 3.47875,
        to: 3.54125
      }, {
        signals: [{
          js: [],
          delta: 3.3,
          multiplicity: 's',
          assignment: 'OH'
        }],
        integration: 1,
        from: 3.295,
        to: 3.3049999999999997
      }],
      nucleus: '1H',
      solvent: 'THF-d₈'
    }, {
      names: ['ethyl acetate'],
      smiles: 'CC(=O)CC',
      ranges: [{
        signals: [{
          js: [],
          delta: 1.94,
          multiplicity: 's',
          assignment: 'CH₃CO'
        }],
        integration: 3,
        from: 1.935,
        to: 1.9449999999999998
      }, {
        signals: [{
          js: [{
            coupling: 7,
            multiplicity: 'q'
          }],
          delta: 4.04,
          assignment: 'CH₂CH₃'
        }],
        integration: 2,
        from: 4.00875,
        to: 4.07125
      }, {
        signals: [{
          js: [{
            coupling: 7,
            multiplicity: 't'
          }],
          delta: 1.19,
          assignment: 'CH₂CH₃'
        }],
        integration: 3,
        from: 1.1675,
        to: 1.2125
      }],
      nucleus: '1H',
      solvent: 'THF-d₈'
    }, {
      names: ['ethylene'],
      smiles: 'C=C',
      ranges: [{
        signals: [{
          js: [],
          delta: 5.36,
          multiplicity: 's',
          assignment: 'CH₂'
        }],
        integration: 4,
        from: 5.355,
        to: 5.365
      }],
      nucleus: '1H',
      solvent: 'THF-d₈'
    }, {
      names: ['ethylene glycol'],
      smiles: 'OCCO',
      ranges: [{
        signals: [{
          js: [],
          delta: 3.48,
          multiplicity: 's',
          assignment: 'CH₂'
        }],
        integration: 4,
        from: 3.475,
        to: 3.485
      }],
      nucleus: '1H',
      solvent: 'THF-d₈'
    }, {
      names: ['H greaseᶠ'],
      smiles: '',
      ranges: [{
        signals: [],
        integration: 6,
        from: 0.85,
        to: 0.91,
        multiplicity: 'm'
      }, {
        signals: [{
          js: [],
          delta: 1.29,
          multiplicity: 'br s',
          assignment: 'CH₂'
        }],
        integration: 10,
        from: 1.27625,
        to: 1.30375
      }],
      nucleus: '1H',
      solvent: 'THF-d₈'
    }, {
      names: ['hexamethylbenzene'],
      smiles: 'CC1=C(C(=C(C(=C1C)C)C)C)C',
      ranges: [{
        signals: [{
          js: [],
          delta: 2.18,
          multiplicity: 's',
          assignment: 'CH₃'
        }],
        integration: 18,
        from: 2.1750000000000003,
        to: 2.185
      }],
      nucleus: '1H',
      solvent: 'THF-d₈'
    }, {
      names: ['n-hexane'],
      smiles: 'CCCCCC',
      ranges: [{
        signals: [{
          js: [{
            coupling: 7,
            multiplicity: 't'
          }],
          delta: 0.89,
          assignment: 'CH₃'
        }],
        integration: 6,
        from: 0.8675,
        to: 0.9125
      }, {
        signals: [{
          js: [],
          delta: 1.29,
          assignment: 'CH₂',
          multiplicity: 'm'
        }],
        integration: 8,
        from: 1.2650000000000001,
        to: 1.315
      }],
      nucleus: '1H',
      solvent: 'THF-d₈'
    }, {
      names: ['HMDSO'],
      smiles: 'C[Si](C)(C)O[Si](C)(C)C',
      ranges: [{
        signals: [{
          js: [],
          delta: 0.07,
          multiplicity: 's',
          assignment: 'CH₃'
        }],
        integration: 18,
        from: 0.065,
        to: 0.07500000000000001
      }],
      nucleus: '1H',
      solvent: 'THF-d₈'
    }, {
      names: ['HMPA'],
      smiles: 'CN(C)P(=O)(N(C)C)N(C)C',
      ranges: [{
        signals: [{
          js: [{
            coupling: 9.5,
            multiplicity: 'd'
          }],
          delta: 2.58,
          assignment: 'CH₃'
        }],
        integration: 18,
        from: 2.563125,
        to: 2.5968750000000003
      }],
      nucleus: '1H',
      solvent: 'THF-d₈'
    }, {
      names: ['hydrogen'],
      smiles: '',
      ranges: [{
        signals: [{
          js: [],
          delta: 4.55,
          multiplicity: 's',
          assignment: 'H₂'
        }],
        integration: 2,
        from: 4.545,
        to: 4.555
      }],
      nucleus: '1H',
      solvent: 'THF-d₈'
    }, {
      names: ['imidazole'],
      smiles: 'C1=CN=CN1',
      ranges: [{
        signals: [{
          js: [],
          delta: 7.48,
          multiplicity: 's',
          assignment: 'CH(2)'
        }],
        integration: 1,
        from: 7.4750000000000005,
        to: 7.485
      }, {
        signals: [{
          js: [],
          delta: 6.94,
          multiplicity: 's',
          assignment: 'CH(4,5)'
        }],
        integration: 2,
        from: 6.9350000000000005,
        to: 6.945
      }],
      nucleus: '1H',
      solvent: 'THF-d₈'
    }, {
      names: ['methane'],
      smiles: 'C',
      ranges: [{
        signals: [{
          js: [],
          delta: 0.19,
          multiplicity: 's',
          assignment: 'CH₄'
        }],
        integration: 4,
        from: 0.185,
        to: 0.195
      }],
      nucleus: '1H',
      solvent: 'THF-d₈'
    }, {
      names: ['methanol'],
      smiles: 'CO',
      ranges: [{
        signals: [{
          js: [],
          delta: 3.27,
          multiplicity: 's',
          assignment: 'CH₃'
        }],
        integration: 3,
        from: 3.265,
        to: 3.275
      }, {
        signals: [{
          js: [],
          delta: 3.02,
          multiplicity: 's',
          assignment: 'OH'
        }],
        integration: 1,
        from: 3.015,
        to: 3.025
      }],
      nucleus: '1H',
      solvent: 'THF-d₈'
    }, {
      names: ['nitromethane'],
      smiles: 'C[N+](=O)[O-]',
      ranges: [{
        signals: [{
          js: [],
          delta: 4.31,
          multiplicity: 's',
          assignment: 'CH₃'
        }],
        integration: 3,
        from: 4.305,
        to: 4.3149999999999995
      }],
      nucleus: '1H',
      solvent: 'THF-d₈'
    }, {
      names: ['n-pentane'],
      smiles: 'CCCCC',
      ranges: [{
        signals: [{
          js: [{
            coupling: 7,
            multiplicity: 't'
          }],
          delta: 0.89,
          assignment: 'CH₃'
        }],
        integration: 6,
        from: 0.8675,
        to: 0.9125
      }, {
        signals: [{
          js: [],
          delta: 1.31,
          assignment: 'CH₂',
          multiplicity: 'm'
        }],
        integration: 6,
        from: 1.2850000000000001,
        to: 1.335
      }],
      nucleus: '1H',
      solvent: 'THF-d₈'
    }, {
      names: ['propane'],
      smiles: 'CCC',
      ranges: [{
        signals: [{
          js: [{
            coupling: 7.3,
            multiplicity: 't'
          }],
          delta: 0.9,
          assignment: 'CH₃'
        }],
        integration: 6,
        from: 0.87675,
        to: 0.92325
      }, {
        signals: [{
          js: [{
            coupling: 7.3,
            multiplicity: 'sept'
          }],
          delta: 1.33,
          assignment: 'CH₂'
        }],
        integration: 2,
        from: 1.27025,
        to: 1.38975
      }],
      nucleus: '1H',
      solvent: 'THF-d₈'
    }, {
      names: ['2-propanol'],
      smiles: 'CC(O)C',
      ranges: [{
        signals: [{
          js: [{
            coupling: 6,
            multiplicity: 'd'
          }],
          delta: 1.08,
          assignment: 'CH₃'
        }],
        integration: 6,
        from: 1.0675000000000001,
        to: 1.0925
      }, {
        signals: [{
          js: [{
            coupling: 6,
            multiplicity: 'sept'
          }],
          delta: 3.82,
          assignment: 'CH'
        }],
        integration: 1,
        from: 3.77,
        to: 3.8699999999999997
      }],
      nucleus: '1H',
      solvent: 'THF-d₈'
    }, {
      names: ['propylene'],
      smiles: 'CC=C',
      ranges: [{
        signals: [{
          js: [{
            coupling: 6.4,
            multiplicity: 'd'
          }, {
            coupling: 1.5,
            multiplicity: 't'
          }],
          delta: 1.69,
          assignment: 'CH₃'
        }],
        integration: 3,
        from: 1.67325,
        to: 1.70675
      }, {
        signals: [{
          js: [{
            coupling: 10,
            multiplicity: 'd'
          }],
          delta: 4.89,
          assignment: 'CH₂(1)'
        }],
        integration: 1,
        from: 4.8725,
        to: 4.9075
      }, {
        signals: [{
          js: [{
            coupling: 17,
            multiplicity: 'd'
          }],
          delta: 4.99,
          assignment: 'CH₂(2)'
        }],
        integration: 1,
        from: 4.96375,
        to: 5.01625
      }, {
        signals: [{
          js: [],
          delta: 5.79,
          assignment: 'CH',
          multiplicity: 'm'
        }],
        integration: 1,
        from: 5.765,
        to: 5.815
      }],
      nucleus: '1H',
      solvent: 'THF-d₈'
    }, {
      names: ['pyridine'],
      smiles: 'C1=CC=NC=C1',
      ranges: [{
        signals: [{
          js: [],
          delta: 8.54,
          assignment: 'CH(2,6)',
          multiplicity: 'm'
        }],
        integration: 2,
        from: 8.514999999999999,
        to: 8.565
      }, {
        signals: [{
          js: [],
          delta: 7.25,
          assignment: 'CH(3,5)',
          multiplicity: 'm'
        }],
        integration: 2,
        from: 7.225,
        to: 7.275
      }, {
        signals: [{
          js: [],
          delta: 7.65,
          assignment: 'CH(4)',
          multiplicity: 'm'
        }],
        integration: 1,
        from: 7.625,
        to: 7.675000000000001
      }],
      nucleus: '1H',
      solvent: 'THF-d₈'
    }, {
      names: ['pyrrole'],
      smiles: 'C1=CNC=C1',
      ranges: [{
        signals: [{
          js: [],
          delta: 9.96,
          assignment: 'NH',
          multiplicity: 'br t'
        }],
        integration: 1,
        from: 9.955,
        to: 9.965000000000002
      }, {
        signals: [{
          js: [],
          delta: 6.66,
          assignment: 'CH(2,5)',
          multiplicity: 'm'
        }],
        integration: 2,
        from: 6.635,
        to: 6.6850000000000005
      }, {
        signals: [{
          js: [],
          delta: 6.02,
          assignment: 'CH(3,4)',
          multiplicity: 'm'
        }],
        integration: 2,
        from: 5.994999999999999,
        to: 6.045
      }],
      nucleus: '1H',
      solvent: 'THF-d₈'
    }, {
      names: ['pyrrolidine'],
      smiles: 'C1CCNC1',
      ranges: [{
        signals: [{
          js: [],
          delta: 2.75,
          assignment: 'CH₂(2,5)',
          multiplicity: 'm'
        }],
        integration: 4,
        from: 2.725,
        to: 2.775
      }, {
        signals: [{
          js: [],
          delta: 1.59,
          assignment: 'CH₂(3,4)',
          multiplicity: 'm'
        }],
        integration: 4,
        from: 1.5650000000000002,
        to: 1.615
      }],
      nucleus: '1H',
      solvent: 'THF-d₈'
    }, {
      names: ['silicone grease'],
      smiles: '',
      ranges: [{
        signals: [{
          js: [],
          delta: 0.11,
          multiplicity: 's',
          assignment: 'CH₃'
        }],
        integration: 6,
        from: 0.105,
        to: 0.115
      }],
      nucleus: '1H',
      solvent: 'THF-d₈'
    }, {
      names: ['tetrahydrofuran'],
      smiles: 'C1COCC1',
      ranges: [{
        signals: [{
          js: [],
          delta: 3.62,
          assignment: 'CH₂(2,5)',
          multiplicity: 'm'
        }],
        integration: 4,
        from: 3.595,
        to: 3.645
      }, {
        signals: [{
          js: [],
          delta: 1.79,
          assignment: 'CH₂(3,4)',
          multiplicity: 'm'
        }],
        integration: 4,
        from: 1.7650000000000001,
        to: 1.815
      }],
      nucleus: '1H',
      solvent: 'THF-d₈'
    }, {
      names: ['toluene'],
      smiles: 'Cc1ccccc1',
      ranges: [{
        signals: [{
          js: [],
          delta: 2.31,
          multiplicity: 's',
          assignment: 'CH₃'
        }],
        integration: 3,
        from: 2.305,
        to: 2.315
      }, {
        signals: [{
          js: [],
          delta: 7.1,
          assignment: 'CH(2,4,6)',
          multiplicity: 'm'
        }],
        integration: 3,
        from: 7.074999999999999,
        to: 7.125
      }, {
        signals: [{
          js: [],
          delta: 7.19,
          assignment: 'CH(3,5)',
          multiplicity: 'm'
        }],
        integration: 2,
        from: 7.165,
        to: 7.215000000000001
      }],
      nucleus: '1H',
      solvent: 'THF-d₈'
    }, {
      names: ['triethylamine'],
      smiles: 'CCN(CC)CC',
      ranges: [{
        signals: [{
          js: [{
            coupling: 7,
            multiplicity: 't'
          }],
          delta: 0.97,
          assignment: 'CH₃'
        }],
        integration: 6,
        from: 0.9475,
        to: 0.9924999999999999
      }, {
        signals: [{
          js: [{
            coupling: 7,
            multiplicity: 'q'
          }],
          delta: 2.46,
          assignment: 'CH₂'
        }],
        integration: 4,
        from: 2.42875,
        to: 2.49125
      }],
      nucleus: '1H',
      solvent: 'THF-d₈'
    }, {
      names: ['water'],
      smiles: 'O',
      ranges: [{
        signals: [{
          js: [],
          delta: 1.52,
          multiplicity: 's',
          assignment: 'OH'
        }],
        integration: 2,
        from: 1.5150000000000001,
        to: 1.525
      }],
      nucleus: '1H',
      solvent: 'CD₂Cl₂'
    }, {
      names: ['acetic acid'],
      smiles: 'CC(=O)O',
      ranges: [{
        signals: [{
          js: [],
          delta: 2.06,
          multiplicity: 's',
          assignment: 'CH₃'
        }],
        integration: 3,
        from: 2.055,
        to: 2.065
      }],
      nucleus: '1H',
      solvent: 'CD₂Cl₂'
    }, {
      names: ['acetone'],
      smiles: 'CC(=O)C',
      ranges: [{
        signals: [{
          js: [],
          delta: 2.12,
          multiplicity: 's',
          assignment: 'CH₃'
        }],
        integration: 6,
        from: 2.115,
        to: 2.125
      }],
      nucleus: '1H',
      solvent: 'CD₂Cl₂'
    }, {
      names: ['acetonitrile'],
      smiles: 'CC#N',
      ranges: [{
        signals: [{
          js: [],
          delta: 1.97,
          multiplicity: 's',
          assignment: 'CH₃'
        }],
        integration: 3,
        from: 1.965,
        to: 1.9749999999999999
      }],
      nucleus: '1H',
      solvent: 'CD₂Cl₂'
    }, {
      names: ['benzene'],
      smiles: 'c1ccccc1',
      ranges: [{
        signals: [{
          js: [],
          delta: 7.35,
          multiplicity: 's',
          assignment: 'CH'
        }],
        integration: 6,
        from: 7.345,
        to: 7.3549999999999995
      }],
      nucleus: '1H',
      solvent: 'CD₂Cl₂'
    }, {
      names: ['tert -butyl alcohol'],
      smiles: 'CC(C)(C)O',
      ranges: [{
        signals: [{
          js: [],
          delta: 1.24,
          multiplicity: 's',
          assignment: 'CH₃'
        }],
        integration: 9,
        from: 1.235,
        to: 1.2449999999999999
      }],
      nucleus: '1H',
      solvent: 'CD₂Cl₂'
    }, {
      names: ['chloroform'],
      smiles: 'ClC(Cl)Cl',
      ranges: [{
        signals: [{
          js: [],
          delta: 7.32,
          multiplicity: 's',
          assignment: 'CH'
        }],
        integration: 1,
        from: 7.315,
        to: 7.325
      }],
      nucleus: '1H',
      solvent: 'CD₂Cl₂'
    }, {
      names: ['18-crown-6'],
      smiles: 'C1COCCOCCOCCOCCOCCO1',
      ranges: [{
        signals: [{
          js: [],
          delta: 3.59,
          multiplicity: 's',
          assignment: 'CH₂'
        }],
        integration: 24,
        from: 3.585,
        to: 3.5949999999999998
      }],
      nucleus: '1H',
      solvent: 'CD₂Cl₂'
    }, {
      names: ['cyclohexane'],
      smiles: 'C1CCCCC1',
      ranges: [{
        signals: [{
          js: [],
          delta: 1.44,
          multiplicity: 's',
          assignment: 'CH₂'
        }],
        integration: 12,
        from: 1.435,
        to: 1.4449999999999998
      }],
      nucleus: '1H',
      solvent: 'CD₂Cl₂'
    }, {
      names: ['1,2-dichloroethane'],
      smiles: 'ClCCCl',
      ranges: [{
        signals: [{
          js: [],
          delta: 3.76,
          multiplicity: 's',
          assignment: 'CH₂'
        }],
        integration: 4,
        from: 3.755,
        to: 3.7649999999999997
      }],
      nucleus: '1H',
      solvent: 'CD₂Cl₂'
    }, {
      names: ['dichloromethane'],
      smiles: 'ClCCl',
      ranges: [{
        signals: [{
          js: [],
          delta: 5.33,
          multiplicity: 's',
          assignment: 'CH₂'
        }],
        integration: 2,
        from: 5.325,
        to: 5.335
      }],
      nucleus: '1H',
      solvent: 'CD₂Cl₂'
    }, {
      names: ['diethyl ether'],
      smiles: 'CCOCC',
      ranges: [{
        signals: [{
          js: [{
            coupling: 7,
            multiplicity: 't'
          }],
          delta: 1.15,
          assignment: 'CH₃'
        }],
        integration: 6,
        from: 1.1275,
        to: 1.1724999999999999
      }, {
        signals: [{
          js: [{
            coupling: 7,
            multiplicity: 'q'
          }],
          delta: 3.43,
          assignment: 'CH₂'
        }],
        integration: 4,
        from: 3.39875,
        to: 3.46125
      }],
      nucleus: '1H',
      solvent: 'CD₂Cl₂'
    }, {
      names: ['diglyme'],
      smiles: 'COCCOCCOC',
      ranges: [{
        signals: [{
          js: [],
          delta: 3.57,
          assignment: 'CH₂',
          multiplicity: 'm'
        }],
        integration: 4,
        from: 3.545,
        to: 3.5949999999999998
      }, {
        signals: [{
          js: [],
          delta: 3.5,
          assignment: 'CH₂',
          multiplicity: 'm'
        }],
        integration: 4,
        from: 3.475,
        to: 3.525
      }, {
        signals: [{
          js: [],
          delta: 3.33,
          multiplicity: 's',
          assignment: 'OCH₃'
        }],
        integration: 6,
        from: 3.325,
        to: 3.335
      }],
      nucleus: '1H',
      solvent: 'CD₂Cl₂'
    }, {
      names: ['dimethylformamide'],
      smiles: 'C(=O)N(C)C',
      ranges: [{
        signals: [{
          js: [],
          delta: 7.96,
          multiplicity: 's',
          assignment: 'CH'
        }],
        integration: 1,
        from: 7.955,
        to: 7.965
      }, {
        signals: [{
          js: [],
          delta: 2.91,
          multiplicity: 's',
          assignment: 'CH₃'
        }],
        integration: 3,
        from: 2.9050000000000002,
        to: 2.915
      }, {
        signals: [{
          js: [],
          delta: 2.82,
          multiplicity: 's',
          assignment: 'CH₃'
        }],
        integration: 3,
        from: 2.815,
        to: 2.8249999999999997
      }],
      nucleus: '1H',
      solvent: 'CD₂Cl₂'
    }, {
      names: ['1,4-dioxane'],
      smiles: 'C1COCCOC1',
      ranges: [{
        signals: [{
          js: [],
          delta: 3.65,
          multiplicity: 's',
          assignment: 'CH₂'
        }],
        integration: 8,
        from: 3.645,
        to: 3.655
      }],
      nucleus: '1H',
      solvent: 'CD₂Cl₂'
    }, {
      names: ['DME', 'diethyl ether'],
      smiles: 'CCOCC',
      ranges: [{
        signals: [{
          js: [],
          delta: 3.34,
          multiplicity: 's',
          assignment: 'CH₃'
        }],
        integration: 6,
        from: 3.335,
        to: 3.3449999999999998
      }, {
        signals: [{
          js: [],
          delta: 3.49,
          multiplicity: 's',
          assignment: 'CH₂'
        }],
        integration: 4,
        from: 3.4850000000000003,
        to: 3.495
      }],
      nucleus: '1H',
      solvent: 'CD₂Cl₂'
    }, {
      names: ['ethane'],
      smiles: 'CC',
      ranges: [{
        signals: [{
          js: [],
          delta: 0.85,
          multiplicity: 's',
          assignment: 'CH₃'
        }],
        integration: 6,
        from: 0.845,
        to: 0.855
      }],
      nucleus: '1H',
      solvent: 'CD₂Cl₂'
    }, {
      names: ['ethanol'],
      smiles: 'CCO',
      ranges: [{
        signals: [{
          js: [{
            coupling: 7,
            multiplicity: 't'
          }],
          delta: 1.19,
          assignment: 'CH₃'
        }],
        integration: 3,
        from: 1.1675,
        to: 1.2125
      }, {
        signals: [{
          js: [{
            coupling: 7,
            multiplicity: 'q'
          }],
          delta: 3.66,
          assignment: 'CH₂'
        }],
        integration: 2,
        from: 3.62875,
        to: 3.69125
      }, {
        signals: [{
          js: [],
          delta: 1.33,
          multiplicity: 's',
          assignment: 'OH'
        }],
        integration: 1,
        from: 1.3250000000000002,
        to: 1.335
      }],
      nucleus: '1H',
      solvent: 'CD₂Cl₂'
    }, {
      names: ['ethyl acetate'],
      smiles: 'CC(=O)CC',
      ranges: [{
        signals: [{
          js: [],
          delta: 2,
          multiplicity: 's',
          assignment: 'CH₃CO'
        }],
        integration: 3,
        from: 1.995,
        to: 2.005
      }, {
        signals: [{
          js: [{
            coupling: 7,
            multiplicity: 'q'
          }],
          delta: 4.08,
          assignment: 'CH₂CH₃'
        }],
        integration: 2,
        from: 4.04875,
        to: 4.11125
      }, {
        signals: [{
          js: [{
            coupling: 7,
            multiplicity: 't'
          }],
          delta: 1.23,
          assignment: 'CH₂CH₃'
        }],
        integration: 3,
        from: 1.2075,
        to: 1.2525
      }],
      nucleus: '1H',
      solvent: 'CD₂Cl₂'
    }, {
      names: ['ethylene'],
      smiles: 'C=C',
      ranges: [{
        signals: [{
          js: [],
          delta: 5.4,
          multiplicity: 's',
          assignment: 'CH₂'
        }],
        integration: 4,
        from: 5.3950000000000005,
        to: 5.405
      }],
      nucleus: '1H',
      solvent: 'CD₂Cl₂'
    }, {
      names: ['ethylene glycol'],
      smiles: 'OCCO',
      ranges: [{
        signals: [{
          js: [],
          delta: 3.66,
          multiplicity: 's',
          assignment: 'CH₂'
        }],
        integration: 4,
        from: 3.6550000000000002,
        to: 3.665
      }],
      nucleus: '1H',
      solvent: 'CD₂Cl₂'
    }, {
      names: ['H greaseᶠ'],
      smiles: '',
      ranges: [{
        signals: [],
        integration: 6,
        from: 0.84,
        to: 0.9,
        multiplicity: 'm'
      }, {
        signals: [{
          js: [],
          delta: 1.27,
          multiplicity: 'br s',
          assignment: 'CH₂'
        }],
        integration: 10,
        from: 1.25625,
        to: 1.28375
      }],
      nucleus: '1H',
      solvent: 'CD₂Cl₂'
    }, {
      names: ['hexamethylbenzene'],
      smiles: 'CC1=C(C(=C(C(=C1C)C)C)C)C',
      ranges: [{
        signals: [{
          js: [],
          delta: 2.2,
          multiplicity: 's',
          assignment: 'CH₃'
        }],
        integration: 18,
        from: 2.1950000000000003,
        to: 2.205
      }],
      nucleus: '1H',
      solvent: 'CD₂Cl₂'
    }, {
      names: ['n-hexane'],
      smiles: 'CCCCCC',
      ranges: [{
        signals: [{
          js: [{
            coupling: 7,
            multiplicity: 't'
          }],
          delta: 0.89,
          assignment: 'CH₃'
        }],
        integration: 6,
        from: 0.8675,
        to: 0.9125
      }, {
        signals: [{
          js: [],
          delta: 1.27,
          assignment: 'CH₂',
          multiplicity: 'm'
        }],
        integration: 8,
        from: 1.245,
        to: 1.295
      }],
      nucleus: '1H',
      solvent: 'CD₂Cl₂'
    }, {
      names: ['HMDSO'],
      smiles: 'C[Si](C)(C)O[Si](C)(C)C',
      ranges: [{
        signals: [{
          js: [],
          delta: 0.07,
          multiplicity: 's',
          assignment: 'CH₃'
        }],
        integration: 18,
        from: 0.065,
        to: 0.07500000000000001
      }],
      nucleus: '1H',
      solvent: 'CD₂Cl₂'
    }, {
      names: ['HMPA'],
      smiles: 'CN(C)P(=O)(N(C)C)N(C)C',
      ranges: [{
        signals: [{
          js: [{
            coupling: 9.5,
            multiplicity: 'd'
          }],
          delta: 2.6,
          assignment: 'CH₃'
        }],
        integration: 18,
        from: 2.583125,
        to: 2.6168750000000003
      }],
      nucleus: '1H',
      solvent: 'CD₂Cl₂'
    }, {
      names: ['hydrogen'],
      smiles: '',
      ranges: [{
        signals: [{
          js: [],
          delta: 4.59,
          multiplicity: 's',
          assignment: 'H₂'
        }],
        integration: 2,
        from: 4.585,
        to: 4.595
      }],
      nucleus: '1H',
      solvent: 'CD₂Cl₂'
    }, {
      names: ['imidazole'],
      smiles: 'C1=CN=CN1',
      ranges: [{
        signals: [{
          js: [],
          delta: 7.63,
          multiplicity: 's',
          assignment: 'CH(2)'
        }],
        integration: 1,
        from: 7.625,
        to: 7.635
      }, {
        signals: [{
          js: [],
          delta: 7.07,
          multiplicity: 's',
          assignment: 'CH(4,5)'
        }],
        integration: 2,
        from: 7.065,
        to: 7.075
      }],
      nucleus: '1H',
      solvent: 'CD₂Cl₂'
    }, {
      names: ['methane'],
      smiles: 'C',
      ranges: [{
        signals: [{
          js: [],
          delta: 0.21,
          multiplicity: 's',
          assignment: 'CH₄'
        }],
        integration: 4,
        from: 0.205,
        to: 0.215
      }],
      nucleus: '1H',
      solvent: 'CD₂Cl₂'
    }, {
      names: ['methanol'],
      smiles: 'CO',
      ranges: [{
        signals: [{
          js: [],
          delta: 3.42,
          multiplicity: 's',
          assignment: 'CH₃'
        }],
        integration: 3,
        from: 3.415,
        to: 3.425
      }, {
        signals: [{
          js: [],
          delta: 1.09,
          multiplicity: 's',
          assignment: 'OH'
        }],
        integration: 1,
        from: 1.0850000000000002,
        to: 1.095
      }],
      nucleus: '1H',
      solvent: 'CD₂Cl₂'
    }, {
      names: ['nitromethane'],
      smiles: 'C[N+](=O)[O-]',
      ranges: [{
        signals: [{
          js: [],
          delta: 4.31,
          multiplicity: 's',
          assignment: 'CH₃'
        }],
        integration: 3,
        from: 4.305,
        to: 4.3149999999999995
      }],
      nucleus: '1H',
      solvent: 'CD₂Cl₂'
    }, {
      names: ['n-pentane'],
      smiles: 'CCCCC',
      ranges: [{
        signals: [{
          js: [{
            coupling: 7,
            multiplicity: 't'
          }],
          delta: 0.89,
          assignment: 'CH₃'
        }],
        integration: 6,
        from: 0.8675,
        to: 0.9125
      }, {
        signals: [{
          js: [],
          delta: 1.3,
          assignment: 'CH₂',
          multiplicity: 'm'
        }],
        integration: 6,
        from: 1.2750000000000001,
        to: 1.325
      }],
      nucleus: '1H',
      solvent: 'CD₂Cl₂'
    }, {
      names: ['propane'],
      smiles: 'CCC',
      ranges: [{
        signals: [{
          js: [{
            coupling: 7.3,
            multiplicity: 't'
          }],
          delta: 0.9,
          assignment: 'CH₃'
        }],
        integration: 6,
        from: 0.87675,
        to: 0.92325
      }, {
        signals: [{
          js: [{
            coupling: 7.3,
            multiplicity: 'sept'
          }],
          delta: 1.32,
          assignment: 'CH₂'
        }],
        integration: 2,
        from: 1.26025,
        to: 1.37975
      }],
      nucleus: '1H',
      solvent: 'CD₂Cl₂'
    }, {
      names: ['2-propanol'],
      smiles: 'CC(O)C',
      ranges: [{
        signals: [{
          js: [{
            coupling: 6,
            multiplicity: 'd'
          }],
          delta: 1.17,
          assignment: 'CH₃'
        }],
        integration: 6,
        from: 1.1575,
        to: 1.1824999999999999
      }, {
        signals: [{
          js: [{
            coupling: 6,
            multiplicity: 'sept'
          }],
          delta: 3.97,
          assignment: 'CH'
        }],
        integration: 1,
        from: 3.9200000000000004,
        to: 4.0200000000000005
      }],
      nucleus: '1H',
      solvent: 'CD₂Cl₂'
    }, {
      names: ['propylene'],
      smiles: 'CC=C',
      ranges: [{
        signals: [{
          js: [{
            coupling: 6.4,
            multiplicity: 'd'
          }, {
            coupling: 1.5,
            multiplicity: 't'
          }],
          delta: 1.71,
          assignment: 'CH₃'
        }],
        integration: 3,
        from: 1.69325,
        to: 1.72675
      }, {
        signals: [{
          js: [{
            coupling: 10,
            multiplicity: 'd'
          }],
          delta: 4.93,
          assignment: 'CH₂(1)'
        }],
        integration: 1,
        from: 4.9125,
        to: 4.9475
      }, {
        signals: [{
          js: [{
            coupling: 17,
            multiplicity: 'd'
          }],
          delta: 5.03,
          assignment: 'CH₂(2)'
        }],
        integration: 1,
        from: 5.00375,
        to: 5.05625
      }, {
        signals: [{
          js: [],
          delta: 5.84,
          assignment: 'CH',
          multiplicity: 'm'
        }],
        integration: 1,
        from: 5.8149999999999995,
        to: 5.865
      }],
      nucleus: '1H',
      solvent: 'CD₂Cl₂'
    }, {
      names: ['pyridine'],
      smiles: 'C1=CC=NC=C1',
      ranges: [{
        signals: [{
          js: [],
          delta: 8.59,
          assignment: 'CH(2,6)',
          multiplicity: 'm'
        }],
        integration: 2,
        from: 8.565,
        to: 8.615
      }, {
        signals: [{
          js: [],
          delta: 7.28,
          assignment: 'CH(3,5)',
          multiplicity: 'm'
        }],
        integration: 2,
        from: 7.255,
        to: 7.305000000000001
      }, {
        signals: [{
          js: [],
          delta: 7.68,
          assignment: 'CH(4)',
          multiplicity: 'm'
        }],
        integration: 1,
        from: 7.654999999999999,
        to: 7.705
      }],
      nucleus: '1H',
      solvent: 'CD₂Cl₂'
    }, {
      names: ['pyrrole'],
      smiles: 'C1=CNC=C1',
      ranges: [{
        signals: [{
          js: [],
          delta: 8.69,
          assignment: 'NH',
          multiplicity: 'br t'
        }],
        integration: 1,
        from: 8.684999999999999,
        to: 8.695
      }, {
        signals: [{
          js: [],
          delta: 6.79,
          assignment: 'CH(2,5)',
          multiplicity: 'm'
        }],
        integration: 2,
        from: 6.765,
        to: 6.815
      }, {
        signals: [{
          js: [],
          delta: 6.19,
          assignment: 'CH(3,4)',
          multiplicity: 'm'
        }],
        integration: 2,
        from: 6.165,
        to: 6.215000000000001
      }],
      nucleus: '1H',
      solvent: 'CD₂Cl₂'
    }, {
      names: ['pyrrolidine'],
      smiles: 'C1CCNC1',
      ranges: [{
        signals: [{
          js: [],
          delta: 2.82,
          assignment: 'CH₂(2,5)',
          multiplicity: 'm'
        }],
        integration: 4,
        from: 2.795,
        to: 2.8449999999999998
      }, {
        signals: [{
          js: [],
          delta: 1.67,
          assignment: 'CH₂(3,4)',
          multiplicity: 'm'
        }],
        integration: 4,
        from: 1.645,
        to: 1.6949999999999998
      }],
      nucleus: '1H',
      solvent: 'CD₂Cl₂'
    }, {
      names: ['silicone grease'],
      smiles: '',
      ranges: [{
        signals: [{
          js: [],
          delta: 0.09,
          multiplicity: 's',
          assignment: 'CH₃'
        }],
        integration: 6,
        from: 0.08499999999999999,
        to: 0.095
      }],
      nucleus: '1H',
      solvent: 'CD₂Cl₂'
    }, {
      names: ['tetrahydrofuran'],
      smiles: 'C1COCC1',
      ranges: [{
        signals: [{
          js: [],
          delta: 3.69,
          assignment: 'CH₂(2,5)',
          multiplicity: 'm'
        }],
        integration: 4,
        from: 3.665,
        to: 3.715
      }, {
        signals: [{
          js: [],
          delta: 1.82,
          assignment: 'CH₂(3,4)',
          multiplicity: 'm'
        }],
        integration: 4,
        from: 1.7950000000000002,
        to: 1.845
      }],
      nucleus: '1H',
      solvent: 'CD₂Cl₂'
    }, {
      names: ['toluene'],
      smiles: 'Cc1ccccc1',
      ranges: [{
        signals: [{
          js: [],
          delta: 2.34,
          multiplicity: 's',
          assignment: 'CH₃'
        }],
        integration: 3,
        from: 2.335,
        to: 2.3449999999999998
      }, {
        signals: [{
          js: [],
          delta: 7.15,
          assignment: 'CH(2,4,6)',
          multiplicity: 'm'
        }],
        integration: 3,
        from: 7.125,
        to: 7.175000000000001
      }, {
        signals: [{
          js: [],
          delta: 7.24,
          assignment: 'CH(3,5)',
          multiplicity: 'm'
        }],
        integration: 2,
        from: 7.215,
        to: 7.265000000000001
      }],
      nucleus: '1H',
      solvent: 'CD₂Cl₂'
    }, {
      names: ['triethylamine'],
      smiles: 'CCN(CC)CC',
      ranges: [{
        signals: [{
          js: [{
            coupling: 7,
            multiplicity: 't'
          }],
          delta: 0.99,
          assignment: 'CH₃'
        }],
        integration: 6,
        from: 0.9675,
        to: 1.0125
      }, {
        signals: [{
          js: [{
            coupling: 7,
            multiplicity: 'q'
          }],
          delta: 2.48,
          assignment: 'CH₂'
        }],
        integration: 4,
        from: 2.44875,
        to: 2.51125
      }],
      nucleus: '1H',
      solvent: 'CD₂Cl₂'
    }, {
      names: ['water'],
      smiles: 'O',
      ranges: [{
        signals: [{
          js: [],
          delta: 1.56,
          multiplicity: 's',
          assignment: 'OH'
        }],
        integration: 2,
        from: 1.5550000000000002,
        to: 1.565
      }],
      nucleus: '1H',
      solvent: 'CDCl₃'
    }, {
      names: ['acetic acid'],
      smiles: 'CC(=O)O',
      ranges: [{
        signals: [{
          js: [],
          delta: 2.1,
          multiplicity: 's',
          assignment: 'CH₃'
        }],
        integration: 3,
        from: 2.095,
        to: 2.105
      }],
      nucleus: '1H',
      solvent: 'CDCl₃'
    }, {
      names: ['acetone'],
      smiles: 'CC(=O)C',
      ranges: [{
        signals: [{
          js: [],
          delta: 2.17,
          multiplicity: 's',
          assignment: 'CH₃'
        }],
        integration: 6,
        from: 2.165,
        to: 2.175
      }],
      nucleus: '1H',
      solvent: 'CDCl₃'
    }, {
      names: ['acetonitrile'],
      smiles: 'CC#N',
      ranges: [{
        signals: [{
          js: [],
          delta: 2.1,
          multiplicity: 's',
          assignment: 'CH₃'
        }],
        integration: 3,
        from: 2.095,
        to: 2.105
      }],
      nucleus: '1H',
      solvent: 'CDCl₃'
    }, {
      names: ['benzene'],
      smiles: 'c1ccccc1',
      ranges: [{
        signals: [{
          js: [],
          delta: 7.36,
          multiplicity: 's',
          assignment: 'CH'
        }],
        integration: 6,
        from: 7.355,
        to: 7.365
      }],
      nucleus: '1H',
      solvent: 'CDCl₃'
    }, {
      names: ['tert -butyl alcohol'],
      smiles: 'CC(C)(C)O',
      ranges: [{
        signals: [{
          js: [],
          delta: 1.28,
          multiplicity: 's',
          assignment: 'CH₃'
        }],
        integration: 9,
        from: 1.2750000000000001,
        to: 1.285
      }],
      nucleus: '1H',
      solvent: 'CDCl₃'
    }, {
      names: ['chloroform'],
      smiles: 'ClC(Cl)Cl',
      ranges: [{
        signals: [{
          js: [],
          delta: 7.26,
          multiplicity: 's',
          assignment: 'CH'
        }],
        integration: 1,
        from: 7.255,
        to: 7.265
      }],
      nucleus: '1H',
      solvent: 'CDCl₃'
    }, {
      names: ['18-crown-6'],
      smiles: 'C1COCCOCCOCCOCCOCCO1',
      ranges: [{
        signals: [{
          js: [],
          delta: 3.67,
          multiplicity: 's',
          assignment: 'CH₂'
        }],
        integration: 24,
        from: 3.665,
        to: 3.675
      }],
      nucleus: '1H',
      solvent: 'CDCl₃'
    }, {
      names: ['cyclohexane'],
      smiles: 'C1CCCCC1',
      ranges: [{
        signals: [{
          js: [],
          delta: 1.43,
          multiplicity: 's',
          assignment: 'CH₂'
        }],
        integration: 12,
        from: 1.425,
        to: 1.4349999999999998
      }],
      nucleus: '1H',
      solvent: 'CDCl₃'
    }, {
      names: ['1,2-dichloroethane'],
      smiles: 'ClCCCl',
      ranges: [{
        signals: [{
          js: [],
          delta: 3.73,
          multiplicity: 's',
          assignment: 'CH₂'
        }],
        integration: 4,
        from: 3.725,
        to: 3.735
      }],
      nucleus: '1H',
      solvent: 'CDCl₃'
    }, {
      names: ['dichloromethane'],
      smiles: 'ClCCl',
      ranges: [{
        signals: [{
          js: [],
          delta: 5.3,
          multiplicity: 's',
          assignment: 'CH₂'
        }],
        integration: 2,
        from: 5.295,
        to: 5.305
      }],
      nucleus: '1H',
      solvent: 'CDCl₃'
    }, {
      names: ['diethyl ether'],
      smiles: 'CCOCC',
      ranges: [{
        signals: [{
          js: [{
            coupling: 7,
            multiplicity: 't'
          }],
          delta: 1.21,
          assignment: 'CH₃'
        }],
        integration: 6,
        from: 1.1875,
        to: 1.2325
      }, {
        signals: [{
          js: [{
            coupling: 7,
            multiplicity: 'q'
          }],
          delta: 3.48,
          assignment: 'CH₂'
        }],
        integration: 4,
        from: 3.44875,
        to: 3.51125
      }],
      nucleus: '1H',
      solvent: 'CDCl₃'
    }, {
      names: ['diglyme'],
      smiles: 'COCCOCCOC',
      ranges: [{
        signals: [{
          js: [],
          delta: 3.65,
          assignment: 'CH₂',
          multiplicity: 'm'
        }],
        integration: 4,
        from: 3.625,
        to: 3.675
      }, {
        signals: [{
          js: [],
          delta: 3.57,
          assignment: 'CH₂',
          multiplicity: 'm'
        }],
        integration: 4,
        from: 3.545,
        to: 3.5949999999999998
      }, {
        signals: [{
          js: [],
          delta: 3.39,
          multiplicity: 's',
          assignment: 'OCH₃'
        }],
        integration: 6,
        from: 3.3850000000000002,
        to: 3.395
      }],
      nucleus: '1H',
      solvent: 'CDCl₃'
    }, {
      names: ['dimethylformamide'],
      smiles: 'C(=O)N(C)C',
      ranges: [{
        signals: [{
          js: [],
          delta: 8.02,
          multiplicity: 's',
          assignment: 'CH'
        }],
        integration: 1,
        from: 8.014999999999999,
        to: 8.025
      }, {
        signals: [{
          js: [],
          delta: 2.96,
          multiplicity: 's',
          assignment: 'CH₃'
        }],
        integration: 3,
        from: 2.955,
        to: 2.965
      }, {
        signals: [{
          js: [],
          delta: 2.88,
          multiplicity: 's',
          assignment: 'CH₃'
        }],
        integration: 3,
        from: 2.875,
        to: 2.885
      }],
      nucleus: '1H',
      solvent: 'CDCl₃'
    }, {
      names: ['1,4-dioxane'],
      smiles: 'C1COCCOC1',
      ranges: [{
        signals: [{
          js: [],
          delta: 3.71,
          multiplicity: 's',
          assignment: 'CH₂'
        }],
        integration: 8,
        from: 3.705,
        to: 3.715
      }],
      nucleus: '1H',
      solvent: 'CDCl₃'
    }, {
      names: ['DME', 'diethyl ether'],
      smiles: 'CCOCC',
      ranges: [{
        signals: [{
          js: [],
          delta: 3.4,
          multiplicity: 's',
          assignment: 'CH₃'
        }],
        integration: 6,
        from: 3.395,
        to: 3.405
      }, {
        signals: [{
          js: [],
          delta: 3.55,
          multiplicity: 's',
          assignment: 'CH₂'
        }],
        integration: 4,
        from: 3.545,
        to: 3.5549999999999997
      }],
      nucleus: '1H',
      solvent: 'CDCl₃'
    }, {
      names: ['ethane'],
      smiles: 'CC',
      ranges: [{
        signals: [{
          js: [],
          delta: 0.87,
          multiplicity: 's',
          assignment: 'CH₃'
        }],
        integration: 6,
        from: 0.865,
        to: 0.875
      }],
      nucleus: '1H',
      solvent: 'CDCl₃'
    }, {
      names: ['ethanol'],
      smiles: 'CCO',
      ranges: [{
        signals: [{
          js: [{
            coupling: 7,
            multiplicity: 't'
          }],
          delta: 1.25,
          assignment: 'CH₃'
        }],
        integration: 3,
        from: 1.2275,
        to: 1.2725
      }, {
        signals: [{
          js: [{
            coupling: 7,
            multiplicity: 'q'
          }],
          delta: 3.72,
          assignment: 'CH₂'
        }],
        integration: 2,
        from: 3.68875,
        to: 3.75125
      }, {
        signals: [{
          js: [],
          delta: 1.32,
          multiplicity: 's',
          assignment: 'OH'
        }],
        integration: 1,
        from: 1.3150000000000002,
        to: 1.325
      }],
      nucleus: '1H',
      solvent: 'CDCl₃'
    }, {
      names: ['ethyl acetate'],
      smiles: 'CC(=O)CC',
      ranges: [{
        signals: [{
          js: [],
          delta: 2.05,
          multiplicity: 's',
          assignment: 'CH₃CO'
        }],
        integration: 3,
        from: 2.045,
        to: 2.0549999999999997
      }, {
        signals: [{
          js: [{
            coupling: 7,
            multiplicity: 'q'
          }],
          delta: 4.12,
          assignment: 'CH₂CH₃'
        }],
        integration: 2,
        from: 4.08875,
        to: 4.15125
      }, {
        signals: [{
          js: [{
            coupling: 7,
            multiplicity: 't'
          }],
          delta: 1.26,
          assignment: 'CH₂CH₃'
        }],
        integration: 3,
        from: 1.2375,
        to: 1.2825
      }],
      nucleus: '1H',
      solvent: 'CDCl₃'
    }, {
      names: ['ethylene'],
      smiles: 'C=C',
      ranges: [{
        signals: [{
          js: [],
          delta: 5.4,
          multiplicity: 's',
          assignment: 'CH₂'
        }],
        integration: 4,
        from: 5.3950000000000005,
        to: 5.405
      }],
      nucleus: '1H',
      solvent: 'CDCl₃'
    }, {
      names: ['ethylene glycol'],
      smiles: 'OCCO',
      ranges: [{
        signals: [{
          js: [],
          delta: 3.76,
          multiplicity: 's',
          assignment: 'CH₂'
        }],
        integration: 4,
        from: 3.755,
        to: 3.7649999999999997
      }],
      nucleus: '1H',
      solvent: 'CDCl₃'
    }, {
      names: ['H greaseᶠ'],
      smiles: '',
      ranges: [{
        signals: [],
        integration: 6,
        from: 0.84,
        to: 0.87,
        multiplicity: 'm'
      }, {
        signals: [{
          js: [],
          delta: 1.25,
          multiplicity: 'br s',
          assignment: 'CH₂'
        }],
        integration: 10,
        from: 1.23625,
        to: 1.26375
      }],
      nucleus: '1H',
      solvent: 'CDCl₃'
    }, {
      names: ['hexamethylbenzene'],
      smiles: 'CC1=C(C(=C(C(=C1C)C)C)C)C',
      ranges: [{
        signals: [{
          js: [],
          delta: 2.24,
          multiplicity: 's',
          assignment: 'CH₃'
        }],
        integration: 18,
        from: 2.2350000000000003,
        to: 2.245
      }],
      nucleus: '1H',
      solvent: 'CDCl₃'
    }, {
      names: ['n-hexane'],
      smiles: 'CCCCCC',
      ranges: [{
        signals: [{
          js: [{
            coupling: 7,
            multiplicity: 't'
          }],
          delta: 0.88,
          assignment: 'CH₃'
        }],
        integration: 6,
        from: 0.8575,
        to: 0.9025
      }, {
        signals: [{
          js: [],
          delta: 1.26,
          assignment: 'CH₂',
          multiplicity: 'm'
        }],
        integration: 8,
        from: 1.235,
        to: 1.285
      }],
      nucleus: '1H',
      solvent: 'CDCl₃'
    }, {
      names: ['HMDSO'],
      smiles: 'C[Si](C)(C)O[Si](C)(C)C',
      ranges: [{
        signals: [{
          js: [],
          delta: 0.07,
          multiplicity: 's',
          assignment: 'CH₃'
        }],
        integration: 18,
        from: 0.065,
        to: 0.07500000000000001
      }],
      nucleus: '1H',
      solvent: 'CDCl₃'
    }, {
      names: ['HMPA'],
      smiles: 'CN(C)P(=O)(N(C)C)N(C)C',
      ranges: [{
        signals: [{
          js: [{
            coupling: 9.5,
            multiplicity: 'd'
          }],
          delta: 2.65,
          assignment: 'CH₃'
        }],
        integration: 18,
        from: 2.6331249999999997,
        to: 2.666875
      }],
      nucleus: '1H',
      solvent: 'CDCl₃'
    }, {
      names: ['hydrogen'],
      smiles: '',
      ranges: [{
        signals: [{
          js: [],
          delta: 4.62,
          multiplicity: 's',
          assignment: 'H₂'
        }],
        integration: 2,
        from: 4.615,
        to: 4.625
      }],
      nucleus: '1H',
      solvent: 'CDCl₃'
    }, {
      names: ['imidazole'],
      smiles: 'C1=CN=CN1',
      ranges: [{
        signals: [{
          js: [],
          delta: 7.67,
          multiplicity: 's',
          assignment: 'CH(2)'
        }],
        integration: 1,
        from: 7.665,
        to: 7.675
      }, {
        signals: [{
          js: [],
          delta: 7.1,
          multiplicity: 's',
          assignment: 'CH(4,5)'
        }],
        integration: 2,
        from: 7.095,
        to: 7.1049999999999995
      }],
      nucleus: '1H',
      solvent: 'CDCl₃'
    }, {
      names: ['methane'],
      smiles: 'C',
      ranges: [{
        signals: [{
          js: [],
          delta: 0.22,
          multiplicity: 's',
          assignment: 'CH₄'
        }],
        integration: 4,
        from: 0.215,
        to: 0.225
      }],
      nucleus: '1H',
      solvent: 'CDCl₃'
    }, {
      names: ['methanol'],
      smiles: 'CO',
      ranges: [{
        signals: [{
          js: [],
          delta: 3.49,
          multiplicity: 's',
          assignment: 'CH₃'
        }],
        integration: 3,
        from: 3.4850000000000003,
        to: 3.495
      }, {
        signals: [{
          js: [],
          delta: 1.09,
          multiplicity: 's',
          assignment: 'OH'
        }],
        integration: 1,
        from: 1.0850000000000002,
        to: 1.095
      }],
      nucleus: '1H',
      solvent: 'CDCl₃'
    }, {
      names: ['nitromethane'],
      smiles: 'C[N+](=O)[O-]',
      ranges: [{
        signals: [{
          js: [],
          delta: 4.33,
          multiplicity: 's',
          assignment: 'CH₃'
        }],
        integration: 3,
        from: 4.325,
        to: 4.335
      }],
      nucleus: '1H',
      solvent: 'CDCl₃'
    }, {
      names: ['n-pentane'],
      smiles: 'CCCCC',
      ranges: [{
        signals: [{
          js: [{
            coupling: 7,
            multiplicity: 't'
          }],
          delta: 0.88,
          assignment: 'CH₃'
        }],
        integration: 6,
        from: 0.8575,
        to: 0.9025
      }, {
        signals: [{
          js: [],
          delta: 1.27,
          assignment: 'CH₂',
          multiplicity: 'm'
        }],
        integration: 6,
        from: 1.245,
        to: 1.295
      }],
      nucleus: '1H',
      solvent: 'CDCl₃'
    }, {
      names: ['propane'],
      smiles: 'CCC',
      ranges: [{
        signals: [{
          js: [{
            coupling: 7.3,
            multiplicity: 't'
          }],
          delta: 0.9,
          assignment: 'CH₃'
        }],
        integration: 6,
        from: 0.87675,
        to: 0.92325
      }, {
        signals: [{
          js: [{
            coupling: 7.3,
            multiplicity: 'sept'
          }],
          delta: 1.32,
          assignment: 'CH₂'
        }],
        integration: 2,
        from: 1.26025,
        to: 1.37975
      }],
      nucleus: '1H',
      solvent: 'CDCl₃'
    }, {
      names: ['2-propanol'],
      smiles: 'CC(O)C',
      ranges: [{
        signals: [{
          js: [{
            coupling: 6,
            multiplicity: 'd'
          }],
          delta: 1.22,
          assignment: 'CH₃'
        }],
        integration: 6,
        from: 1.2075,
        to: 1.2325
      }, {
        signals: [{
          js: [{
            coupling: 6,
            multiplicity: 'sept'
          }],
          delta: 4.04,
          assignment: 'CH'
        }],
        integration: 1,
        from: 3.99,
        to: 4.09
      }],
      nucleus: '1H',
      solvent: 'CDCl₃'
    }, {
      names: ['propylene'],
      smiles: 'CC=C',
      ranges: [{
        signals: [{
          js: [{
            coupling: 6.4,
            multiplicity: 'd'
          }, {
            coupling: 1.5,
            multiplicity: 't'
          }],
          delta: 1.73,
          assignment: 'CH₃'
        }],
        integration: 3,
        from: 1.71325,
        to: 1.74675
      }, {
        signals: [{
          js: [{
            coupling: 10,
            multiplicity: 'd'
          }],
          delta: 4.94,
          assignment: 'CH₂(1)'
        }],
        integration: 1,
        from: 4.9225,
        to: 4.9575000000000005
      }, {
        signals: [{
          js: [{
            coupling: 17,
            multiplicity: 'd'
          }],
          delta: 5.03,
          assignment: 'CH₂(2)'
        }],
        integration: 1,
        from: 5.00375,
        to: 5.05625
      }, {
        signals: [{
          js: [],
          delta: 5.83,
          assignment: 'CH',
          multiplicity: 'm'
        }],
        integration: 1,
        from: 5.805,
        to: 5.855
      }],
      nucleus: '1H',
      solvent: 'CDCl₃'
    }, {
      names: ['pyridine'],
      smiles: 'C1=CC=NC=C1',
      ranges: [{
        signals: [{
          js: [],
          delta: 8.62,
          assignment: 'CH(2,6)',
          multiplicity: 'm'
        }],
        integration: 2,
        from: 8.594999999999999,
        to: 8.645
      }, {
        signals: [{
          js: [],
          delta: 7.29,
          assignment: 'CH(3,5)',
          multiplicity: 'm'
        }],
        integration: 2,
        from: 7.265,
        to: 7.315
      }, {
        signals: [{
          js: [],
          delta: 7.68,
          assignment: 'CH(4)',
          multiplicity: 'm'
        }],
        integration: 1,
        from: 7.654999999999999,
        to: 7.705
      }],
      nucleus: '1H',
      solvent: 'CDCl₃'
    }, {
      names: ['pyrrole'],
      smiles: 'C1=CNC=C1',
      ranges: [{
        signals: [{
          js: [],
          delta: 8.4,
          assignment: 'NH',
          multiplicity: 'br t'
        }],
        integration: 1,
        from: 8.395,
        to: 8.405000000000001
      }, {
        signals: [{
          js: [],
          delta: 6.83,
          assignment: 'CH(2,5)',
          multiplicity: 'm'
        }],
        integration: 2,
        from: 6.805,
        to: 6.855
      }, {
        signals: [{
          js: [],
          delta: 6.26,
          assignment: 'CH(3,4)',
          multiplicity: 'm'
        }],
        integration: 2,
        from: 6.234999999999999,
        to: 6.285
      }],
      nucleus: '1H',
      solvent: 'CDCl₃'
    }, {
      names: ['pyrrolidine'],
      smiles: 'C1CCNC1',
      ranges: [{
        signals: [{
          js: [],
          delta: 2.87,
          assignment: 'CH₂(2,5)',
          multiplicity: 'm'
        }],
        integration: 4,
        from: 2.845,
        to: 2.895
      }, {
        signals: [{
          js: [],
          delta: 1.68,
          assignment: 'CH₂(3,4)',
          multiplicity: 'm'
        }],
        integration: 4,
        from: 1.655,
        to: 1.7049999999999998
      }],
      nucleus: '1H',
      solvent: 'CDCl₃'
    }, {
      names: ['silicone grease'],
      smiles: '',
      ranges: [{
        signals: [{
          js: [],
          delta: 0.07,
          multiplicity: 's',
          assignment: 'CH₃'
        }],
        integration: 6,
        from: 0.065,
        to: 0.07500000000000001
      }],
      nucleus: '1H',
      solvent: 'CDCl₃'
    }, {
      names: ['tetrahydrofuran'],
      smiles: 'C1COCC1',
      ranges: [{
        signals: [{
          js: [],
          delta: 3.76,
          assignment: 'CH₂(2,5)',
          multiplicity: 'm'
        }],
        integration: 4,
        from: 3.735,
        to: 3.7849999999999997
      }, {
        signals: [{
          js: [],
          delta: 1.85,
          assignment: 'CH₂(3,4)',
          multiplicity: 'm'
        }],
        integration: 4,
        from: 1.8250000000000002,
        to: 1.875
      }],
      nucleus: '1H',
      solvent: 'CDCl₃'
    }, {
      names: ['toluene'],
      smiles: 'Cc1ccccc1',
      ranges: [{
        signals: [{
          js: [],
          delta: 2.36,
          multiplicity: 's',
          assignment: 'CH₃'
        }],
        integration: 3,
        from: 2.355,
        to: 2.3649999999999998
      }, {
        signals: [{
          js: [],
          delta: 7.17,
          assignment: 'CH(2,4,6)',
          multiplicity: 'm'
        }],
        integration: 3,
        from: 7.145,
        to: 7.195
      }, {
        signals: [{
          js: [],
          delta: 7.25,
          assignment: 'CH(3,5)',
          multiplicity: 'm'
        }],
        integration: 2,
        from: 7.225,
        to: 7.275
      }],
      nucleus: '1H',
      solvent: 'CDCl₃'
    }, {
      names: ['triethylamine'],
      smiles: 'CCN(CC)CC',
      ranges: [{
        signals: [{
          js: [{
            coupling: 7,
            multiplicity: 't'
          }],
          delta: 1.03,
          assignment: 'CH₃'
        }],
        integration: 6,
        from: 1.0075,
        to: 1.0525
      }, {
        signals: [{
          js: [{
            coupling: 7,
            multiplicity: 'q'
          }],
          delta: 2.53,
          assignment: 'CH₂'
        }],
        integration: 4,
        from: 2.49875,
        to: 2.56125
      }],
      nucleus: '1H',
      solvent: 'CDCl₃'
    }, {
      names: ['water'],
      smiles: 'O',
      ranges: [{
        signals: [{
          js: [],
          delta: 0.43,
          multiplicity: 's',
          assignment: 'OH'
        }],
        integration: 2,
        from: 0.425,
        to: 0.435
      }],
      nucleus: '1H',
      solvent: 'toluene-d₈'
    }, {
      names: ['acetic acid'],
      smiles: 'CC(=O)O',
      ranges: [{
        signals: [{
          js: [],
          delta: 1.57,
          multiplicity: 's',
          assignment: 'CH₃'
        }],
        integration: 3,
        from: 1.5650000000000002,
        to: 1.575
      }],
      nucleus: '1H',
      solvent: 'toluene-d₈'
    }, {
      names: ['acetone'],
      smiles: 'CC(=O)C',
      ranges: [{
        signals: [{
          js: [],
          delta: 1.57,
          multiplicity: 's',
          assignment: 'CH₃'
        }],
        integration: 6,
        from: 1.5650000000000002,
        to: 1.575
      }],
      nucleus: '1H',
      solvent: 'toluene-d₈'
    }, {
      names: ['acetonitrile'],
      smiles: 'CC#N',
      ranges: [{
        signals: [{
          js: [],
          delta: 0.69,
          multiplicity: 's',
          assignment: 'CH₃'
        }],
        integration: 3,
        from: 0.6849999999999999,
        to: 0.695
      }],
      nucleus: '1H',
      solvent: 'toluene-d₈'
    }, {
      names: ['benzene'],
      smiles: 'c1ccccc1',
      ranges: [{
        signals: [{
          js: [],
          delta: 7.12,
          multiplicity: 's',
          assignment: 'CH'
        }],
        integration: 6,
        from: 7.115,
        to: 7.125
      }],
      nucleus: '1H',
      solvent: 'toluene-d₈'
    }, {
      names: ['tert -butyl alcohol'],
      smiles: 'CC(C)(C)O',
      ranges: [{
        signals: [{
          js: [],
          delta: 1.03,
          multiplicity: 's',
          assignment: 'CH₃'
        }],
        integration: 9,
        from: 1.0250000000000001,
        to: 1.035
      }, {
        signals: [{
          js: [],
          delta: 0.58,
          multiplicity: 's',
          assignment: 'OH'
        }],
        integration: 1,
        from: 0.575,
        to: 0.585
      }],
      nucleus: '1H',
      solvent: 'toluene-d₈'
    }, {
      names: ['chloroform'],
      smiles: 'ClC(Cl)Cl',
      ranges: [{
        signals: [{
          js: [],
          delta: 6.1,
          multiplicity: 's',
          assignment: 'CH'
        }],
        integration: 1,
        from: 6.095,
        to: 6.1049999999999995
      }],
      nucleus: '1H',
      solvent: 'toluene-d₈'
    }, {
      names: ['18-crown-6'],
      smiles: 'C1COCCOCCOCCOCCOCCO1',
      ranges: [{
        signals: [{
          js: [],
          delta: 3.36,
          multiplicity: 's',
          assignment: 'CH₂'
        }],
        integration: 24,
        from: 3.355,
        to: 3.3649999999999998
      }],
      nucleus: '1H',
      solvent: 'toluene-d₈'
    }, {
      names: ['cyclohexane'],
      smiles: 'C1CCCCC1',
      ranges: [{
        signals: [{
          js: [],
          delta: 1.4,
          multiplicity: 's',
          assignment: 'CH₂'
        }],
        integration: 12,
        from: 1.395,
        to: 1.4049999999999998
      }],
      nucleus: '1H',
      solvent: 'toluene-d₈'
    }, {
      names: ['1,2-dichloroethane'],
      smiles: 'ClCCCl',
      ranges: [{
        signals: [{
          js: [],
          delta: 2.91,
          multiplicity: 's',
          assignment: 'CH₂'
        }],
        integration: 4,
        from: 2.9050000000000002,
        to: 2.915
      }],
      nucleus: '1H',
      solvent: 'toluene-d₈'
    }, {
      names: ['dichloromethane'],
      smiles: 'ClCCl',
      ranges: [{
        signals: [{
          js: [],
          delta: 4.32,
          multiplicity: 's',
          assignment: 'CH₂'
        }],
        integration: 2,
        from: 4.315,
        to: 4.325
      }],
      nucleus: '1H',
      solvent: 'toluene-d₈'
    }, {
      names: ['diethyl ether'],
      smiles: 'CCOCC',
      ranges: [{
        signals: [{
          js: [{
            coupling: 7,
            multiplicity: 't'
          }],
          delta: 1.1,
          assignment: 'CH₃'
        }],
        integration: 6,
        from: 1.0775000000000001,
        to: 1.1225
      }, {
        signals: [{
          js: [{
            coupling: 7,
            multiplicity: 'q'
          }],
          delta: 3.25,
          assignment: 'CH₂'
        }],
        integration: 4,
        from: 3.21875,
        to: 3.28125
      }],
      nucleus: '1H',
      solvent: 'toluene-d₈'
    }, {
      names: ['diglyme'],
      smiles: 'COCCOCCOC',
      ranges: [{
        signals: [{
          js: [],
          delta: 3.43,
          assignment: 'CH₂',
          multiplicity: 'm'
        }],
        integration: 4,
        from: 3.4050000000000002,
        to: 3.455
      }, {
        signals: [{
          js: [],
          delta: 3.31,
          assignment: 'CH₂',
          multiplicity: 'm'
        }],
        integration: 4,
        from: 3.285,
        to: 3.335
      }, {
        signals: [{
          js: [],
          delta: 3.12,
          multiplicity: 's',
          assignment: 'OCH₃'
        }],
        integration: 6,
        from: 3.115,
        to: 3.125
      }],
      nucleus: '1H',
      solvent: 'toluene-d₈'
    }, {
      names: ['dimethylformamide'],
      smiles: 'C(=O)N(C)C',
      ranges: [{
        signals: [{
          js: [],
          delta: 7.57,
          multiplicity: 's',
          assignment: 'CH'
        }],
        integration: 1,
        from: 7.565,
        to: 7.575
      }, {
        signals: [{
          js: [],
          delta: 2.37,
          multiplicity: 's',
          assignment: 'CH₃'
        }],
        integration: 3,
        from: 2.365,
        to: 2.375
      }, {
        signals: [{
          js: [],
          delta: 1.96,
          multiplicity: 's',
          assignment: 'CH₃'
        }],
        integration: 3,
        from: 1.955,
        to: 1.9649999999999999
      }],
      nucleus: '1H',
      solvent: 'toluene-d₈'
    }, {
      names: ['1,4-dioxane'],
      smiles: 'C1COCCOC1',
      ranges: [{
        signals: [{
          js: [],
          delta: 3.33,
          multiplicity: 's',
          assignment: 'CH₂'
        }],
        integration: 8,
        from: 3.325,
        to: 3.335
      }],
      nucleus: '1H',
      solvent: 'toluene-d₈'
    }, {
      names: ['DME', 'diethyl ether'],
      smiles: 'CCOCC',
      ranges: [{
        signals: [{
          js: [],
          delta: 3.12,
          multiplicity: 's',
          assignment: 'CH₃'
        }],
        integration: 6,
        from: 3.115,
        to: 3.125
      }, {
        signals: [{
          js: [],
          delta: 3.31,
          multiplicity: 's',
          assignment: 'CH₂'
        }],
        integration: 4,
        from: 3.305,
        to: 3.315
      }],
      nucleus: '1H',
      solvent: 'toluene-d₈'
    }, {
      names: ['ethane'],
      smiles: 'CC',
      ranges: [{
        signals: [{
          js: [],
          delta: 0.81,
          multiplicity: 's',
          assignment: 'CH₃'
        }],
        integration: 6,
        from: 0.805,
        to: 0.8150000000000001
      }],
      nucleus: '1H',
      solvent: 'toluene-d₈'
    }, {
      names: ['ethanol'],
      smiles: 'CCO',
      ranges: [{
        signals: [{
          js: [{
            coupling: 7,
            multiplicity: 't'
          }],
          delta: 0.97,
          assignment: 'CH₃'
        }],
        integration: 3,
        from: 0.9475,
        to: 0.9924999999999999
      }, {
        signals: [{
          js: [{
            coupling: 7,
            multiplicity: 'q'
          }],
          delta: 3.36,
          assignment: 'CH₂'
        }],
        integration: 2,
        from: 3.32875,
        to: 3.39125
      }, {
        signals: [{
          js: [],
          delta: 0.83,
          multiplicity: 's',
          assignment: 'OH'
        }],
        integration: 1,
        from: 0.825,
        to: 0.835
      }],
      nucleus: '1H',
      solvent: 'toluene-d₈'
    }, {
      names: ['ethyl acetate'],
      smiles: 'CC(=O)CC',
      ranges: [{
        signals: [{
          js: [],
          delta: 1.69,
          multiplicity: 's',
          assignment: 'CH₃CO'
        }],
        integration: 3,
        from: 1.685,
        to: 1.6949999999999998
      }, {
        signals: [{
          js: [{
            coupling: 7,
            multiplicity: 'q'
          }],
          delta: 3.87,
          assignment: 'CH₂CH₃'
        }],
        integration: 2,
        from: 3.83875,
        to: 3.90125
      }, {
        signals: [{
          js: [{
            coupling: 7,
            multiplicity: 't'
          }],
          delta: 0.94,
          assignment: 'CH₂CH₃'
        }],
        integration: 3,
        from: 0.9175,
        to: 0.9624999999999999
      }],
      nucleus: '1H',
      solvent: 'toluene-d₈'
    }, {
      names: ['ethylene'],
      smiles: 'C=C',
      ranges: [{
        signals: [{
          js: [],
          delta: 5.25,
          multiplicity: 's',
          assignment: 'CH₂'
        }],
        integration: 4,
        from: 5.245,
        to: 5.255
      }],
      nucleus: '1H',
      solvent: 'toluene-d₈'
    }, {
      names: ['ethylene glycol'],
      smiles: 'OCCO',
      ranges: [{
        signals: [{
          js: [],
          delta: 3.36,
          multiplicity: 's',
          assignment: 'CH₂'
        }],
        integration: 4,
        from: 3.355,
        to: 3.3649999999999998
      }],
      nucleus: '1H',
      solvent: 'toluene-d₈'
    }, {
      names: ['H greaseᶠ'],
      smiles: '',
      ranges: [{
        signals: [],
        integration: 6,
        from: 0.89,
        to: 0.96,
        multiplicity: 'm'
      }, {
        signals: [{
          js: [],
          delta: 1.33,
          multiplicity: 'br s',
          assignment: 'CH₂'
        }],
        integration: 10,
        from: 1.3162500000000001,
        to: 1.34375
      }],
      nucleus: '1H',
      solvent: 'toluene-d₈'
    }, {
      names: ['hexamethylbenzene'],
      smiles: 'CC1=C(C(=C(C(=C1C)C)C)C)C',
      ranges: [{
        signals: [{
          js: [],
          delta: 2.1,
          multiplicity: 's',
          assignment: 'CH₃'
        }],
        integration: 18,
        from: 2.095,
        to: 2.105
      }],
      nucleus: '1H',
      solvent: 'toluene-d₈'
    }, {
      names: ['n-hexane'],
      smiles: 'CCCCCC',
      ranges: [{
        signals: [{
          js: [{
            coupling: 7,
            multiplicity: 't'
          }],
          delta: 0.88,
          assignment: 'CH₃'
        }],
        integration: 6,
        from: 0.8575,
        to: 0.9025
      }, {
        signals: [{
          js: [],
          delta: 1.22,
          assignment: 'CH₂',
          multiplicity: 'm'
        }],
        integration: 8,
        from: 1.195,
        to: 1.2449999999999999
      }],
      nucleus: '1H',
      solvent: 'toluene-d₈'
    }, {
      names: ['HMDSO'],
      smiles: 'C[Si](C)(C)O[Si](C)(C)C',
      ranges: [{
        signals: [{
          js: [],
          delta: 0.1,
          multiplicity: 's',
          assignment: 'CH₃'
        }],
        integration: 18,
        from: 0.095,
        to: 0.10500000000000001
      }],
      nucleus: '1H',
      solvent: 'toluene-d₈'
    }, {
      names: ['HMPA'],
      smiles: 'CN(C)P(=O)(N(C)C)N(C)C',
      ranges: [{
        signals: [{
          js: [{
            coupling: 9.5,
            multiplicity: 'd'
          }],
          delta: 2.42,
          assignment: 'CH₃'
        }],
        integration: 18,
        from: 2.4031249999999997,
        to: 2.436875
      }],
      nucleus: '1H',
      solvent: 'toluene-d₈'
    }, {
      names: ['hydrogen'],
      smiles: '',
      ranges: [{
        signals: [{
          js: [],
          delta: 4.5,
          multiplicity: 's',
          assignment: 'H₂'
        }],
        integration: 2,
        from: 4.495,
        to: 4.505
      }],
      nucleus: '1H',
      solvent: 'toluene-d₈'
    }, {
      names: ['imidazole'],
      smiles: 'C1=CN=CN1',
      ranges: [{
        signals: [{
          js: [],
          delta: 7.3,
          multiplicity: 's',
          assignment: 'CH(2)'
        }],
        integration: 1,
        from: 7.295,
        to: 7.305
      }, {
        signals: [{
          js: [],
          delta: 6.86,
          multiplicity: 's',
          assignment: 'CH(4,5)'
        }],
        integration: 2,
        from: 6.855,
        to: 6.865
      }],
      nucleus: '1H',
      solvent: 'toluene-d₈'
    }, {
      names: ['methane'],
      smiles: 'C',
      ranges: [{
        signals: [{
          js: [],
          delta: 0.17,
          multiplicity: 's',
          assignment: 'CH₄'
        }],
        integration: 4,
        from: 0.165,
        to: 0.17500000000000002
      }],
      nucleus: '1H',
      solvent: 'toluene-d₈'
    }, {
      names: ['methanol'],
      smiles: 'CO',
      ranges: [{
        signals: [{
          js: [],
          delta: 3.03,
          multiplicity: 's',
          assignment: 'CH₃'
        }],
        integration: 3,
        from: 3.025,
        to: 3.0349999999999997
      }],
      nucleus: '1H',
      solvent: 'toluene-d₈'
    }, {
      names: ['nitromethane'],
      smiles: 'C[N+](=O)[O-]',
      ranges: [{
        signals: [{
          js: [],
          delta: 3.01,
          multiplicity: 's',
          assignment: 'CH₃'
        }],
        integration: 3,
        from: 3.005,
        to: 3.0149999999999997
      }],
      nucleus: '1H',
      solvent: 'toluene-d₈'
    }, {
      names: ['n-pentane'],
      smiles: 'CCCCC',
      ranges: [{
        signals: [{
          js: [{
            coupling: 7,
            multiplicity: 't'
          }],
          delta: 0.87,
          assignment: 'CH₃'
        }],
        integration: 6,
        from: 0.8475,
        to: 0.8925
      }, {
        signals: [{
          js: [],
          delta: 1.25,
          assignment: 'CH₂',
          multiplicity: 'm'
        }],
        integration: 6,
        from: 1.225,
        to: 1.275
      }],
      nucleus: '1H',
      solvent: 'toluene-d₈'
    }, {
      names: ['propane'],
      smiles: 'CCC',
      ranges: [{
        signals: [{
          js: [{
            coupling: 7.3,
            multiplicity: 't'
          }],
          delta: 0.89,
          assignment: 'CH₃'
        }],
        integration: 6,
        from: 0.86675,
        to: 0.91325
      }, {
        signals: [{
          js: [{
            coupling: 7.3,
            multiplicity: 'sept'
          }],
          delta: 1.32,
          assignment: 'CH₂'
        }],
        integration: 2,
        from: 1.26025,
        to: 1.37975
      }],
      nucleus: '1H',
      solvent: 'toluene-d₈'
    }, {
      names: ['2-propanol'],
      smiles: 'CC(O)C',
      ranges: [{
        signals: [{
          js: [{
            coupling: 6,
            multiplicity: 'd'
          }],
          delta: 0.95,
          assignment: 'CH₃'
        }],
        integration: 6,
        from: 0.9375,
        to: 0.9624999999999999
      }, {
        signals: [{
          js: [{
            coupling: 6,
            multiplicity: 'sept'
          }],
          delta: 3.65,
          assignment: 'CH'
        }],
        integration: 1,
        from: 3.6,
        to: 3.6999999999999997
      }],
      nucleus: '1H',
      solvent: 'toluene-d₈'
    }, {
      names: ['propylene'],
      smiles: 'CC=C',
      ranges: [{
        signals: [{
          js: [{
            coupling: 6.4,
            multiplicity: 'd'
          }, {
            coupling: 1.5,
            multiplicity: 't'
          }],
          delta: 1.55,
          assignment: 'CH₃'
        }],
        integration: 3,
        from: 1.53325,
        to: 1.56675
      }, {
        signals: [{
          js: [{
            coupling: 10,
            multiplicity: 'd'
          }],
          delta: 4.92,
          assignment: 'CH₂(1)'
        }],
        integration: 1,
        from: 4.9025,
        to: 4.9375
      }, {
        signals: [{
          js: [{
            coupling: 17,
            multiplicity: 'd'
          }],
          delta: 4.98,
          assignment: 'CH₂(2)'
        }],
        integration: 1,
        from: 4.95375,
        to: 5.0062500000000005
      }, {
        signals: [{
          js: [],
          delta: 5.7,
          assignment: 'CH',
          multiplicity: 'm'
        }],
        integration: 1,
        from: 5.675,
        to: 5.7250000000000005
      }],
      nucleus: '1H',
      solvent: 'toluene-d₈'
    }, {
      names: ['pyridine'],
      smiles: 'C1=CC=NC=C1',
      ranges: [{
        signals: [{
          js: [],
          delta: 8.47,
          assignment: 'CH(2,6)',
          multiplicity: 'm'
        }],
        integration: 2,
        from: 8.445,
        to: 8.495000000000001
      }, {
        signals: [{
          js: [],
          delta: 6.67,
          assignment: 'CH(3,5)',
          multiplicity: 'm'
        }],
        integration: 2,
        from: 6.645,
        to: 6.695
      }, {
        signals: [{
          js: [],
          delta: 6.99,
          assignment: 'CH(4)',
          multiplicity: 'm'
        }],
        integration: 1,
        from: 6.965,
        to: 7.015000000000001
      }],
      nucleus: '1H',
      solvent: 'toluene-d₈'
    }, {
      names: ['pyrrole'],
      smiles: 'C1=CNC=C1',
      ranges: [{
        signals: [{
          js: [],
          delta: 7.71,
          assignment: 'NH',
          multiplicity: 'br t'
        }],
        integration: 1,
        from: 7.705,
        to: 7.715
      }, {
        signals: [{
          js: [],
          delta: 6.43,
          assignment: 'CH(2,5)',
          multiplicity: 'm'
        }],
        integration: 2,
        from: 6.404999999999999,
        to: 6.455
      }, {
        signals: [{
          js: [],
          delta: 6.27,
          assignment: 'CH(3,4)',
          multiplicity: 'm'
        }],
        integration: 2,
        from: 6.244999999999999,
        to: 6.295
      }],
      nucleus: '1H',
      solvent: 'toluene-d₈'
    }, {
      names: ['pyrrolidine'],
      smiles: 'C1CCNC1',
      ranges: [{
        signals: [{
          js: [],
          delta: 2.54,
          assignment: 'CH₂(2,5)',
          multiplicity: 'm'
        }],
        integration: 4,
        from: 2.515,
        to: 2.565
      }, {
        signals: [{
          js: [],
          delta: 1.36,
          assignment: 'CH₂(3,4)',
          multiplicity: 'm'
        }],
        integration: 4,
        from: 1.3350000000000002,
        to: 1.385
      }],
      nucleus: '1H',
      solvent: 'toluene-d₈'
    }, {
      names: ['silicone grease'],
      smiles: '',
      ranges: [{
        signals: [{
          js: [],
          delta: 0.26,
          multiplicity: 's',
          assignment: 'CH₃'
        }],
        integration: 6,
        from: 0.255,
        to: 0.265
      }],
      nucleus: '1H',
      solvent: 'toluene-d₈'
    }, {
      names: ['tetrahydrofuran'],
      smiles: 'C1COCC1',
      ranges: [{
        signals: [{
          js: [],
          delta: 3.54,
          assignment: 'CH₂(2,5)',
          multiplicity: 'm'
        }],
        integration: 4,
        from: 3.515,
        to: 3.565
      }, {
        signals: [{
          js: [],
          delta: 1.43,
          assignment: 'CH₂(3,4)',
          multiplicity: 'm'
        }],
        integration: 4,
        from: 1.405,
        to: 1.4549999999999998
      }],
      nucleus: '1H',
      solvent: 'toluene-d₈'
    }, {
      names: ['toluene'],
      smiles: 'Cc1ccccc1',
      ranges: [{
        signals: [{
          js: [],
          delta: 2.11,
          multiplicity: 's',
          assignment: 'CH₃'
        }],
        integration: 3,
        from: 2.105,
        to: 2.1149999999999998
      }, {
        signals: [],
        integration: 3,
        from: 6.96,
        to: 7.01,
        multiplicity: 'm'
      }, {
        signals: [{
          js: [],
          delta: 7.09,
          assignment: 'CH(3,5)',
          multiplicity: 'm'
        }],
        integration: 2,
        from: 7.0649999999999995,
        to: 7.115
      }],
      nucleus: '1H',
      solvent: 'toluene-d₈'
    }, {
      names: ['triethylamine'],
      smiles: 'CCN(CC)CC',
      ranges: [{
        signals: [{
          js: [{
            coupling: 7,
            multiplicity: 't'
          }],
          delta: 0.95,
          assignment: 'CH₃'
        }],
        integration: 6,
        from: 0.9275,
        to: 0.9724999999999999
      }, {
        signals: [{
          js: [{
            coupling: 7,
            multiplicity: 'q'
          }],
          delta: 2.39,
          assignment: 'CH₂'
        }],
        integration: 4,
        from: 2.35875,
        to: 2.42125
      }],
      nucleus: '1H',
      solvent: 'toluene-d₈'
    }, {
      names: ['water'],
      smiles: 'O',
      ranges: [{
        signals: [{
          js: [],
          delta: 0.4,
          multiplicity: 's',
          assignment: 'OH'
        }],
        integration: 2,
        from: 0.395,
        to: 0.405
      }],
      nucleus: '1H',
      solvent: 'C₆D₆'
    }, {
      names: ['acetic acid'],
      smiles: 'CC(=O)O',
      ranges: [{
        signals: [{
          js: [],
          delta: 1.52,
          multiplicity: 's',
          assignment: 'CH₃'
        }],
        integration: 3,
        from: 1.5150000000000001,
        to: 1.525
      }],
      nucleus: '1H',
      solvent: 'C₆D₆'
    }, {
      names: ['acetone'],
      smiles: 'CC(=O)C',
      ranges: [{
        signals: [{
          js: [],
          delta: 1.55,
          multiplicity: 's',
          assignment: 'CH₃'
        }],
        integration: 6,
        from: 1.5450000000000002,
        to: 1.555
      }],
      nucleus: '1H',
      solvent: 'C₆D₆'
    }, {
      names: ['acetonitrile'],
      smiles: 'CC#N',
      ranges: [{
        signals: [{
          js: [],
          delta: 0.58,
          multiplicity: 's',
          assignment: 'CH₃'
        }],
        integration: 3,
        from: 0.575,
        to: 0.585
      }],
      nucleus: '1H',
      solvent: 'C₆D₆'
    }, {
      names: ['benzene'],
      smiles: 'c1ccccc1',
      ranges: [{
        signals: [{
          js: [],
          delta: 7.15,
          multiplicity: 's',
          assignment: 'CH'
        }],
        integration: 6,
        from: 7.1450000000000005,
        to: 7.155
      }],
      nucleus: '1H',
      solvent: 'C₆D₆'
    }, {
      names: ['tert -butyl alcohol'],
      smiles: 'CC(C)(C)O',
      ranges: [{
        signals: [{
          js: [],
          delta: 1.05,
          multiplicity: 's',
          assignment: 'CH₃'
        }],
        integration: 9,
        from: 1.0450000000000002,
        to: 1.055
      }, {
        signals: [{
          js: [],
          delta: 0.63,
          multiplicity: 's',
          assignment: 'OH'
        }],
        integration: 1,
        from: 0.625,
        to: 0.635
      }],
      nucleus: '1H',
      solvent: 'C₆D₆'
    }, {
      names: ['chloroform'],
      smiles: 'ClC(Cl)Cl',
      ranges: [{
        signals: [{
          js: [],
          delta: 6.15,
          multiplicity: 's',
          assignment: 'CH'
        }],
        integration: 1,
        from: 6.1450000000000005,
        to: 6.155
      }],
      nucleus: '1H',
      solvent: 'C₆D₆'
    }, {
      names: ['18-crown-6'],
      smiles: 'C1COCCOCCOCCOCCOCCO1',
      ranges: [{
        signals: [{
          js: [],
          delta: 3.39,
          multiplicity: 's',
          assignment: 'CH₂'
        }],
        integration: 24,
        from: 3.3850000000000002,
        to: 3.395
      }],
      nucleus: '1H',
      solvent: 'C₆D₆'
    }, {
      names: ['cyclohexane'],
      smiles: 'C1CCCCC1',
      ranges: [{
        signals: [{
          js: [],
          delta: 1.4,
          multiplicity: 's',
          assignment: 'CH₂'
        }],
        integration: 12,
        from: 1.395,
        to: 1.4049999999999998
      }],
      nucleus: '1H',
      solvent: 'C₆D₆'
    }, {
      names: ['1,2-dichloroethane'],
      smiles: 'ClCCCl',
      ranges: [{
        signals: [{
          js: [],
          delta: 2.9,
          multiplicity: 's',
          assignment: 'CH₂'
        }],
        integration: 4,
        from: 2.895,
        to: 2.905
      }],
      nucleus: '1H',
      solvent: 'C₆D₆'
    }, {
      names: ['dichloromethane'],
      smiles: 'ClCCl',
      ranges: [{
        signals: [{
          js: [],
          delta: 4.27,
          multiplicity: 's',
          assignment: 'CH₂'
        }],
        integration: 2,
        from: 4.265,
        to: 4.2749999999999995
      }],
      nucleus: '1H',
      solvent: 'C₆D₆'
    }, {
      names: ['diethyl ether'],
      smiles: 'CCOCC',
      ranges: [{
        signals: [{
          js: [{
            coupling: 7,
            multiplicity: 't'
          }],
          delta: 1.11,
          assignment: 'CH₃'
        }],
        integration: 6,
        from: 1.0875000000000001,
        to: 1.1325
      }, {
        signals: [{
          js: [{
            coupling: 7,
            multiplicity: 'q'
          }],
          delta: 3.26,
          assignment: 'CH₂'
        }],
        integration: 4,
        from: 3.22875,
        to: 3.29125
      }],
      nucleus: '1H',
      solvent: 'C₆D₆'
    }, {
      names: ['diglyme'],
      smiles: 'COCCOCCOC',
      ranges: [{
        signals: [{
          js: [],
          delta: 3.46,
          assignment: 'CH₂',
          multiplicity: 'm'
        }],
        integration: 4,
        from: 3.435,
        to: 3.485
      }, {
        signals: [{
          js: [],
          delta: 3.34,
          assignment: 'CH₂',
          multiplicity: 'm'
        }],
        integration: 4,
        from: 3.315,
        to: 3.3649999999999998
      }, {
        signals: [{
          js: [],
          delta: 3.11,
          multiplicity: 's',
          assignment: 'OCH₃'
        }],
        integration: 6,
        from: 3.105,
        to: 3.1149999999999998
      }],
      nucleus: '1H',
      solvent: 'C₆D₆'
    }, {
      names: ['dimethylformamide'],
      smiles: 'C(=O)N(C)C',
      ranges: [{
        signals: [{
          js: [],
          delta: 7.63,
          multiplicity: 's',
          assignment: 'CH'
        }],
        integration: 1,
        from: 7.625,
        to: 7.635
      }, {
        signals: [{
          js: [],
          delta: 2.36,
          multiplicity: 's',
          assignment: 'CH₃'
        }],
        integration: 3,
        from: 2.355,
        to: 2.3649999999999998
      }, {
        signals: [{
          js: [],
          delta: 1.86,
          multiplicity: 's',
          assignment: 'CH₃'
        }],
        integration: 3,
        from: 1.8550000000000002,
        to: 1.865
      }],
      nucleus: '1H',
      solvent: 'C₆D₆'
    }, {
      names: ['1,4-dioxane'],
      smiles: 'C1COCCOC1',
      ranges: [{
        signals: [{
          js: [],
          delta: 3.35,
          multiplicity: 's',
          assignment: 'CH₂'
        }],
        integration: 8,
        from: 3.345,
        to: 3.355
      }],
      nucleus: '1H',
      solvent: 'C₆D₆'
    }, {
      names: ['DME', 'diethyl ether'],
      smiles: 'CCOCC',
      ranges: [{
        signals: [{
          js: [],
          delta: 3.12,
          multiplicity: 's',
          assignment: 'CH₃'
        }],
        integration: 6,
        from: 3.115,
        to: 3.125
      }, {
        signals: [{
          js: [],
          delta: 3.33,
          multiplicity: 's',
          assignment: 'CH₂'
        }],
        integration: 4,
        from: 3.325,
        to: 3.335
      }],
      nucleus: '1H',
      solvent: 'C₆D₆'
    }, {
      names: ['ethane'],
      smiles: 'CC',
      ranges: [{
        signals: [{
          js: [],
          delta: 0.8,
          multiplicity: 's',
          assignment: 'CH₃'
        }],
        integration: 6,
        from: 0.795,
        to: 0.805
      }],
      nucleus: '1H',
      solvent: 'C₆D₆'
    }, {
      names: ['ethanol'],
      smiles: 'CCO',
      ranges: [{
        signals: [{
          js: [{
            coupling: 7,
            multiplicity: 't'
          }],
          delta: 0.96,
          assignment: 'CH₃'
        }],
        integration: 3,
        from: 0.9375,
        to: 0.9824999999999999
      }, {
        signals: [{
          js: [{
            coupling: 7,
            multiplicity: 'q'
          }],
          delta: 3.34,
          assignment: 'CH₂'
        }],
        integration: 2,
        from: 3.30875,
        to: 3.37125
      }, {
        signals: [{
          js: [],
          delta: 0.5,
          multiplicity: 's',
          assignment: 'OH'
        }],
        integration: 1,
        from: 0.495,
        to: 0.505
      }],
      nucleus: '1H',
      solvent: 'C₆D₆'
    }, {
      names: ['ethyl acetate'],
      smiles: 'CC(=O)CC',
      ranges: [{
        signals: [{
          js: [],
          delta: 1.65,
          multiplicity: 's',
          assignment: 'CH₃CO'
        }],
        integration: 3,
        from: 1.645,
        to: 1.6549999999999998
      }, {
        signals: [{
          js: [{
            coupling: 7,
            multiplicity: 'q'
          }],
          delta: 3.89,
          assignment: 'CH₂CH₃'
        }],
        integration: 2,
        from: 3.85875,
        to: 3.92125
      }, {
        signals: [{
          js: [{
            coupling: 7,
            multiplicity: 't'
          }],
          delta: 0.92,
          assignment: 'CH₂CH₃'
        }],
        integration: 3,
        from: 0.8975000000000001,
        to: 0.9425
      }],
      nucleus: '1H',
      solvent: 'C₆D₆'
    }, {
      names: ['ethylene'],
      smiles: 'C=C',
      ranges: [{
        signals: [{
          js: [],
          delta: 5.25,
          multiplicity: 's',
          assignment: 'CH₂'
        }],
        integration: 4,
        from: 5.245,
        to: 5.255
      }],
      nucleus: '1H',
      solvent: 'C₆D₆'
    }, {
      names: ['ethylene glycol'],
      smiles: 'OCCO',
      ranges: [{
        signals: [{
          js: [],
          delta: 3.41,
          multiplicity: 's',
          assignment: 'CH₂'
        }],
        integration: 4,
        from: 3.4050000000000002,
        to: 3.415
      }],
      nucleus: '1H',
      solvent: 'C₆D₆'
    }, {
      names: ['H greaseᶠ'],
      smiles: '',
      ranges: [{
        signals: [],
        integration: 6,
        from: 0.9,
        to: 0.98,
        multiplicity: 'm'
      }, {
        signals: [{
          js: [],
          delta: 1.32,
          multiplicity: 'br s',
          assignment: 'CH₂'
        }],
        integration: 10,
        from: 1.3062500000000001,
        to: 1.33375
      }],
      nucleus: '1H',
      solvent: 'C₆D₆'
    }, {
      names: ['hexamethylbenzene'],
      smiles: 'CC1=C(C(=C(C(=C1C)C)C)C)C',
      ranges: [{
        signals: [{
          js: [],
          delta: 2.13,
          multiplicity: 's',
          assignment: 'CH₃'
        }],
        integration: 18,
        from: 2.125,
        to: 2.135
      }],
      nucleus: '1H',
      solvent: 'C₆D₆'
    }, {
      names: ['n-hexane'],
      smiles: 'CCCCCC',
      ranges: [{
        signals: [{
          js: [{
            coupling: 7,
            multiplicity: 't'
          }],
          delta: 0.89,
          assignment: 'CH₃'
        }],
        integration: 6,
        from: 0.8675,
        to: 0.9125
      }, {
        signals: [{
          js: [],
          delta: 1.24,
          assignment: 'CH₂',
          multiplicity: 'm'
        }],
        integration: 8,
        from: 1.215,
        to: 1.265
      }],
      nucleus: '1H',
      solvent: 'C₆D₆'
    }, {
      names: ['HMDSO'],
      smiles: 'C[Si](C)(C)O[Si](C)(C)C',
      ranges: [{
        signals: [{
          js: [],
          delta: 0.12,
          multiplicity: 's',
          assignment: 'CH₃'
        }],
        integration: 18,
        from: 0.11499999999999999,
        to: 0.125
      }],
      nucleus: '1H',
      solvent: 'C₆D₆'
    }, {
      names: ['HMPA'],
      smiles: 'CN(C)P(=O)(N(C)C)N(C)C',
      ranges: [{
        signals: [{
          js: [{
            coupling: 9.5,
            multiplicity: 'd'
          }],
          delta: 2.4,
          assignment: 'CH₃'
        }],
        integration: 18,
        from: 2.3831249999999997,
        to: 2.416875
      }],
      nucleus: '1H',
      solvent: 'C₆D₆'
    }, {
      names: ['hydrogen'],
      smiles: '',
      ranges: [{
        signals: [{
          js: [],
          delta: 4.47,
          multiplicity: 's',
          assignment: 'H₂'
        }],
        integration: 2,
        from: 4.465,
        to: 4.475
      }],
      nucleus: '1H',
      solvent: 'C₆D₆'
    }, {
      names: ['imidazole'],
      smiles: 'C1=CN=CN1',
      ranges: [{
        signals: [{
          js: [],
          delta: 7.33,
          multiplicity: 's',
          assignment: 'CH(2)'
        }],
        integration: 1,
        from: 7.325,
        to: 7.335
      }, {
        signals: [{
          js: [],
          delta: 6.9,
          multiplicity: 's',
          assignment: 'CH(4,5)'
        }],
        integration: 2,
        from: 6.8950000000000005,
        to: 6.905
      }],
      nucleus: '1H',
      solvent: 'C₆D₆'
    }, {
      names: ['methane'],
      smiles: 'C',
      ranges: [{
        signals: [{
          js: [],
          delta: 0.16,
          multiplicity: 's',
          assignment: 'CH₄'
        }],
        integration: 4,
        from: 0.155,
        to: 0.165
      }],
      nucleus: '1H',
      solvent: 'C₆D₆'
    }, {
      names: ['methanol'],
      smiles: 'CO',
      ranges: [{
        signals: [{
          js: [],
          delta: 3.07,
          multiplicity: 's',
          assignment: 'CH₃'
        }],
        integration: 3,
        from: 3.065,
        to: 3.0749999999999997
      }],
      nucleus: '1H',
      solvent: 'C₆D₆'
    }, {
      names: ['nitromethane'],
      smiles: 'C[N+](=O)[O-]',
      ranges: [{
        signals: [{
          js: [],
          delta: 2.94,
          multiplicity: 's',
          assignment: 'CH₃'
        }],
        integration: 3,
        from: 2.935,
        to: 2.945
      }],
      nucleus: '1H',
      solvent: 'C₆D₆'
    }, {
      names: ['n-pentane'],
      smiles: 'CCCCC',
      ranges: [{
        signals: [{
          js: [{
            coupling: 7,
            multiplicity: 't'
          }],
          delta: 0.87,
          assignment: 'CH₃'
        }],
        integration: 6,
        from: 0.8475,
        to: 0.8925
      }, {
        signals: [{
          js: [],
          delta: 1.23,
          assignment: 'CH₂',
          multiplicity: 'm'
        }],
        integration: 6,
        from: 1.205,
        to: 1.255
      }],
      nucleus: '1H',
      solvent: 'C₆D₆'
    }, {
      names: ['propane'],
      smiles: 'CCC',
      ranges: [{
        signals: [{
          js: [{
            coupling: 7.3,
            multiplicity: 't'
          }],
          delta: 0.86,
          assignment: 'CH₃'
        }],
        integration: 6,
        from: 0.83675,
        to: 0.88325
      }, {
        signals: [{
          js: [{
            coupling: 7.3,
            multiplicity: 'sept'
          }],
          delta: 1.26,
          assignment: 'CH₂'
        }],
        integration: 2,
        from: 1.20025,
        to: 1.31975
      }],
      nucleus: '1H',
      solvent: 'C₆D₆'
    }, {
      names: ['2-propanol'],
      smiles: 'CC(O)C',
      ranges: [{
        signals: [{
          js: [{
            coupling: 6,
            multiplicity: 'd'
          }],
          delta: 0.95,
          assignment: 'CH₃'
        }],
        integration: 6,
        from: 0.9375,
        to: 0.9624999999999999
      }, {
        signals: [{
          js: [{
            coupling: 6,
            multiplicity: 'sept'
          }],
          delta: 3.67,
          assignment: 'CH'
        }],
        integration: 1,
        from: 3.62,
        to: 3.7199999999999998
      }],
      nucleus: '1H',
      solvent: 'C₆D₆'
    }, {
      names: ['propylene'],
      smiles: 'CC=C',
      ranges: [{
        signals: [{
          js: [{
            coupling: 6.4,
            multiplicity: 'd'
          }, {
            coupling: 1.5,
            multiplicity: 't'
          }],
          delta: 1.55,
          assignment: 'CH₃'
        }],
        integration: 3,
        from: 1.53325,
        to: 1.56675
      }, {
        signals: [{
          js: [{
            coupling: 10,
            multiplicity: 'd'
          }],
          delta: 4.95,
          assignment: 'CH₂(1)'
        }],
        integration: 1,
        from: 4.9325,
        to: 4.9675
      }, {
        signals: [{
          js: [{
            coupling: 17,
            multiplicity: 'd'
          }],
          delta: 5.01,
          assignment: 'CH₂(2)'
        }],
        integration: 1,
        from: 4.98375,
        to: 5.03625
      }, {
        signals: [{
          js: [],
          delta: 5.72,
          assignment: 'CH',
          multiplicity: 'm'
        }],
        integration: 1,
        from: 5.694999999999999,
        to: 5.745
      }],
      nucleus: '1H',
      solvent: 'C₆D₆'
    }, {
      names: ['pyridine'],
      smiles: 'C1=CC=NC=C1',
      ranges: [{
        signals: [{
          js: [],
          delta: 8.53,
          assignment: 'CH(2,6)',
          multiplicity: 'm'
        }],
        integration: 2,
        from: 8.504999999999999,
        to: 8.555
      }, {
        signals: [{
          js: [],
          delta: 6.66,
          assignment: 'CH(3,5)',
          multiplicity: 'm'
        }],
        integration: 2,
        from: 6.635,
        to: 6.6850000000000005
      }, {
        signals: [{
          js: [],
          delta: 6.98,
          assignment: 'CH(4)',
          multiplicity: 'm'
        }],
        integration: 1,
        from: 6.955,
        to: 7.005000000000001
      }],
      nucleus: '1H',
      solvent: 'C₆D₆'
    }, {
      names: ['pyrrole'],
      smiles: 'C1=CNC=C1',
      ranges: [{
        signals: [{
          js: [],
          delta: 7.8,
          assignment: 'NH',
          multiplicity: 'br t'
        }],
        integration: 1,
        from: 7.795,
        to: 7.805
      }, {
        signals: [{
          js: [],
          delta: 6.48,
          assignment: 'CH(2,5)',
          multiplicity: 'm'
        }],
        integration: 2,
        from: 6.455,
        to: 6.505000000000001
      }, {
        signals: [{
          js: [],
          delta: 6.37,
          assignment: 'CH(3,4)',
          multiplicity: 'm'
        }],
        integration: 2,
        from: 6.345,
        to: 6.3950000000000005
      }],
      nucleus: '1H',
      solvent: 'C₆D₆'
    }, {
      names: ['pyrrolidine'],
      smiles: 'C1CCNC1',
      ranges: [{
        signals: [{
          js: [],
          delta: 2.54,
          assignment: 'CH₂(2,5)',
          multiplicity: 'm'
        }],
        integration: 4,
        from: 2.515,
        to: 2.565
      }, {
        signals: [{
          js: [],
          delta: 1.33,
          assignment: 'CH₂(3,4)',
          multiplicity: 'm'
        }],
        integration: 4,
        from: 1.3050000000000002,
        to: 1.355
      }],
      nucleus: '1H',
      solvent: 'C₆D₆'
    }, {
      names: ['silicone grease'],
      smiles: '',
      ranges: [{
        signals: [{
          js: [],
          delta: 0.29,
          multiplicity: 's',
          assignment: 'CH₃'
        }],
        integration: 6,
        from: 0.285,
        to: 0.295
      }],
      nucleus: '1H',
      solvent: 'C₆D₆'
    }, {
      names: ['tetrahydrofuran'],
      smiles: 'C1COCC1',
      ranges: [{
        signals: [{
          js: [],
          delta: 3.57,
          assignment: 'CH₂(2,5)',
          multiplicity: 'm'
        }],
        integration: 4,
        from: 3.545,
        to: 3.5949999999999998
      }, {
        signals: [{
          js: [],
          delta: 1.4,
          assignment: 'CH₂(3,4)',
          multiplicity: 'm'
        }],
        integration: 4,
        from: 1.375,
        to: 1.4249999999999998
      }],
      nucleus: '1H',
      solvent: 'C₆D₆'
    }, {
      names: ['toluene'],
      smiles: 'Cc1ccccc1',
      ranges: [{
        signals: [{
          js: [],
          delta: 2.11,
          multiplicity: 's',
          assignment: 'CH₃'
        }],
        integration: 3,
        from: 2.105,
        to: 2.1149999999999998
      }, {
        signals: [{
          js: [],
          delta: 7.02,
          assignment: 'CH(2,4,6)',
          multiplicity: 'm'
        }],
        integration: 3,
        from: 6.994999999999999,
        to: 7.045
      }, {
        signals: [{
          js: [],
          delta: 7.13,
          assignment: 'CH(3,5)',
          multiplicity: 'm'
        }],
        integration: 2,
        from: 7.1049999999999995,
        to: 7.155
      }],
      nucleus: '1H',
      solvent: 'C₆D₆'
    }, {
      names: ['triethylamine'],
      smiles: 'CCN(CC)CC',
      ranges: [{
        signals: [{
          js: [{
            coupling: 7,
            multiplicity: 't'
          }],
          delta: 0.96,
          assignment: 'CH₃'
        }],
        integration: 6,
        from: 0.9375,
        to: 0.9824999999999999
      }, {
        signals: [{
          js: [{
            coupling: 7,
            multiplicity: 'q'
          }],
          delta: 2.4,
          assignment: 'CH₂'
        }],
        integration: 4,
        from: 2.36875,
        to: 2.43125
      }],
      nucleus: '1H',
      solvent: 'C₆D₆'
    }, {
      names: ['water'],
      smiles: 'O',
      ranges: [{
        signals: [{
          js: [],
          delta: 1.03,
          multiplicity: 's',
          assignment: 'OH'
        }],
        integration: 2,
        from: 1.0250000000000001,
        to: 1.035
      }],
      nucleus: '1H',
      solvent: 'C₆D₅Cl'
    }, {
      names: ['acetic acid'],
      smiles: 'CC(=O)O',
      ranges: [{
        signals: [{
          js: [],
          delta: 1.76,
          multiplicity: 's',
          assignment: 'CH₃'
        }],
        integration: 3,
        from: 1.7550000000000001,
        to: 1.765
      }],
      nucleus: '1H',
      solvent: 'C₆D₅Cl'
    }, {
      names: ['acetone'],
      smiles: 'CC(=O)C',
      ranges: [{
        signals: [{
          js: [],
          delta: 1.77,
          multiplicity: 's',
          assignment: 'CH₃'
        }],
        integration: 6,
        from: 1.7650000000000001,
        to: 1.775
      }],
      nucleus: '1H',
      solvent: 'C₆D₅Cl'
    }, {
      names: ['acetonitrile'],
      smiles: 'CC#N',
      ranges: [{
        signals: [{
          js: [],
          delta: 1.21,
          multiplicity: 's',
          assignment: 'CH₃'
        }],
        integration: 3,
        from: 1.205,
        to: 1.2149999999999999
      }],
      nucleus: '1H',
      solvent: 'C₆D₅Cl'
    }, {
      names: ['benzene'],
      smiles: 'c1ccccc1',
      ranges: [{
        signals: [{
          js: [],
          delta: 7.2,
          multiplicity: 's',
          assignment: 'CH'
        }],
        integration: 6,
        from: 7.195,
        to: 7.205
      }],
      nucleus: '1H',
      solvent: 'C₆D₅Cl'
    }, {
      names: ['tert -butyl alcohol'],
      smiles: 'CC(C)(C)O',
      ranges: [{
        signals: [{
          js: [],
          delta: 1.12,
          multiplicity: 's',
          assignment: 'CH₃'
        }],
        integration: 9,
        from: 1.1150000000000002,
        to: 1.125
      }, {
        signals: [{
          js: [],
          delta: 1.3,
          multiplicity: 's',
          assignment: 'OH'
        }],
        integration: 1,
        from: 1.2950000000000002,
        to: 1.305
      }],
      nucleus: '1H',
      solvent: 'C₆D₅Cl'
    }, {
      names: ['chloroform'],
      smiles: 'ClC(Cl)Cl',
      ranges: [{
        signals: [{
          js: [],
          delta: 6.74,
          multiplicity: 's',
          assignment: 'CH'
        }],
        integration: 1,
        from: 6.735,
        to: 6.745
      }],
      nucleus: '1H',
      solvent: 'C₆D₅Cl'
    }, {
      names: ['18-crown-6'],
      smiles: 'C1COCCOCCOCCOCCOCCO1',
      ranges: [{
        signals: [{
          js: [],
          delta: 3.41,
          multiplicity: 's',
          assignment: 'CH₂'
        }],
        integration: 24,
        from: 3.4050000000000002,
        to: 3.415
      }],
      nucleus: '1H',
      solvent: 'C₆D₅Cl'
    }, {
      names: ['cyclohexane'],
      smiles: 'C1CCCCC1',
      ranges: [{
        signals: [{
          js: [],
          delta: 1.37,
          multiplicity: 's',
          assignment: 'CH₂'
        }],
        integration: 12,
        from: 1.3650000000000002,
        to: 1.375
      }],
      nucleus: '1H',
      solvent: 'C₆D₅Cl'
    }, {
      names: ['1,2-dichloroethane'],
      smiles: 'ClCCCl',
      ranges: [{
        signals: [{
          js: [],
          delta: 3.26,
          multiplicity: 's',
          assignment: 'CH₂'
        }],
        integration: 4,
        from: 3.255,
        to: 3.2649999999999997
      }],
      nucleus: '1H',
      solvent: 'C₆D₅Cl'
    }, {
      names: ['dichloromethane'],
      smiles: 'ClCCl',
      ranges: [{
        signals: [{
          js: [],
          delta: 4.77,
          multiplicity: 's',
          assignment: 'CH₂'
        }],
        integration: 2,
        from: 4.765,
        to: 4.7749999999999995
      }],
      nucleus: '1H',
      solvent: 'C₆D₅Cl'
    }, {
      names: ['diethyl ether'],
      smiles: 'CCOCC',
      ranges: [{
        signals: [{
          js: [{
            coupling: 7,
            multiplicity: 't'
          }],
          delta: 1.1,
          assignment: 'CH₃'
        }],
        integration: 6,
        from: 1.0775000000000001,
        to: 1.1225
      }, {
        signals: [{
          js: [{
            coupling: 7,
            multiplicity: 'q'
          }],
          delta: 3.31,
          assignment: 'CH₂'
        }],
        integration: 4,
        from: 3.27875,
        to: 3.34125
      }],
      nucleus: '1H',
      solvent: 'C₆D₅Cl'
    }, {
      names: ['diglyme'],
      smiles: 'COCCOCCOC',
      ranges: [{
        signals: [{
          js: [],
          delta: 3.49,
          assignment: 'CH₂',
          multiplicity: 'm'
        }],
        integration: 4,
        from: 3.4650000000000003,
        to: 3.515
      }, {
        signals: [{
          js: [],
          delta: 3.37,
          assignment: 'CH₂',
          multiplicity: 'm'
        }],
        integration: 4,
        from: 3.345,
        to: 3.395
      }, {
        signals: [{
          js: [],
          delta: 3.16,
          multiplicity: 's',
          assignment: 'OCH₃'
        }],
        integration: 6,
        from: 3.1550000000000002,
        to: 3.165
      }],
      nucleus: '1H',
      solvent: 'C₆D₅Cl'
    }, {
      names: ['dimethylformamide'],
      smiles: 'C(=O)N(C)C',
      ranges: [{
        signals: [{
          js: [],
          delta: 7.73,
          multiplicity: 's',
          assignment: 'CH'
        }],
        integration: 1,
        from: 7.7250000000000005,
        to: 7.735
      }, {
        signals: [{
          js: [],
          delta: 2.51,
          multiplicity: 's',
          assignment: 'CH₃'
        }],
        integration: 3,
        from: 2.505,
        to: 2.5149999999999997
      }, {
        signals: [{
          js: [],
          delta: 2.3,
          multiplicity: 's',
          assignment: 'CH₃'
        }],
        integration: 3,
        from: 2.295,
        to: 2.3049999999999997
      }],
      nucleus: '1H',
      solvent: 'C₆D₅Cl'
    }, {
      names: ['1,4-dioxane'],
      smiles: 'C1COCCOC1',
      ranges: [{
        signals: [{
          js: [],
          delta: 3.45,
          multiplicity: 's',
          assignment: 'CH₂'
        }],
        integration: 8,
        from: 3.4450000000000003,
        to: 3.455
      }],
      nucleus: '1H',
      solvent: 'C₆D₅Cl'
    }, {
      names: ['DME', 'diethyl ether'],
      smiles: 'CCOCC',
      ranges: [{
        signals: [{
          js: [],
          delta: 3.17,
          multiplicity: 's',
          assignment: 'CH₃'
        }],
        integration: 6,
        from: 3.165,
        to: 3.175
      }, {
        signals: [{
          js: [],
          delta: 3.37,
          multiplicity: 's',
          assignment: 'CH₂'
        }],
        integration: 4,
        from: 3.365,
        to: 3.375
      }],
      nucleus: '1H',
      solvent: 'C₆D₅Cl'
    }, {
      names: ['ethane'],
      smiles: 'CC',
      ranges: [{
        signals: [{
          js: [],
          delta: 0.79,
          multiplicity: 's',
          assignment: 'CH₃'
        }],
        integration: 6,
        from: 0.785,
        to: 0.795
      }],
      nucleus: '1H',
      solvent: 'C₆D₅Cl'
    }, {
      names: ['ethanol'],
      smiles: 'CCO',
      ranges: [{
        signals: [{
          js: [{
            coupling: 7,
            multiplicity: 't'
          }],
          delta: 1.06,
          assignment: 'CH₃'
        }],
        integration: 3,
        from: 1.0375,
        to: 1.0825
      }, {
        signals: [{
          js: [{
            coupling: 7,
            multiplicity: 'q'
          }],
          delta: 3.51,
          assignment: 'CH₂'
        }],
        integration: 2,
        from: 3.47875,
        to: 3.54125
      }, {
        signals: [{
          js: [],
          delta: 1.39,
          multiplicity: 's',
          assignment: 'OH'
        }],
        integration: 1,
        from: 1.385,
        to: 1.3949999999999998
      }],
      nucleus: '1H',
      solvent: 'C₆D₅Cl'
    }, {
      names: ['ethyl acetate'],
      smiles: 'CC(=O)CC',
      ranges: [{
        signals: [{
          js: [],
          delta: 1.78,
          multiplicity: 's',
          assignment: 'CH₃CO'
        }],
        integration: 3,
        from: 1.7750000000000001,
        to: 1.785
      }, {
        signals: [{
          js: [{
            coupling: 7,
            multiplicity: 'q'
          }],
          delta: 3.96,
          assignment: 'CH₂CH₃'
        }],
        integration: 2,
        from: 3.92875,
        to: 3.99125
      }, {
        signals: [{
          js: [{
            coupling: 7,
            multiplicity: 't'
          }],
          delta: 1.04,
          assignment: 'CH₂CH₃'
        }],
        integration: 3,
        from: 1.0175,
        to: 1.0625
      }],
      nucleus: '1H',
      solvent: 'C₆D₅Cl'
    }, {
      names: ['ethylene'],
      smiles: 'C=C',
      ranges: [{
        signals: [{
          js: [],
          delta: 5.29,
          multiplicity: 's',
          assignment: 'CH₂'
        }],
        integration: 4,
        from: 5.285,
        to: 5.295
      }],
      nucleus: '1H',
      solvent: 'C₆D₅Cl'
    }, {
      names: ['ethylene glycol'],
      smiles: 'OCCO',
      ranges: [{
        signals: [{
          js: [],
          delta: 3.58,
          multiplicity: 's',
          assignment: 'CH₂'
        }],
        integration: 4,
        from: 3.575,
        to: 3.585
      }],
      nucleus: '1H',
      solvent: 'C₆D₅Cl'
    }, {
      names: ['H greaseᶠ'],
      smiles: '',
      ranges: [{
        signals: [],
        integration: 6,
        from: 0.86,
        to: 0.92,
        multiplicity: 'm'
      }, {
        signals: [{
          js: [],
          delta: 1.3,
          multiplicity: 'br s',
          assignment: 'CH₂'
        }],
        integration: 10,
        from: 1.2862500000000001,
        to: 1.31375
      }],
      nucleus: '1H',
      solvent: 'C₆D₅Cl'
    }, {
      names: ['hexamethylbenzene'],
      smiles: 'CC1=C(C(=C(C(=C1C)C)C)C)C',
      ranges: [{
        signals: [{
          js: [],
          delta: 2.1,
          multiplicity: 's',
          assignment: 'CH₃'
        }],
        integration: 18,
        from: 2.095,
        to: 2.105
      }],
      nucleus: '1H',
      solvent: 'C₆D₅Cl'
    }, {
      names: ['n-hexane'],
      smiles: 'CCCCCC',
      ranges: [{
        signals: [{
          js: [{
            coupling: 7,
            multiplicity: 't'
          }],
          delta: 0.85,
          assignment: 'CH₃'
        }],
        integration: 6,
        from: 0.8275,
        to: 0.8724999999999999
      }, {
        signals: [{
          js: [],
          delta: 1.19,
          assignment: 'CH₂',
          multiplicity: 'm'
        }],
        integration: 8,
        from: 1.165,
        to: 1.2149999999999999
      }],
      nucleus: '1H',
      solvent: 'C₆D₅Cl'
    }, {
      names: ['HMDSO'],
      smiles: 'C[Si](C)(C)O[Si](C)(C)C',
      ranges: [{
        signals: [{
          js: [],
          delta: 0.1,
          multiplicity: 's',
          assignment: 'CH₃'
        }],
        integration: 18,
        from: 0.095,
        to: 0.10500000000000001
      }],
      nucleus: '1H',
      solvent: 'C₆D₅Cl'
    }, {
      names: ['HMPA'],
      smiles: 'CN(C)P(=O)(N(C)C)N(C)C',
      ranges: [{
        signals: [{
          js: [{
            coupling: 9.5,
            multiplicity: 'd'
          }],
          delta: 2.47,
          assignment: 'CH₃'
        }],
        integration: 18,
        from: 2.453125,
        to: 2.4868750000000004
      }],
      nucleus: '1H',
      solvent: 'C₆D₅Cl'
    }, {
      names: ['hydrogen'],
      smiles: '',
      ranges: [{
        signals: [{
          js: [],
          delta: 4.49,
          multiplicity: 's',
          assignment: 'H₂'
        }],
        integration: 2,
        from: 4.485,
        to: 4.495
      }],
      nucleus: '1H',
      solvent: 'C₆D₅Cl'
    }, {
      names: ['imidazole'],
      smiles: 'C1=CN=CN1',
      ranges: [{
        signals: [{
          js: [],
          delta: 7.53,
          multiplicity: 's',
          assignment: 'CH(2)'
        }],
        integration: 1,
        from: 7.525,
        to: 7.535
      }, {
        signals: [{
          js: [],
          delta: 7.01,
          multiplicity: 's',
          assignment: 'CH(4,5)'
        }],
        integration: 2,
        from: 7.005,
        to: 7.015
      }],
      nucleus: '1H',
      solvent: 'C₆D₅Cl'
    }, {
      names: ['methane'],
      smiles: 'C',
      ranges: [{
        signals: [{
          js: [],
          delta: 0.15,
          multiplicity: 's',
          assignment: 'CH₄'
        }],
        integration: 4,
        from: 0.145,
        to: 0.155
      }],
      nucleus: '1H',
      solvent: 'C₆D₅Cl'
    }, {
      names: ['methanol'],
      smiles: 'CO',
      ranges: [{
        signals: [{
          js: [],
          delta: 3.25,
          multiplicity: 's',
          assignment: 'CH₃'
        }],
        integration: 3,
        from: 3.245,
        to: 3.255
      }, {
        signals: [{
          js: [],
          delta: 1.3,
          multiplicity: 's',
          assignment: 'OH'
        }],
        integration: 1,
        from: 1.2950000000000002,
        to: 1.305
      }],
      nucleus: '1H',
      solvent: 'C₆D₅Cl'
    }, {
      names: ['nitromethane'],
      smiles: 'C[N+](=O)[O-]',
      ranges: [{
        signals: [{
          js: [],
          delta: 3.59,
          multiplicity: 's',
          assignment: 'CH₃'
        }],
        integration: 3,
        from: 3.585,
        to: 3.5949999999999998
      }],
      nucleus: '1H',
      solvent: 'C₆D₅Cl'
    }, {
      names: ['n-pentane'],
      smiles: 'CCCCC',
      ranges: [{
        signals: [{
          js: [{
            coupling: 7,
            multiplicity: 't'
          }],
          delta: 0.84,
          assignment: 'CH₃'
        }],
        integration: 6,
        from: 0.8175,
        to: 0.8624999999999999
      }, {
        signals: [{
          js: [],
          delta: 1.23,
          assignment: 'CH₂',
          multiplicity: 'm'
        }],
        integration: 6,
        from: 1.205,
        to: 1.255
      }],
      nucleus: '1H',
      solvent: 'C₆D₅Cl'
    }, {
      names: ['propane'],
      smiles: 'CCC',
      ranges: [{
        signals: [{
          js: [{
            coupling: 7.3,
            multiplicity: 't'
          }],
          delta: 0.84,
          assignment: 'CH₃'
        }],
        integration: 6,
        from: 0.81675,
        to: 0.86325
      }, {
        signals: [{
          js: [{
            coupling: 7.3,
            multiplicity: 'sept'
          }],
          delta: 1.26,
          assignment: 'CH₂'
        }],
        integration: 2,
        from: 1.20025,
        to: 1.31975
      }],
      nucleus: '1H',
      solvent: 'C₆D₅Cl'
    }, {
      names: ['2-propanol'],
      smiles: 'CC(O)C',
      ranges: [{
        signals: [{
          js: [{
            coupling: 6,
            multiplicity: 'd'
          }],
          delta: 1.04,
          assignment: 'CH₃'
        }],
        integration: 6,
        from: 1.0275,
        to: 1.0525
      }, {
        signals: [{
          js: [{
            coupling: 6,
            multiplicity: 'sept'
          }],
          delta: 3.82,
          assignment: 'CH'
        }],
        integration: 1,
        from: 3.77,
        to: 3.8699999999999997
      }],
      nucleus: '1H',
      solvent: 'C₆D₅Cl'
    }, {
      names: ['propylene'],
      smiles: 'CC=C',
      ranges: [{
        signals: [{
          js: [{
            coupling: 6.4,
            multiplicity: 'd'
          }, {
            coupling: 1.5,
            multiplicity: 't'
          }],
          delta: 1.58,
          assignment: 'CH₃'
        }],
        integration: 3,
        from: 1.56325,
        to: 1.5967500000000001
      }, {
        signals: [{
          js: [{
            coupling: 10,
            multiplicity: 'd'
          }],
          delta: 4.91,
          assignment: 'CH₂(1)'
        }],
        integration: 1,
        from: 4.8925,
        to: 4.9275
      }, {
        signals: [{
          js: [{
            coupling: 17,
            multiplicity: 'd'
          }],
          delta: 4.98,
          assignment: 'CH₂(2)'
        }],
        integration: 1,
        from: 4.95375,
        to: 5.0062500000000005
      }, {
        signals: [{
          js: [],
          delta: 5.72,
          assignment: 'CH',
          multiplicity: 'm'
        }],
        integration: 1,
        from: 5.694999999999999,
        to: 5.745
      }],
      nucleus: '1H',
      solvent: 'C₆D₅Cl'
    }, {
      names: ['pyridine'],
      smiles: 'C1=CC=NC=C1',
      ranges: [{
        signals: [{
          js: [],
          delta: 8.51,
          assignment: 'CH(2,6)',
          multiplicity: 'm'
        }],
        integration: 2,
        from: 8.485,
        to: 8.535
      }, {
        signals: [{
          js: [],
          delta: 6.9,
          assignment: 'CH(3,5)',
          multiplicity: 'm'
        }],
        integration: 2,
        from: 6.875,
        to: 6.925000000000001
      }, {
        signals: [{
          js: [],
          delta: 7.25,
          assignment: 'CH(4)',
          multiplicity: 'm'
        }],
        integration: 1,
        from: 7.225,
        to: 7.275
      }],
      nucleus: '1H',
      solvent: 'C₆D₅Cl'
    }, {
      names: ['pyrrole'],
      smiles: 'C1=CNC=C1',
      ranges: [{
        signals: [{
          js: [],
          delta: 8.61,
          assignment: 'NH',
          multiplicity: 'br t'
        }],
        integration: 1,
        from: 8.604999999999999,
        to: 8.615
      }, {
        signals: [{
          js: [],
          delta: 6.62,
          assignment: 'CH(2,5)',
          multiplicity: 'm'
        }],
        integration: 2,
        from: 6.595,
        to: 6.6450000000000005
      }, {
        signals: [{
          js: [],
          delta: 6.27,
          assignment: 'CH(3,4)',
          multiplicity: 'm'
        }],
        integration: 2,
        from: 6.244999999999999,
        to: 6.295
      }],
      nucleus: '1H',
      solvent: 'C₆D₅Cl'
    }, {
      names: ['pyrrolidine'],
      smiles: 'C1CCNC1',
      ranges: [{
        signals: [{
          js: [],
          delta: 2.64,
          assignment: 'CH₂(2,5)',
          multiplicity: 'm'
        }],
        integration: 4,
        from: 2.615,
        to: 2.665
      }, {
        signals: [{
          js: [],
          delta: 1.43,
          assignment: 'CH₂(3,4)',
          multiplicity: 'm'
        }],
        integration: 4,
        from: 1.405,
        to: 1.4549999999999998
      }],
      nucleus: '1H',
      solvent: 'C₆D₅Cl'
    }, {
      names: ['silicone grease'],
      smiles: '',
      ranges: [{
        signals: [{
          js: [],
          delta: 0.14,
          multiplicity: 's',
          assignment: 'CH₃'
        }],
        integration: 6,
        from: 0.135,
        to: 0.14500000000000002
      }],
      nucleus: '1H',
      solvent: 'C₆D₅Cl'
    }, {
      names: ['tetrahydrofuran'],
      smiles: 'C1COCC1',
      ranges: [{
        signals: [{
          js: [],
          delta: 3.59,
          assignment: 'CH₂(2,5)',
          multiplicity: 'm'
        }],
        integration: 4,
        from: 3.565,
        to: 3.6149999999999998
      }, {
        signals: [{
          js: [],
          delta: 1.55,
          assignment: 'CH₂(3,4)',
          multiplicity: 'm'
        }],
        integration: 4,
        from: 1.5250000000000001,
        to: 1.575
      }],
      nucleus: '1H',
      solvent: 'C₆D₅Cl'
    }, {
      names: ['toluene'],
      smiles: 'Cc1ccccc1',
      ranges: [{
        signals: [{
          js: [],
          delta: 2.16,
          multiplicity: 's',
          assignment: 'CH₃'
        }],
        integration: 3,
        from: 2.1550000000000002,
        to: 2.165
      }, {
        signals: [],
        integration: 3,
        from: 7.01,
        to: 7.08,
        multiplicity: 'm'
      }, {
        signals: [],
        integration: 2,
        from: 7.1,
        to: 7.17,
        multiplicity: 'm'
      }],
      nucleus: '1H',
      solvent: 'C₆D₅Cl'
    }, {
      names: ['triethylamine'],
      smiles: 'CCN(CC)CC',
      ranges: [{
        signals: [{
          js: [{
            coupling: 7,
            multiplicity: 't'
          }],
          delta: 0.93,
          assignment: 'CH₃'
        }],
        integration: 6,
        from: 0.9075000000000001,
        to: 0.9525
      }, {
        signals: [{
          js: [{
            coupling: 7,
            multiplicity: 'q'
          }],
          delta: 2.39,
          assignment: 'CH₂'
        }],
        integration: 4,
        from: 2.35875,
        to: 2.42125
      }],
      nucleus: '1H',
      solvent: 'C₆D₅Cl'
    }, {
      names: ['water'],
      smiles: 'O',
      ranges: [{
        signals: [{
          js: [],
          delta: 2.84,
          multiplicity: 's',
          assignment: 'OH'
        }],
        integration: 2,
        from: 2.835,
        to: 2.8449999999999998
      }],
      nucleus: '1H',
      solvent: '(CD₃)₂CO'
    }, {
      names: ['acetic acid'],
      smiles: 'CC(=O)O',
      ranges: [{
        signals: [{
          js: [],
          delta: 1.96,
          multiplicity: 's',
          assignment: 'CH₃'
        }],
        integration: 3,
        from: 1.955,
        to: 1.9649999999999999
      }],
      nucleus: '1H',
      solvent: '(CD₃)₂CO'
    }, {
      names: ['acetone'],
      smiles: 'CC(=O)C',
      ranges: [{
        signals: [{
          js: [],
          delta: 2.09,
          multiplicity: 's',
          assignment: 'CH₃'
        }],
        integration: 6,
        from: 2.085,
        to: 2.0949999999999998
      }],
      nucleus: '1H',
      solvent: '(CD₃)₂CO'
    }, {
      names: ['acetonitrile'],
      smiles: 'CC#N',
      ranges: [{
        signals: [{
          js: [],
          delta: 2.05,
          multiplicity: 's',
          assignment: 'CH₃'
        }],
        integration: 3,
        from: 2.045,
        to: 2.0549999999999997
      }],
      nucleus: '1H',
      solvent: '(CD₃)₂CO'
    }, {
      names: ['benzene'],
      smiles: 'c1ccccc1',
      ranges: [{
        signals: [{
          js: [],
          delta: 7.36,
          multiplicity: 's',
          assignment: 'CH'
        }],
        integration: 6,
        from: 7.355,
        to: 7.365
      }],
      nucleus: '1H',
      solvent: '(CD₃)₂CO'
    }, {
      names: ['tert -butyl alcohol'],
      smiles: 'CC(C)(C)O',
      ranges: [{
        signals: [{
          js: [],
          delta: 1.18,
          multiplicity: 's',
          assignment: 'CH₃'
        }],
        integration: 9,
        from: 1.175,
        to: 1.1849999999999998
      }],
      nucleus: '1H',
      solvent: '(CD₃)₂CO'
    }, {
      names: ['chloroform'],
      smiles: 'ClC(Cl)Cl',
      ranges: [{
        signals: [{
          js: [],
          delta: 8.02,
          multiplicity: 's',
          assignment: 'CH'
        }],
        integration: 1,
        from: 8.014999999999999,
        to: 8.025
      }],
      nucleus: '1H',
      solvent: '(CD₃)₂CO'
    }, {
      names: ['18-crown-6'],
      smiles: 'C1COCCOCCOCCOCCOCCO1',
      ranges: [{
        signals: [{
          js: [],
          delta: 3.59,
          multiplicity: 's',
          assignment: 'CH₂'
        }],
        integration: 24,
        from: 3.585,
        to: 3.5949999999999998
      }],
      nucleus: '1H',
      solvent: '(CD₃)₂CO'
    }, {
      names: ['cyclohexane'],
      smiles: 'C1CCCCC1',
      ranges: [{
        signals: [{
          js: [],
          delta: 1.43,
          multiplicity: 's',
          assignment: 'CH₂'
        }],
        integration: 12,
        from: 1.425,
        to: 1.4349999999999998
      }],
      nucleus: '1H',
      solvent: '(CD₃)₂CO'
    }, {
      names: ['1,2-dichloroethane'],
      smiles: 'ClCCCl',
      ranges: [{
        signals: [{
          js: [],
          delta: 3.87,
          multiplicity: 's',
          assignment: 'CH₂'
        }],
        integration: 4,
        from: 3.865,
        to: 3.875
      }],
      nucleus: '1H',
      solvent: '(CD₃)₂CO'
    }, {
      names: ['dichloromethane'],
      smiles: 'ClCCl',
      ranges: [{
        signals: [{
          js: [],
          delta: 5.63,
          multiplicity: 's',
          assignment: 'CH₂'
        }],
        integration: 2,
        from: 5.625,
        to: 5.635
      }],
      nucleus: '1H',
      solvent: '(CD₃)₂CO'
    }, {
      names: ['diethyl ether'],
      smiles: 'CCOCC',
      ranges: [{
        signals: [{
          js: [{
            coupling: 7,
            multiplicity: 't'
          }],
          delta: 1.11,
          assignment: 'CH₃'
        }],
        integration: 6,
        from: 1.0875000000000001,
        to: 1.1325
      }, {
        signals: [{
          js: [{
            coupling: 7,
            multiplicity: 'q'
          }],
          delta: 3.41,
          assignment: 'CH₂'
        }],
        integration: 4,
        from: 3.37875,
        to: 3.44125
      }],
      nucleus: '1H',
      solvent: '(CD₃)₂CO'
    }, {
      names: ['diglyme'],
      smiles: 'COCCOCCOC',
      ranges: [{
        signals: [{
          js: [],
          delta: 3.56,
          assignment: 'CH₂',
          multiplicity: 'm'
        }],
        integration: 4,
        from: 3.535,
        to: 3.585
      }, {
        signals: [{
          js: [],
          delta: 3.47,
          assignment: 'CH₂',
          multiplicity: 'm'
        }],
        integration: 4,
        from: 3.4450000000000003,
        to: 3.495
      }, {
        signals: [{
          js: [],
          delta: 3.28,
          multiplicity: 's',
          assignment: 'OCH₃'
        }],
        integration: 6,
        from: 3.275,
        to: 3.2849999999999997
      }],
      nucleus: '1H',
      solvent: '(CD₃)₂CO'
    }, {
      names: ['dimethylformamide'],
      smiles: 'C(=O)N(C)C',
      ranges: [{
        signals: [{
          js: [],
          delta: 7.96,
          multiplicity: 's',
          assignment: 'CH'
        }],
        integration: 1,
        from: 7.955,
        to: 7.965
      }, {
        signals: [{
          js: [],
          delta: 2.94,
          multiplicity: 's',
          assignment: 'CH₃'
        }],
        integration: 3,
        from: 2.935,
        to: 2.945
      }, {
        signals: [{
          js: [],
          delta: 2.78,
          multiplicity: 's',
          assignment: 'CH₃'
        }],
        integration: 3,
        from: 2.775,
        to: 2.7849999999999997
      }],
      nucleus: '1H',
      solvent: '(CD₃)₂CO'
    }, {
      names: ['1,4-dioxane'],
      smiles: 'C1COCCOC1',
      ranges: [{
        signals: [{
          js: [],
          delta: 3.59,
          multiplicity: 's',
          assignment: 'CH₂'
        }],
        integration: 8,
        from: 3.585,
        to: 3.5949999999999998
      }],
      nucleus: '1H',
      solvent: '(CD₃)₂CO'
    }, {
      names: ['DME', 'diethyl ether'],
      smiles: 'CCOCC',
      ranges: [{
        signals: [{
          js: [],
          delta: 3.28,
          multiplicity: 's',
          assignment: 'CH₃'
        }],
        integration: 6,
        from: 3.275,
        to: 3.2849999999999997
      }, {
        signals: [{
          js: [],
          delta: 3.46,
          multiplicity: 's',
          assignment: 'CH₂'
        }],
        integration: 4,
        from: 3.455,
        to: 3.465
      }],
      nucleus: '1H',
      solvent: '(CD₃)₂CO'
    }, {
      names: ['ethane'],
      smiles: 'CC',
      ranges: [{
        signals: [{
          js: [],
          delta: 0.83,
          multiplicity: 's',
          assignment: 'CH₃'
        }],
        integration: 6,
        from: 0.825,
        to: 0.835
      }],
      nucleus: '1H',
      solvent: '(CD₃)₂CO'
    }, {
      names: ['ethanol'],
      smiles: 'CCO',
      ranges: [{
        signals: [{
          js: [{
            coupling: 7,
            multiplicity: 't'
          }],
          delta: 1.12,
          assignment: 'CH₃'
        }],
        integration: 3,
        from: 1.0975000000000001,
        to: 1.1425
      }, {
        signals: [{
          js: [{
            coupling: 7,
            multiplicity: 'q'
          }],
          delta: 3.57,
          assignment: 'CH₂'
        }],
        integration: 2,
        from: 3.53875,
        to: 3.60125
      }, {
        signals: [{
          js: [],
          delta: 3.39,
          multiplicity: 's',
          assignment: 'OH'
        }],
        integration: 1,
        from: 3.3850000000000002,
        to: 3.395
      }],
      nucleus: '1H',
      solvent: '(CD₃)₂CO'
    }, {
      names: ['ethyl acetate'],
      smiles: 'CC(=O)CC',
      ranges: [{
        signals: [{
          js: [],
          delta: 1.97,
          multiplicity: 's',
          assignment: 'CH₃CO'
        }],
        integration: 3,
        from: 1.965,
        to: 1.9749999999999999
      }, {
        signals: [{
          js: [{
            coupling: 7,
            multiplicity: 'q'
          }],
          delta: 4.05,
          assignment: 'CH₂CH₃'
        }],
        integration: 2,
        from: 4.01875,
        to: 4.08125
      }, {
        signals: [{
          js: [{
            coupling: 7,
            multiplicity: 't'
          }],
          delta: 1.2,
          assignment: 'CH₂CH₃'
        }],
        integration: 3,
        from: 1.1775,
        to: 1.2225
      }],
      nucleus: '1H',
      solvent: '(CD₃)₂CO'
    }, {
      names: ['ethylene'],
      smiles: 'C=C',
      ranges: [{
        signals: [{
          js: [],
          delta: 5.38,
          multiplicity: 's',
          assignment: 'CH₂'
        }],
        integration: 4,
        from: 5.375,
        to: 5.385
      }],
      nucleus: '1H',
      solvent: '(CD₃)₂CO'
    }, {
      names: ['ethylene glycol'],
      smiles: 'OCCO',
      ranges: [{
        signals: [{
          js: [],
          delta: 3.28,
          multiplicity: 's',
          assignment: 'CH₂'
        }],
        integration: 4,
        from: 3.275,
        to: 3.2849999999999997
      }],
      nucleus: '1H',
      solvent: '(CD₃)₂CO'
    }, {
      names: ['H greaseᶠ'],
      smiles: '',
      ranges: [{
        signals: [{
          js: [],
          delta: 0.9,
          assignment: 'CH₃',
          multiplicity: 'm'
        }],
        integration: 6,
        from: 0.875,
        to: 0.925
      }, {
        signals: [{
          js: [],
          delta: 1.29,
          multiplicity: 'br s',
          assignment: 'CH₂'
        }],
        integration: 10,
        from: 1.27625,
        to: 1.30375
      }],
      nucleus: '1H',
      solvent: '(CD₃)₂CO'
    }, {
      names: ['hexamethylbenzene'],
      smiles: 'CC1=C(C(=C(C(=C1C)C)C)C)C',
      ranges: [{
        signals: [{
          js: [],
          delta: 2.17,
          multiplicity: 's',
          assignment: 'CH₃'
        }],
        integration: 18,
        from: 2.165,
        to: 2.175
      }],
      nucleus: '1H',
      solvent: '(CD₃)₂CO'
    }, {
      names: ['n-hexane'],
      smiles: 'CCCCCC',
      ranges: [{
        signals: [{
          js: [{
            coupling: 7,
            multiplicity: 't'
          }],
          delta: 0.88,
          assignment: 'CH₃'
        }],
        integration: 6,
        from: 0.8575,
        to: 0.9025
      }, {
        signals: [{
          js: [],
          delta: 1.28,
          assignment: 'CH₂',
          multiplicity: 'm'
        }],
        integration: 8,
        from: 1.2550000000000001,
        to: 1.305
      }],
      nucleus: '1H',
      solvent: '(CD₃)₂CO'
    }, {
      names: ['HMDSO'],
      smiles: 'C[Si](C)(C)O[Si](C)(C)C',
      ranges: [{
        signals: [{
          js: [],
          delta: 0.07,
          multiplicity: 's',
          assignment: 'CH₃'
        }],
        integration: 18,
        from: 0.065,
        to: 0.07500000000000001
      }],
      nucleus: '1H',
      solvent: '(CD₃)₂CO'
    }, {
      names: ['HMPA'],
      smiles: 'CN(C)P(=O)(N(C)C)N(C)C',
      ranges: [{
        signals: [{
          js: [{
            coupling: 9.5,
            multiplicity: 'd'
          }],
          delta: 2.59,
          assignment: 'CH₃'
        }],
        integration: 18,
        from: 2.5731249999999997,
        to: 2.606875
      }],
      nucleus: '1H',
      solvent: '(CD₃)₂CO'
    }, {
      names: ['hydrogen'],
      smiles: '',
      ranges: [{
        signals: [{
          js: [],
          delta: 4.54,
          multiplicity: 's',
          assignment: 'H₂'
        }],
        integration: 2,
        from: 4.535,
        to: 4.545
      }],
      nucleus: '1H',
      solvent: '(CD₃)₂CO'
    }, {
      names: ['imidazole'],
      smiles: 'C1=CN=CN1',
      ranges: [{
        signals: [{
          js: [],
          delta: 7.62,
          multiplicity: 's',
          assignment: 'CH(2)'
        }],
        integration: 1,
        from: 7.615,
        to: 7.625
      }, {
        signals: [{
          js: [],
          delta: 7.04,
          multiplicity: 's',
          assignment: 'CH(4,5)'
        }],
        integration: 2,
        from: 7.035,
        to: 7.045
      }],
      nucleus: '1H',
      solvent: '(CD₃)₂CO'
    }, {
      names: ['methane'],
      smiles: 'C',
      ranges: [{
        signals: [{
          js: [],
          delta: 0.17,
          multiplicity: 's',
          assignment: 'CH₄'
        }],
        integration: 4,
        from: 0.165,
        to: 0.17500000000000002
      }],
      nucleus: '1H',
      solvent: '(CD₃)₂CO'
    }, {
      names: ['methanol'],
      smiles: 'CO',
      ranges: [{
        signals: [{
          js: [],
          delta: 3.31,
          multiplicity: 's',
          assignment: 'CH₃'
        }],
        integration: 3,
        from: 3.305,
        to: 3.315
      }, {
        signals: [{
          js: [],
          delta: 3.12,
          multiplicity: 's',
          assignment: 'OH'
        }],
        integration: 1,
        from: 3.115,
        to: 3.125
      }],
      nucleus: '1H',
      solvent: '(CD₃)₂CO'
    }, {
      names: ['nitromethane'],
      smiles: 'C[N+](=O)[O-]',
      ranges: [{
        signals: [{
          js: [],
          delta: 4.43,
          multiplicity: 's',
          assignment: 'CH₃'
        }],
        integration: 3,
        from: 4.425,
        to: 4.435
      }],
      nucleus: '1H',
      solvent: '(CD₃)₂CO'
    }, {
      names: ['n-pentane'],
      smiles: 'CCCCC',
      ranges: [{
        signals: [{
          js: [{
            coupling: 7,
            multiplicity: 't'
          }],
          delta: 0.88,
          assignment: 'CH₃'
        }],
        integration: 6,
        from: 0.8575,
        to: 0.9025
      }, {
        signals: [{
          js: [],
          delta: 1.27,
          assignment: 'CH₂',
          multiplicity: 'm'
        }],
        integration: 6,
        from: 1.245,
        to: 1.295
      }],
      nucleus: '1H',
      solvent: '(CD₃)₂CO'
    }, {
      names: ['propane'],
      smiles: 'CCC',
      ranges: [{
        signals: [{
          js: [{
            coupling: 7.3,
            multiplicity: 't'
          }],
          delta: 0.88,
          assignment: 'CH₃'
        }],
        integration: 6,
        from: 0.85675,
        to: 0.90325
      }, {
        signals: [{
          js: [{
            coupling: 7.3,
            multiplicity: 'sept'
          }],
          delta: 1.31,
          assignment: 'CH₂'
        }],
        integration: 2,
        from: 1.25025,
        to: 1.36975
      }],
      nucleus: '1H',
      solvent: '(CD₃)₂CO'
    }, {
      names: ['2-propanol'],
      smiles: 'CC(O)C',
      ranges: [{
        signals: [{
          js: [{
            coupling: 6,
            multiplicity: 'd'
          }],
          delta: 1.1,
          assignment: 'CH₃'
        }],
        integration: 6,
        from: 1.0875000000000001,
        to: 1.1125
      }, {
        signals: [{
          js: [{
            coupling: 6,
            multiplicity: 'sept'
          }],
          delta: 3.9,
          assignment: 'CH'
        }],
        integration: 1,
        from: 3.85,
        to: 3.9499999999999997
      }],
      nucleus: '1H',
      solvent: '(CD₃)₂CO'
    }, {
      names: ['propylene'],
      smiles: 'CC=C',
      ranges: [{
        signals: [{
          js: [{
            coupling: 6.4,
            multiplicity: 'd'
          }, {
            coupling: 1.5,
            multiplicity: 't'
          }],
          delta: 1.68,
          assignment: 'CH₃'
        }],
        integration: 3,
        from: 1.66325,
        to: 1.69675
      }, {
        signals: [{
          js: [{
            coupling: 10,
            multiplicity: 'd'
          }],
          delta: 4.9,
          assignment: 'CH₂(1)'
        }],
        integration: 1,
        from: 4.8825,
        to: 4.9175
      }, {
        signals: [{
          js: [{
            coupling: 17,
            multiplicity: 'd'
          }],
          delta: 5,
          assignment: 'CH₂(2)'
        }],
        integration: 1,
        from: 4.97375,
        to: 5.02625
      }, {
        signals: [{
          js: [],
          delta: 5.81,
          assignment: 'CH',
          multiplicity: 'm'
        }],
        integration: 1,
        from: 5.784999999999999,
        to: 5.835
      }],
      nucleus: '1H',
      solvent: '(CD₃)₂CO'
    }, {
      names: ['pyridine'],
      smiles: 'C1=CC=NC=C1',
      ranges: [{
        signals: [{
          js: [],
          delta: 8.58,
          assignment: 'CH(2,6)',
          multiplicity: 'm'
        }],
        integration: 2,
        from: 8.555,
        to: 8.605
      }, {
        signals: [{
          js: [],
          delta: 7.35,
          assignment: 'CH(3,5)',
          multiplicity: 'm'
        }],
        integration: 2,
        from: 7.324999999999999,
        to: 7.375
      }, {
        signals: [{
          js: [],
          delta: 7.76,
          assignment: 'CH(4)',
          multiplicity: 'm'
        }],
        integration: 1,
        from: 7.734999999999999,
        to: 7.785
      }],
      nucleus: '1H',
      solvent: '(CD₃)₂CO'
    }, {
      names: ['pyrrole'],
      smiles: 'C1=CNC=C1',
      ranges: [{
        signals: [{
          js: [],
          delta: 10.02,
          assignment: 'NH',
          multiplicity: 'br t'
        }],
        integration: 1,
        from: 10.014999999999999,
        to: 10.025
      }, {
        signals: [{
          js: [],
          delta: 6.77,
          assignment: 'CH(2,5)',
          multiplicity: 'm'
        }],
        integration: 2,
        from: 6.744999999999999,
        to: 6.795
      }, {
        signals: [{
          js: [],
          delta: 6.07,
          assignment: 'CH(3,4)',
          multiplicity: 'm'
        }],
        integration: 2,
        from: 6.045,
        to: 6.095000000000001
      }],
      nucleus: '1H',
      solvent: '(CD₃)₂CO'
    }, {
      names: ['pyrrolidine'],
      smiles: 'C1CCNC1',
      ranges: [],
      nucleus: '1H',
      solvent: '(CD₃)₂CO'
    }, {
      names: ['silicone grease'],
      smiles: '',
      ranges: [{
        signals: [{
          js: [],
          delta: 0.13,
          multiplicity: 's',
          assignment: 'CH₃'
        }],
        integration: 6,
        from: 0.125,
        to: 0.135
      }],
      nucleus: '1H',
      solvent: '(CD₃)₂CO'
    }, {
      names: ['tetrahydrofuran'],
      smiles: 'C1COCC1',
      ranges: [{
        signals: [{
          js: [],
          delta: 3.63,
          assignment: 'CH₂(2,5)',
          multiplicity: 'm'
        }],
        integration: 4,
        from: 3.605,
        to: 3.655
      }, {
        signals: [{
          js: [],
          delta: 1.79,
          assignment: 'CH₂(3,4)',
          multiplicity: 'm'
        }],
        integration: 4,
        from: 1.7650000000000001,
        to: 1.815
      }],
      nucleus: '1H',
      solvent: '(CD₃)₂CO'
    }, {
      names: ['toluene'],
      smiles: 'Cc1ccccc1',
      ranges: [{
        signals: [{
          js: [],
          delta: 2.32,
          multiplicity: 's',
          assignment: 'CH₃'
        }],
        integration: 3,
        from: 2.315,
        to: 2.3249999999999997
      }, {
        signals: [],
        integration: 3,
        from: 7.1,
        to: 7.2,
        multiplicity: 'm'
      }, {
        signals: [],
        integration: 2,
        from: 7.1,
        to: 7.2,
        multiplicity: 'm'
      }],
      nucleus: '1H',
      solvent: '(CD₃)₂CO'
    }, {
      names: ['triethylamine'],
      smiles: 'CCN(CC)CC',
      ranges: [{
        signals: [{
          js: [{
            coupling: 7,
            multiplicity: 't'
          }],
          delta: 0.96,
          assignment: 'CH₃'
        }],
        integration: 6,
        from: 0.9375,
        to: 0.9824999999999999
      }, {
        signals: [{
          js: [{
            coupling: 7,
            multiplicity: 'q'
          }],
          delta: 2.45,
          assignment: 'CH₂'
        }],
        integration: 4,
        from: 2.41875,
        to: 2.48125
      }],
      nucleus: '1H',
      solvent: '(CD₃)₂CO'
    }, {
      names: ['water'],
      smiles: 'O',
      ranges: [{
        signals: [{
          js: [],
          delta: 3.33,
          multiplicity: 's',
          assignment: 'OH'
        }],
        integration: 2,
        from: 3.325,
        to: 3.335
      }],
      nucleus: '1H',
      solvent: '(CD₃)₂SO'
    }, {
      names: ['acetic acid'],
      smiles: 'CC(=O)O',
      ranges: [{
        signals: [{
          js: [],
          delta: 1.91,
          multiplicity: 's',
          assignment: 'CH₃'
        }],
        integration: 3,
        from: 1.905,
        to: 1.9149999999999998
      }],
      nucleus: '1H',
      solvent: '(CD₃)₂SO'
    }, {
      names: ['acetone'],
      smiles: 'CC(=O)C',
      ranges: [{
        signals: [{
          js: [],
          delta: 2.09,
          multiplicity: 's',
          assignment: 'CH₃'
        }],
        integration: 6,
        from: 2.085,
        to: 2.0949999999999998
      }],
      nucleus: '1H',
      solvent: '(CD₃)₂SO'
    }, {
      names: ['acetonitrile'],
      smiles: 'CC#N',
      ranges: [{
        signals: [{
          js: [],
          delta: 2.07,
          multiplicity: 's',
          assignment: 'CH₃'
        }],
        integration: 3,
        from: 2.065,
        to: 2.0749999999999997
      }],
      nucleus: '1H',
      solvent: '(CD₃)₂SO'
    }, {
      names: ['benzene'],
      smiles: 'c1ccccc1',
      ranges: [{
        signals: [{
          js: [],
          delta: 7.37,
          multiplicity: 's',
          assignment: 'CH'
        }],
        integration: 6,
        from: 7.365,
        to: 7.375
      }],
      nucleus: '1H',
      solvent: '(CD₃)₂SO'
    }, {
      names: ['tert -butyl alcohol'],
      smiles: 'CC(C)(C)O',
      ranges: [{
        signals: [{
          js: [],
          delta: 1.11,
          multiplicity: 's',
          assignment: 'CH₃'
        }],
        integration: 9,
        from: 1.1050000000000002,
        to: 1.115
      }, {
        signals: [{
          js: [],
          delta: 4.19,
          multiplicity: 's',
          assignment: 'OH'
        }],
        integration: 1,
        from: 4.1850000000000005,
        to: 4.195
      }],
      nucleus: '1H',
      solvent: '(CD₃)₂SO'
    }, {
      names: ['chloroform'],
      smiles: 'ClC(Cl)Cl',
      ranges: [{
        signals: [{
          js: [],
          delta: 8.32,
          multiplicity: 's',
          assignment: 'CH'
        }],
        integration: 1,
        from: 8.315,
        to: 8.325000000000001
      }],
      nucleus: '1H',
      solvent: '(CD₃)₂SO'
    }, {
      names: ['18-crown-6'],
      smiles: 'C1COCCOCCOCCOCCOCCO1',
      ranges: [{
        signals: [{
          js: [],
          delta: 3.51,
          multiplicity: 's',
          assignment: 'CH₂'
        }],
        integration: 24,
        from: 3.505,
        to: 3.5149999999999997
      }],
      nucleus: '1H',
      solvent: '(CD₃)₂SO'
    }, {
      names: ['cyclohexane'],
      smiles: 'C1CCCCC1',
      ranges: [{
        signals: [{
          js: [],
          delta: 1.4,
          multiplicity: 's',
          assignment: 'CH₂'
        }],
        integration: 12,
        from: 1.395,
        to: 1.4049999999999998
      }],
      nucleus: '1H',
      solvent: '(CD₃)₂SO'
    }, {
      names: ['1,2-dichloroethane'],
      smiles: 'ClCCCl',
      ranges: [{
        signals: [{
          js: [],
          delta: 3.9,
          multiplicity: 's',
          assignment: 'CH₂'
        }],
        integration: 4,
        from: 3.895,
        to: 3.905
      }],
      nucleus: '1H',
      solvent: '(CD₃)₂SO'
    }, {
      names: ['dichloromethane'],
      smiles: 'ClCCl',
      ranges: [{
        signals: [{
          js: [],
          delta: 5.76,
          multiplicity: 's',
          assignment: 'CH₂'
        }],
        integration: 2,
        from: 5.755,
        to: 5.765
      }],
      nucleus: '1H',
      solvent: '(CD₃)₂SO'
    }, {
      names: ['diethyl ether'],
      smiles: 'CCOCC',
      ranges: [{
        signals: [{
          js: [{
            coupling: 7,
            multiplicity: 't'
          }],
          delta: 1.09,
          assignment: 'CH₃'
        }],
        integration: 6,
        from: 1.0675000000000001,
        to: 1.1125
      }, {
        signals: [{
          js: [{
            coupling: 7,
            multiplicity: 'q'
          }],
          delta: 3.38,
          assignment: 'CH₂'
        }],
        integration: 4,
        from: 3.34875,
        to: 3.41125
      }],
      nucleus: '1H',
      solvent: '(CD₃)₂SO'
    }, {
      names: ['diglyme'],
      smiles: 'COCCOCCOC',
      ranges: [{
        signals: [{
          js: [],
          delta: 3.51,
          assignment: 'CH₂',
          multiplicity: 'm'
        }],
        integration: 4,
        from: 3.485,
        to: 3.5349999999999997
      }, {
        signals: [{
          js: [],
          delta: 3.38,
          assignment: 'CH₂',
          multiplicity: 'm'
        }],
        integration: 4,
        from: 3.355,
        to: 3.405
      }, {
        signals: [{
          js: [],
          delta: 3.24,
          multiplicity: 's',
          assignment: 'OCH₃'
        }],
        integration: 6,
        from: 3.2350000000000003,
        to: 3.245
      }],
      nucleus: '1H',
      solvent: '(CD₃)₂SO'
    }, {
      names: ['dimethylformamide'],
      smiles: 'C(=O)N(C)C',
      ranges: [{
        signals: [{
          js: [],
          delta: 7.95,
          multiplicity: 's',
          assignment: 'CH'
        }],
        integration: 1,
        from: 7.945,
        to: 7.955
      }, {
        signals: [{
          js: [],
          delta: 2.89,
          multiplicity: 's',
          assignment: 'CH₃'
        }],
        integration: 3,
        from: 2.8850000000000002,
        to: 2.895
      }, {
        signals: [{
          js: [],
          delta: 2.73,
          multiplicity: 's',
          assignment: 'CH₃'
        }],
        integration: 3,
        from: 2.725,
        to: 2.735
      }],
      nucleus: '1H',
      solvent: '(CD₃)₂SO'
    }, {
      names: ['1,4-dioxane'],
      smiles: 'C1COCCOC1',
      ranges: [{
        signals: [{
          js: [],
          delta: 3.57,
          multiplicity: 's',
          assignment: 'CH₂'
        }],
        integration: 8,
        from: 3.565,
        to: 3.5749999999999997
      }],
      nucleus: '1H',
      solvent: '(CD₃)₂SO'
    }, {
      names: ['DME', 'diethyl ether'],
      smiles: 'CCOCC',
      ranges: [{
        signals: [{
          js: [],
          delta: 3.24,
          multiplicity: 's',
          assignment: 'CH₃'
        }],
        integration: 6,
        from: 3.2350000000000003,
        to: 3.245
      }, {
        signals: [{
          js: [],
          delta: 3.43,
          multiplicity: 's',
          assignment: 'CH₂'
        }],
        integration: 4,
        from: 3.4250000000000003,
        to: 3.435
      }],
      nucleus: '1H',
      solvent: '(CD₃)₂SO'
    }, {
      names: ['ethane'],
      smiles: 'CC',
      ranges: [{
        signals: [{
          js: [],
          delta: 0.82,
          multiplicity: 's',
          assignment: 'CH₃'
        }],
        integration: 6,
        from: 0.815,
        to: 0.825
      }],
      nucleus: '1H',
      solvent: '(CD₃)₂SO'
    }, {
      names: ['ethanol'],
      smiles: 'CCO',
      ranges: [{
        signals: [{
          js: [{
            coupling: 7,
            multiplicity: 't'
          }],
          delta: 1.06,
          assignment: 'CH₃'
        }],
        integration: 3,
        from: 1.0375,
        to: 1.0825
      }, {
        signals: [{
          js: [{
            coupling: 7,
            multiplicity: 'q'
          }],
          delta: 3.44,
          assignment: 'CH₂'
        }],
        integration: 2,
        from: 3.40875,
        to: 3.47125
      }, {
        signals: [{
          js: [],
          delta: 4.63,
          multiplicity: 's',
          assignment: 'OH'
        }],
        integration: 1,
        from: 4.625,
        to: 4.635
      }],
      nucleus: '1H',
      solvent: '(CD₃)₂SO'
    }, {
      names: ['ethyl acetate'],
      smiles: 'CC(=O)CC',
      ranges: [{
        signals: [{
          js: [],
          delta: 1.99,
          multiplicity: 's',
          assignment: 'CH₃CO'
        }],
        integration: 3,
        from: 1.985,
        to: 1.9949999999999999
      }, {
        signals: [{
          js: [{
            coupling: 7,
            multiplicity: 'q'
          }],
          delta: 4.03,
          assignment: 'CH₂CH₃'
        }],
        integration: 2,
        from: 3.9987500000000002,
        to: 4.06125
      }, {
        signals: [{
          js: [{
            coupling: 7,
            multiplicity: 't'
          }],
          delta: 1.17,
          assignment: 'CH₂CH₃'
        }],
        integration: 3,
        from: 1.1475,
        to: 1.1925
      }],
      nucleus: '1H',
      solvent: '(CD₃)₂SO'
    }, {
      names: ['ethylene'],
      smiles: 'C=C',
      ranges: [{
        signals: [{
          js: [],
          delta: 5.41,
          multiplicity: 's',
          assignment: 'CH₂'
        }],
        integration: 4,
        from: 5.405,
        to: 5.415
      }],
      nucleus: '1H',
      solvent: '(CD₃)₂SO'
    }, {
      names: ['ethylene glycol'],
      smiles: 'OCCO',
      ranges: [{
        signals: [{
          js: [],
          delta: 3.34,
          multiplicity: 's',
          assignment: 'CH₂'
        }],
        integration: 4,
        from: 3.335,
        to: 3.3449999999999998
      }],
      nucleus: '1H',
      solvent: '(CD₃)₂SO'
    }, {
      names: ['H greaseᶠ'],
      smiles: '',
      ranges: [{
        signals: [],
        integration: 6,
        from: 0.82,
        to: 0.88,
        multiplicity: 'm'
      }, {
        signals: [{
          js: [],
          delta: 1.24,
          multiplicity: 'br s',
          assignment: 'CH₂'
        }],
        integration: 10,
        from: 1.22625,
        to: 1.25375
      }],
      nucleus: '1H',
      solvent: '(CD₃)₂SO'
    }, {
      names: ['hexamethylbenzene'],
      smiles: 'CC1=C(C(=C(C(=C1C)C)C)C)C',
      ranges: [{
        signals: [{
          js: [],
          delta: 2.14,
          multiplicity: 's',
          assignment: 'CH₃'
        }],
        integration: 18,
        from: 2.1350000000000002,
        to: 2.145
      }],
      nucleus: '1H',
      solvent: '(CD₃)₂SO'
    }, {
      names: ['n-hexane'],
      smiles: 'CCCCCC',
      ranges: [{
        signals: [{
          js: [{
            coupling: 7,
            multiplicity: 't'
          }],
          delta: 0.86,
          assignment: 'CH₃'
        }],
        integration: 6,
        from: 0.8375,
        to: 0.8825
      }, {
        signals: [{
          js: [],
          delta: 1.25,
          assignment: 'CH₂',
          multiplicity: 'm'
        }],
        integration: 8,
        from: 1.225,
        to: 1.275
      }],
      nucleus: '1H',
      solvent: '(CD₃)₂SO'
    }, {
      names: ['HMDSO'],
      smiles: 'C[Si](C)(C)O[Si](C)(C)C',
      ranges: [{
        signals: [{
          js: [],
          delta: 0.06,
          multiplicity: 's',
          assignment: 'CH₃'
        }],
        integration: 18,
        from: 0.055,
        to: 0.065
      }],
      nucleus: '1H',
      solvent: '(CD₃)₂SO'
    }, {
      names: ['HMPA'],
      smiles: 'CN(C)P(=O)(N(C)C)N(C)C',
      ranges: [{
        signals: [{
          js: [{
            coupling: 9.5,
            multiplicity: 'd'
          }],
          delta: 2.53,
          assignment: 'CH₃'
        }],
        integration: 18,
        from: 2.5131249999999996,
        to: 2.546875
      }],
      nucleus: '1H',
      solvent: '(CD₃)₂SO'
    }, {
      names: ['hydrogen'],
      smiles: '',
      ranges: [{
        signals: [{
          js: [],
          delta: 4.61,
          multiplicity: 's',
          assignment: 'H₂'
        }],
        integration: 2,
        from: 4.605,
        to: 4.615
      }],
      nucleus: '1H',
      solvent: '(CD₃)₂SO'
    }, {
      names: ['imidazole'],
      smiles: 'C1=CN=CN1',
      ranges: [{
        signals: [{
          js: [],
          delta: 7.63,
          multiplicity: 's',
          assignment: 'CH(2)'
        }],
        integration: 1,
        from: 7.625,
        to: 7.635
      }, {
        signals: [{
          js: [],
          delta: 7.01,
          multiplicity: 's',
          assignment: 'CH(4,5)'
        }],
        integration: 2,
        from: 7.005,
        to: 7.015
      }],
      nucleus: '1H',
      solvent: '(CD₃)₂SO'
    }, {
      names: ['methane'],
      smiles: 'C',
      ranges: [{
        signals: [{
          js: [],
          delta: 0.2,
          multiplicity: 's',
          assignment: 'CH₄'
        }],
        integration: 4,
        from: 0.195,
        to: 0.20500000000000002
      }],
      nucleus: '1H',
      solvent: '(CD₃)₂SO'
    }, {
      names: ['methanol'],
      smiles: 'CO',
      ranges: [{
        signals: [{
          js: [],
          delta: 3.16,
          multiplicity: 's',
          assignment: 'CH₃'
        }],
        integration: 3,
        from: 3.1550000000000002,
        to: 3.165
      }, {
        signals: [{
          js: [],
          delta: 4.01,
          multiplicity: 's',
          assignment: 'OH'
        }],
        integration: 1,
        from: 4.005,
        to: 4.015
      }],
      nucleus: '1H',
      solvent: '(CD₃)₂SO'
    }, {
      names: ['nitromethane'],
      smiles: 'C[N+](=O)[O-]',
      ranges: [{
        signals: [{
          js: [],
          delta: 4.42,
          multiplicity: 's',
          assignment: 'CH₃'
        }],
        integration: 3,
        from: 4.415,
        to: 4.425
      }],
      nucleus: '1H',
      solvent: '(CD₃)₂SO'
    }, {
      names: ['n-pentane'],
      smiles: 'CCCCC',
      ranges: [{
        signals: [{
          js: [{
            coupling: 7,
            multiplicity: 't'
          }],
          delta: 0.86,
          assignment: 'CH₃'
        }],
        integration: 6,
        from: 0.8375,
        to: 0.8825
      }, {
        signals: [{
          js: [],
          delta: 1.27,
          assignment: 'CH₂',
          multiplicity: 'm'
        }],
        integration: 6,
        from: 1.245,
        to: 1.295
      }],
      nucleus: '1H',
      solvent: '(CD₃)₂SO'
    }, {
      names: ['propane'],
      smiles: 'CCC',
      ranges: [{
        signals: [{
          js: [{
            coupling: 7.3,
            multiplicity: 't'
          }],
          delta: 0.87,
          assignment: 'CH₃'
        }],
        integration: 6,
        from: 0.84675,
        to: 0.89325
      }, {
        signals: [{
          js: [{
            coupling: 7.3,
            multiplicity: 'sept'
          }],
          delta: 1.29,
          assignment: 'CH₂'
        }],
        integration: 2,
        from: 1.23025,
        to: 1.34975
      }],
      nucleus: '1H',
      solvent: '(CD₃)₂SO'
    }, {
      names: ['2-propanol'],
      smiles: 'CC(O)C',
      ranges: [{
        signals: [{
          js: [{
            coupling: 6,
            multiplicity: 'd'
          }],
          delta: 1.04,
          assignment: 'CH₃'
        }],
        integration: 6,
        from: 1.0275,
        to: 1.0525
      }, {
        signals: [{
          js: [{
            coupling: 6,
            multiplicity: 'sept'
          }],
          delta: 3.78,
          assignment: 'CH'
        }],
        integration: 1,
        from: 3.73,
        to: 3.8299999999999996
      }],
      nucleus: '1H',
      solvent: '(CD₃)₂SO'
    }, {
      names: ['propylene'],
      smiles: 'CC=C',
      ranges: [{
        signals: [{
          js: [{
            coupling: 6.4,
            multiplicity: 'd'
          }, {
            coupling: 1.5,
            multiplicity: 't'
          }],
          delta: 1.68,
          assignment: 'CH₃'
        }],
        integration: 3,
        from: 1.66325,
        to: 1.69675
      }, {
        signals: [{
          js: [{
            coupling: 10,
            multiplicity: 'd'
          }],
          delta: 4.94,
          assignment: 'CH₂(1)'
        }],
        integration: 1,
        from: 4.9225,
        to: 4.9575000000000005
      }, {
        signals: [{
          js: [{
            coupling: 17,
            multiplicity: 'd'
          }],
          delta: 5.03,
          assignment: 'CH₂(2)'
        }],
        integration: 1,
        from: 5.00375,
        to: 5.05625
      }, {
        signals: [{
          js: [],
          delta: 5.8,
          assignment: 'CH',
          multiplicity: 'm'
        }],
        integration: 1,
        from: 5.7749999999999995,
        to: 5.825
      }],
      nucleus: '1H',
      solvent: '(CD₃)₂SO'
    }, {
      names: ['pyridine'],
      smiles: 'C1=CC=NC=C1',
      ranges: [{
        signals: [{
          js: [],
          delta: 8.58,
          assignment: 'CH(2,6)',
          multiplicity: 'm'
        }],
        integration: 2,
        from: 8.555,
        to: 8.605
      }, {
        signals: [{
          js: [],
          delta: 7.39,
          assignment: 'CH(3,5)',
          multiplicity: 'm'
        }],
        integration: 2,
        from: 7.364999999999999,
        to: 7.415
      }, {
        signals: [{
          js: [],
          delta: 7.79,
          assignment: 'CH(4)',
          multiplicity: 'm'
        }],
        integration: 1,
        from: 7.765,
        to: 7.815
      }],
      nucleus: '1H',
      solvent: '(CD₃)₂SO'
    }, {
      names: ['pyrrole'],
      smiles: 'C1=CNC=C1',
      ranges: [{
        signals: [{
          js: [],
          delta: 10.75,
          assignment: 'NH',
          multiplicity: 'br t'
        }],
        integration: 1,
        from: 10.745,
        to: 10.755
      }, {
        signals: [{
          js: [],
          delta: 6.73,
          assignment: 'CH(2,5)',
          multiplicity: 'm'
        }],
        integration: 2,
        from: 6.705,
        to: 6.755000000000001
      }, {
        signals: [{
          js: [],
          delta: 6.01,
          assignment: 'CH(3,4)',
          multiplicity: 'm'
        }],
        integration: 2,
        from: 5.984999999999999,
        to: 6.035
      }],
      nucleus: '1H',
      solvent: '(CD₃)₂SO'
    }, {
      names: ['pyrrolidine'],
      smiles: 'C1CCNC1',
      ranges: [{
        signals: [{
          js: [],
          delta: 2.67,
          assignment: 'CH₂(2,5)',
          multiplicity: 'm'
        }],
        integration: 4,
        from: 2.645,
        to: 2.695
      }, {
        signals: [{
          js: [],
          delta: 1.55,
          assignment: 'CH₂(3,4)',
          multiplicity: 'm'
        }],
        integration: 4,
        from: 1.5250000000000001,
        to: 1.575
      }],
      nucleus: '1H',
      solvent: '(CD₃)₂SO'
    }, {
      names: ['silicone grease'],
      smiles: '',
      ranges: [{
        signals: [{
          js: [],
          delta: -0.06,
          multiplicity: 's',
          assignment: 'CH₃'
        }],
        integration: 6,
        from: -0.065,
        to: -0.055
      }],
      nucleus: '1H',
      solvent: '(CD₃)₂SO'
    }, {
      names: ['tetrahydrofuran'],
      smiles: 'C1COCC1',
      ranges: [{
        signals: [{
          js: [],
          delta: 3.6,
          assignment: 'CH₂(2,5)',
          multiplicity: 'm'
        }],
        integration: 4,
        from: 3.575,
        to: 3.625
      }, {
        signals: [{
          js: [],
          delta: 1.76,
          assignment: 'CH₂(3,4)',
          multiplicity: 'm'
        }],
        integration: 4,
        from: 1.735,
        to: 1.785
      }],
      nucleus: '1H',
      solvent: '(CD₃)₂SO'
    }, {
      names: ['toluene'],
      smiles: 'Cc1ccccc1',
      ranges: [{
        signals: [{
          js: [],
          delta: 2.3,
          multiplicity: 's',
          assignment: 'CH₃'
        }],
        integration: 3,
        from: 2.295,
        to: 2.3049999999999997
      }, {
        signals: [{
          js: [],
          delta: 7.18,
          assignment: 'CH(2,4,6)',
          multiplicity: 'm'
        }],
        integration: 3,
        from: 7.154999999999999,
        to: 7.205
      }, {
        signals: [{
          js: [],
          delta: 7.25,
          assignment: 'CH(3,5)',
          multiplicity: 'm'
        }],
        integration: 2,
        from: 7.225,
        to: 7.275
      }],
      nucleus: '1H',
      solvent: '(CD₃)₂SO'
    }, {
      names: ['triethylamine'],
      smiles: 'CCN(CC)CC',
      ranges: [{
        signals: [{
          js: [{
            coupling: 7,
            multiplicity: 't'
          }],
          delta: 0.93,
          assignment: 'CH₃'
        }],
        integration: 6,
        from: 0.9075000000000001,
        to: 0.9525
      }, {
        signals: [{
          js: [{
            coupling: 7,
            multiplicity: 'q'
          }],
          delta: 2.43,
          assignment: 'CH₂'
        }],
        integration: 4,
        from: 2.39875,
        to: 2.46125
      }],
      nucleus: '1H',
      solvent: '(CD₃)₂SO'
    }, {
      names: ['water'],
      smiles: 'O',
      ranges: [{
        signals: [{
          js: [],
          delta: 2.13,
          multiplicity: 's',
          assignment: 'OH'
        }],
        integration: 2,
        from: 2.125,
        to: 2.135
      }],
      nucleus: '1H',
      solvent: 'CD₃CN'
    }, {
      names: ['acetic acid'],
      smiles: 'CC(=O)O',
      ranges: [{
        signals: [{
          js: [],
          delta: 1.96,
          multiplicity: 's',
          assignment: 'CH₃'
        }],
        integration: 3,
        from: 1.955,
        to: 1.9649999999999999
      }],
      nucleus: '1H',
      solvent: 'CD₃CN'
    }, {
      names: ['acetone'],
      smiles: 'CC(=O)C',
      ranges: [{
        signals: [{
          js: [],
          delta: 2.08,
          multiplicity: 's',
          assignment: 'CH₃'
        }],
        integration: 6,
        from: 2.075,
        to: 2.085
      }],
      nucleus: '1H',
      solvent: 'CD₃CN'
    }, {
      names: ['acetonitrile'],
      smiles: 'CC#N',
      ranges: [{
        signals: [{
          js: [],
          delta: 1.96,
          multiplicity: 's',
          assignment: 'CH₃'
        }],
        integration: 3,
        from: 1.955,
        to: 1.9649999999999999
      }],
      nucleus: '1H',
      solvent: 'CD₃CN'
    }, {
      names: ['benzene'],
      smiles: 'c1ccccc1',
      ranges: [{
        signals: [{
          js: [],
          delta: 7.37,
          multiplicity: 's',
          assignment: 'CH'
        }],
        integration: 6,
        from: 7.365,
        to: 7.375
      }],
      nucleus: '1H',
      solvent: 'CD₃CN'
    }, {
      names: ['tert -butyl alcohol'],
      smiles: 'CC(C)(C)O',
      ranges: [{
        signals: [{
          js: [],
          delta: 1.16,
          multiplicity: 's',
          assignment: 'CH₃'
        }],
        integration: 9,
        from: 1.155,
        to: 1.1649999999999998
      }, {
        signals: [{
          js: [],
          delta: 2.18,
          multiplicity: 's',
          assignment: 'OH'
        }],
        integration: 1,
        from: 2.1750000000000003,
        to: 2.185
      }],
      nucleus: '1H',
      solvent: 'CD₃CN'
    }, {
      names: ['chloroform'],
      smiles: 'ClC(Cl)Cl',
      ranges: [{
        signals: [{
          js: [],
          delta: 7.58,
          multiplicity: 's',
          assignment: 'CH'
        }],
        integration: 1,
        from: 7.575,
        to: 7.585
      }],
      nucleus: '1H',
      solvent: 'CD₃CN'
    }, {
      names: ['18-crown-6'],
      smiles: 'C1COCCOCCOCCOCCOCCO1',
      ranges: [{
        signals: [{
          js: [],
          delta: 3.51,
          multiplicity: 's',
          assignment: 'CH₂'
        }],
        integration: 24,
        from: 3.505,
        to: 3.5149999999999997
      }],
      nucleus: '1H',
      solvent: 'CD₃CN'
    }, {
      names: ['cyclohexane'],
      smiles: 'C1CCCCC1',
      ranges: [{
        signals: [{
          js: [],
          delta: 1.44,
          multiplicity: 's',
          assignment: 'CH₂'
        }],
        integration: 12,
        from: 1.435,
        to: 1.4449999999999998
      }],
      nucleus: '1H',
      solvent: 'CD₃CN'
    }, {
      names: ['1,2-dichloroethane'],
      smiles: 'ClCCCl',
      ranges: [{
        signals: [{
          js: [],
          delta: 3.81,
          multiplicity: 's',
          assignment: 'CH₂'
        }],
        integration: 4,
        from: 3.805,
        to: 3.815
      }],
      nucleus: '1H',
      solvent: 'CD₃CN'
    }, {
      names: ['dichloromethane'],
      smiles: 'ClCCl',
      ranges: [{
        signals: [{
          js: [],
          delta: 5.44,
          multiplicity: 's',
          assignment: 'CH₂'
        }],
        integration: 2,
        from: 5.4350000000000005,
        to: 5.445
      }],
      nucleus: '1H',
      solvent: 'CD₃CN'
    }, {
      names: ['diethyl ether'],
      smiles: 'CCOCC',
      ranges: [{
        signals: [{
          js: [{
            coupling: 7,
            multiplicity: 't'
          }],
          delta: 1.12,
          assignment: 'CH₃'
        }],
        integration: 6,
        from: 1.0975000000000001,
        to: 1.1425
      }, {
        signals: [{
          js: [{
            coupling: 7,
            multiplicity: 'q'
          }],
          delta: 3.42,
          assignment: 'CH₂'
        }],
        integration: 4,
        from: 3.38875,
        to: 3.45125
      }],
      nucleus: '1H',
      solvent: 'CD₃CN'
    }, {
      names: ['diglyme'],
      smiles: 'COCCOCCOC',
      ranges: [{
        signals: [{
          js: [],
          delta: 3.53,
          assignment: 'CH₂',
          multiplicity: 'm'
        }],
        integration: 4,
        from: 3.505,
        to: 3.5549999999999997
      }, {
        signals: [{
          js: [],
          delta: 3.45,
          assignment: 'CH₂',
          multiplicity: 'm'
        }],
        integration: 4,
        from: 3.4250000000000003,
        to: 3.475
      }, {
        signals: [{
          js: [],
          delta: 3.29,
          multiplicity: 's',
          assignment: 'OCH₃'
        }],
        integration: 6,
        from: 3.285,
        to: 3.295
      }],
      nucleus: '1H',
      solvent: 'CD₃CN'
    }, {
      names: ['dimethylformamide'],
      smiles: 'C(=O)N(C)C',
      ranges: [{
        signals: [{
          js: [],
          delta: 7.92,
          multiplicity: 's',
          assignment: 'CH'
        }],
        integration: 1,
        from: 7.915,
        to: 7.925
      }, {
        signals: [{
          js: [],
          delta: 2.89,
          multiplicity: 's',
          assignment: 'CH₃'
        }],
        integration: 3,
        from: 2.8850000000000002,
        to: 2.895
      }, {
        signals: [{
          js: [],
          delta: 2.77,
          multiplicity: 's',
          assignment: 'CH₃'
        }],
        integration: 3,
        from: 2.765,
        to: 2.775
      }],
      nucleus: '1H',
      solvent: 'CD₃CN'
    }, {
      names: ['1,4-dioxane'],
      smiles: 'C1COCCOC1',
      ranges: [{
        signals: [{
          js: [],
          delta: 3.6,
          multiplicity: 's',
          assignment: 'CH₂'
        }],
        integration: 8,
        from: 3.595,
        to: 3.605
      }],
      nucleus: '1H',
      solvent: 'CD₃CN'
    }, {
      names: ['DME', 'diethyl ether'],
      smiles: 'CCOCC',
      ranges: [{
        signals: [{
          js: [],
          delta: 3.28,
          multiplicity: 's',
          assignment: 'CH₃'
        }],
        integration: 6,
        from: 3.275,
        to: 3.2849999999999997
      }, {
        signals: [{
          js: [],
          delta: 3.45,
          multiplicity: 's',
          assignment: 'CH₂'
        }],
        integration: 4,
        from: 3.4450000000000003,
        to: 3.455
      }],
      nucleus: '1H',
      solvent: 'CD₃CN'
    }, {
      names: ['ethane'],
      smiles: 'CC',
      ranges: [{
        signals: [{
          js: [],
          delta: 0.85,
          multiplicity: 's',
          assignment: 'CH₃'
        }],
        integration: 6,
        from: 0.845,
        to: 0.855
      }],
      nucleus: '1H',
      solvent: 'CD₃CN'
    }, {
      names: ['ethanol'],
      smiles: 'CCO',
      ranges: [{
        signals: [{
          js: [{
            coupling: 7,
            multiplicity: 't'
          }],
          delta: 1.12,
          assignment: 'CH₃'
        }],
        integration: 3,
        from: 1.0975000000000001,
        to: 1.1425
      }, {
        signals: [{
          js: [{
            coupling: 7,
            multiplicity: 'q'
          }],
          delta: 3.54,
          assignment: 'CH₂'
        }],
        integration: 2,
        from: 3.50875,
        to: 3.57125
      }, {
        signals: [{
          js: [],
          delta: 2.47,
          multiplicity: 's',
          assignment: 'OH'
        }],
        integration: 1,
        from: 2.4650000000000003,
        to: 2.475
      }],
      nucleus: '1H',
      solvent: 'CD₃CN'
    }, {
      names: ['ethyl acetate'],
      smiles: 'CC(=O)CC',
      ranges: [{
        signals: [{
          js: [],
          delta: 1.97,
          multiplicity: 's',
          assignment: 'CH₃CO'
        }],
        integration: 3,
        from: 1.965,
        to: 1.9749999999999999
      }, {
        signals: [{
          js: [{
            coupling: 7,
            multiplicity: 'q'
          }],
          delta: 4.06,
          assignment: 'CH₂CH₃'
        }],
        integration: 2,
        from: 4.02875,
        to: 4.09125
      }, {
        signals: [{
          js: [{
            coupling: 7,
            multiplicity: 't'
          }],
          delta: 1.2,
          assignment: 'CH₂CH₃'
        }],
        integration: 3,
        from: 1.1775,
        to: 1.2225
      }],
      nucleus: '1H',
      solvent: 'CD₃CN'
    }, {
      names: ['ethylene'],
      smiles: 'C=C',
      ranges: [{
        signals: [{
          js: [],
          delta: 5.41,
          multiplicity: 's',
          assignment: 'CH₂'
        }],
        integration: 4,
        from: 5.405,
        to: 5.415
      }],
      nucleus: '1H',
      solvent: 'CD₃CN'
    }, {
      names: ['ethylene glycol'],
      smiles: 'OCCO',
      ranges: [{
        signals: [{
          js: [],
          delta: 3.51,
          multiplicity: 's',
          assignment: 'CH₂'
        }],
        integration: 4,
        from: 3.505,
        to: 3.5149999999999997
      }],
      nucleus: '1H',
      solvent: 'CD₃CN'
    }, {
      names: ['H greaseᶠ'],
      smiles: '',
      ranges: [],
      nucleus: '1H',
      solvent: 'CD₃CN'
    }, {
      names: ['hexamethylbenzene'],
      smiles: 'CC1=C(C(=C(C(=C1C)C)C)C)C',
      ranges: [{
        signals: [{
          js: [],
          delta: 2.19,
          multiplicity: 's',
          assignment: 'CH₃'
        }],
        integration: 18,
        from: 2.185,
        to: 2.195
      }],
      nucleus: '1H',
      solvent: 'CD₃CN'
    }, {
      names: ['n-hexane'],
      smiles: 'CCCCCC',
      ranges: [{
        signals: [{
          js: [{
            coupling: 7,
            multiplicity: 't'
          }],
          delta: 0.89,
          assignment: 'CH₃'
        }],
        integration: 6,
        from: 0.8675,
        to: 0.9125
      }, {
        signals: [{
          js: [],
          delta: 1.28,
          assignment: 'CH₂',
          multiplicity: 'm'
        }],
        integration: 8,
        from: 1.2550000000000001,
        to: 1.305
      }],
      nucleus: '1H',
      solvent: 'CD₃CN'
    }, {
      names: ['HMDSO'],
      smiles: 'C[Si](C)(C)O[Si](C)(C)C',
      ranges: [{
        signals: [{
          js: [],
          delta: 0.07,
          multiplicity: 's',
          assignment: 'CH₃'
        }],
        integration: 18,
        from: 0.065,
        to: 0.07500000000000001
      }],
      nucleus: '1H',
      solvent: 'CD₃CN'
    }, {
      names: ['HMPA'],
      smiles: 'CN(C)P(=O)(N(C)C)N(C)C',
      ranges: [{
        signals: [{
          js: [{
            coupling: 9.5,
            multiplicity: 'd'
          }],
          delta: 2.57,
          assignment: 'CH₃'
        }],
        integration: 18,
        from: 2.5531249999999996,
        to: 2.586875
      }],
      nucleus: '1H',
      solvent: 'CD₃CN'
    }, {
      names: ['hydrogen'],
      smiles: '',
      ranges: [{
        signals: [{
          js: [],
          delta: 4.57,
          multiplicity: 's',
          assignment: 'H₂'
        }],
        integration: 2,
        from: 4.565,
        to: 4.575
      }],
      nucleus: '1H',
      solvent: 'CD₃CN'
    }, {
      names: ['imidazole'],
      smiles: 'C1=CN=CN1',
      ranges: [{
        signals: [{
          js: [],
          delta: 7.57,
          multiplicity: 's',
          assignment: 'CH(2)'
        }],
        integration: 1,
        from: 7.565,
        to: 7.575
      }, {
        signals: [{
          js: [],
          delta: 7.01,
          multiplicity: 's',
          assignment: 'CH(4,5)'
        }],
        integration: 2,
        from: 7.005,
        to: 7.015
      }],
      nucleus: '1H',
      solvent: 'CD₃CN'
    }, {
      names: ['methane'],
      smiles: 'C',
      ranges: [{
        signals: [{
          js: [],
          delta: 0.2,
          multiplicity: 's',
          assignment: 'CH₄'
        }],
        integration: 4,
        from: 0.195,
        to: 0.20500000000000002
      }],
      nucleus: '1H',
      solvent: 'CD₃CN'
    }, {
      names: ['methanol'],
      smiles: 'CO',
      ranges: [{
        signals: [{
          js: [],
          delta: 3.28,
          multiplicity: 's',
          assignment: 'CH₃'
        }],
        integration: 3,
        from: 3.275,
        to: 3.2849999999999997
      }, {
        signals: [{
          js: [],
          delta: 2.16,
          multiplicity: 's',
          assignment: 'OH'
        }],
        integration: 1,
        from: 2.1550000000000002,
        to: 2.165
      }],
      nucleus: '1H',
      solvent: 'CD₃CN'
    }, {
      names: ['nitromethane'],
      smiles: 'C[N+](=O)[O-]',
      ranges: [{
        signals: [{
          js: [],
          delta: 4.31,
          multiplicity: 's',
          assignment: 'CH₃'
        }],
        integration: 3,
        from: 4.305,
        to: 4.3149999999999995
      }],
      nucleus: '1H',
      solvent: 'CD₃CN'
    }, {
      names: ['n-pentane'],
      smiles: 'CCCCC',
      ranges: [{
        signals: [{
          js: [{
            coupling: 7,
            multiplicity: 't'
          }],
          delta: 0.89,
          assignment: 'CH₃'
        }],
        integration: 6,
        from: 0.8675,
        to: 0.9125
      }, {
        signals: [{
          js: [],
          delta: 1.29,
          assignment: 'CH₂',
          multiplicity: 'm'
        }],
        integration: 6,
        from: 1.2650000000000001,
        to: 1.315
      }],
      nucleus: '1H',
      solvent: 'CD₃CN'
    }, {
      names: ['propane'],
      smiles: 'CCC',
      ranges: [{
        signals: [{
          js: [{
            coupling: 7.3,
            multiplicity: 't'
          }],
          delta: 0.9,
          assignment: 'CH₃'
        }],
        integration: 6,
        from: 0.87675,
        to: 0.92325
      }, {
        signals: [{
          js: [{
            coupling: 7.3,
            multiplicity: 'sept'
          }],
          delta: 1.33,
          assignment: 'CH₂'
        }],
        integration: 2,
        from: 1.27025,
        to: 1.38975
      }],
      nucleus: '1H',
      solvent: 'CD₃CN'
    }, {
      names: ['2-propanol'],
      smiles: 'CC(O)C',
      ranges: [{
        signals: [{
          js: [{
            coupling: 6,
            multiplicity: 'd'
          }],
          delta: 1.09,
          assignment: 'CH₃'
        }],
        integration: 6,
        from: 1.0775000000000001,
        to: 1.1025
      }, {
        signals: [{
          js: [{
            coupling: 6,
            multiplicity: 'sept'
          }],
          delta: 3.87,
          assignment: 'CH'
        }],
        integration: 1,
        from: 3.8200000000000003,
        to: 3.92
      }],
      nucleus: '1H',
      solvent: 'CD₃CN'
    }, {
      names: ['propylene'],
      smiles: 'CC=C',
      ranges: [{
        signals: [{
          js: [{
            coupling: 6.4,
            multiplicity: 'd'
          }, {
            coupling: 1.5,
            multiplicity: 't'
          }],
          delta: 1.7,
          assignment: 'CH₃'
        }],
        integration: 3,
        from: 1.68325,
        to: 1.71675
      }, {
        signals: [{
          js: [{
            coupling: 10,
            multiplicity: 'd'
          }],
          delta: 4.93,
          assignment: 'CH₂(1)'
        }],
        integration: 1,
        from: 4.9125,
        to: 4.9475
      }, {
        signals: [{
          js: [{
            coupling: 17,
            multiplicity: 'd'
          }],
          delta: 5.04,
          assignment: 'CH₂(2)'
        }],
        integration: 1,
        from: 5.01375,
        to: 5.06625
      }, {
        signals: [{
          js: [],
          delta: 5.85,
          assignment: 'CH',
          multiplicity: 'm'
        }],
        integration: 1,
        from: 5.824999999999999,
        to: 5.875
      }],
      nucleus: '1H',
      solvent: 'CD₃CN'
    }, {
      names: ['pyridine'],
      smiles: 'C1=CC=NC=C1',
      ranges: [{
        signals: [{
          js: [],
          delta: 8.57,
          assignment: 'CH(2,6)',
          multiplicity: 'm'
        }],
        integration: 2,
        from: 8.545,
        to: 8.595
      }, {
        signals: [{
          js: [],
          delta: 7.33,
          assignment: 'CH(3,5)',
          multiplicity: 'm'
        }],
        integration: 2,
        from: 7.305,
        to: 7.355
      }, {
        signals: [{
          js: [],
          delta: 7.73,
          assignment: 'CH(4)',
          multiplicity: 'm'
        }],
        integration: 1,
        from: 7.705,
        to: 7.755000000000001
      }],
      nucleus: '1H',
      solvent: 'CD₃CN'
    }, {
      names: ['pyrrole'],
      smiles: 'C1=CNC=C1',
      ranges: [{
        signals: [{
          js: [],
          delta: 9.27,
          assignment: 'NH',
          multiplicity: 'br t'
        }],
        integration: 1,
        from: 9.264999999999999,
        to: 9.275
      }, {
        signals: [{
          js: [],
          delta: 6.75,
          assignment: 'CH(2,5)',
          multiplicity: 'm'
        }],
        integration: 2,
        from: 6.725,
        to: 6.775
      }, {
        signals: [{
          js: [],
          delta: 6.1,
          assignment: 'CH(3,4)',
          multiplicity: 'm'
        }],
        integration: 2,
        from: 6.074999999999999,
        to: 6.125
      }],
      nucleus: '1H',
      solvent: 'CD₃CN'
    }, {
      names: ['pyrrolidine'],
      smiles: 'C1CCNC1',
      ranges: [{
        signals: [{
          js: [],
          delta: 2.75,
          assignment: 'CH₂(2,5)',
          multiplicity: 'm'
        }],
        integration: 4,
        from: 2.725,
        to: 2.775
      }, {
        signals: [{
          js: [],
          delta: 1.61,
          assignment: 'CH₂(3,4)',
          multiplicity: 'm'
        }],
        integration: 4,
        from: 1.5850000000000002,
        to: 1.635
      }],
      nucleus: '1H',
      solvent: 'CD₃CN'
    }, {
      names: ['silicone grease'],
      smiles: '',
      ranges: [{
        signals: [{
          js: [],
          delta: 0.08,
          multiplicity: 's',
          assignment: 'CH₃'
        }],
        integration: 6,
        from: 0.075,
        to: 0.085
      }],
      nucleus: '1H',
      solvent: 'CD₃CN'
    }, {
      names: ['tetrahydrofuran'],
      smiles: 'C1COCC1',
      ranges: [{
        signals: [{
          js: [],
          delta: 3.64,
          assignment: 'CH₂(2,5)',
          multiplicity: 'm'
        }],
        integration: 4,
        from: 3.615,
        to: 3.665
      }, {
        signals: [{
          js: [],
          delta: 1.8,
          assignment: 'CH₂(3,4)',
          multiplicity: 'm'
        }],
        integration: 4,
        from: 1.7750000000000001,
        to: 1.825
      }],
      nucleus: '1H',
      solvent: 'CD₃CN'
    }, {
      names: ['toluene'],
      smiles: 'Cc1ccccc1',
      ranges: [{
        signals: [{
          js: [],
          delta: 2.33,
          multiplicity: 's',
          assignment: 'CH₃'
        }],
        integration: 3,
        from: 2.325,
        to: 2.335
      }, {
        signals: [],
        integration: 3,
        from: 7.1,
        to: 7.3,
        multiplicity: 'm'
      }, {
        signals: [],
        integration: 2,
        from: 7.1,
        to: 7.3,
        multiplicity: 'm'
      }],
      nucleus: '1H',
      solvent: 'CD₃CN'
    }, {
      names: ['triethylamine'],
      smiles: 'CCN(CC)CC',
      ranges: [{
        signals: [{
          js: [{
            coupling: 7,
            multiplicity: 't'
          }],
          delta: 0.96,
          assignment: 'CH₃'
        }],
        integration: 6,
        from: 0.9375,
        to: 0.9824999999999999
      }, {
        signals: [{
          js: [{
            coupling: 7,
            multiplicity: 'q'
          }],
          delta: 2.45,
          assignment: 'CH₂'
        }],
        integration: 4,
        from: 2.41875,
        to: 2.48125
      }],
      nucleus: '1H',
      solvent: 'CD₃CN'
    }, {
      names: ['water'],
      smiles: 'O',
      ranges: [{
        signals: [{
          js: [],
          delta: 3.66,
          multiplicity: 's',
          assignment: 'OH'
        }],
        integration: 2,
        from: 3.6550000000000002,
        to: 3.665
      }],
      nucleus: '1H',
      solvent: 'TFE-d₃'
    }, {
      names: ['acetic acid'],
      smiles: 'CC(=O)O',
      ranges: [{
        signals: [{
          js: [],
          delta: 2.06,
          multiplicity: 's',
          assignment: 'CH₃'
        }],
        integration: 3,
        from: 2.055,
        to: 2.065
      }],
      nucleus: '1H',
      solvent: 'TFE-d₃'
    }, {
      names: ['acetone'],
      smiles: 'CC(=O)C',
      ranges: [{
        signals: [{
          js: [],
          delta: 2.19,
          multiplicity: 's',
          assignment: 'CH₃'
        }],
        integration: 6,
        from: 2.185,
        to: 2.195
      }],
      nucleus: '1H',
      solvent: 'TFE-d₃'
    }, {
      names: ['acetonitrile'],
      smiles: 'CC#N',
      ranges: [{
        signals: [{
          js: [],
          delta: 1.95,
          multiplicity: 's',
          assignment: 'CH₃'
        }],
        integration: 3,
        from: 1.945,
        to: 1.9549999999999998
      }],
      nucleus: '1H',
      solvent: 'TFE-d₃'
    }, {
      names: ['benzene'],
      smiles: 'c1ccccc1',
      ranges: [{
        signals: [{
          js: [],
          delta: 7.36,
          multiplicity: 's',
          assignment: 'CH'
        }],
        integration: 6,
        from: 7.355,
        to: 7.365
      }],
      nucleus: '1H',
      solvent: 'TFE-d₃'
    }, {
      names: ['tert -butyl alcohol'],
      smiles: 'CC(C)(C)O',
      ranges: [{
        signals: [{
          js: [],
          delta: 1.28,
          multiplicity: 's',
          assignment: 'CH₃'
        }],
        integration: 9,
        from: 1.2750000000000001,
        to: 1.285
      }, {
        signals: [{
          js: [],
          delta: 2.2,
          multiplicity: 's',
          assignment: 'OH'
        }],
        integration: 1,
        from: 2.1950000000000003,
        to: 2.205
      }],
      nucleus: '1H',
      solvent: 'TFE-d₃'
    }, {
      names: ['chloroform'],
      smiles: 'ClC(Cl)Cl',
      ranges: [{
        signals: [{
          js: [],
          delta: 7.33,
          multiplicity: 's',
          assignment: 'CH'
        }],
        integration: 1,
        from: 7.325,
        to: 7.335
      }],
      nucleus: '1H',
      solvent: 'TFE-d₃'
    }, {
      names: ['18-crown-6'],
      smiles: 'C1COCCOCCOCCOCCOCCO1',
      ranges: [{
        signals: [{
          js: [],
          delta: 3.64,
          multiplicity: 's',
          assignment: 'CH₂'
        }],
        integration: 24,
        from: 3.6350000000000002,
        to: 3.645
      }],
      nucleus: '1H',
      solvent: 'TFE-d₃'
    }, {
      names: ['cyclohexane'],
      smiles: 'C1CCCCC1',
      ranges: [{
        signals: [{
          js: [],
          delta: 1.47,
          multiplicity: 's',
          assignment: 'CH₂'
        }],
        integration: 12,
        from: 1.465,
        to: 1.4749999999999999
      }],
      nucleus: '1H',
      solvent: 'TFE-d₃'
    }, {
      names: ['1,2-dichloroethane'],
      smiles: 'ClCCCl',
      ranges: [{
        signals: [{
          js: [],
          delta: 3.71,
          multiplicity: 's',
          assignment: 'CH₂'
        }],
        integration: 4,
        from: 3.705,
        to: 3.715
      }],
      nucleus: '1H',
      solvent: 'TFE-d₃'
    }, {
      names: ['dichloromethane'],
      smiles: 'ClCCl',
      ranges: [{
        signals: [{
          js: [],
          delta: 5.24,
          multiplicity: 's',
          assignment: 'CH₂'
        }],
        integration: 2,
        from: 5.235,
        to: 5.245
      }],
      nucleus: '1H',
      solvent: 'TFE-d₃'
    }, {
      names: ['diethyl ether'],
      smiles: 'CCOCC',
      ranges: [{
        signals: [{
          js: [{
            coupling: 7,
            multiplicity: 't'
          }],
          delta: 1.2,
          assignment: 'CH₃'
        }],
        integration: 6,
        from: 1.1775,
        to: 1.2225
      }, {
        signals: [{
          js: [{
            coupling: 7,
            multiplicity: 'q'
          }],
          delta: 3.58,
          assignment: 'CH₂'
        }],
        integration: 4,
        from: 3.54875,
        to: 3.61125
      }],
      nucleus: '1H',
      solvent: 'TFE-d₃'
    }, {
      names: ['diglyme'],
      smiles: 'COCCOCCOC',
      ranges: [{
        signals: [{
          js: [],
          delta: 3.67,
          assignment: 'CH₂',
          multiplicity: 'm'
        }],
        integration: 4,
        from: 3.645,
        to: 3.695
      }, {
        signals: [{
          js: [],
          delta: 3.62,
          assignment: 'CH₂',
          multiplicity: 'm'
        }],
        integration: 4,
        from: 3.595,
        to: 3.645
      }, {
        signals: [{
          js: [],
          delta: 3.41,
          multiplicity: 's',
          assignment: 'OCH₃'
        }],
        integration: 6,
        from: 3.4050000000000002,
        to: 3.415
      }],
      nucleus: '1H',
      solvent: 'TFE-d₃'
    }, {
      names: ['dimethylformamide'],
      smiles: 'C(=O)N(C)C',
      ranges: [{
        signals: [{
          js: [],
          delta: 7.86,
          multiplicity: 's',
          assignment: 'CH'
        }],
        integration: 1,
        from: 7.855,
        to: 7.865
      }, {
        signals: [{
          js: [],
          delta: 2.98,
          multiplicity: 's',
          assignment: 'CH₃'
        }],
        integration: 3,
        from: 2.975,
        to: 2.985
      }, {
        signals: [{
          js: [],
          delta: 2.88,
          multiplicity: 's',
          assignment: 'CH₃'
        }],
        integration: 3,
        from: 2.875,
        to: 2.885
      }],
      nucleus: '1H',
      solvent: 'TFE-d₃'
    }, {
      names: ['1,4-dioxane'],
      smiles: 'C1COCCOC1',
      ranges: [{
        signals: [{
          js: [],
          delta: 3.76,
          multiplicity: 's',
          assignment: 'CH₂'
        }],
        integration: 8,
        from: 3.755,
        to: 3.7649999999999997
      }],
      nucleus: '1H',
      solvent: 'TFE-d₃'
    }, {
      names: ['DME', 'diethyl ether'],
      smiles: 'CCOCC',
      ranges: [{
        signals: [{
          js: [],
          delta: 3.4,
          multiplicity: 's',
          assignment: 'CH₃'
        }],
        integration: 6,
        from: 3.395,
        to: 3.405
      }, {
        signals: [{
          js: [],
          delta: 3.61,
          multiplicity: 's',
          assignment: 'CH₂'
        }],
        integration: 4,
        from: 3.605,
        to: 3.6149999999999998
      }],
      nucleus: '1H',
      solvent: 'TFE-d₃'
    }, {
      names: ['ethane'],
      smiles: 'CC',
      ranges: [{
        signals: [{
          js: [],
          delta: 0.85,
          multiplicity: 's',
          assignment: 'CH₃'
        }],
        integration: 6,
        from: 0.845,
        to: 0.855
      }],
      nucleus: '1H',
      solvent: 'TFE-d₃'
    }, {
      names: ['ethanol'],
      smiles: 'CCO',
      ranges: [{
        signals: [{
          js: [{
            coupling: 7,
            multiplicity: 't'
          }],
          delta: 1.22,
          assignment: 'CH₃'
        }],
        integration: 3,
        from: 1.1975,
        to: 1.2425
      }, {
        signals: [{
          js: [{
            coupling: 7,
            multiplicity: 'q'
          }],
          delta: 3.71,
          assignment: 'CH₂'
        }],
        integration: 2,
        from: 3.67875,
        to: 3.74125
      }],
      nucleus: '1H',
      solvent: 'TFE-d₃'
    }, {
      names: ['ethyl acetate'],
      smiles: 'CC(=O)CC',
      ranges: [{
        signals: [{
          js: [],
          delta: 2.03,
          multiplicity: 's',
          assignment: 'CH₃CO'
        }],
        integration: 3,
        from: 2.025,
        to: 2.0349999999999997
      }, {
        signals: [{
          js: [{
            coupling: 7,
            multiplicity: 'q'
          }],
          delta: 4.14,
          assignment: 'CH₂CH₃'
        }],
        integration: 2,
        from: 4.10875,
        to: 4.17125
      }, {
        signals: [{
          js: [{
            coupling: 7,
            multiplicity: 't'
          }],
          delta: 1.26,
          assignment: 'CH₂CH₃'
        }],
        integration: 3,
        from: 1.2375,
        to: 1.2825
      }],
      nucleus: '1H',
      solvent: 'TFE-d₃'
    }, {
      names: ['ethylene'],
      smiles: 'C=C',
      ranges: [{
        signals: [{
          js: [],
          delta: 5.4,
          multiplicity: 's',
          assignment: 'CH₂'
        }],
        integration: 4,
        from: 5.3950000000000005,
        to: 5.405
      }],
      nucleus: '1H',
      solvent: 'TFE-d₃'
    }, {
      names: ['ethylene glycol'],
      smiles: 'OCCO',
      ranges: [{
        signals: [{
          js: [],
          delta: 3.72,
          multiplicity: 's',
          assignment: 'CH₂'
        }],
        integration: 4,
        from: 3.7150000000000003,
        to: 3.725
      }],
      nucleus: '1H',
      solvent: 'TFE-d₃'
    }, {
      names: ['H greaseᶠ'],
      smiles: '',
      ranges: [{
        signals: [],
        integration: 6,
        from: 0.88,
        to: 0.94,
        multiplicity: 'm'
      }, {
        signals: [{
          js: [],
          delta: 1.33,
          multiplicity: 'br s',
          assignment: 'CH₂'
        }],
        integration: 10,
        from: 1.3162500000000001,
        to: 1.34375
      }],
      nucleus: '1H',
      solvent: 'TFE-d₃'
    }, {
      names: ['hexamethylbenzene'],
      smiles: 'CC1=C(C(=C(C(=C1C)C)C)C)C',
      ranges: [{
        signals: [{
          js: [],
          delta: 2.24,
          multiplicity: 's',
          assignment: 'CH₃'
        }],
        integration: 18,
        from: 2.2350000000000003,
        to: 2.245
      }],
      nucleus: '1H',
      solvent: 'TFE-d₃'
    }, {
      names: ['n-hexane'],
      smiles: 'CCCCCC',
      ranges: [{
        signals: [{
          js: [{
            coupling: 7,
            multiplicity: 't'
          }],
          delta: 0.91,
          assignment: 'CH₃'
        }],
        integration: 6,
        from: 0.8875000000000001,
        to: 0.9325
      }, {
        signals: [{
          js: [],
          delta: 1.31,
          assignment: 'CH₂',
          multiplicity: 'm'
        }],
        integration: 8,
        from: 1.2850000000000001,
        to: 1.335
      }],
      nucleus: '1H',
      solvent: 'TFE-d₃'
    }, {
      names: ['HMDSO'],
      smiles: 'C[Si](C)(C)O[Si](C)(C)C',
      ranges: [{
        signals: [{
          js: [],
          delta: 0.08,
          multiplicity: 's',
          assignment: 'CH₃'
        }],
        integration: 18,
        from: 0.075,
        to: 0.085
      }],
      nucleus: '1H',
      solvent: 'TFE-d₃'
    }, {
      names: ['HMPA'],
      smiles: 'CN(C)P(=O)(N(C)C)N(C)C',
      ranges: [{
        signals: [{
          js: [{
            coupling: 9.5,
            multiplicity: 'd'
          }],
          delta: 2.63,
          assignment: 'CH₃'
        }],
        integration: 18,
        from: 2.6131249999999997,
        to: 2.646875
      }],
      nucleus: '1H',
      solvent: 'TFE-d₃'
    }, {
      names: ['hydrogen'],
      smiles: '',
      ranges: [{
        signals: [{
          js: [],
          delta: 4.53,
          multiplicity: 's',
          assignment: 'H₂'
        }],
        integration: 2,
        from: 4.525,
        to: 4.535
      }],
      nucleus: '1H',
      solvent: 'TFE-d₃'
    }, {
      names: ['imidazole'],
      smiles: 'C1=CN=CN1',
      ranges: [{
        signals: [{
          js: [],
          delta: 7.61,
          multiplicity: 's',
          assignment: 'CH(2)'
        }],
        integration: 1,
        from: 7.605,
        to: 7.615
      }, {
        signals: [{
          js: [],
          delta: 7.03,
          multiplicity: 's',
          assignment: 'CH(4,5)'
        }],
        integration: 2,
        from: 7.025,
        to: 7.035
      }],
      nucleus: '1H',
      solvent: 'TFE-d₃'
    }, {
      names: ['methane'],
      smiles: 'C',
      ranges: [{
        signals: [{
          js: [],
          delta: 0.18,
          multiplicity: 's',
          assignment: 'CH₄'
        }],
        integration: 4,
        from: 0.175,
        to: 0.185
      }],
      nucleus: '1H',
      solvent: 'TFE-d₃'
    }, {
      names: ['methanol'],
      smiles: 'CO',
      ranges: [{
        signals: [{
          js: [],
          delta: 3.44,
          multiplicity: 's',
          assignment: 'CH₃'
        }],
        integration: 3,
        from: 3.435,
        to: 3.445
      }],
      nucleus: '1H',
      solvent: 'TFE-d₃'
    }, {
      names: ['nitromethane'],
      smiles: 'C[N+](=O)[O-]',
      ranges: [{
        signals: [{
          js: [],
          delta: 4.28,
          multiplicity: 's',
          assignment: 'CH₃'
        }],
        integration: 3,
        from: 4.275,
        to: 4.285
      }],
      nucleus: '1H',
      solvent: 'TFE-d₃'
    }, {
      names: ['n-pentane'],
      smiles: 'CCCCC',
      ranges: [{
        signals: [{
          js: [{
            coupling: 7,
            multiplicity: 't'
          }],
          delta: 0.9,
          assignment: 'CH₃'
        }],
        integration: 6,
        from: 0.8775000000000001,
        to: 0.9225
      }, {
        signals: [{
          js: [],
          delta: 1.33,
          assignment: 'CH₂',
          multiplicity: 'm'
        }],
        integration: 6,
        from: 1.3050000000000002,
        to: 1.355
      }],
      nucleus: '1H',
      solvent: 'TFE-d₃'
    }, {
      names: ['propane'],
      smiles: 'CCC',
      ranges: [{
        signals: [{
          js: [{
            coupling: 7.3,
            multiplicity: 't'
          }],
          delta: 0.9,
          assignment: 'CH₃'
        }],
        integration: 6,
        from: 0.87675,
        to: 0.92325
      }, {
        signals: [{
          js: [{
            coupling: 7.3,
            multiplicity: 'sept'
          }],
          delta: 1.33,
          assignment: 'CH₂'
        }],
        integration: 2,
        from: 1.27025,
        to: 1.38975
      }],
      nucleus: '1H',
      solvent: 'TFE-d₃'
    }, {
      names: ['2-propanol'],
      smiles: 'CC(O)C',
      ranges: [{
        signals: [{
          js: [{
            coupling: 6,
            multiplicity: 'd'
          }],
          delta: 1.2,
          assignment: 'CH₃'
        }],
        integration: 6,
        from: 1.1875,
        to: 1.2125
      }, {
        signals: [{
          js: [{
            coupling: 6,
            multiplicity: 'sept'
          }],
          delta: 4.05,
          assignment: 'CH'
        }],
        integration: 1,
        from: 4,
        to: 4.1
      }],
      nucleus: '1H',
      solvent: 'TFE-d₃'
    }, {
      names: ['propylene'],
      smiles: 'CC=C',
      ranges: [{
        signals: [{
          js: [{
            coupling: 6.4,
            multiplicity: 'd'
          }, {
            coupling: 1.5,
            multiplicity: 't'
          }],
          delta: 1.7,
          assignment: 'CH₃'
        }],
        integration: 3,
        from: 1.68325,
        to: 1.71675
      }, {
        signals: [{
          js: [{
            coupling: 10,
            multiplicity: 'd'
          }],
          delta: 4.93,
          assignment: 'CH₂(1)'
        }],
        integration: 1,
        from: 4.9125,
        to: 4.9475
      }, {
        signals: [{
          js: [{
            coupling: 17,
            multiplicity: 'd'
          }],
          delta: 5.03,
          assignment: 'CH₂(2)'
        }],
        integration: 1,
        from: 5.00375,
        to: 5.05625
      }, {
        signals: [{
          js: [],
          delta: 5.87,
          assignment: 'CH',
          multiplicity: 'm'
        }],
        integration: 1,
        from: 5.845,
        to: 5.8950000000000005
      }],
      nucleus: '1H',
      solvent: 'TFE-d₃'
    }, {
      names: ['pyridine'],
      smiles: 'C1=CC=NC=C1',
      ranges: [{
        signals: [{
          js: [],
          delta: 8.45,
          assignment: 'CH(2,6)',
          multiplicity: 'm'
        }],
        integration: 2,
        from: 8.424999999999999,
        to: 8.475
      }, {
        signals: [{
          js: [],
          delta: 7.4,
          assignment: 'CH(3,5)',
          multiplicity: 'm'
        }],
        integration: 2,
        from: 7.375,
        to: 7.425000000000001
      }, {
        signals: [{
          js: [],
          delta: 7.82,
          assignment: 'CH(4)',
          multiplicity: 'm'
        }],
        integration: 1,
        from: 7.795,
        to: 7.845000000000001
      }],
      nucleus: '1H',
      solvent: 'TFE-d₃'
    }, {
      names: ['pyrrole'],
      smiles: 'C1=CNC=C1',
      ranges: [{
        signals: [{
          js: [],
          delta: 6.84,
          assignment: 'CH(2,5)',
          multiplicity: 'm'
        }],
        integration: 2,
        from: 6.8149999999999995,
        to: 6.865
      }, {
        signals: [{
          js: [],
          delta: 6.24,
          assignment: 'CH(3,4)',
          multiplicity: 'm'
        }],
        integration: 2,
        from: 6.215,
        to: 6.265000000000001
      }],
      nucleus: '1H',
      solvent: 'TFE-d₃'
    }, {
      names: ['pyrrolidine'],
      smiles: 'C1CCNC1',
      ranges: [{
        signals: [{
          js: [],
          delta: 3.11,
          assignment: 'CH₂(2,5)',
          multiplicity: 'm'
        }],
        integration: 4,
        from: 3.085,
        to: 3.135
      }, {
        signals: [{
          js: [],
          delta: 1.93,
          assignment: 'CH₂(3,4)',
          multiplicity: 'm'
        }],
        integration: 4,
        from: 1.905,
        to: 1.9549999999999998
      }],
      nucleus: '1H',
      solvent: 'TFE-d₃'
    }, {
      names: ['silicone grease'],
      smiles: '',
      ranges: [{
        signals: [{
          js: [],
          delta: 0.16,
          multiplicity: 's',
          assignment: 'CH₃'
        }],
        integration: 6,
        from: 0.155,
        to: 0.165
      }],
      nucleus: '1H',
      solvent: 'TFE-d₃'
    }, {
      names: ['tetrahydrofuran'],
      smiles: 'C1COCC1',
      ranges: [{
        signals: [{
          js: [],
          delta: 3.78,
          assignment: 'CH₂(2,5)',
          multiplicity: 'm'
        }],
        integration: 4,
        from: 3.755,
        to: 3.8049999999999997
      }, {
        signals: [{
          js: [],
          delta: 1.91,
          assignment: 'CH₂(3,4)',
          multiplicity: 'm'
        }],
        integration: 4,
        from: 1.885,
        to: 1.9349999999999998
      }],
      nucleus: '1H',
      solvent: 'TFE-d₃'
    }, {
      names: ['toluene'],
      smiles: 'Cc1ccccc1',
      ranges: [{
        signals: [{
          js: [],
          delta: 2.33,
          multiplicity: 's',
          assignment: 'CH₃'
        }],
        integration: 3,
        from: 2.325,
        to: 2.335
      }, {
        signals: [],
        integration: 3,
        from: 7.1,
        to: 7.3,
        multiplicity: 'm'
      }, {
        signals: [],
        integration: 2,
        from: 7.1,
        to: 7.3,
        multiplicity: 'm'
      }],
      nucleus: '1H',
      solvent: 'TFE-d₃'
    }, {
      names: ['triethylamine'],
      smiles: 'CCN(CC)CC',
      ranges: [{
        signals: [{
          js: [{
            coupling: 7,
            multiplicity: 't'
          }],
          delta: 1.31,
          assignment: 'CH₃'
        }],
        integration: 6,
        from: 1.2875,
        to: 1.3325
      }, {
        signals: [{
          js: [{
            coupling: 7,
            multiplicity: 'q'
          }],
          delta: 3.12,
          assignment: 'CH₂'
        }],
        integration: 4,
        from: 3.08875,
        to: 3.15125
      }],
      nucleus: '1H',
      solvent: 'TFE-d₃'
    }, {
      names: ['water'],
      smiles: 'O',
      ranges: [{
        signals: [{
          js: [],
          delta: 4.87,
          multiplicity: 's',
          assignment: 'OH'
        }],
        integration: 2,
        from: 4.865,
        to: 4.875
      }],
      nucleus: '1H',
      solvent: 'CD₃OD'
    }, {
      names: ['acetic acid'],
      smiles: 'CC(=O)O',
      ranges: [{
        signals: [{
          js: [],
          delta: 1.99,
          multiplicity: 's',
          assignment: 'CH₃'
        }],
        integration: 3,
        from: 1.985,
        to: 1.9949999999999999
      }],
      nucleus: '1H',
      solvent: 'CD₃OD'
    }, {
      names: ['acetone'],
      smiles: 'CC(=O)C',
      ranges: [{
        signals: [{
          js: [],
          delta: 2.15,
          multiplicity: 's',
          assignment: 'CH₃'
        }],
        integration: 6,
        from: 2.145,
        to: 2.155
      }],
      nucleus: '1H',
      solvent: 'CD₃OD'
    }, {
      names: ['acetonitrile'],
      smiles: 'CC#N',
      ranges: [{
        signals: [{
          js: [],
          delta: 2.03,
          multiplicity: 's',
          assignment: 'CH₃'
        }],
        integration: 3,
        from: 2.025,
        to: 2.0349999999999997
      }],
      nucleus: '1H',
      solvent: 'CD₃OD'
    }, {
      names: ['benzene'],
      smiles: 'c1ccccc1',
      ranges: [{
        signals: [{
          js: [],
          delta: 7.33,
          multiplicity: 's',
          assignment: 'CH'
        }],
        integration: 6,
        from: 7.325,
        to: 7.335
      }],
      nucleus: '1H',
      solvent: 'CD₃OD'
    }, {
      names: ['tert -butyl alcohol'],
      smiles: 'CC(C)(C)O',
      ranges: [{
        signals: [{
          js: [],
          delta: 1.4,
          multiplicity: 's',
          assignment: 'CH₃'
        }],
        integration: 9,
        from: 1.395,
        to: 1.4049999999999998
      }],
      nucleus: '1H',
      solvent: 'CD₃OD'
    }, {
      names: ['chloroform'],
      smiles: 'ClC(Cl)Cl',
      ranges: [{
        signals: [{
          js: [],
          delta: 7.9,
          multiplicity: 's',
          assignment: 'CH'
        }],
        integration: 1,
        from: 7.8950000000000005,
        to: 7.905
      }],
      nucleus: '1H',
      solvent: 'CD₃OD'
    }, {
      names: ['18-crown-6'],
      smiles: 'C1COCCOCCOCCOCCOCCO1',
      ranges: [{
        signals: [{
          js: [],
          delta: 3.64,
          multiplicity: 's',
          assignment: 'CH₂'
        }],
        integration: 24,
        from: 3.6350000000000002,
        to: 3.645
      }],
      nucleus: '1H',
      solvent: 'CD₃OD'
    }, {
      names: ['cyclohexane'],
      smiles: 'C1CCCCC1',
      ranges: [{
        signals: [{
          js: [],
          delta: 1.45,
          multiplicity: 's',
          assignment: 'CH₂'
        }],
        integration: 12,
        from: 1.445,
        to: 1.4549999999999998
      }],
      nucleus: '1H',
      solvent: 'CD₃OD'
    }, {
      names: ['1,2-dichloroethane'],
      smiles: 'ClCCCl',
      ranges: [{
        signals: [{
          js: [],
          delta: 3.78,
          multiplicity: 's',
          assignment: 'CH₂'
        }],
        integration: 4,
        from: 3.775,
        to: 3.7849999999999997
      }],
      nucleus: '1H',
      solvent: 'CD₃OD'
    }, {
      names: ['dichloromethane'],
      smiles: 'ClCCl',
      ranges: [{
        signals: [{
          js: [],
          delta: 5.49,
          multiplicity: 's',
          assignment: 'CH₂'
        }],
        integration: 2,
        from: 5.485,
        to: 5.495
      }],
      nucleus: '1H',
      solvent: 'CD₃OD'
    }, {
      names: ['diethyl ether'],
      smiles: 'CCOCC',
      ranges: [{
        signals: [{
          js: [{
            coupling: 7,
            multiplicity: 't'
          }],
          delta: 1.18,
          assignment: 'CH₃'
        }],
        integration: 6,
        from: 1.1575,
        to: 1.2025
      }, {
        signals: [{
          js: [{
            coupling: 7,
            multiplicity: 'q'
          }],
          delta: 3.49,
          assignment: 'CH₂'
        }],
        integration: 4,
        from: 3.45875,
        to: 3.52125
      }],
      nucleus: '1H',
      solvent: 'CD₃OD'
    }, {
      names: ['diglyme'],
      smiles: 'COCCOCCOC',
      ranges: [{
        signals: [{
          js: [],
          delta: 3.61,
          assignment: 'CH₂',
          multiplicity: 'm'
        }],
        integration: 4,
        from: 3.585,
        to: 3.635
      }, {
        signals: [{
          js: [],
          delta: 3.58,
          assignment: 'CH₂',
          multiplicity: 'm'
        }],
        integration: 4,
        from: 3.555,
        to: 3.605
      }, {
        signals: [{
          js: [],
          delta: 3.35,
          multiplicity: 's',
          assignment: 'OCH₃'
        }],
        integration: 6,
        from: 3.345,
        to: 3.355
      }],
      nucleus: '1H',
      solvent: 'CD₃OD'
    }, {
      names: ['dimethylformamide'],
      smiles: 'C(=O)N(C)C',
      ranges: [{
        signals: [{
          js: [],
          delta: 7.97,
          multiplicity: 's',
          assignment: 'CH'
        }],
        integration: 1,
        from: 7.965,
        to: 7.975
      }, {
        signals: [{
          js: [],
          delta: 2.99,
          multiplicity: 's',
          assignment: 'CH₃'
        }],
        integration: 3,
        from: 2.9850000000000003,
        to: 2.995
      }, {
        signals: [{
          js: [],
          delta: 2.86,
          multiplicity: 's',
          assignment: 'CH₃'
        }],
        integration: 3,
        from: 2.855,
        to: 2.8649999999999998
      }],
      nucleus: '1H',
      solvent: 'CD₃OD'
    }, {
      names: ['1,4-dioxane'],
      smiles: 'C1COCCOC1',
      ranges: [{
        signals: [{
          js: [],
          delta: 3.66,
          multiplicity: 's',
          assignment: 'CH₂'
        }],
        integration: 8,
        from: 3.6550000000000002,
        to: 3.665
      }],
      nucleus: '1H',
      solvent: 'CD₃OD'
    }, {
      names: ['DME', 'diethyl ether'],
      smiles: 'CCOCC',
      ranges: [{
        signals: [{
          js: [],
          delta: 3.35,
          multiplicity: 's',
          assignment: 'CH₃'
        }],
        integration: 6,
        from: 3.345,
        to: 3.355
      }, {
        signals: [{
          js: [],
          delta: 3.52,
          multiplicity: 's',
          assignment: 'CH₂'
        }],
        integration: 4,
        from: 3.515,
        to: 3.525
      }],
      nucleus: '1H',
      solvent: 'CD₃OD'
    }, {
      names: ['ethane'],
      smiles: 'CC',
      ranges: [{
        signals: [{
          js: [],
          delta: 0.85,
          multiplicity: 's',
          assignment: 'CH₃'
        }],
        integration: 6,
        from: 0.845,
        to: 0.855
      }],
      nucleus: '1H',
      solvent: 'CD₃OD'
    }, {
      names: ['ethanol'],
      smiles: 'CCO',
      ranges: [{
        signals: [{
          js: [{
            coupling: 7,
            multiplicity: 't'
          }],
          delta: 1.19,
          assignment: 'CH₃'
        }],
        integration: 3,
        from: 1.1675,
        to: 1.2125
      }, {
        signals: [{
          js: [{
            coupling: 7,
            multiplicity: 'q'
          }],
          delta: 3.6,
          assignment: 'CH₂'
        }],
        integration: 2,
        from: 3.56875,
        to: 3.63125
      }],
      nucleus: '1H',
      solvent: 'CD₃OD'
    }, {
      names: ['ethyl acetate'],
      smiles: 'CC(=O)CC',
      ranges: [{
        signals: [{
          js: [],
          delta: 2.01,
          multiplicity: 's',
          assignment: 'CH₃CO'
        }],
        integration: 3,
        from: 2.005,
        to: 2.0149999999999997
      }, {
        signals: [{
          js: [{
            coupling: 7,
            multiplicity: 'q'
          }],
          delta: 4.09,
          assignment: 'CH₂CH₃'
        }],
        integration: 2,
        from: 4.05875,
        to: 4.12125
      }, {
        signals: [{
          js: [{
            coupling: 7,
            multiplicity: 't'
          }],
          delta: 1.24,
          assignment: 'CH₂CH₃'
        }],
        integration: 3,
        from: 1.2175,
        to: 1.2625
      }],
      nucleus: '1H',
      solvent: 'CD₃OD'
    }, {
      names: ['ethylene'],
      smiles: 'C=C',
      ranges: [{
        signals: [{
          js: [],
          delta: 5.39,
          multiplicity: 's',
          assignment: 'CH₂'
        }],
        integration: 4,
        from: 5.385,
        to: 5.395
      }],
      nucleus: '1H',
      solvent: 'CD₃OD'
    }, {
      names: ['ethylene glycol'],
      smiles: 'OCCO',
      ranges: [{
        signals: [{
          js: [],
          delta: 3.59,
          multiplicity: 's',
          assignment: 'CH₂'
        }],
        integration: 4,
        from: 3.585,
        to: 3.5949999999999998
      }],
      nucleus: '1H',
      solvent: 'CD₃OD'
    }, {
      names: ['H greaseᶠ'],
      smiles: '',
      ranges: [{
        signals: [],
        integration: 6,
        from: 0.86,
        to: 0.93,
        multiplicity: 'm'
      }, {
        signals: [{
          js: [],
          delta: 1.29,
          multiplicity: 'br s',
          assignment: 'CH₂'
        }],
        integration: 10,
        from: 1.27625,
        to: 1.30375
      }],
      nucleus: '1H',
      solvent: 'CD₃OD'
    }, {
      names: ['hexamethylbenzene'],
      smiles: 'CC1=C(C(=C(C(=C1C)C)C)C)C',
      ranges: [{
        signals: [{
          js: [],
          delta: 2.19,
          multiplicity: 's',
          assignment: 'CH₃'
        }],
        integration: 18,
        from: 2.185,
        to: 2.195
      }],
      nucleus: '1H',
      solvent: 'CD₃OD'
    }, {
      names: ['n-hexane'],
      smiles: 'CCCCCC',
      ranges: [{
        signals: [{
          js: [{
            coupling: 7,
            multiplicity: 't'
          }],
          delta: 0.9,
          assignment: 'CH₃'
        }],
        integration: 6,
        from: 0.8775000000000001,
        to: 0.9225
      }, {
        signals: [{
          js: [],
          delta: 1.29,
          assignment: 'CH₂',
          multiplicity: 'm'
        }],
        integration: 8,
        from: 1.2650000000000001,
        to: 1.315
      }],
      nucleus: '1H',
      solvent: 'CD₃OD'
    }, {
      names: ['HMDSO'],
      smiles: 'C[Si](C)(C)O[Si](C)(C)C',
      ranges: [{
        signals: [{
          js: [],
          delta: 0.07,
          multiplicity: 's',
          assignment: 'CH₃'
        }],
        integration: 18,
        from: 0.065,
        to: 0.07500000000000001
      }],
      nucleus: '1H',
      solvent: 'CD₃OD'
    }, {
      names: ['HMPA'],
      smiles: 'CN(C)P(=O)(N(C)C)N(C)C',
      ranges: [{
        signals: [{
          js: [{
            coupling: 9.5,
            multiplicity: 'd'
          }],
          delta: 2.64,
          assignment: 'CH₃'
        }],
        integration: 18,
        from: 2.623125,
        to: 2.6568750000000003
      }],
      nucleus: '1H',
      solvent: 'CD₃OD'
    }, {
      names: ['hydrogen'],
      smiles: '',
      ranges: [{
        signals: [{
          js: [],
          delta: 4.56,
          multiplicity: 's',
          assignment: 'H₂'
        }],
        integration: 2,
        from: 4.555,
        to: 4.5649999999999995
      }],
      nucleus: '1H',
      solvent: 'CD₃OD'
    }, {
      names: ['imidazole'],
      smiles: 'C1=CN=CN1',
      ranges: [{
        signals: [{
          js: [],
          delta: 7.67,
          multiplicity: 's',
          assignment: 'CH(2)'
        }],
        integration: 1,
        from: 7.665,
        to: 7.675
      }, {
        signals: [{
          js: [],
          delta: 7.05,
          multiplicity: 's',
          assignment: 'CH(4,5)'
        }],
        integration: 2,
        from: 7.045,
        to: 7.055
      }],
      nucleus: '1H',
      solvent: 'CD₃OD'
    }, {
      names: ['methane'],
      smiles: 'C',
      ranges: [{
        signals: [{
          js: [],
          delta: 0.2,
          multiplicity: 's',
          assignment: 'CH₄'
        }],
        integration: 4,
        from: 0.195,
        to: 0.20500000000000002
      }],
      nucleus: '1H',
      solvent: 'CD₃OD'
    }, {
      names: ['methanol'],
      smiles: 'CO',
      ranges: [{
        signals: [{
          js: [],
          delta: 3.34,
          multiplicity: 's',
          assignment: 'CH₃'
        }],
        integration: 3,
        from: 3.335,
        to: 3.3449999999999998
      }],
      nucleus: '1H',
      solvent: 'CD₃OD'
    }, {
      names: ['nitromethane'],
      smiles: 'C[N+](=O)[O-]',
      ranges: [{
        signals: [{
          js: [],
          delta: 4.34,
          multiplicity: 's',
          assignment: 'CH₃'
        }],
        integration: 3,
        from: 4.335,
        to: 4.345
      }],
      nucleus: '1H',
      solvent: 'CD₃OD'
    }, {
      names: ['n-pentane'],
      smiles: 'CCCCC',
      ranges: [{
        signals: [{
          js: [{
            coupling: 7,
            multiplicity: 't'
          }],
          delta: 0.9,
          assignment: 'CH₃'
        }],
        integration: 6,
        from: 0.8775000000000001,
        to: 0.9225
      }, {
        signals: [{
          js: [],
          delta: 1.29,
          assignment: 'CH₂',
          multiplicity: 'm'
        }],
        integration: 6,
        from: 1.2650000000000001,
        to: 1.315
      }],
      nucleus: '1H',
      solvent: 'CD₃OD'
    }, {
      names: ['propane'],
      smiles: 'CCC',
      ranges: [{
        signals: [{
          js: [{
            coupling: 7.3,
            multiplicity: 't'
          }],
          delta: 0.91,
          assignment: 'CH₃'
        }],
        integration: 6,
        from: 0.88675,
        to: 0.93325
      }, {
        signals: [{
          js: [{
            coupling: 7.3,
            multiplicity: 'sept'
          }],
          delta: 1.34,
          assignment: 'CH₂'
        }],
        integration: 2,
        from: 1.28025,
        to: 1.39975
      }],
      nucleus: '1H',
      solvent: 'CD₃OD'
    }, {
      names: ['2-propanol'],
      smiles: 'CC(O)C',
      ranges: [{
        signals: [{
          js: [{
            coupling: 6,
            multiplicity: 'd'
          }],
          delta: 1.5,
          assignment: 'CH₃'
        }],
        integration: 6,
        from: 1.4875,
        to: 1.5125
      }, {
        signals: [{
          js: [{
            coupling: 6,
            multiplicity: 'sept'
          }],
          delta: 3.92,
          assignment: 'CH'
        }],
        integration: 1,
        from: 3.87,
        to: 3.9699999999999998
      }],
      nucleus: '1H',
      solvent: 'CD₃OD'
    }, {
      names: ['propylene'],
      smiles: 'CC=C',
      ranges: [{
        signals: [{
          js: [{
            coupling: 6.4,
            multiplicity: 'd'
          }, {
            coupling: 1.5,
            multiplicity: 't'
          }],
          delta: 1.7,
          assignment: 'CH₃'
        }],
        integration: 3,
        from: 1.68325,
        to: 1.71675
      }, {
        signals: [{
          js: [{
            coupling: 10,
            multiplicity: 'd'
          }],
          delta: 4.91,
          assignment: 'CH₂(1)'
        }],
        integration: 1,
        from: 4.8925,
        to: 4.9275
      }, {
        signals: [{
          js: [{
            coupling: 17,
            multiplicity: 'd'
          }],
          delta: 5.01,
          assignment: 'CH₂(2)'
        }],
        integration: 1,
        from: 4.98375,
        to: 5.03625
      }, {
        signals: [{
          js: [],
          delta: 5.82,
          assignment: 'CH',
          multiplicity: 'm'
        }],
        integration: 1,
        from: 5.795,
        to: 5.845000000000001
      }],
      nucleus: '1H',
      solvent: 'CD₃OD'
    }, {
      names: ['pyridine'],
      smiles: 'C1=CC=NC=C1',
      ranges: [{
        signals: [{
          js: [],
          delta: 8.53,
          assignment: 'CH(2,6)',
          multiplicity: 'm'
        }],
        integration: 2,
        from: 8.504999999999999,
        to: 8.555
      }, {
        signals: [{
          js: [],
          delta: 7.44,
          assignment: 'CH(3,5)',
          multiplicity: 'm'
        }],
        integration: 2,
        from: 7.415,
        to: 7.465000000000001
      }, {
        signals: [{
          js: [],
          delta: 7.85,
          assignment: 'CH(4)',
          multiplicity: 'm'
        }],
        integration: 1,
        from: 7.824999999999999,
        to: 7.875
      }],
      nucleus: '1H',
      solvent: 'CD₃OD'
    }, {
      names: ['pyrrole'],
      smiles: 'C1=CNC=C1',
      ranges: [{
        signals: [{
          js: [],
          delta: 6.72,
          assignment: 'CH(2,5)',
          multiplicity: 'm'
        }],
        integration: 2,
        from: 6.694999999999999,
        to: 6.745
      }, {
        signals: [{
          js: [],
          delta: 6.08,
          assignment: 'CH(3,4)',
          multiplicity: 'm'
        }],
        integration: 2,
        from: 6.055,
        to: 6.105
      }],
      nucleus: '1H',
      solvent: 'CD₃OD'
    }, {
      names: ['pyrrolidine'],
      smiles: 'C1CCNC1',
      ranges: [{
        signals: [{
          js: [],
          delta: 2.8,
          assignment: 'CH₂(2,5)',
          multiplicity: 'm'
        }],
        integration: 4,
        from: 2.775,
        to: 2.8249999999999997
      }, {
        signals: [{
          js: [],
          delta: 1.72,
          assignment: 'CH₂(3,4)',
          multiplicity: 'm'
        }],
        integration: 4,
        from: 1.695,
        to: 1.7449999999999999
      }],
      nucleus: '1H',
      solvent: 'CD₃OD'
    }, {
      names: ['silicone grease'],
      smiles: '',
      ranges: [{
        signals: [{
          js: [],
          delta: 0.1,
          multiplicity: 's',
          assignment: 'CH₃'
        }],
        integration: 6,
        from: 0.095,
        to: 0.10500000000000001
      }],
      nucleus: '1H',
      solvent: 'CD₃OD'
    }, {
      names: ['tetrahydrofuran'],
      smiles: 'C1COCC1',
      ranges: [{
        signals: [{
          js: [],
          delta: 3.71,
          assignment: 'CH₂(2,5)',
          multiplicity: 'm'
        }],
        integration: 4,
        from: 3.685,
        to: 3.735
      }, {
        signals: [{
          js: [],
          delta: 1.87,
          assignment: 'CH₂(3,4)',
          multiplicity: 'm'
        }],
        integration: 4,
        from: 1.8450000000000002,
        to: 1.895
      }],
      nucleus: '1H',
      solvent: 'CD₃OD'
    }, {
      names: ['toluene'],
      smiles: 'Cc1ccccc1',
      ranges: [{
        signals: [{
          js: [],
          delta: 2.32,
          multiplicity: 's',
          assignment: 'CH₃'
        }],
        integration: 3,
        from: 2.315,
        to: 2.3249999999999997
      }, {
        signals: [{
          js: [],
          delta: 7.16,
          assignment: 'CH(2,4,6)',
          multiplicity: 'm'
        }],
        integration: 3,
        from: 7.135,
        to: 7.1850000000000005
      }, {
        signals: [{
          js: [],
          delta: 7.16,
          assignment: 'CH(3,5)',
          multiplicity: 'm'
        }],
        integration: 2,
        from: 7.135,
        to: 7.1850000000000005
      }],
      nucleus: '1H',
      solvent: 'CD₃OD'
    }, {
      names: ['triethylamine'],
      smiles: 'CCN(CC)CC',
      ranges: [{
        signals: [{
          js: [{
            coupling: 7,
            multiplicity: 't'
          }],
          delta: 1.05,
          assignment: 'CH₃'
        }],
        integration: 6,
        from: 1.0275,
        to: 1.0725
      }, {
        signals: [{
          js: [{
            coupling: 7,
            multiplicity: 'q'
          }],
          delta: 2.58,
          assignment: 'CH₂'
        }],
        integration: 4,
        from: 2.54875,
        to: 2.61125
      }],
      nucleus: '1H',
      solvent: 'CD₃OD'
    }, {
      names: ['water'],
      smiles: 'O',
      ranges: [],
      nucleus: '1H',
      solvent: 'D₂O'
    }, {
      names: ['acetic acid'],
      smiles: 'CC(=O)O',
      ranges: [{
        signals: [{
          js: [],
          delta: 2.08,
          multiplicity: 's',
          assignment: 'CH₃'
        }],
        integration: 3,
        from: 2.075,
        to: 2.085
      }],
      nucleus: '1H',
      solvent: 'D₂O'
    }, {
      names: ['acetone'],
      smiles: 'CC(=O)C',
      ranges: [{
        signals: [{
          js: [],
          delta: 2.22,
          multiplicity: 's',
          assignment: 'CH₃'
        }],
        integration: 6,
        from: 2.2150000000000003,
        to: 2.225
      }],
      nucleus: '1H',
      solvent: 'D₂O'
    }, {
      names: ['acetonitrile'],
      smiles: 'CC#N',
      ranges: [{
        signals: [{
          js: [],
          delta: 2.06,
          multiplicity: 's',
          assignment: 'CH₃'
        }],
        integration: 3,
        from: 2.055,
        to: 2.065
      }],
      nucleus: '1H',
      solvent: 'D₂O'
    }, {
      names: ['benzene'],
      smiles: 'c1ccccc1',
      ranges: [],
      nucleus: '1H',
      solvent: 'D₂O'
    }, {
      names: ['tert -butyl alcohol'],
      smiles: 'CC(C)(C)O',
      ranges: [{
        signals: [{
          js: [],
          delta: 1.24,
          multiplicity: 's',
          assignment: 'CH₃'
        }],
        integration: 9,
        from: 1.235,
        to: 1.2449999999999999
      }],
      nucleus: '1H',
      solvent: 'D₂O'
    }, {
      names: ['chloroform'],
      smiles: 'ClC(Cl)Cl',
      ranges: [],
      nucleus: '1H',
      solvent: 'D₂O'
    }, {
      names: ['18-crown-6'],
      smiles: 'C1COCCOCCOCCOCCOCCO1',
      ranges: [{
        signals: [{
          js: [],
          delta: 3.8,
          multiplicity: 's',
          assignment: 'CH₂'
        }],
        integration: 24,
        from: 3.795,
        to: 3.8049999999999997
      }],
      nucleus: '1H',
      solvent: 'D₂O'
    }, {
      names: ['cyclohexane'],
      smiles: 'C1CCCCC1',
      ranges: [],
      nucleus: '1H',
      solvent: 'D₂O'
    }, {
      names: ['1,2-dichloroethane'],
      smiles: 'ClCCCl',
      ranges: [],
      nucleus: '1H',
      solvent: 'D₂O'
    }, {
      names: ['dichloromethane'],
      smiles: 'ClCCl',
      ranges: [],
      nucleus: '1H',
      solvent: 'D₂O'
    }, {
      names: ['diethyl ether'],
      smiles: 'CCOCC',
      ranges: [{
        signals: [{
          js: [{
            coupling: 7,
            multiplicity: 't'
          }],
          delta: 1.17,
          assignment: 'CH₃'
        }],
        integration: 6,
        from: 1.1475,
        to: 1.1925
      }, {
        signals: [{
          js: [{
            coupling: 7,
            multiplicity: 'q'
          }],
          delta: 3.56,
          assignment: 'CH₂'
        }],
        integration: 4,
        from: 3.52875,
        to: 3.59125
      }],
      nucleus: '1H',
      solvent: 'D₂O'
    }, {
      names: ['diglyme'],
      smiles: 'COCCOCCOC',
      ranges: [{
        signals: [{
          js: [],
          delta: 3.67,
          assignment: 'CH₂',
          multiplicity: 'm'
        }],
        integration: 4,
        from: 3.645,
        to: 3.695
      }, {
        signals: [{
          js: [],
          delta: 3.61,
          assignment: 'CH₂',
          multiplicity: 'm'
        }],
        integration: 4,
        from: 3.585,
        to: 3.635
      }, {
        signals: [{
          js: [],
          delta: 3.37,
          multiplicity: 's',
          assignment: 'OCH₃'
        }],
        integration: 6,
        from: 3.365,
        to: 3.375
      }],
      nucleus: '1H',
      solvent: 'D₂O'
    }, {
      names: ['dimethylformamide'],
      smiles: 'C(=O)N(C)C',
      ranges: [{
        signals: [{
          js: [],
          delta: 7.92,
          multiplicity: 's',
          assignment: 'CH'
        }],
        integration: 1,
        from: 7.915,
        to: 7.925
      }, {
        signals: [{
          js: [],
          delta: 3.01,
          multiplicity: 's',
          assignment: 'CH₃'
        }],
        integration: 3,
        from: 3.005,
        to: 3.0149999999999997
      }, {
        signals: [{
          js: [],
          delta: 2.85,
          multiplicity: 's',
          assignment: 'CH₃'
        }],
        integration: 3,
        from: 2.845,
        to: 2.855
      }],
      nucleus: '1H',
      solvent: 'D₂O'
    }, {
      names: ['1,4-dioxane'],
      smiles: 'C1COCCOC1',
      ranges: [{
        signals: [{
          js: [],
          delta: 3.75,
          multiplicity: 's',
          assignment: 'CH₂'
        }],
        integration: 8,
        from: 3.745,
        to: 3.755
      }],
      nucleus: '1H',
      solvent: 'D₂O'
    }, {
      names: ['DME', 'diethyl ether'],
      smiles: 'CCOCC',
      ranges: [{
        signals: [{
          js: [],
          delta: 3.37,
          multiplicity: 's',
          assignment: 'CH₃'
        }],
        integration: 6,
        from: 3.365,
        to: 3.375
      }, {
        signals: [{
          js: [],
          delta: 3.6,
          multiplicity: 's',
          assignment: 'CH₂'
        }],
        integration: 4,
        from: 3.595,
        to: 3.605
      }],
      nucleus: '1H',
      solvent: 'D₂O'
    }, {
      names: ['ethane'],
      smiles: 'CC',
      ranges: [{
        signals: [{
          js: [],
          delta: 0.82,
          multiplicity: 's',
          assignment: 'CH₃'
        }],
        integration: 6,
        from: 0.815,
        to: 0.825
      }],
      nucleus: '1H',
      solvent: 'D₂O'
    }, {
      names: ['ethanol'],
      smiles: 'CCO',
      ranges: [{
        signals: [{
          js: [{
            coupling: 7,
            multiplicity: 't'
          }],
          delta: 1.17,
          assignment: 'CH₃'
        }],
        integration: 3,
        from: 1.1475,
        to: 1.1925
      }, {
        signals: [{
          js: [{
            coupling: 7,
            multiplicity: 'q'
          }],
          delta: 3.65,
          assignment: 'CH₂'
        }],
        integration: 2,
        from: 3.61875,
        to: 3.68125
      }],
      nucleus: '1H',
      solvent: 'D₂O'
    }, {
      names: ['ethyl acetate'],
      smiles: 'CC(=O)CC',
      ranges: [{
        signals: [{
          js: [],
          delta: 2.07,
          multiplicity: 's',
          assignment: 'CH₃CO'
        }],
        integration: 3,
        from: 2.065,
        to: 2.0749999999999997
      }, {
        signals: [{
          js: [{
            coupling: 7,
            multiplicity: 'q'
          }],
          delta: 4.14,
          assignment: 'CH₂CH₃'
        }],
        integration: 2,
        from: 4.10875,
        to: 4.17125
      }, {
        signals: [{
          js: [{
            coupling: 7,
            multiplicity: 't'
          }],
          delta: 1.24,
          assignment: 'CH₂CH₃'
        }],
        integration: 3,
        from: 1.2175,
        to: 1.2625
      }],
      nucleus: '1H',
      solvent: 'D₂O'
    }, {
      names: ['ethylene'],
      smiles: 'C=C',
      ranges: [{
        signals: [{
          js: [],
          delta: 5.44,
          multiplicity: 's',
          assignment: 'CH₂'
        }],
        integration: 4,
        from: 5.4350000000000005,
        to: 5.445
      }],
      nucleus: '1H',
      solvent: 'D₂O'
    }, {
      names: ['ethylene glycol'],
      smiles: 'OCCO',
      ranges: [{
        signals: [{
          js: [],
          delta: 3.65,
          multiplicity: 's',
          assignment: 'CH₂'
        }],
        integration: 4,
        from: 3.645,
        to: 3.655
      }],
      nucleus: '1H',
      solvent: 'D₂O'
    }, {
      names: ['H greaseᶠ'],
      smiles: '',
      ranges: [],
      nucleus: '1H',
      solvent: 'D₂O'
    }, {
      names: ['hexamethylbenzene'],
      smiles: 'CC1=C(C(=C(C(=C1C)C)C)C)C',
      ranges: [],
      nucleus: '1H',
      solvent: 'D₂O'
    }, {
      names: ['n-hexane'],
      smiles: 'CCCCCC',
      ranges: [],
      nucleus: '1H',
      solvent: 'D₂O'
    }, {
      names: ['HMDSO'],
      smiles: 'C[Si](C)(C)O[Si](C)(C)C',
      ranges: [{
        signals: [{
          js: [],
          delta: 0.28,
          multiplicity: 's',
          assignment: 'CH₃'
        }],
        integration: 18,
        from: 0.275,
        to: 0.28500000000000003
      }],
      nucleus: '1H',
      solvent: 'D₂O'
    }, {
      names: ['HMPA'],
      smiles: 'CN(C)P(=O)(N(C)C)N(C)C',
      ranges: [{
        signals: [{
          js: [{
            coupling: 9.5,
            multiplicity: 'd'
          }],
          delta: 2.61,
          assignment: 'CH₃'
        }],
        integration: 18,
        from: 2.5931249999999997,
        to: 2.626875
      }],
      nucleus: '1H',
      solvent: 'D₂O'
    }, {
      names: ['hydrogen'],
      smiles: '',
      ranges: [],
      nucleus: '1H',
      solvent: 'D₂O'
    }, {
      names: ['imidazole'],
      smiles: 'C1=CN=CN1',
      ranges: [{
        signals: [{
          js: [],
          delta: 7.78,
          multiplicity: 's',
          assignment: 'CH(2)'
        }],
        integration: 1,
        from: 7.775,
        to: 7.785
      }, {
        signals: [{
          js: [],
          delta: 7.14,
          multiplicity: 's',
          assignment: 'CH(4,5)'
        }],
        integration: 2,
        from: 7.135,
        to: 7.145
      }],
      nucleus: '1H',
      solvent: 'D₂O'
    }, {
      names: ['methane'],
      smiles: 'C',
      ranges: [{
        signals: [{
          js: [],
          delta: 0.18,
          multiplicity: 's',
          assignment: 'CH₄'
        }],
        integration: 4,
        from: 0.175,
        to: 0.185
      }],
      nucleus: '1H',
      solvent: 'D₂O'
    }, {
      names: ['methanol'],
      smiles: 'CO',
      ranges: [{
        signals: [{
          js: [],
          delta: 3.34,
          multiplicity: 's',
          assignment: 'CH₃'
        }],
        integration: 3,
        from: 3.335,
        to: 3.3449999999999998
      }],
      nucleus: '1H',
      solvent: 'D₂O'
    }, {
      names: ['nitromethane'],
      smiles: 'C[N+](=O)[O-]',
      ranges: [{
        signals: [{
          js: [],
          delta: 4.4,
          multiplicity: 's',
          assignment: 'CH₃'
        }],
        integration: 3,
        from: 4.3950000000000005,
        to: 4.405
      }],
      nucleus: '1H',
      solvent: 'D₂O'
    }, {
      names: ['n-pentane'],
      smiles: 'CCCCC',
      ranges: [],
      nucleus: '1H',
      solvent: 'D₂O'
    }, {
      names: ['propane'],
      smiles: 'CCC',
      ranges: [{
        signals: [{
          js: [{
            coupling: 7.3,
            multiplicity: 't'
          }],
          delta: 0.88,
          assignment: 'CH₃'
        }],
        integration: 6,
        from: 0.85675,
        to: 0.90325
      }, {
        signals: [{
          js: [{
            coupling: 7.3,
            multiplicity: 'sept'
          }],
          delta: 1.3,
          assignment: 'CH₂'
        }],
        integration: 2,
        from: 1.24025,
        to: 1.35975
      }],
      nucleus: '1H',
      solvent: 'D₂O'
    }, {
      names: ['2-propanol'],
      smiles: 'CC(O)C',
      ranges: [{
        signals: [{
          js: [{
            coupling: 6,
            multiplicity: 'd'
          }],
          delta: 1.17,
          assignment: 'CH₃'
        }],
        integration: 6,
        from: 1.1575,
        to: 1.1824999999999999
      }, {
        signals: [{
          js: [{
            coupling: 6,
            multiplicity: 'sept'
          }],
          delta: 4.02,
          assignment: 'CH'
        }],
        integration: 1,
        from: 3.9699999999999998,
        to: 4.069999999999999
      }],
      nucleus: '1H',
      solvent: 'D₂O'
    }, {
      names: ['propylene'],
      smiles: 'CC=C',
      ranges: [{
        signals: [{
          js: [{
            coupling: 6.4,
            multiplicity: 'd'
          }, {
            coupling: 1.5,
            multiplicity: 't'
          }],
          delta: 1.7,
          assignment: 'CH₃'
        }],
        integration: 3,
        from: 1.68325,
        to: 1.71675
      }, {
        signals: [{
          js: [{
            coupling: 10,
            multiplicity: 'd'
          }],
          delta: 4.95,
          assignment: 'CH₂(1)'
        }],
        integration: 1,
        from: 4.9325,
        to: 4.9675
      }, {
        signals: [{
          js: [{
            coupling: 17,
            multiplicity: 'd'
          }],
          delta: 5.06,
          assignment: 'CH₂(2)'
        }],
        integration: 1,
        from: 5.0337499999999995,
        to: 5.08625
      }, {
        signals: [{
          js: [],
          delta: 5.9,
          assignment: 'CH',
          multiplicity: 'm'
        }],
        integration: 1,
        from: 5.875,
        to: 5.925000000000001
      }],
      nucleus: '1H',
      solvent: 'D₂O'
    }, {
      names: ['pyridine'],
      smiles: 'C1=CC=NC=C1',
      ranges: [{
        signals: [{
          js: [],
          delta: 8.52,
          assignment: 'CH(2,6)',
          multiplicity: 'm'
        }],
        integration: 2,
        from: 8.495,
        to: 8.545
      }, {
        signals: [{
          js: [],
          delta: 7.45,
          assignment: 'CH(3,5)',
          multiplicity: 'm'
        }],
        integration: 2,
        from: 7.425,
        to: 7.4750000000000005
      }, {
        signals: [{
          js: [],
          delta: 7.87,
          assignment: 'CH(4)',
          multiplicity: 'm'
        }],
        integration: 1,
        from: 7.845,
        to: 7.8950000000000005
      }],
      nucleus: '1H',
      solvent: 'D₂O'
    }, {
      names: ['pyrrole'],
      smiles: 'C1=CNC=C1',
      ranges: [{
        signals: [{
          js: [],
          delta: 6.93,
          assignment: 'CH(2,5)',
          multiplicity: 'm'
        }],
        integration: 2,
        from: 6.904999999999999,
        to: 6.955
      }, {
        signals: [{
          js: [],
          delta: 6.26,
          assignment: 'CH(3,4)',
          multiplicity: 'm'
        }],
        integration: 2,
        from: 6.234999999999999,
        to: 6.285
      }],
      nucleus: '1H',
      solvent: 'D₂O'
    }, {
      names: ['pyrrolidine'],
      smiles: 'C1CCNC1',
      ranges: [{
        signals: [{
          js: [],
          delta: 3.07,
          assignment: 'CH₂(2,5)',
          multiplicity: 'm'
        }],
        integration: 4,
        from: 3.045,
        to: 3.0949999999999998
      }, {
        signals: [{
          js: [],
          delta: 1.87,
          assignment: 'CH₂(3,4)',
          multiplicity: 'm'
        }],
        integration: 4,
        from: 1.8450000000000002,
        to: 1.895
      }],
      nucleus: '1H',
      solvent: 'D₂O'
    }, {
      names: ['silicone grease'],
      smiles: '',
      ranges: [],
      nucleus: '1H',
      solvent: 'D₂O'
    }, {
      names: ['tetrahydrofuran'],
      smiles: 'C1COCC1',
      ranges: [{
        signals: [{
          js: [],
          delta: 3.74,
          assignment: 'CH₂(2,5)',
          multiplicity: 'm'
        }],
        integration: 4,
        from: 3.7150000000000003,
        to: 3.765
      }, {
        signals: [{
          js: [],
          delta: 1.88,
          assignment: 'CH₂(3,4)',
          multiplicity: 'm'
        }],
        integration: 4,
        from: 1.855,
        to: 1.9049999999999998
      }],
      nucleus: '1H',
      solvent: 'D₂O'
    }, {
      names: ['toluene'],
      smiles: 'Cc1ccccc1',
      ranges: [],
      nucleus: '1H',
      solvent: 'D₂O'
    }, {
      names: ['triethylamine'],
      smiles: 'CCN(CC)CC',
      ranges: [{
        signals: [{
          js: [{
            coupling: 7,
            multiplicity: 't'
          }],
          delta: 0.99,
          assignment: 'CH₃'
        }],
        integration: 6,
        from: 0.9675,
        to: 1.0125
      }, {
        signals: [{
          js: [{
            coupling: 7,
            multiplicity: 'q'
          }],
          delta: 2.57,
          assignment: 'CH₂'
        }],
        integration: 4,
        from: 2.53875,
        to: 2.60125
      }],
      nucleus: '1H',
      solvent: 'D₂O'
    }];

    function getShortestPath(options) {
      let {
        currUnAssCol,
        dualVariableForColumns,
        dualVariableForRows,
        rowAssignments,
        columnAssignments,
        matrix
      } = options;
      let nbRows = matrix.rows;
      let nbColumns = matrix.columns;
      let pred = new Float64Array(nbRows);
      let scannedColumns = new Float64Array(nbColumns);
      let scannedRows = new Float64Array(nbRows);
      let rows2Scan = Array.from(xSequentialFill({
        from: 0,
        to: nbRows - 1
      }));
      let numRows2Scan = nbRows;
      let sink = -1;
      let delta = 0;
      let curColumn = currUnAssCol;
      let shortestPathCost = new Array(nbRows).fill(Number.POSITIVE_INFINITY);
      while (sink === -1) {
        scannedColumns[curColumn] = 1;
        let minVal = Number.POSITIVE_INFINITY;
        let closestRowScan = -1;
        for (let curRowScan = 0; curRowScan < numRows2Scan; curRowScan++) {
          let curRow = rows2Scan[curRowScan];
          let reducedCost = delta + matrix.get(curRow, curColumn) - dualVariableForColumns[curColumn] - dualVariableForRows[curRow];
          if (reducedCost < shortestPathCost[curRow]) {
            pred[curRow] = curColumn;
            shortestPathCost[curRow] = reducedCost;
          }
          if (shortestPathCost[curRow] < minVal) {
            minVal = shortestPathCost[curRow];
            closestRowScan = curRowScan;
          }
        }
        if (!Number.isFinite(minVal)) {
          return {
            dualVariableForColumns,
            dualVariableForRows,
            sink,
            pred
          };
        }
        let closestRow = rows2Scan[closestRowScan];
        scannedRows[closestRow] = 1;
        numRows2Scan -= 1;
        rows2Scan.splice(closestRowScan, 1);
        delta = shortestPathCost[closestRow];
        if (rowAssignments[closestRow] === -1) {
          sink = closestRow;
        } else {
          curColumn = rowAssignments[closestRow];
        }
      }
      dualVariableForColumns[currUnAssCol] += delta;
      for (let sel = 0; sel < nbColumns; sel++) {
        if (scannedColumns[sel] === 0) continue;
        if (sel === currUnAssCol) continue;
        dualVariableForColumns[sel] += delta - shortestPathCost[columnAssignments[sel]];
      }
      for (let sel = 0; sel < nbRows; sel++) {
        if (scannedRows[sel] === 0) continue;
        dualVariableForRows[sel] -= delta - shortestPathCost[sel];
      }
      return {
        sink,
        pred,
        dualVariableForColumns,
        dualVariableForRows
      };
    }

    /**
     * Implementation of Jonker-Volgenant shortest augmenting path algorithm based
     * on the publication [On implementing 2D rectangular assignment algorithms](https://doi.org/10.1109/TAES.2016.140952)
     */
    function linearSumAssignment(
    /**
     * Cost matrix
     */
    input, options = {}) {
      const {
        maximaze = true
      } = options;
      let matrix = Matrix.checkMatrix(input);
      let didFlip = false;
      if (matrix.columns > matrix.rows) {
        didFlip = true;
        matrix = matrix.transpose();
      }
      let nbRows = matrix.rows;
      let nbColumns = matrix.columns;
      let matrixDelta = maximaze ? matrix.max() : matrix.min();
      matrix = matrix.subtract(matrixDelta);
      if (maximaze) matrix = matrix.mul(-1);
      let rowAssignments = new Float64Array(nbRows).fill(-1);
      let columnAssignments = new Float64Array(nbColumns).fill(-1);
      let dualVariableForColumns = new Float64Array(nbColumns);
      let dualVariableForRows = new Float64Array(nbRows);
      for (let currUnAssCol = 0; currUnAssCol < nbColumns; currUnAssCol++) {
        let currentAugmenting = getShortestPath({
          matrix,
          currUnAssCol,
          dualVariableForColumns,
          dualVariableForRows,
          rowAssignments,
          columnAssignments
        });
        let {
          sink,
          pred
        } = currentAugmenting;
        if (sink === -1) {
          return {
            rowAssignments,
            columnAssignments,
            gain: -1,
            dualVariableForColumns,
            dualVariableForRows
          };
        }
        dualVariableForColumns = currentAugmenting.dualVariableForColumns;
        dualVariableForRows = currentAugmenting.dualVariableForRows;
        let j = sink;
        for (let i = pred[j]; true; i = pred[j]) {
          rowAssignments[j] = i;
          let h = columnAssignments[i];
          columnAssignments[i] = j;
          j = h;
          if (i === currUnAssCol) break;
        }
      }
      let gain = 0;
      for (let curCol = 0; curCol < nbColumns; curCol++) {
        gain += matrix.get(columnAssignments[curCol], curCol);
      }
      gain = (maximaze ? -1 : 1) * gain + matrixDelta * nbColumns;
      if (didFlip) {
        [columnAssignments, rowAssignments] = [rowAssignments, columnAssignments];
        [dualVariableForColumns, dualVariableForRows] = [dualVariableForRows, dualVariableForColumns];
      }
      return {
        rowAssignments,
        columnAssignments,
        gain,
        dualVariableForColumns,
        dualVariableForRows
      };
    }

    const {
      stringify,
      parse
    } = JSON;
    function cloneArray(data) {
      return parse(stringify(data));
    }

    function solventSuppression(peakList, solvent, options = {}) {
      const peaks = cloneArray(peakList).sort((a, b) => a.x - b.x);
      const xValues = peaks.map(peak => peak.x);
      const {
        markSolventPeaks = false,
        solventZoneExtension = 1.2
      } = options;
      for (const solventSignal of solvent) {
        const solventXYPeaks = solventSignal.peaks ? solventSignal.peaks : getSolventPeaks(solventSignal);
        solventXYPeaks.sort((a, b) => a.x - b.x);
        const upIndex = xFindClosestIndex(xValues, solventXYPeaks[solventXYPeaks.length - 1].x + solventZoneExtension);
        const lowIndex = xFindClosestIndex(xValues, solventXYPeaks[0].x - solventZoneExtension);
        const nearPeaks = peaks.filter((peak, index) => index >= lowIndex && index <= upIndex);
        const amplitudeResiduals = [];
        const deltaResiduals = [];
        const positionResiduals = [];
        for (const peak of nearPeaks) {
          const {
            peaks: shiftedSolventPeaks,
            delta: currentDelta
          } = getShiftedSolventPeaks(peak, solventSignal, solventXYPeaks);
          const closestPeaks = getClosestPeaks(shiftedSolventPeaks, nearPeaks);
          let deltaResidual = 0;
          let amplitudeResidual = 0;
          let positionResidual = 0;
          for (let i = 0; i < closestPeaks.length; i++) {
            amplitudeResidual += Math.abs(shiftedSolventPeaks[i].y - closestPeaks[i].y);
            deltaResidual += Math.abs(shiftedSolventPeaks[i].x - closestPeaks[i].x);
          }
          if (closestPeaks.length === 0) {
            deltaResidual = Number.MAX_SAFE_INTEGER;
            amplitudeResidual = Number.MAX_SAFE_INTEGER;
            positionResidual = Number.MAX_SAFE_INTEGER;
          } else {
            positionResidual = gaussianFct(Math.abs(solventSignal.delta - currentDelta), 0.5);
          }
          amplitudeResiduals.push(amplitudeResidual);
          deltaResiduals.push(deltaResidual);
          positionResiduals.push(positionResidual);
        }
        const [maxAmplitude, maxDelta, maxPosition] = [amplitudeResiduals, deltaResiduals, positionResiduals].map(data => {
          const max = xMaxValue(data);
          return max === 0 ? 1 : max;
        });
        let minIndex = -1;
        let minScore = Number.MAX_SAFE_INTEGER;
        for (let i = 0; i < deltaResiduals.length; i++) {
          const value = (amplitudeResiduals[i] / maxAmplitude + deltaResiduals[i] / maxDelta + 1 - positionResiduals[i] / maxPosition) / 3;
          if (minScore > value) {
            minIndex = i;
            minScore = value;
          }
        }
        if (minScore < 0 || minIndex < 0) {
          return peaks;
        }
        const {
          peaks: shiftedSolventPeaks
        } = getShiftedSolventPeaks(nearPeaks[minIndex], solventSignal, solventXYPeaks);
        const diff = getDiffMatrix(shiftedSolventPeaks, nearPeaks);
        const {
          rowAssignments,
          gain
        } = linearSumAssignment(diff, {
          maximaze: false
        });
        if (gain < 0) {
          return peaks;
        }
        if (markSolventPeaks) {
          for (const index of rowAssignments) {
            peaks[index + lowIndex].kind = 'solvent';
          }
        } else {
          rowAssignments.sort((a, b) => b - a);
          for (const index of rowAssignments) {
            peaks.splice(index + lowIndex, 1);
          }
        }
      }
      return peaks;
    }
    function getSolventPeaks(signal, options = {}) {
      const signals = addDummySignals([signal]);
      const spinSystem = signalsToSpinSystem(signals);
      const {
        frequency = 400,
        maxClusterSize = 8
      } = options;
      spinSystem.clusters = splitSpinSystem(spinSystem, {
        frequency,
        maxClusterSize
      });
      const peaks = simulateXYPeaks(spinSystem);
      return peaks.filter(peak => peak.x < 1000);
    }
    function getShiftedSolventPeaks(peak, solventSignal, solventXYPeaks) {
      const shiftedSolventPeaks = JSON.parse(JSON.stringify(solventXYPeaks));
      // shift x values of solventPeaks to center it to the current peak.
      let deltaPPM = peak.x - solventSignal.delta;
      if (solventXYPeaks.length > 1 && solventXYPeaks.length % 2 === 0) {
        deltaPPM += solventXYPeaks[0].x;
      }
      const maxIntensity = shiftedSolventPeaks.reduce((max, current) => current.y > max ? current.y : max, shiftedSolventPeaks[0].y);
      let currentDelta = 0;
      for (const shiftedSolventPeak of shiftedSolventPeaks) {
        shiftedSolventPeak.x += deltaPPM;
        shiftedSolventPeak.y /= maxIntensity;
        currentDelta += shiftedSolventPeak.x;
      }
      return {
        peaks: shiftedSolventPeaks,
        delta: currentDelta / shiftedSolventPeaks.length
      };
    }
    function getClosestPeaks(shiftedSolventPeaks, nearPeaks) {
      const diff = getDiffMatrix(shiftedSolventPeaks, nearPeaks);
      const {
        rowAssignments,
        gain
      } = linearSumAssignment(diff, {
        maximaze: false
      });
      if (gain === -1) return [];
      const assignmentPeaks = [];
      let maxValue = Number.MIN_SAFE_INTEGER;
      for (const index of rowAssignments) {
        if (maxValue < nearPeaks[index].y) maxValue = nearPeaks[index].y;
        assignmentPeaks.push({
          ...nearPeaks[index]
        });
      }
      assignmentPeaks.forEach((peak, i, arr) => arr[i].y /= maxValue);
      return assignmentPeaks;
    }
    function getDiffMatrix(rows, columns) {
      const nbColumns = columns.length;
      const nbRows = rows.length;
      const diff = new Matrix(nbRows, nbColumns);
      for (let r = 0; r < nbRows; r++) {
        for (let c = 0; c < nbColumns; c++) {
          diff.set(r, c, Math.abs(rows[r].x - columns[c].x));
        }
      }
      return diff;
    }

    function markSolventPeaks(input, solventSignals, options = {}) {
      const {
        frequency = 400
      } = options;
      const ranges = addIDs(cloneArray(input));
      const peakList = [];
      for (const range of ranges) {
        const rangeID = range.id;
        for (const signal of range.signals || []) {
          const signalID = signal.id;
          for (const peak of signal.peaks || []) {
            peakList.push({
              ...peak,
              rangeID,
              signalID
            });
          }
        }
      }
      const peaks = solventSuppression(peakList, solventSignals, {
        markSolventPeaks: true
      });
      const signalsWithSolvent = getSignalIDsWithSolvent(peaks);
      for (const range of ranges) {
        if (!range.signals) continue;
        for (const signal of range.signals) {
          if (!signalsWithSolvent.includes(signal.id)) continue;
          const signalPeaks = [];
          const solventSignalPeaks = [];
          for (const peak of peaks) {
            if (peak.signalID === signal.id) {
              const {
                signalID,
                rangeID,
                ...newPeak
              } = peak;
              if (peak.kind === 'solvent') {
                solventSignalPeaks.push(newPeak);
              } else {
                signalPeaks.push(newPeak);
              }
            }
          }
          const newSignals = signalPeaks.length > 0 ? detectSignals({
            x: [],
            y: []
          }, signalPeaks, {
            integralType: 'peak',
            frequency
          }) : [];
          solventSignalPeaks.sort((a, b) => a.x - b.x);
          const lowPPMValue = solventSignalPeaks[0].x;
          const highPPMValue = solventSignalPeaks[solventSignalPeaks.length - 1].x;
          const newSolventSignals = detectSignals({
            x: [],
            y: []
          }, solventSignalPeaks, {
            integralType: 'peak',
            frequencyCluster: (highPPMValue - lowPPMValue) * frequency,
            frequency
          });
          range.signals = [...adaptSignals(newSignals), ...adaptSignals(newSolventSignals), ...range.signals.filter(currSignal => currSignal.id !== signal.id)];
        }
      }
      return ranges;
    }
    function getSignalIDsWithSolvent(solventPeaks) {
      const uniqueSignalID = new Set();
      for (const peak of solventPeaks) {
        if (peak.kind !== 'solvent') continue;
        uniqueSignalID.add(peak.signalID);
      }
      return Array.from(uniqueSignalID);
    }
    function adaptSignals(signals) {
      const newSignals = [];
      for (const signal of signals) {
        const signalResult = {
          id: v4(),
          delta: signal.delta,
          kind: signal.kind || 'signal',
          multiplicity: signal.multiplicity,
          integration: signal.integralData.value
        };
        signalResult.peaks = signal.peaks.map(peak => {
          const newResult = {
            y: peak.intensity,
            ...peak
          };
          delete newResult.intensity;
          return newResult;
        });
        signalResult.js = signal.nmrJs || [];
        newSignals.push(signalResult);
      }
      return newSignals;
    }

    /**
     * look for a multiplet based on the pattern name or abbreviation.
     * @param name - pattern name or abbreviation.
     */
    function findMultiplet(name) {
      return MultiplicityPatterns.find(multiplet => {
        const {
          label,
          value,
          names
        } = multiplet;
        return [label, value, ...names].includes(name.replace(/\./, ''));
      });
    }

    /**
     * check if the input string corresponds to a multiplet (pattern), it could has a common name, acs or one letter abbreviation.
     * @param string - pattern name or abbreviation.
     * @param rejected - list of patterns that should not match with the input pattern. e.g 'massive' as input will return false if rejected = ['m'].
     */
    function checkMultiplet(string, rejected = []) {
      const multiplet = findMultiplet(string);
      return multiplet && !rejected.includes(multiplet.label) && !rejected.includes(multiplet.value) && !multiplet.names.some(name => rejected.includes(name));
    }

    /**
     * check the input string corresponds to a composed pattern like dt or dd. it splits into indivitual patterns and use checkMultiplet function.
     * @param multiplicity - pattern name or abbreviation.
     * @param rejected - list of patterns that should not match with any pattern. e.g 'dt' as input will return false if rejected = ['t'].
     */
    function checkMultiplicity(multiplicity, rejected = []) {
      // options to determine whether a massive (m), for example, should be considered as rejected multiplicity
      if (multiplicity === undefined || multiplicity.length === 0) {
        return false;
      }
      const multiplets = splitPatterns(multiplicity);
      const allIncluded = multiplets.join('').length === multiplicity.length;
      return allIncluded && multiplets.every(_multiplet => checkMultiplet(_multiplet, rejected));
    }

    /**
     * convert from pattern name to only one letter abbreviation or any supported abbreviation to pattern name.
     * @param name - pattern name or abbreviation.
     * @returns
     */
    function translateMultiplet(name) {
      const multiplet = findMultiplet(name);
      if (!multiplet) {
        throw new Error(`multiplet ${name} is not supported`);
      }
      const {
        value,
        label
      } = multiplet;
      return name === label ? value : label;
    }

    function apodizationFilter(datum1D, options) {
      const {
        lineBroadening,
        gaussBroadening,
        lineBroadeningCenter
      } = options;
      const grpdly = datum1D.info?.digitalFilter || 0;
      const pointsToShift = grpdly > 0 ? Math.floor(grpdly) : 0;
      const re = datum1D.data.re;
      const im = datum1D.data.im;
      const t = datum1D.data.x;
      const length = re.length;
      const dw = (t[length - 1] - t[0]) / (length - 1); //REPLACE CONSTANT with calculated value... : for this we need AQ or DW to set it right...
      return apodization$1({
        re,
        im
      }, {
        pointsToShift,
        compose: {
          length,
          shapes: [{
            start: 0,
            shape: {
              kind: 'lorentzToGauss',
              options: {
                length,
                dw,
                exponentialHz: gaussBroadening > 0 ? lineBroadening : -lineBroadening,
                gaussianHz: gaussBroadening,
                center: lineBroadeningCenter
              }
            }
          }]
        }
      });
    }

    const id$d = 'apodization';
    const name$d = 'Apodization';
    const DOMAIN_UPDATE_RULES$d = {
      updateXDomain: true,
      updateYDomain: false
    };
    function apply$f(datum1D, options) {
      if (!isApplicable$d(datum1D)) {
        throw new Error('apodization not applicable on this data');
      }
      const {
        lineBroadening = 1,
        gaussBroadening = 0,
        lineBroadeningCenter = 0
      } = options;
      // if no options is set, we add the default value to filter
      if (Object.keys(options).length === 0) {
        const filter = datum1D.filters?.find(filter => filter.name === id$d);
        if (filter) {
          filter.value = {
            lineBroadening,
            gaussBroadening,
            lineBroadeningCenter
          };
        }
      }
      const newData = apodizationFilter(datum1D, {
        lineBroadening,
        gaussBroadening,
        lineBroadeningCenter
      });
      datum1D.data = {
        ...datum1D.data,
        re: newData.re,
        im: newData.im
      };
    }
    function isApplicable$d(datum1D) {
      if (datum1D.info.isComplex && datum1D.info.isFid) return true;
      return false;
    }
    function reduce$d(previousValue, newValue) {
      return {
        once: true,
        reduce: newValue
      };
    }

    var apodization = /*#__PURE__*/Object.freeze({
        __proto__: null,
        id: id$d,
        name: name$d,
        DOMAIN_UPDATE_RULES: DOMAIN_UPDATE_RULES$d,
        apply: apply$f,
        isApplicable: isApplicable$d,
        reduce: reduce$d
    });

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

    /**
     * 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/master/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.factorCriterion = 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 x axis values to force that baseline cross those points.
     * @param {Array<number>} [options.baseLineZones = []] - 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 = {}) {
      let {
        maxIterations = 100,
        lambda = 100,
        factorCriterion = 0.001,
        weights = new Array(y.length).fill(1),
        controlPoints = [],
        baseLineZones = []
      } = options;
      if (controlPoints.length > 0) {
        controlPoints.forEach((e, i, arr) => arr[i] = getCloseIndex(x, e));
      }
      if (baseLineZones.length > 0) {
        baseLineZones.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.push(i);
          }
        });
      }
      let baseline, iteration;
      let nbPoints = y.length;
      let l = nbPoints - 1;
      let sumNegDifferences = Number.MAX_SAFE_INTEGER;
      let stopCriterion = factorCriterion * y.reduce((sum, e) => Math.abs(e) + sum, 0);
      let {
        lowerTriangularNonZeros,
        permutationEncodedArray
      } = getDeltaMatrix(nbPoints, lambda);
      for (iteration = 0; iteration < maxIterations && Math.abs(sumNegDifferences) > stopCriterion; iteration++) {
        let [leftHandSide, rightHandSide] = updateSystem(lowerTriangularNonZeros, y, weights);
        let cho = prepare(leftHandSide, nbPoints, permutationEncodedArray);
        baseline = cho(rightHandSide);
        sumNegDifferences = 0;
        let difference = y.map(calculateError);
        let maxNegativeDiff = -1 * Number.MAX_SAFE_INTEGER;
        for (let i = 1; i < l; i++) {
          let diff = difference[i];
          if (diff >= 0) {
            weights[i] = 0;
          } else {
            weights[i] = Math.exp(iteration * diff / sumNegDifferences);
            if (maxNegativeDiff < diff) maxNegativeDiff = diff;
          }
        }
        let value = Math.exp(iteration * maxNegativeDiff / sumNegDifferences);
        weights[0] = value;
        weights[l] = value;
        controlPoints.forEach(i => weights[i] = value);
      }
      return {
        corrected: y.map((e, i) => e - baseline[i]),
        baseline,
        iteration,
        error: sumNegDifferences
      };
      function calculateError(e, i) {
        let diff = e - baseline[i];
        if (diff < 0) sumNegDifferences += diff;
        return diff;
      }
    }

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

    function checkArraySize(x, y) {
      if (!isAnyArray(x) || !isAnyArray(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(x)) {
          const y = [];
          for (let i = 0; i < x.length; i++) {
            y.push(this._predict(x[i]));
          }
          return y;
        } else {
          throw new TypeError('x must be a number or array');
        }
      }
      _predict() {
        throw new Error('_predict must be implemented');
      }
      train() {
        // Do nothing for this package
      }
      toString() {
        return '';
      }
      toLaTeX() {
        return '';
      }

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

    class PolynomialRegression extends BaseRegression {
      constructor(x, y, degree) {
        super();
        if (x === true) {
          this.degree = y.degree;
          this.powers = y.powers;
          this.coefficients = y.coefficients;
        } else {
          checkArraySize(x, y);
          regress(this, x, y, degree);
        }
      }
      _predict(x) {
        let y = 0;
        for (let k = 0; k < this.powers.length; k++) {
          y += this.coefficients[k] * Math.pow(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.charAt(0) === '+') {
          fn = fn.slice(1);
        }
        return `f(x) = ${fn}`;
      }
      static load(json) {
        if (json.name !== 'polynomialRegression') {
          throw new TypeError('not a polynomial regression model');
        }
        return new PolynomialRegression(true, json);
      }
    }
    function regress(pr, x, y, degree) {
      const n = x.length;
      let powers;
      if (Array.isArray(degree)) {
        powers = degree;
        degree = powers.length;
      } else {
        degree++;
        powers = new Array(degree);
        for (let k = 0; k < degree; k++) {
          powers[k] = k;
        }
      }
      const F = new Matrix(n, degree);
      const Y = new Matrix([y]);
      for (let k = 0; k < degree; k++) {
        for (let i = 0; i < n; i++) {
          if (powers[k] === 0) {
            F.set(i, k, 1);
          } else {
            F.set(i, k, Math.pow(x[i], powers[k]));
          }
        }
      }
      const FT = new MatrixTransposeView(F);
      const A = FT.mmul(F);
      const B = FT.mmul(new MatrixTransposeView(Y));
      pr.degree = degree - 1;
      pr.powers = powers;
      pr.coefficients = solve(A, B).to1DArray();
    }

    /**
     * Iterative regression-based baseline correction
     * @param {Array<number>} x - Independent axis variable
     * @param {Array<number>} y - Dependent axis variable
     * @param {object} [options] - Options object
     * @param {number} [options.maxIterations = 100] - Maximum number of allowed iterations
     * @param {function} [options.Regression = PolynomialRegression] - Regression class with a predict method
     * @param {*} [options.regressionOptions] - Options for regressionFunction
     * @param {number} [options.tolerance = 0.001] - Convergence error tolerance
     * @return {{corrected: Array<number>, delta: number, iteration: number, baseline: Array<number>}}
     */
    function baselineCorrectionRegression(x, y, options = {}) {
      let {
        maxIterations = 100,
        Regression = PolynomialRegression,
        regressionOptions,
        tolerance = 0.001
      } = options;
      if (!regressionOptions && Regression === PolynomialRegression) {
        regressionOptions = 3;
      }
      let baseline = y.slice();
      let fitting = y.slice();
      let oldFitting = y;
      let iteration = 0;
      let delta;
      let regression;
      while (iteration < maxIterations) {
        // Calculate the fitting result
        regression = new Regression(x, baseline, regressionOptions);
        delta = 0;
        for (let i = 0; i < baseline.length; i++) {
          fitting[i] = regression.predict(x[i]);
          if (baseline[i] > fitting[i]) {
            baseline[i] = fitting[i];
          }
          delta += Math.abs((fitting[i] - oldFitting[i]) / oldFitting[i]);
        }

        // Stop criterion
        if (delta < tolerance) {
          break;
        } else {
          oldFitting = fitting.slice();
          iteration++;
        }
      }

      // removes baseline
      let corrected = new Array(baseline.length);
      for (let j = 0; j < baseline.length; j++) {
        corrected[j] = y[j] - baseline[j];
      }
      return {
        corrected,
        delta,
        iteration,
        baseline,
        regression
      };
    }

    const id$c = 'baselineCorrection';
    const name$c = 'Baseline correction';
    const DOMAIN_UPDATE_RULES$c = {
      updateXDomain: false,
      updateYDomain: true
    };
    function apply$e(datum1D, options) {
      if (!isApplicable$c(datum1D)) {
        throw new Error('baselineCorrection not applicable on this data');
      }
      const {
        algorithm
      } = options;
      const {
        x,
        re
      } = datum1D.data;
      let corrected;
      switch (algorithm) {
        case 'airpls':
          corrected = airPLS(x, re, options).corrected;
          break;
        case 'polynomial':
          {
            const {
              degree,
              zones
            } = options;
            const reduced = xyEquallySpaced({
              x,
              y: re
            }, {
              numberOfPoints: 4096,
              zones
            });
            const result = baselineCorrectionRegression(reduced.x, reduced.y, {
              regressionOptions: degree
            });
            const {
              regression
            } = result;
            corrected = new Float64Array(x.length);
            for (let i = 0; i < re.length; i++) {
              corrected[i] = re[i] - regression.predict(x[i]);
            }
          }
          break;
        default:
          // eslint-disable-next-line @typescript-eslint/restrict-template-expressions
          throw new Error(`baselineCorrection: algorithm unknown: ${algorithm}`);
      }
      Object.assign(datum1D.data, {
        re: corrected
      });
    }
    function isApplicable$c(datum1D) {
      if (!datum1D.info.isFid) return true;
      return false;
    }
    function reduce$c(previousValue, newValue) {
      return {
        once: true,
        reduce: newValue
      };
    }

    var baselineCorrection = /*#__PURE__*/Object.freeze({
        __proto__: null,
        id: id$c,
        name: name$c,
        DOMAIN_UPDATE_RULES: DOMAIN_UPDATE_RULES$c,
        apply: apply$e,
        isApplicable: isApplicable$c,
        reduce: reduce$c
    });

    const id$b = 'digitalFilter';
    const name$b = 'Digital Filter';
    const DOMAIN_UPDATE_RULES$b = {
      updateXDomain: true,
      updateYDomain: true
    };
    /**
     * Move points from the beginning to the end of FID and performs a first order phase correction
     * @param {Datum1d} datum1d
     */
    function apply$d(spectrum, options = {}) {
      if (!isApplicable$b(spectrum)) {
        throw new Error('Digital Filter is not applicable on this data');
      }
      let {
        digitalFilterValue
      } = options;
      if (!digitalFilterValue) {
        digitalFilterValue = spectrum.info.digitalFilter || 0;
        const filter = spectrum.filters.find(filter => filter.name === id$b);
        if (filter) {
          filter.value = {
            digitalFilterValue
          };
        }
      }
      const re = new Float64Array(spectrum.data.re);
      const im = new Float64Array(spectrum.data.im);
      let pointsToShift = Math.floor(digitalFilterValue);
      const skip = 0;
      pointsToShift += 0;
      const newRe = new Float64Array(re.length);
      const newIm = new Float64Array(im.length);
      newRe.set(re.slice(pointsToShift));
      newRe.set(re.slice(skip, pointsToShift), re.length - pointsToShift);
      newIm.set(im.slice(pointsToShift));
      newIm.set(im.slice(skip, pointsToShift), im.length - pointsToShift);
      spectrum.data.re = newRe;
      spectrum.data.im = newIm;
    }
    function isApplicable$b(datum1D) {
      if (datum1D.info.isComplex && datum1D.info.isFid) return true;
      return false;
    }
    function reduce$b() {
      return {
        once: true,
        reduce: undefined
      };
    }

    var digitalFilter = /*#__PURE__*/Object.freeze({
        __proto__: null,
        id: id$b,
        name: name$b,
        DOMAIN_UPDATE_RULES: DOMAIN_UPDATE_RULES$b,
        apply: apply$d,
        isApplicable: isApplicable$b,
        reduce: reduce$b
    });

    const id$a = 'equallySpaced';
    const name$a = 'Equally spaced';
    const DOMAIN_UPDATE_RULES$a = {
      updateXDomain: false,
      updateYDomain: false
    };
    /**
     * Equally Spaced
     * **
     * Function that returns a Number array of equally spaced numberOfPoints
     * containing a representation of intensities of the spectra arguments x
     * and y.
     *
     * The options parameter contains an object in the following form:
     * from: starting point
     * to: last point
     * numberOfPoints: number of points between from and to
     * variant: "slot" or "smooth" - smooth is the default option
     *
     * The slot variant consist that each point in the new array is calculated
     * averaging the existing points between the slot that belongs to the current
     * value. The smooth variant is the same but takes the integral of the range
     * of the slot and divide by the step size between two points in the new array.
     *
     * @param {Array<{from:number;to:number}>} options.exclusions
     */
    function apply$c(spectrum, options = {}) {
      if (!isApplicable$a(spectrum)) {
        throw new Error('Equally Spaced is not applicable on this data');
      }
      const {
        from,
        to,
        numberOfPoints,
        exclusions
      } = options;
      const {
        x,
        re,
        im
      } = spectrum.data;
      const XREdata = xyEquallySpaced({
        x,
        y: re
      }, {
        from,
        to,
        numberOfPoints,
        exclusions
      });
      spectrum.data.x = xEnsureFloat64(XREdata.x);
      spectrum.data.re = xEnsureFloat64(XREdata.y);
      if (im) {
        const XIMdata = xyEquallySpaced({
          x,
          y: re
        }, {
          from,
          to,
          numberOfPoints,
          exclusions
        });
        spectrum.data.im = xEnsureFloat64(XIMdata.y);
      }
    }
    function isApplicable$a(spectrum) {
      if (spectrum.info.isComplex && !spectrum.info.isFid) return true;
      return false;
    }
    function reduce$a() {
      return {
        once: false,
        reduce: null
      };
    }

    var equallySpaced$1 = /*#__PURE__*/Object.freeze({
        __proto__: null,
        id: id$a,
        name: name$a,
        DOMAIN_UPDATE_RULES: DOMAIN_UPDATE_RULES$a,
        apply: apply$c,
        isApplicable: isApplicable$a,
        reduce: reduce$a
    });

    const id$9 = 'exclusionZones';
    const name$9 = 'Exclusion zones';
    const DOMAIN_UPDATE_RULES$9 = {
      updateXDomain: false,
      updateYDomain: false
    };
    function apply$b(spectrum, zones = []) {
      if (!isApplicable$9(spectrum)) {
        throw new Error('Exclusion Zones filter not applicable on this data');
      }
      const {
        x,
        re,
        im
      } = spectrum.data;
      spectrum.data.re = Float64Array.from(xySetYValue({
        x,
        y: re
      }, {
        zones
      }).y);
      spectrum.data.im = im ? Float64Array.from(xySetYValue({
        x,
        y: re
      }, {
        zones
      }).y) : im;
    }
    function isApplicable$9(spectrum) {
      if (spectrum.info.isFt) return true;
      return false;
    }
    function reduce$9(previousValue, newValue) {
      return {
        once: true,
        reduce: zonesNormalize(previousValue.concat(newValue))
      };
    }

    var exclusionZones = /*#__PURE__*/Object.freeze({
        __proto__: null,
        id: id$9,
        name: name$9,
        DOMAIN_UPDATE_RULES: DOMAIN_UPDATE_RULES$9,
        apply: apply$b,
        isApplicable: isApplicable$9,
        reduce: reduce$9
    });

    function padDataToNextPowerOfTwo(datum1D, digitalFilterApplied) {
      const {
        x,
        re,
        im
      } = datum1D.data;
      const size = nextPowerOfTwo(x.length);
      const newRE = new Float64Array(size);
      const newIM = new Float64Array(size);
      const grpdly = datum1D.info?.digitalFilter || 0;
      const pointsToShift = grpdly > 0 ? Math.floor(grpdly) : 0;
      newRE.set(re.slice(0, size - pointsToShift));
      newIM.set(im.slice(0, size - pointsToShift));
      if (pointsToShift > 0 && digitalFilterApplied) {
        newRE.set(re.slice(re.length - pointsToShift), size - pointsToShift);
        newIM.set(im.slice(re.length - pointsToShift), size - pointsToShift);
      }
      const newX = xSequentialFill({
        from: x[0],
        size,
        step: x[1] - x[0]
      });
      datum1D.data = {
        ...datum1D.data,
        re: newRE,
        im: newIM,
        x: newX
      };
    }
    function nextPowerOfTwo(n) {
      if (n === 0) return 1;
      n--;
      n |= n >> 1;
      n |= n >> 2;
      n |= n >> 4;
      n |= n >> 8;
      n |= n >> 16;
      return n + 1;
    }

    const id$8 = 'fft';
    const name$8 = 'FFT';
    const DOMAIN_UPDATE_RULES$8 = {
      updateXDomain: true,
      updateYDomain: true
    };
    function apply$a(datum1D) {
      if (!isApplicable$8(datum1D)) {
        throw new Error('fft not applicable on this data');
      }
      checkSameLength(datum1D);
      const digitalFilterApplied = datum1D.filters.some(e => e.name === 'digitalFilter' && e.flag);
      const {
        info: {
          aqMod
        }
      } = datum1D;
      if (aqMod === 1) {
        removeDCOffset(datum1D, digitalFilterApplied);
      }
      if (!isPowerOfTwo(datum1D.data.x.length)) {
        padDataToNextPowerOfTwo(datum1D, digitalFilterApplied);
      }
      const {
        data,
        info
      } = datum1D;
      Object.assign(data, reimFFT({
        re: data.re,
        im: data.im
      }, {
        applyZeroShift: true
      }));
      if (digitalFilterApplied) {
        const {
          digitalFilter = 0
        } = info;
        const ph1 = (digitalFilter - Math.floor(digitalFilter)) * Math.PI * 2;
        Object.assign(data, reimPhaseCorrection(data, 0, -ph1, {
          reverse: true
        }));
      }
      datum1D.data.x = generateXAxis(datum1D);
      datum1D.info = {
        ...datum1D.info,
        isFid: false,
        isFt: true
      };
    }
    function isApplicable$8(datum1D) {
      if (datum1D.info.isComplex && datum1D.info.isFid) return true;
      return false;
    }
    function reduce$8() {
      return {
        once: true,
        reduce: undefined
      };
    }
    function generateXAxis(datum1D) {
      const info = datum1D.info;
      const baseFrequency = Number(info.baseFrequency);
      const frequencyOffset = Number(info.frequencyOffset);
      const spectralWidth = Number(info.spectralWidth);
      const offset = frequencyOffset / baseFrequency;
      const spectralHalfWidth = 0.5 * spectralWidth;
      const nbPoints = datum1D.data.x.length;
      let firstPoint = offset - spectralHalfWidth;
      const dx = spectralWidth / (nbPoints - 1);
      const xAxis = new Float64Array(nbPoints);
      for (let i = 0; i < nbPoints; i++) {
        xAxis[i] = firstPoint;
        firstPoint += dx;
      }
      return xAxis;
    }
    function isPowerOfTwo(n) {
      return n !== 0 && (n & n - 1) === 0;
    }
    function removeDCOffset(spectrum, digitalFilterApplied) {
      const {
        digitalFilter = 0
      } = digitalFilterApplied && spectrum.info ? spectrum.info : {};
      const data = spectrum.data;
      const nbPoints = data.re.length;
      const newRe = new Float64Array(data.re);
      const newIm = new Float64Array(data.im);
      const averageRe = xMean(data.re.slice(nbPoints * 0.75 >> 0, nbPoints - digitalFilter));
      const averageIm = xMean(data.im.slice(nbPoints * 0.75 >> 0, nbPoints - digitalFilter));
      for (let i = digitalFilterApplied ? 0 : digitalFilter; i < nbPoints - digitalFilter; i++) {
        newRe[i] -= averageRe;
        newIm[i] -= averageIm;
      }
      Object.assign(spectrum.data, {
        re: newRe,
        im: newIm
      });
      return spectrum;
    }
    function checkSameLength(spectrum) {
      const {
        data
      } = spectrum;
      if (data.x.length !== data.re.length || data.x.length !== data.im.length) {
        throw new Error('The length of data should be equal');
      }
    }

    var fft = /*#__PURE__*/Object.freeze({
        __proto__: null,
        id: id$8,
        name: name$8,
        DOMAIN_UPDATE_RULES: DOMAIN_UPDATE_RULES$8,
        apply: apply$a,
        isApplicable: isApplicable$8,
        reduce: reduce$8
    });

    const id$7 = 'phaseCorrection';
    const name$7 = 'Phase correction';
    const DOMAIN_UPDATE_RULES$7 = {
      updateXDomain: false,
      updateYDomain: false
    };
    /**
     *
     * @param {Datum1d} datum1d
     * @param {Object} [options={}]
     * @param {number} [options.ph0=0]
     * @param {number} [options.ph1=0]
     */
    function apply$9(datum1D, options = {}) {
      if (!isApplicable$7(datum1D)) {
        throw new Error('phaseCorrection not applicable on this data');
      }
      const {
        absolute = false
      } = options;
      const filter = datum1D.filters?.find(filter => filter.name === id$7);
      if (absolute) {
        const {
          re,
          im
        } = datum1D.data;
        datum1D.data.re = reimAbsolute({
          re,
          im
        });
        datum1D.data.im = new Float64Array(0);
        if (filter) {
          filter.value = {
            ...filter.value,
            ph0: 0,
            ph1: 0,
            absolute
          };
        }
      } else if ('ph0' in options && 'ph1' in options) {
        const {
          ph0,
          ph1
        } = options;
        phaseCorrection(datum1D, {
          ph0,
          ph1
        });
        if (filter) {
          filter.value = {
            ...filter.value,
            absolute
          };
        }
      } else {
        const {
          ph0,
          ph1
        } = autoPhaseCorrection(datum1D);
        phaseCorrection(datum1D, {
          ph0,
          ph1
        });
        if (filter) {
          filter.value = {
            ...filter.value,
            absolute,
            ph0,
            ph1
          };
        }
      }
    }
    function phaseCorrection(spectrum, phaseValues) {
      const {
        ph0,
        ph1
      } = phaseValues;
      const {
        re,
        im
      } = spectrum.data;
      const phased = reimPhaseCorrection({
        re,
        im
      }, ph0 * Math.PI / 180, ph1 * Math.PI / 180, {
        reverse: true
      });
      spectrum.data = {
        ...spectrum.data,
        ...{
          re: xEnsureFloat64(phased.re),
          im: xEnsureFloat64(phased.im)
        }
      };
    }
    function autoPhaseCorrection(spectrum, options = {}) {
      const {
        minRegSize = 5,
        maxDistanceToJoin = 128,
        magnitudeMode = true,
        factorNoise = 5,
        reverse = true
      } = options;
      return reimAutoPhaseCorrection(spectrum.data, {
        minRegSize,
        maxDistanceToJoin,
        magnitudeMode,
        factorNoise,
        reverse
      });
    }
    function isApplicable$7(spectrum) {
      if (spectrum.info.isComplex && !spectrum.info.isFid) return true;
      return false;
    }
    function reduce$7(previousValue, newValue) {
      return {
        once: true,
        reduce: newValue
      };
    }

    var phaseCorrection$1 = /*#__PURE__*/Object.freeze({
        __proto__: null,
        id: id$7,
        name: name$7,
        DOMAIN_UPDATE_RULES: DOMAIN_UPDATE_RULES$7,
        apply: apply$9,
        isApplicable: isApplicable$7,
        reduce: reduce$7
    });

    /**
     *
     * @param {Object} datum1d
     * @param {number} [shiftValue=0]
     */
    const id$6 = 'shiftX';
    const name$6 = 'Shift X';
    const DOMAIN_UPDATE_RULES$6 = {
      updateXDomain: true,
      updateYDomain: false
    };
    function apply$8(spectrum, options = {}) {
      const {
        shift = 0
      } = options;
      spectrum.data.x = spectrum.data.x.map(val => val + shift);
    }
    function isApplicable$6() {
      return true;
    }
    function reduce$6(previous, next) {
      const {
        shift: previousValue = 0
      } = previous;
      const {
        shift: nextShift = 0
      } = next;
      return {
        once: true,
        reduce: {
          shift: previousValue + nextShift
        }
      };
    }

    var shiftX = /*#__PURE__*/Object.freeze({
        __proto__: null,
        id: id$6,
        name: name$6,
        DOMAIN_UPDATE_RULES: DOMAIN_UPDATE_RULES$6,
        apply: apply$8,
        isApplicable: isApplicable$6,
        reduce: reduce$6
    });

    /**
     * Center the mean
     * @param data
     */
    function centerMean(data) {
      const {
        y
      } = data;
      const mean = xMean(y);
      for (let i = 0; i < y.length; i++) {
        y[i] -= mean;
      }
      return {
        data
      };
    }

    /**
     * Center the median
     * @param data
     */
    function centerMedian(data) {
      const {
        y
      } = data;
      const median = xMedian(y);
      for (let i = 0; i < y.length; i++) {
        y[i] -= median;
      }
      return {
        data
      };
    }

    /**
     * Filter that allows to
     * @param data
     * @param options
     */
    function fromTo(data, options = {}) {
      const {
        fromIndex,
        toIndex
      } = xGetFromToIndex(data.x, options);
      return {
        data: {
          x: data.x.subarray(fromIndex, toIndex + 1),
          y: data.y.subarray(fromIndex, toIndex + 1)
        }
      };
    }

    /**
     * Norm the Y values
     * @param data
     */
    function normed(data, options = {}) {
      xNormed(data.y, {
        ...options,
        output: data.y
      });
      return {
        data
      };
    }

    /**
     * Center the mean
     * @param data
     */
    function divideBySD(data) {
      const {
        y
      } = data;
      const sd = xStandardDeviation(y);
      for (let i = 0; i < y.length; i++) {
        y[i] /= sd;
      }
      return {
        data
      };
    }

    /**
     * Center the mean
     * @param data
     */
    function rescale(data, options = {}) {
      xRescale(data.y, {
        ...options,
        output: data.y
      });
      return {
        data
      };
    }

    /**
     * Filter that allows to
     * @param data
     */
    function paretoNormalization(data) {
      return {
        data: {
          x: data.x,
          y: xParetoNormalization(data.y)
        }
      };
    }

    function _typeof(obj) {
      "@babel/helpers - typeof";

      if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") {
        _typeof = function (obj) {
          return typeof obj;
        };
      } else {
        _typeof = function (obj) {
          return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj;
        };
      }
      return _typeof(obj);
    }

    /**
     * Fill an array with sequential numbers
     * @param {Array<number>} [input] - optional destination array (if not provided a new array will be created)
     * @param {object} [options={}]
     * @param {number} [options.from=0] - first value in the array
     * @param {number} [options.to=10] - last value in the array
     * @param {number} [options.size=input.length] - size of the array (if not provided calculated from step)
     * @param {number} [options.step] - if not provided calculated from size
     * @return {Array<number>}
     */

    function sequentialFill() {
      var input = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : [];
      var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
      if (_typeof(input) === 'object' && !isAnyArray(input)) {
        options = input;
        input = [];
      }
      if (!isAnyArray(input)) {
        throw new TypeError('input must be an array');
      }
      var _options = options,
        _options$from = _options.from,
        from = _options$from === void 0 ? 0 : _options$from,
        _options$to = _options.to,
        to = _options$to === void 0 ? 10 : _options$to,
        _options$size = _options.size,
        size = _options$size === void 0 ? input.length : _options$size,
        step = _options.step;
      if (size !== 0 && step) {
        throw new Error('step is defined by the array size');
      }
      if (!size) {
        if (step) {
          size = Math.floor((to - from) / step) + 1;
        } else {
          size = to - from + 1;
        }
      }
      if (!step && size) {
        step = (to - from) / (size - 1);
      }
      if (Array.isArray(input)) {
        // only works with normal array
        input.length = 0;
        for (var i = 0; i < size; i++) {
          input.push(from);
          from += step;
        }
      } else {
        if (input.length !== size) {
          throw new Error('sequentialFill typed array must have the correct length');
        }
        for (var _i = 0; _i < size; _i++) {
          input[_i] = from;
          from += step;
        }
      }
      return input;
    }

    /**
     * Adaptive iteratively reweighted penalized least squares [1]
     *
     * This function calls ml-airpls
     *
     * References:
     * [1] Zhang, Z.-M.; Chen, S.; Liang, Y.-Z.
     * Baseline Correction Using Adaptive Iteratively Reweighted Penalized Least Squares.
     * Analyst 2010, 135 (5), 1138–1146. https://doi.org/10.1039/B922045C.
     * @export
     * @param {Array<number>} ys
     * @param {object} [options] - Options object
     * @param {Array<number>} [options.x] Optional, Independent axis variable. If not specified, we use a linear grid
     * @param {object} [options.regression] - Options for the regression
     * @param {number} [options.regression.maxIterations = 100] - Maximum number of allowed iterations
     * @param {function} [options.regression.§Regression = PolynomialRegression] - Regression class with a predict method
     * @param {*} [options.regression.regressionOptions] - Options for regressionFunction
     * @param {number} [options.regression.tolerance = 0.001] - Convergence error tolerance
     * @returns {BaselineOutput}
     */
    function airPLSBaseline$1(ys, options = {}) {
      const numberPoints = ys.length;
      let {
        x,
        regressionOptions
      } = options;
      if (!x) {
        x = sequentialFill({
          from: 0,
          to: numberPoints - 1,
          size: numberPoints
        });
      }
      let output = airPLS(x, ys, regressionOptions);
      return {
        baseline: output.baseline,
        correctedSpectrum: output.corrected
      };
    }

    /**
     * Iterative polynomial fitting [1]
     *
     * Implementation based on ml-baseline-correction-regression
     *
     * References:
     * [1] Gan, F.; Ruan, G.; Mo, J.
     * Baseline Correction by Improved Iterative Polynomial Fitting with Automatic Threshold.
     *  Chemometrics and Intelligent Laboratory Systems 2006, 82 (1), 59–65.
     * https://doi.org/10.1016/j.chemolab.2005.08.009.
     * @export
     * @param {Array<number>} ys
     * @param {object} [options] - Options object
     * @param {Array<number>} [options.x] Optional, Independent axis variable. If not specified, we use a linear grid
     * @param {Object} [options.regression]
     * @param {number} [options.regression.maxIterations = 100] - Maximum number of allowed iterations
     * @param {Object} [options.regression]
     * @param {function} [options.regression.Regression = PolynomialRegression] - Regression class with a predict method
     * @param {Object} [options.regression.regressionOptions] - Options for regressionFunction
     * @param {number} [options.regression.tolerance = 0.001] - Convergence error tolerance
     * @returns {BaselineOutput}
     */
    function iterativePolynomialBaseline$1(ys, options = {}) {
      const numberPoints = ys.length;
      let {
        x,
        regressionOptions
      } = options;
      if (!x) {
        x = sequentialFill({
          from: 0,
          to: numberPoints - 1,
          size: numberPoints
        });
      }
      let output = baselineCorrectionRegression(x, ys, regressionOptions);
      return {
        baseline: output.baseline,
        correctedSpectrum: output.corrected
      };
    }

    /**

     *
     * @export
     * @param {Array<number>} ys
     * @param {Object} [options={}]
     * @param {number} [options.window] rolling window size, defaults to 10% of the length of the spectrum
     * @param {string} [options.padding.size=window-1] none, value, circular, duplicate
     * @param {string} [options.padding.algorithm='duplicate'] none, value, circular, duplicate
     * @param {number} [options.padding.value=0] value to use for padding (if algorithm='value')
     * @returns {BaselineOutput}
     */
    function rollingAverageBaseline$1(ys, options = {}) {
      let window = Math.max(Math.round(ys.length * 0.1), 2);
      let defaults = {
        window,
        padding: {
          size: window - 1,
          algorithm: 'duplicate',
          value: 0
        }
      };
      let actualOptions = {
        ...defaults,
        ...options
      };
      let baseline = xRollingAverage(ys, actualOptions);
      let corrected = new Float64Array(ys.length);
      for (let i = 0; i < corrected.length; i++) {
        corrected[i] = ys[i] - baseline[i];
      }
      return {
        baseline,
        correctedSpectrum: corrected
      };
    }

    function rollingBall(spectrum, options = {}) {
      if (!isAnyArray(spectrum)) {
        throw new Error('Spectrum must be an array');
      }
      if (spectrum.length === 0) {
        throw new TypeError('Spectrum must not be empty');
      }
      const numberPoints = spectrum.length;
      const maxima = new Float64Array(numberPoints);
      const minima = new Float64Array(numberPoints);
      const baseline = new Float64Array(numberPoints);
      // windowM 4 percent of spectrum length
      // windowS 8 percent of spectrum length
      const {
        windowM = Math.round(numberPoints * 0.04),
        windowS = Math.round(numberPoints * 0.08)
      } = options;
      // fi(1) in original paper
      for (let i = 0; i < spectrum.length; i++) {
        let windowLeft = Math.max(0, i - windowM);
        let windowRight = Math.min(i + windowM + 1, spectrum.length);
        minima[i] = xMinValue(spectrum, {
          fromIndex: windowLeft,
          toIndex: windowRight
        });
      }
      // fi in original paper
      for (let i = 0; i < minima.length; i++) {
        let windowLeft = Math.max(0, i - windowM);
        let windowRight = Math.min(i + windowM + 1, minima.length);
        maxima[i] = xMaxValue(minima, {
          fromIndex: windowLeft,
          toIndex: windowRight
        });
      }
      for (let i = 0; i < minima.length; i++) {
        let windowLeft = Math.max(0, i - windowS);
        let windowRight = Math.min(i + windowS + 1, maxima.length);
        baseline[i] = xMean(maxima.subarray(windowLeft, windowRight));
      }
      return baseline;
    }

    /**
     * Rolling ball baseline correction algorithm.
     * From the abstract of (1):
     * "This algorithm behaves equivalently to traditional polynomial backgrounds in simple spectra,
     * [...] and is considerably more robust for multiple overlapping peaks, rapidly varying background [...]
     *
     * The baseline is the trace one gets by rolling a ball below a spectrum. Algorithm has three steps:
     * Finding the minima in each window, find maxima among minima and then smooth over them by averaging.
     *
     * Algorithm described in (1), but in the implementation here the window width does not change.
     *
     * Reference:
     * (1) Kneen, M. A.; Annegarn, H. J.
     *     Algorithm for Fitting XRF, SEM and PIXE X-Ray Spectra Backgrounds.
     *     Nuclear Instruments and Methods in Physics Research Section B: Beam Interactions with Materials and Atoms 1996, 109–110, 209–213.
     *     https://doi.org/10.1016/0168-583X(95)00908-6.
     * (2) Kristian Hovde Liland, Bjørn-Helge Mevik, Roberto Canteri: baseline.
     *     https://cran.r-project.org/web/packages/baseline/index.html
     *
     * @export
     * @param {Array<number>} ys
     * @param {Object} [options={}]
     * @param {Number} [options.windowM] - width of local window for minimization/maximization, defaults to 4% of the spectrum length
     * @param {Number} [options.windowS] - width of local window for smoothing, defaults to 8% of the specturm length
     * @returns {BaselineOutput}
     */
    function rollingBallBaseline$1(ys, options = {}) {
      const baseline = rollingBall(ys, options);
      let corrected = new Float64Array(ys.length);
      for (let i = 0; i < corrected.length; i++) {
        corrected[i] = ys[i] - baseline[i];
      }
      return {
        baseline,
        correctedSpectrum: corrected
      };
    }

    /**

     *
     * @export
     * @param {Array<number>} ys
     * @param {Object} [options={}]
     * @param {number} [options.window] rolling window size, defaults to 10% of the length of the spectrum
     * @param {string} [options.padding.size=window-1] none, value, circular, duplicate
     * @param {string} [options.padding.algorithm='duplicate'] none, value, circular, duplicate
     * @param {number} [options.padding.value=0] value to use for padding (if algorithm='value')
     * @returns {BaselineOutput}
     */
    function rollingMedianBaseline$1(ys, options = {}) {
      let window = Math.max(Math.round(ys.length * 0.1), 2);
      let defaults = {
        window,
        padding: {
          size: window - 1,
          algorithm: 'duplicate',
          value: 0
        }
      };
      let actualOptions = {
        ...defaults,
        ...options
      };
      let baseline = xRollingMedian(ys, actualOptions);
      let corrected = new Float64Array(ys.length);
      for (let i = 0; i < corrected.length; i++) {
        corrected[i] = ys[i] - baseline[i];
      }
      return {
        baseline,
        correctedSpectrum: corrected
      };
    }

    //@ts-expect-error no type definition for baselines
    /**
     * @param data
     */
    function airPLSBaseline(data) {
      data.y = airPLSBaseline$1(data.y).correctedSpectrum;
      return {
        data
      };
    }

    //@ts-expect-error no type definition for baselines
    /**
     * @param data
     */
    function iterativePolynomialBaseline(data) {
      data.y = iterativePolynomialBaseline$1(data.y).correctedSpectrum;
      return {
        data
      };
    }

    //@ts-expect-error no type definition for baselines
    /**
     * @param data
     */
    function rollingAverageBaseline(data) {
      data.y = rollingAverageBaseline$1(data.y).correctedSpectrum;
      return {
        data
      };
    }

    //@ts-expect-error no type definition for baselines
    /**
     * @param data
     */
    function rollingBallBaseline(data) {
      data.y = rollingBallBaseline$1(data.y).correctedSpectrum;
      return {
        data
      };
    }

    //@ts-expect-error no type definition for baselines
    /**
     * @param data
     */
    function rollingMedianBaseline(data) {
      data.y = rollingMedianBaseline$1(data.y).correctedSpectrum;
      return {
        data
      };
    }

    /**
     * Calculate the first derivative using Savitzky–Golay filter.
     * @param data
     */
    function firstDerivative(data, options = {}) {
      const {
        x,
        y
      } = data;
      return {
        data: {
          x,
          y: sgg(y, x, {
            ...options,
            derivative: 1
          })
        }
      };
    }

    /**
     * Calculate the second derivative using Savitzky–Golay filter.
     * @param data
     */
    function secondDerivative(data, options = {}) {
      const {
        x,
        y
      } = data;
      return {
        data: {
          x,
          y: sgg(y, x, {
            ...options,
            derivative: 2
          })
        }
      };
    }

    /**
     * Calculate the third derivative using Savitzky–Golay filter.
     * @param data
     */
    function thirdDerivative(data, options = {}) {
      const {
        x,
        y
      } = data;
      return {
        data: {
          x,
          y: sgg(y, x, {
            ...options,
            derivative: 3
          })
        }
      };
    }

    /**
     * Apply the Savitzky Golay Generalized Filter
     * @param data
     */
    function savitzkyGolay(data, options = {}) {
      const {
        x,
        y
      } = data;
      return {
        data: {
          x,
          y: sgg(y, x, options)
        }
      };
    }

    /**
     * Ensure X values are strictly monotonic increasing
     * http://www-groups.mcs.st-andrews.ac.uk/~john/analysis/Lectures/L8.html
     * @param data
     */
    function ensureGrowing(data) {
      return {
        data: xyEnsureGrowingX(data)
      };
    }

    /**
     * Filter that allows to
     * @param data
     * @param options
     */
    function equallySpaced(data, options = {}) {
      return {
        data: xyEquallySpaced(data, options)
      };
    }

    /**
     * Filter that allows to
     * @param data
     * @param options
     */
    function filterX(data, options = {}) {
      return {
        data: xyFilterX(data, options)
      };
    }

    /**
     * Filter that allows to calibrateX the x axis based on the presence of peaks
     */
    function calibrateX(data, options = {}) {
      const {
        targetX = 0,
        nbPeaks = 1,
        from = data.x[0],
        to = data.x[data.x.length - 1],
        gsd: gsdOptions = {
          minMaxRatio: 0.1,
          realTopDetection: true,
          smoothY: true,
          sgOptions: {
            windowSize: 7,
            polynomial: 3
          }
        }
      } = options;
      const fromIndex = xFindClosestIndex(data.x, from);
      const toIndex = xFindClosestIndex(data.x, to);
      let peaks = gsd({
        x: data.x.subarray(fromIndex, toIndex),
        y: data.y.subarray(fromIndex, toIndex)
      }, gsdOptions).sort((a, b) => b.y - a.y).slice(0, nbPeaks);
      if (peaks.length < nbPeaks) return {
        data
      };
      const middle = xMean(peaks.map(peak => peak.x));
      return {
        data: {
          x: xAdd(data.x, targetX - middle),
          y: data.y
        }
      };
    }

    /**
     * Filter that allows to
     * @param data
     * @param options
     */
    function xFunction(data, options = {}) {
      return {
        data: {
          x: xApplyFunctionStr(data.x, {
            variableLabel: 'x',
            fctString: options.function
          }),
          y: data.y
        }
      };
    }

    /**
     * Filter that allows to
     * @param data
     * @param options
     */
    function yFunction(data, options = {}) {
      return {
        data: {
          x: data.x,
          y: xApplyFunctionStr(data.y, {
            variableLabel: 'y',
            fctString: options.function
          })
        }
      };
    }

    var Filters$2 = /*#__PURE__*/Object.freeze({
        __proto__: null,
        centerMean: centerMean,
        centerMedian: centerMedian,
        fromTo: fromTo,
        normed: normed,
        divideBySD: divideBySD,
        rescale: rescale,
        paretoNormalization: paretoNormalization,
        airPLSBaseline: airPLSBaseline,
        iterativePolynomialBaseline: iterativePolynomialBaseline,
        rollingAverageBaseline: rollingAverageBaseline,
        rollingBallBaseline: rollingBallBaseline,
        rollingMedianBaseline: rollingMedianBaseline,
        firstDerivative: firstDerivative,
        secondDerivative: secondDerivative,
        thirdDerivative: thirdDerivative,
        savitzkyGolay: savitzkyGolay,
        ensureGrowing: ensureGrowing,
        equallySpaced: equallySpaced,
        filterX: filterX,
        calibrateX: calibrateX,
        xFunction: xFunction,
        yFunction: yFunction
    });

    /**
     * Apply filters on {x:[], y:[]}
     * @returns A very important number
     */
    function filterXY$1(data, filters) {
      let result = {
        data: {
          x: xEnsureFloat64(data.x),
          y: xEnsureFloat64(data.y)
        }
      };
      const logs = [];
      for (let filter of filters) {
        const start = Date.now();
        // eslint-disable-next-line import/namespace
        const filterFct = Filters$2[filter.name];
        if (!filterFct) {
          throw new Error(`Unknown filter: ${filter.name}`);
        }
        // @ts-expect-error some method have options and some other ones don't have any options
        result = filterFct(result.data, filter.options);
        logs.push({
          name: filter.name,
          time: Date.now() - start
        });
      }
      return {
        logs,
        data: result.data
      };
    }

    var Filters$1 = /*#__PURE__*/Object.freeze({
        __proto__: null,
        filterXY: filterXY$1,
        centerMean: centerMean,
        centerMedian: centerMedian,
        fromTo: fromTo,
        normed: normed,
        divideBySD: divideBySD,
        rescale: rescale,
        paretoNormalization: paretoNormalization,
        airPLSBaseline: airPLSBaseline,
        iterativePolynomialBaseline: iterativePolynomialBaseline,
        rollingAverageBaseline: rollingAverageBaseline,
        rollingBallBaseline: rollingBallBaseline,
        rollingMedianBaseline: rollingMedianBaseline,
        firstDerivative: firstDerivative,
        secondDerivative: secondDerivative,
        thirdDerivative: thirdDerivative,
        savitzkyGolay: savitzkyGolay,
        ensureGrowing: ensureGrowing,
        equallySpaced: equallySpaced,
        filterX: filterX,
        calibrateX: calibrateX,
        xFunction: xFunction,
        yFunction: yFunction
    });

    const id$5 = 'signalProcessing';
    const name$5 = 'Signal processing';
    const DOMAIN_UPDATE_RULES$5 = {
      updateXDomain: true,
      updateYDomain: true
    };
    function apply$7(spectrum, options) {
      if (!isApplicable$5(spectrum)) {
        throw new Error('Signal Processing is not applicable on this data');
      }
      spectrum.data = filterXY(spectrum, options);
    }
    function isApplicable$5(spectrum) {
      if (spectrum.info.isFt) return true;
      return false;
    }
    function reduce$5(previousValue, newValue) {
      return {
        once: true,
        reduce: newValue
      };
    }
    function filterXY(spectrum, options) {
      let x = spectrum.data.x.slice(0);
      const cloneX = spectrum.data.x.slice(0);
      let re = spectrum.data.re.slice(0);
      let im = spectrum.data.im?.slice(0);
      const filters = options.filters.slice();
      const {
        range: {
          from,
          to
        },
        numberOfPoints,
        exclusionsZones: exclusions
      } = options;
      filters.push({
        name: 'equallySpaced',
        options: {
          from,
          to,
          numberOfPoints,
          exclusions
        },
        properties: {}
      });
      for (const filter of filters) {
        // eslint-disable-next-line import/namespace
        const filterFunction = Filters$1[filter.name];
        if (!filterFunction) {
          throw new Error(`Unknown filter: ${filter.name}`);
        }
        const filterOptions = filter?.options || {};
        // @ts-expect-error some method have options and some other ones don't have any options
        const realResult = filterFunction({
          x,
          y: re
        }, filterOptions);
        x = realResult.data.x;
        re = realResult.data.y;
        if (im) {
          const imaginaryResult = filterFunction({
            x: cloneX,
            y: im
          },
          // @ts-expect-error some method have options and some other ones don't have any options
          filterOptions);
          im = imaginaryResult.data.y;
        }
      }
      return {
        x,
        re,
        im
      };
    }

    var signalProcessing = /*#__PURE__*/Object.freeze({
        __proto__: null,
        id: id$5,
        name: name$5,
        DOMAIN_UPDATE_RULES: DOMAIN_UPDATE_RULES$5,
        apply: apply$7,
        isApplicable: isApplicable$5,
        reduce: reduce$5,
        filterXY: filterXY
    });

    const id$4 = 'zeroFilling';
    const name$4 = 'Zero Filling';
    const DOMAIN_UPDATE_RULES$4 = {
      updateXDomain: true,
      updateYDomain: false
    };
    function apply$6(datum1D, options) {
      if (!isApplicable$4(datum1D)) {
        throw new Error('zeroFilling not applicable on this data');
      }
      const {
        factor = 2
      } = options;
      const {
        nbPoints = 2 ** Math.round(Math.log2(datum1D.data.x.length * factor))
      } = options;
      if (!options.nbPoints) {
        const filter = datum1D.filters.find(filter => filter.name === id$4);
        if (filter) {
          filter.value = {
            nbPoints
          };
        }
      }
      const digitalFilterApplied = datum1D.filters.some(e => e.name === 'digitalFilter' && e.flag);
      const grpdly = datum1D.info?.digitalFilter || 0;
      const pointsToShift = digitalFilterApplied && grpdly > 0 ? Math.floor(grpdly) : 0;
      const {
        re,
        im,
        x
      } = datum1D.data;
      const newRE = new Float64Array(nbPoints);
      const newIM = new Float64Array(nbPoints);
      const newX = new Float64Array(nbPoints);
      const length = Math.min(nbPoints, re.length);
      newRE.set(re.slice(0, length - pointsToShift));
      newIM.set(im.slice(0, length - pointsToShift));
      newX.set(x.slice(0, length - pointsToShift));
      const diff = x[1] - x[0];
      let currentX = x[length - pointsToShift - 1];
      for (let i = length - pointsToShift; i < nbPoints; i++) {
        currentX += diff;
        newX[i] = currentX;
      }
      if (pointsToShift > 0 && pointsToShift < nbPoints) {
        newRE.set(re.slice(re.length - pointsToShift), nbPoints - pointsToShift);
        newIM.set(im.slice(re.length - pointsToShift), nbPoints - pointsToShift);
      }
      datum1D.data = {
        ...datum1D.data,
        re: newRE,
        im: newIM,
        x: newX
      };
    }
    function isApplicable$4(datum1D) {
      if (datum1D.info?.isComplex && datum1D.info?.isFid) {
        return true;
      }
      return false;
    }
    function reduce$4(_previousValue, newValue) {
      delete newValue.factor;
      return {
        once: true,
        reduce: newValue
      };
    }

    var zeroFilling = /*#__PURE__*/Object.freeze({
        __proto__: null,
        id: id$4,
        name: name$4,
        DOMAIN_UPDATE_RULES: DOMAIN_UPDATE_RULES$4,
        apply: apply$6,
        isApplicable: isApplicable$4,
        reduce: reduce$4
    });

    const id$3 = 'shift2DX';
    const name$3 = 'Shift 2D X';
    const DOMAIN_UPDATE_RULES$3 = {
      updateXDomain: true,
      updateYDomain: false
    };
    /**
     *
     * @param {Object} datum2d
     * @param {number} [shiftValue=0]
     */
    function apply$5(datum2D, shiftValue = 0) {
      const data = {
        ...datum2D.data
      };
      for (const key in data) {
        data[key].minX += shiftValue;
        data[key].maxX += shiftValue;
      }
    }
    function isApplicable$3() {
      return true;
    }
    function reduce$3(previousValue, newValue) {
      return {
        once: true,
        reduce: previousValue + newValue
      };
    }

    var shift2DX = /*#__PURE__*/Object.freeze({
        __proto__: null,
        id: id$3,
        name: name$3,
        DOMAIN_UPDATE_RULES: DOMAIN_UPDATE_RULES$3,
        apply: apply$5,
        isApplicable: isApplicable$3,
        reduce: reduce$3
    });

    const id$2 = 'shift2DY';
    const name$2 = 'Shift 2D Y';
    const DOMAIN_UPDATE_RULES$2 = {
      updateXDomain: false,
      updateYDomain: true
    };
    /**
     *
     * @param {Object} datum2d
     * @param {number} [shiftValue=0]
     */
    function apply$4(datum2D, shiftValue = 0) {
      const data = {
        ...datum2D.data
      };
      for (const key in data) {
        data[key].minY += shiftValue;
        data[key].maxY += shiftValue;
      }
    }
    function isApplicable$2() {
      return true;
    }
    function reduce$2(previousValue, newValue) {
      return {
        once: true,
        reduce: previousValue + newValue
      };
    }

    var shift2DY = /*#__PURE__*/Object.freeze({
        __proto__: null,
        id: id$2,
        name: name$2,
        DOMAIN_UPDATE_RULES: DOMAIN_UPDATE_RULES$2,
        apply: apply$4,
        isApplicable: isApplicable$2,
        reduce: reduce$2
    });

    const id$1 = 'fftDimension1';
    const name$1 = 'FFT Dimension 1';
    const DOMAIN_UPDATE_RULES$1 = {
      updateXDomain: true,
      updateYDomain: true
    };
    function apply$3(datum2D, options) {
      if (!isApplicable$1(datum2D)) {
        throw new Error('fft dimension 1 not applicable on this data');
      }
      // eslint-disable-next-line @typescript-eslint/no-unused-vars
      const {
        data,
        info
      } = datum2D;
      const {
        digitalFilter: digitalFilterValue = 0
      } = info;
      const fnMode = info.fnMode[1].toLowerCase();
      const orderedData = fnMode === 'echo-antiecho' ? echoQuadrature(data) : {
        re: data.re.z,
        im: data.im.z
      };
      const {
        zeroFilling = {}
      } = options;
      const nbColumns = orderedData.re[0].length;
      const {
        factor = 1
      } = zeroFilling;
      const nbPoints = zeroFilling?.nbPoints || 2 ** Math.round(Math.log2(nbColumns * factor));
      const transformed = fftDirectDimension(orderedData, {
        fnMode,
        ...options,
        digitalFilterValue,
        zeroFilling: {
          nbPoints
        }
      });
      datum2D.data.re.z = transformed.re.to2DArray().map(xEnsureFloat64);
      datum2D.data.im.z = transformed.im.to2DArray().map(xEnsureFloat64);
    }
    function isApplicable$1(datum2D) {
      if (datum2D.info.isComplex && datum2D.info.isFid) return true;
      return false;
    }
    function reduce$1() {
      return {
        once: true,
        reduce: undefined
      };
    }
    function echoQuadrature(data) {
      const {
        re: {
          z: reMatrix
        },
        im: {
          z: imMatrix
        }
      } = data;
      const newRe = [];
      const newIm = [];
      for (let i = 0; i < reMatrix.length; i += 2) {
        const reCosine = reMatrix[i];
        const imCosine = imMatrix[i];
        const reSine = reMatrix[i + 1];
        const imSine = imMatrix[i + 1];
        newRe.push(xSubtract(xMultiply(imSine, -1), imCosine));
        newRe.push(xSubtract(reSine, reCosine));
        newIm.push(xAdd(reSine, reCosine));
        newIm.push(xSubtract(imSine, imCosine));
      }
      return {
        re: newRe,
        im: newIm
      };
    }

    var fftDimension1 = /*#__PURE__*/Object.freeze({
        __proto__: null,
        id: id$1,
        name: name$1,
        DOMAIN_UPDATE_RULES: DOMAIN_UPDATE_RULES$1,
        apply: apply$3,
        isApplicable: isApplicable$1,
        reduce: reduce$1
    });

    const id = 'fftDimension2';
    const name = 'FFT Dimension 2';
    const DOMAIN_UPDATE_RULES = {
      updateXDomain: true,
      updateYDomain: true
    };
    function apply$2(datum2D, options) {
      if (!isApplicable(datum2D)) {
        throw new Error('fft dimension 2 not applicable on this data');
      }
      const {
        data,
        info
      } = datum2D;
      const fnMode = info.fnMode[1];
      const orderedData = {
        re: data.re.z,
        im: data.im.z
      };
      const {
        zeroFilling = {}
      } = options;
      const nbRows = orderedData.re.length;
      const {
        factor = 2
      } = zeroFilling;
      const nbPoints = zeroFilling?.nbPoints || 2 ** Math.round(Math.log2(nbRows * factor));
      const transformed = fftIndirectDimension({
        re: orderedData.re,
        im: orderedData.im
      }, {
        fnMode,
        ...options,
        zeroFilling: {
          nbPoints
        }
      });
      datum2D.data = {
        ...getQuadrants(transformed, info)
      };
      const {
        isFid,
        isComplex,
        ...resInfo
      } = info;
      datum2D.info = {
        ...resInfo,
        isFt: true
      };
    }
    function getQuadrants(transformed, info) {
      const quadrants = {};
      const {
        fnMode
      } = info;
      const minMaxXY = getMinMaxXY(info);
      const buildQuandrants = keys => {
        for (const key of keys) {
          quadrants[key] = {
            z: transformed[key].to2DArray().map(xEnsureFloat64),
            maxZ: transformed[key].max(),
            minZ: transformed[key].min(),
            ...minMaxXY
          };
        }
        return quadrants;
      };
      if (fnMode === 'QF') {
        return buildQuandrants(['rr', 'ir']);
      }
      return buildQuandrants(['rr', 'ir', 'ri', 'ii']);
    }
    function isApplicable(datum2D) {
      if (datum2D.info.isComplex && datum2D.info.isFid) return true;
      return false;
    }
    function reduce() {
      return {
        once: true,
        reduce: undefined
      };
    }

    var fftDimension2 = /*#__PURE__*/Object.freeze({
        __proto__: null,
        id: id,
        name: name,
        DOMAIN_UPDATE_RULES: DOMAIN_UPDATE_RULES,
        apply: apply$2,
        isApplicable: isApplicable,
        reduce: reduce
    });

    const Filters = {
      apodization,
      exclusionZones,
      signalProcessing,
      baselineCorrection,
      digitalFilter,
      equallySpaced: equallySpaced$1,
      fft,
      fftDimension1,
      fftDimension2,
      phaseCorrection: phaseCorrection$1,
      shiftX,
      zeroFilling,
      shift2DX,
      shift2DY
    };

    function cloneData1D(datum) {
      const {
        x,
        re,
        im
      } = datum || {
        x: [],
        re: [],
        im: []
      };
      return {
        x: x.slice(),
        re: re.slice(),
        im: im?.slice() || null
      };
    }

    function cloneData2D(data) {
      const newData = {};
      if (isData2DFt(data)) {
        for (const key in data) {
          addCloneData(newData, key, data[key]);
        }
      } else {
        for (const key in data) {
          addCloneData(newData, key, data[key]);
        }
      }
      return newData;
    }
    function isData2DFt(data) {
      return 're' in data;
    }
    function addCloneData(newData, key, data) {
      if (data) {
        const {
          z,
          ...other
        } = data;
        newData[key] = {
          z: z.slice(),
          ...other
        };
      }
    }

    function getShiftX(spectrum) {
      const filter = spectrum?.filters && spectrum?.filters.find(filter => filter.name === Filters.shiftX.id);
      return filter?.flag ? filter.value.shift : 0;
    }

    function getRange$1(integral, shiftTarget, shift) {
      const {
        originFrom,
        originTo,
        from,
        to
      } = integral;
      if (shiftTarget === 'origin') {
        return {
          originFrom: from - shift,
          originTo: to - shift,
          from,
          to
        };
      } else {
        return {
          originFrom,
          originTo,
          from: originFrom + shift,
          to: originTo + shift
        };
      }
    }
    function mapIntegrals(integrals, spectrum, shiftTarget = 'origin') {
      const {
        x,
        re
      } = spectrum.data;
      const shiftX = getShiftX(spectrum);
      return integrals.map(integral => {
        const integralBoundary = getRange$1(integral, shiftTarget, shiftX);
        const absolute = xyIntegration({
          x,
          y: re
        }, {
          from: integralBoundary.from,
          to: integralBoundary.to,
          reverse: true
        });
        return {
          ...integral,
          id: integral.id || v4(),
          ...integralBoundary,
          absolute
        };
      });
    }

    /**
     * Sum values for specific key in the object
     * @param values array of object
     * @param key key
     * @param check custom check function to to indicate if certain object included or not in the summation
     * @returns number
     */
    function getSum(values, key, check = null) {
      let sum = 0;
      for (const current of values) {
        if (check?.(current)) {
          const value = current[key];
          if (!value || isNaN(value)) continue;
          sum += Math.abs(value);
        }
      }
      return sum;
    }

    /**
     * check whether integral based on its kind can be included in another operation or not
     * @param integral  integral
     * @returns {boolean}
     */
    function checkIntegralKind(integral) {
      return SignalKindsToInclude.includes(integral.kind);
    }

    function mapValues(values, key, factor, check) {
      return values.map(value => {
        return {
          ...value,
          ...(check(value) && {
            [key]: value.absolute * factor
          })
        };
      });
    }

    function updateIntegralsRelativeValues(spectrum, forceCalculateIntegral = false) {
      const {
        values,
        options
      } = spectrum.integrals;
      const currentSum = getSum(values, 'absolute', checkIntegralKind);
      let factor = 0;
      if (options?.sum) {
        if (options.isSumConstant || forceCalculateIntegral) {
          factor = currentSum > 0 ? options.sum / currentSum : 0;
        } else if (values?.[0]) {
          const item = values[0];
          factor = (item.integral || options.sum) / item.absolute;
        }
      }
      spectrum.integrals.values = mapValues(values, 'integral', factor, checkIntegralKind);
      if (!options.isSumConstant && !forceCalculateIntegral) {
        options.sum = getSum(values, 'integral', checkIntegralKind);
      }
    }

    function updateIntegrals(spectrum) {
      spectrum.integrals.values = mapIntegrals(spectrum.integrals.values, spectrum, 'current');
      updateIntegralsRelativeValues(spectrum, true);
    }

    function getSpectrumErrorValue(spectrum) {
      const {
        x
      } = spectrum.data;
      return (x[x.length - 1] - x[0]) / 10000;
    }

    function isExists(peak, spectrum, error) {
      // check if the Peak is already exists
      for (const {
        x
      } of spectrum.peaks?.values || []) {
        if (Math.abs(peak.x - x) < error) {
          return true;
        }
      }
      return false;
    }
    function getPeakDelta(peak, shiftTarget, shift) {
      const {
        originalX = 0,
        x
      } = peak;
      if (shiftTarget === 'origin') {
        return {
          originalX: x - shift,
          x
        };
      } else {
        return {
          originalX,
          x: originalX + shift
        };
      }
    }
    function mapPeaks(peaks, spectrum, options = {}) {
      const {
        checkIsExisting = true,
        shiftTarget = 'origin'
      } = options;
      const shiftX = getShiftX(spectrum);
      const error = getSpectrumErrorValue(spectrum);
      const newPeaks = [];
      for (const peak of peaks) {
        const peakDelta = getPeakDelta(peak, shiftTarget, shiftX);
        if (!checkIsExisting || checkIsExisting && !isExists(peak, spectrum, error)) {
          newPeaks.push({
            ...peak,
            id: peak?.id || v4(),
            ...peakDelta
          });
        }
      }
      return newPeaks;
    }

    function updatePeaks(spectrum) {
      spectrum.peaks.values = mapPeaks(spectrum.peaks.values, spectrum, {
        checkIsExisting: false,
        shiftTarget: 'current'
      });
    }

    function checkRange(range, spectrum, error) {
      // check if the range is already exists
      for (const {
        from,
        to
      } of spectrum.ranges?.values || []) {
        if (Math.abs(range.from - from) < error && Math.abs(range.to - to) < error) {
          return true;
        }
      }
    }
    function getRange(range, shiftTarget, shift) {
      const {
        originFrom,
        originTo,
        from,
        to
      } = range;
      if (shiftTarget === 'origin') {
        return {
          originFrom: from - shift,
          originTo: to - shift,
          from,
          to
        };
      } else {
        return {
          originFrom,
          originTo,
          from: (originFrom || 0) + shift,
          to: (originTo || 0) + shift
        };
      }
    }
    function getSignalDelta(signal, shiftTarget, shift) {
      const {
        delta,
        originDelta
      } = signal;
      if (shiftTarget === 'origin') {
        return {
          originDelta: delta - shift,
          delta
        };
      } else {
        return {
          originDelta,
          delta: (originDelta || 0) + shift
        };
      }
    }
    function nmrRangeToRange(range) {
      const {
        signals = [],
        integration = 0,
        ...restRange
      } = range;
      const newSignals = [];
      for (const signal of signals) {
        const {
          multiplicity,
          ...restSignal
        } = signal;
        newSignals.push({
          id: v4(),
          js: [],
          multiplicity: multiplicity || signalMultiplicityPattern(signal),
          ...restSignal
        });
      }
      return {
        id: v4(),
        integration,
        absolute: integration || 0,
        signals: newSignals,
        ...restRange
      };
    }
    function mapRanges(ranges, spectrum, options = {}) {
      const {
        checkIsExisting = true,
        shiftTarget = 'origin'
      } = options;
      const {
        x,
        re
      } = spectrum.data;
      const shiftX = getShiftX(spectrum);
      const error = getSpectrumErrorValue(spectrum);
      let newRanges = ranges.map(nmrRangeToRange);
      if (checkIsExisting) {
        newRanges = newRanges.filter(r => checkIsExisting && !checkRange(r, spectrum, error) || r.id === 'new');
      }
      return newRanges.map(range => {
        const rangeBoundary = getRange(range, shiftTarget, shiftX);
        const absolute = xyIntegration({
          x,
          y: re
        }, {
          from: rangeBoundary.from,
          to: rangeBoundary.to,
          reverse: true
        });
        const signals = range.signals.map(signal => {
          const {
            kind = null,
            id,
            ...resSignal
          } = signal;
          return {
            kind: kind || 'signal',
            id: id || v4(),
            ...resSignal,
            ...getSignalDelta(signal, shiftTarget, shiftX)
          };
        });
        return {
          ...range,
          id: range.id || v4(),
          kind: signals?.[0].kind || DatumKind.signal,
          ...rangeBoundary,
          absolute,
          signals
        };
      });
    }

    /**
     * Gets the last element of `array`.
     *
     * @static
     * @memberOf _
     * @since 0.1.0
     * @category Array
     * @param {Array} array The array to query.
     * @returns {*} Returns the last element of `array`.
     * @example
     *
     * _.last([1, 2, 3]);
     * // => 3
     */
    function last$1(array) {
      var length = array == null ? 0 : array.length;
      return length ? array[length - 1] : undefined;
    }
    var last_1 = last$1;

    /**
     * The base implementation of `_.slice` without an iteratee call guard.
     *
     * @private
     * @param {Array} array The array to slice.
     * @param {number} [start=0] The start position.
     * @param {number} [end=array.length] The end position.
     * @returns {Array} Returns the slice of `array`.
     */
    function baseSlice$1(array, start, end) {
      var index = -1,
        length = array.length;
      if (start < 0) {
        start = -start > length ? 0 : length + start;
      }
      end = end > length ? length : end;
      if (end < 0) {
        end += length;
      }
      length = start > end ? 0 : end - start >>> 0;
      start >>>= 0;
      var result = Array(length);
      while (++index < length) {
        result[index] = array[index + start];
      }
      return result;
    }
    var _baseSlice = baseSlice$1;

    var baseGet = _baseGet,
      baseSlice = _baseSlice;

    /**
     * Gets the parent value at `path` of `object`.
     *
     * @private
     * @param {Object} object The object to query.
     * @param {Array} path The path to get the parent value of.
     * @returns {*} Returns the parent value.
     */
    function parent$1(object, path) {
      return path.length < 2 ? object : baseGet(object, baseSlice(path, 0, -1));
    }
    var _parent = parent$1;

    var castPath$1 = _castPath,
      last = last_1,
      parent = _parent,
      toKey = _toKey;

    /**
     * The base implementation of `_.unset`.
     *
     * @private
     * @param {Object} object The object to modify.
     * @param {Array|string} path The property path to unset.
     * @returns {boolean} Returns `true` if the property is deleted, else `false`.
     */
    function baseUnset$1(object, path) {
      path = castPath$1(path, object);
      object = parent(object, path);
      return object == null || delete object[toKey(last(path))];
    }
    var _baseUnset = baseUnset$1;

    var baseGetTag = _baseGetTag,
      getPrototype = _getPrototype,
      isObjectLike = isObjectLike_1;

    /** `Object#toString` result references. */
    var objectTag = '[object Object]';

    /** Used for built-in method references. */
    var funcProto = Function.prototype,
      objectProto = Object.prototype;

    /** Used to resolve the decompiled source of functions. */
    var funcToString = funcProto.toString;

    /** Used to check objects for own properties. */
    var hasOwnProperty = objectProto.hasOwnProperty;

    /** Used to infer the `Object` constructor. */
    var objectCtorString = funcToString.call(Object);

    /**
     * Checks if `value` is a plain object, that is, an object created by the
     * `Object` constructor or one with a `[[Prototype]]` of `null`.
     *
     * @static
     * @memberOf _
     * @since 0.8.0
     * @category Lang
     * @param {*} value The value to check.
     * @returns {boolean} Returns `true` if `value` is a plain object, else `false`.
     * @example
     *
     * function Foo() {
     *   this.a = 1;
     * }
     *
     * _.isPlainObject(new Foo);
     * // => false
     *
     * _.isPlainObject([1, 2, 3]);
     * // => false
     *
     * _.isPlainObject({ 'x': 0, 'y': 0 });
     * // => true
     *
     * _.isPlainObject(Object.create(null));
     * // => true
     */
    function isPlainObject$1(value) {
      if (!isObjectLike(value) || baseGetTag(value) != objectTag) {
        return false;
      }
      var proto = getPrototype(value);
      if (proto === null) {
        return true;
      }
      var Ctor = hasOwnProperty.call(proto, 'constructor') && proto.constructor;
      return typeof Ctor == 'function' && Ctor instanceof Ctor && funcToString.call(Ctor) == objectCtorString;
    }
    var isPlainObject_1 = isPlainObject$1;

    var isPlainObject = isPlainObject_1;

    /**
     * Used by `_.omit` to customize its `_.cloneDeep` use to only clone plain
     * objects.
     *
     * @private
     * @param {*} value The value to inspect.
     * @param {string} key The key of the property to inspect.
     * @returns {*} Returns the uncloned value or `undefined` to defer cloning to `_.cloneDeep`.
     */
    function customOmitClone$1(value) {
      return isPlainObject(value) ? undefined : value;
    }
    var _customOmitClone = customOmitClone$1;

    var Symbol$1 = _Symbol,
      isArguments = isArguments_1,
      isArray = isArray_1;

    /** Built-in value references. */
    var spreadableSymbol = Symbol$1 ? Symbol$1.isConcatSpreadable : undefined;

    /**
     * Checks if `value` is a flattenable `arguments` object or array.
     *
     * @private
     * @param {*} value The value to check.
     * @returns {boolean} Returns `true` if `value` is flattenable, else `false`.
     */
    function isFlattenable$1(value) {
      return isArray(value) || isArguments(value) || !!(spreadableSymbol && value && value[spreadableSymbol]);
    }
    var _isFlattenable = isFlattenable$1;

    var arrayPush = _arrayPush,
      isFlattenable = _isFlattenable;

    /**
     * The base implementation of `_.flatten` with support for restricting flattening.
     *
     * @private
     * @param {Array} array The array to flatten.
     * @param {number} depth The maximum recursion depth.
     * @param {boolean} [predicate=isFlattenable] The function invoked per iteration.
     * @param {boolean} [isStrict] Restrict to values that pass `predicate` checks.
     * @param {Array} [result=[]] The initial result value.
     * @returns {Array} Returns the new flattened array.
     */
    function baseFlatten$1(array, depth, predicate, isStrict, result) {
      var index = -1,
        length = array.length;
      predicate || (predicate = isFlattenable);
      result || (result = []);
      while (++index < length) {
        var value = array[index];
        if (depth > 0 && predicate(value)) {
          if (depth > 1) {
            // Recursively flatten arrays (susceptible to call stack limits).
            baseFlatten$1(value, depth - 1, predicate, isStrict, result);
          } else {
            arrayPush(result, value);
          }
        } else if (!isStrict) {
          result[result.length] = value;
        }
      }
      return result;
    }
    var _baseFlatten = baseFlatten$1;

    var baseFlatten = _baseFlatten;

    /**
     * Flattens `array` a single level deep.
     *
     * @static
     * @memberOf _
     * @since 0.1.0
     * @category Array
     * @param {Array} array The array to flatten.
     * @returns {Array} Returns the new flattened array.
     * @example
     *
     * _.flatten([1, [2, [3, [4]], 5]]);
     * // => [1, 2, [3, [4]], 5]
     */
    function flatten$1(array) {
      var length = array == null ? 0 : array.length;
      return length ? baseFlatten(array, 1) : [];
    }
    var flatten_1 = flatten$1;

    /**
     * A faster alternative to `Function#apply`, this function invokes `func`
     * with the `this` binding of `thisArg` and the arguments of `args`.
     *
     * @private
     * @param {Function} func The function to invoke.
     * @param {*} thisArg The `this` binding of `func`.
     * @param {Array} args The arguments to invoke `func` with.
     * @returns {*} Returns the result of `func`.
     */
    function apply$1(func, thisArg, args) {
      switch (args.length) {
        case 0:
          return func.call(thisArg);
        case 1:
          return func.call(thisArg, args[0]);
        case 2:
          return func.call(thisArg, args[0], args[1]);
        case 3:
          return func.call(thisArg, args[0], args[1], args[2]);
      }
      return func.apply(thisArg, args);
    }
    var _apply = apply$1;

    var apply = _apply;

    /* Built-in method references for those with the same name as other `lodash` methods. */
    var nativeMax = Math.max;

    /**
     * A specialized version of `baseRest` which transforms the rest array.
     *
     * @private
     * @param {Function} func The function to apply a rest parameter to.
     * @param {number} [start=func.length-1] The start position of the rest parameter.
     * @param {Function} transform The rest array transform.
     * @returns {Function} Returns the new function.
     */
    function overRest$1(func, start, transform) {
      start = nativeMax(start === undefined ? func.length - 1 : start, 0);
      return function () {
        var args = arguments,
          index = -1,
          length = nativeMax(args.length - start, 0),
          array = Array(length);
        while (++index < length) {
          array[index] = args[start + index];
        }
        index = -1;
        var otherArgs = Array(start + 1);
        while (++index < start) {
          otherArgs[index] = args[index];
        }
        otherArgs[start] = transform(array);
        return apply(func, this, otherArgs);
      };
    }
    var _overRest = overRest$1;

    /**
     * Creates a function that returns `value`.
     *
     * @static
     * @memberOf _
     * @since 2.4.0
     * @category Util
     * @param {*} value The value to return from the new function.
     * @returns {Function} Returns the new constant function.
     * @example
     *
     * var objects = _.times(2, _.constant({ 'a': 1 }));
     *
     * console.log(objects);
     * // => [{ 'a': 1 }, { 'a': 1 }]
     *
     * console.log(objects[0] === objects[1]);
     * // => true
     */
    function constant$1(value) {
      return function () {
        return value;
      };
    }
    var constant_1 = constant$1;

    /**
     * This method returns the first argument it receives.
     *
     * @static
     * @since 0.1.0
     * @memberOf _
     * @category Util
     * @param {*} value Any value.
     * @returns {*} Returns `value`.
     * @example
     *
     * var object = { 'a': 1 };
     *
     * console.log(_.identity(object) === object);
     * // => true
     */
    function identity$1(value) {
      return value;
    }
    var identity_1 = identity$1;

    var constant = constant_1,
      defineProperty = _defineProperty,
      identity = identity_1;

    /**
     * The base implementation of `setToString` without support for hot loop shorting.
     *
     * @private
     * @param {Function} func The function to modify.
     * @param {Function} string The `toString` result.
     * @returns {Function} Returns `func`.
     */
    var baseSetToString$1 = !defineProperty ? identity : function (func, string) {
      return defineProperty(func, 'toString', {
        'configurable': true,
        'enumerable': false,
        'value': constant(string),
        'writable': true
      });
    };
    var _baseSetToString = baseSetToString$1;

    /** Used to detect hot functions by number of calls within a span of milliseconds. */
    var HOT_COUNT = 800,
      HOT_SPAN = 16;

    /* Built-in method references for those with the same name as other `lodash` methods. */
    var nativeNow = Date.now;

    /**
     * Creates a function that'll short out and invoke `identity` instead
     * of `func` when it's called `HOT_COUNT` or more times in `HOT_SPAN`
     * milliseconds.
     *
     * @private
     * @param {Function} func The function to restrict.
     * @returns {Function} Returns the new shortable function.
     */
    function shortOut$1(func) {
      var count = 0,
        lastCalled = 0;
      return function () {
        var stamp = nativeNow(),
          remaining = HOT_SPAN - (stamp - lastCalled);
        lastCalled = stamp;
        if (remaining > 0) {
          if (++count >= HOT_COUNT) {
            return arguments[0];
          }
        } else {
          count = 0;
        }
        return func.apply(undefined, arguments);
      };
    }
    var _shortOut = shortOut$1;

    var baseSetToString = _baseSetToString,
      shortOut = _shortOut;

    /**
     * Sets the `toString` method of `func` to return `string`.
     *
     * @private
     * @param {Function} func The function to modify.
     * @param {Function} string The `toString` result.
     * @returns {Function} Returns `func`.
     */
    var setToString$1 = shortOut(baseSetToString);
    var _setToString = setToString$1;

    var flatten = flatten_1,
      overRest = _overRest,
      setToString = _setToString;

    /**
     * A specialized version of `baseRest` which flattens the rest array.
     *
     * @private
     * @param {Function} func The function to apply a rest parameter to.
     * @returns {Function} Returns the new function.
     */
    function flatRest$1(func) {
      return setToString(overRest(func, undefined, flatten), func + '');
    }
    var _flatRest = flatRest$1;

    var arrayMap = _arrayMap,
      baseClone = _baseClone,
      baseUnset = _baseUnset,
      castPath = _castPath,
      copyObject = _copyObject,
      customOmitClone = _customOmitClone,
      flatRest = _flatRest,
      getAllKeysIn = _getAllKeysIn;

    /** Used to compose bitmasks for cloning. */
    var CLONE_DEEP_FLAG = 1,
      CLONE_FLAT_FLAG = 2,
      CLONE_SYMBOLS_FLAG = 4;

    /**
     * The opposite of `_.pick`; this method creates an object composed of the
     * own and inherited enumerable property paths of `object` that are not omitted.
     *
     * **Note:** This method is considerably slower than `_.pick`.
     *
     * @static
     * @since 0.1.0
     * @memberOf _
     * @category Object
     * @param {Object} object The source object.
     * @param {...(string|string[])} [paths] The property paths to omit.
     * @returns {Object} Returns the new object.
     * @example
     *
     * var object = { 'a': 1, 'b': '2', 'c': 3 };
     *
     * _.omit(object, ['a', 'c']);
     * // => { 'b': '2' }
     */
    var omit = flatRest(function (object, paths) {
      var result = {};
      if (object == null) {
        return result;
      }
      var isDeep = false;
      paths = arrayMap(paths, function (path) {
        path = castPath(path, object);
        isDeep || (isDeep = path.length > 1);
        return path;
      });
      copyObject(object, getAllKeysIn(object), result);
      if (isDeep) {
        result = baseClone(result, CLONE_DEEP_FLAG | CLONE_FLAT_FLAG | CLONE_SYMBOLS_FLAG, customOmitClone);
      }
      var length = paths.length;
      while (length--) {
        baseUnset(result, paths[length]);
      }
      return result;
    });
    var omit_1 = omit;
    var omit$1 = omit_1;

    function getDiaIDs(range) {
      return [].concat(range.diaIDs || [], range.signals ? range.signals.flatMap(_signal => _signal.diaIDs || []) : []);
    }
    function getNbAtoms(range, signalIndex) {
      if (signalIndex === undefined) {
        let sum = 0;
        if (range.signals) {
          for (const signal of range.signals) {
            sum += signal.nbAtoms || 0;
          }
        }
        return sum;
      }
      return range.signals[signalIndex].nbAtoms || 0;
    }
    function setNbAtoms(range, signalIndex) {
      range.nbAtoms = getNbAtoms(range, signalIndex);
      if (range.nbAtoms === 0) {
        delete range.nbAtoms;
      }
    }
    function resetDiaIDs(range) {
      const deletedKeys = ['diaIDs', 'nbAtoms'];
      range = omit$1(range, deletedKeys);
      range.signals = range.signals.map(signal => {
        return omit$1(signal, deletedKeys);
      });
    }
    function unlink(range, options = {}) {
      const {
        unlinkType = 'both',
        signalIndex = -1
      } = options;
      const keys = ['diaIDs', 'nbAtoms'];
      switch (unlinkType) {
        case 'both':
          resetDiaIDs(range);
          break;
        case 'range':
          range = omit$1(range, keys);
          break;
        case 'signal':
          if (signalIndex !== -1) {
            range.signals[signalIndex] = omit$1(range.signals[signalIndex], keys);
          }
          break;
      }
      return range;
    }
    function addDefaultSignal(range) {
      range.signals.push({
        id: v4(),
        multiplicity: 'm',
        kind: 'signal',
        js: [],
        delta: (range.to + range.from) / 2
      });
    }
    function checkRangeKind$1(range) {
      return range.kind === DatumKind.signal;
    }
    function checkSignalKinds(range, kinds) {
      return !range.signals.some(_signal => _signal.kind === undefined || !kinds.includes(_signal.kind));
    }

    var RangeUtilities = /*#__PURE__*/Object.freeze({
        __proto__: null,
        getDiaIDs: getDiaIDs,
        getNbAtoms: getNbAtoms,
        setNbAtoms: setNbAtoms,
        resetDiaIDs: resetDiaIDs,
        unlink: unlink,
        addDefaultSignal: addDefaultSignal,
        checkRangeKind: checkRangeKind$1,
        checkSignalKinds: checkSignalKinds
    });

    function checkRangeKind(range) {
      return range.signals && checkSignalKinds(range, SignalKindsToInclude);
    }

    function updateRangesRelativeValues(spectrum, forceCalculateIntegral = false) {
      const {
        values,
        options
      } = spectrum.ranges;
      const currentSum = getSum(values, 'absolute', checkRangeKind);
      let factor = 0;
      if (options?.sum) {
        if (options.isSumConstant || forceCalculateIntegral) {
          factor = currentSum > 0 ? options.sum / currentSum : 0;
        } else if (values?.[0]) {
          const item = values[0];
          factor = (item.integration || options.sum) / item.absolute;
        }
      }
      spectrum.ranges.values = mapValues(values, 'integration', factor, checkRangeKind);
      if (!options.isSumConstant && !forceCalculateIntegral) {
        options.sum = getSum(values, 'integration', checkRangeKind);
      }
    }

    function updateRanges(spectrum) {
      spectrum.ranges.values = mapRanges(spectrum.ranges.values, spectrum, {
        checkIsExisting: false,
        shiftTarget: 'current'
      });
      updateRangesRelativeValues(spectrum, true);
    }

    function get2DSpectrumErrorValue(spectrum) {
      const {
        data,
        info
      } = spectrum;
      const {
        minX,
        maxX,
        minY,
        maxY
      } = info.isFid ? data.re : data.rr;
      const x = Math.abs(maxX - minX) / 10000;
      const y = Math.abs(maxY - minY) / 10000;
      return {
        x,
        y
      };
    }

    function getShift(spectrum) {
      const shift = {
        x: 0,
        y: 0
      };
      if (spectrum?.filters) {
        for (const filter of spectrum.filters) {
          if (filter.name === Filters.shift2DX.id) {
            shift.x = filter?.flag ? filter.value.shift : 0;
          }
          if (filter.name === Filters.shift2DY.id) {
            shift.y = filter?.flag ? filter.value.shift : 0;
          }
        }
      }
      return shift;
    }

    function isZoneExists(zone, spectrum, error) {
      const newXRange = zone.x || {
        from: 0,
        to: 0
      };
      const newYRange = zone.y || {
        from: 0,
        to: 0
      };
      if (!spectrum.zones?.values) return false;
      for (const {
        x,
        y
      } of spectrum.zones.values) {
        if (Math.abs(newXRange.from - x.from) < error.x && Math.abs(newXRange.to - x.to) < error.x && Math.abs(newYRange.from - y.from) < error.y && Math.abs(newYRange.to - y.to) < error.y) {
          return true;
        }
      }
      return false;
    }

    function getSignal(signal, shiftTarget, shift) {
      const {
        x,
        y
      } = signal;
      if (shiftTarget === 'origin') {
        return {
          ...signal,
          x: {
            ...x,
            originDelta: x.delta - shift.x
          },
          y: {
            ...y,
            originDelta: y.delta - shift.y
          }
        };
      } else {
        return {
          ...signal,
          x: {
            ...x,
            delta: x.originDelta + shift.x
          },
          y: {
            ...y,
            delta: y.originDelta + shift.y
          }
        };
      }
    }
    function mapZones(zones, datum, options = {}) {
      const {
        checkIsExisting = true,
        shiftTarget = 'origin'
      } = options;
      const shift = getShift(datum);
      const error = get2DSpectrumErrorValue(datum);
      let _zones = zones;
      if (checkIsExisting) {
        _zones = zones.filter(zone => !isZoneExists(zone, datum, error));
      }
      return _zones.map(zone => {
        const x = zone.x || {
          from: 0,
          to: 0
        };
        const y = zone.y || {
          from: 0,
          to: 0
        };
        const signals = zone.signals.map(signal => {
          const {
            id,
            kind
          } = signal;
          return {
            ...getSignal(signal, shiftTarget, shift),
            id: id || v4(),
            kind: kind || 'signal'
          };
        });
        return {
          ...zone,
          id: zone.id || v4(),
          x: {
            from: x.from,
            to: x.to
          },
          y: {
            from: y.from,
            to: y.to
          },
          signals,
          kind: zone.kind || DatumKind.signal
        };
      });
    }

    function updateZones(spectrum) {
      spectrum.zones.values = mapZones(spectrum.zones.values, spectrum, {
        checkIsExisting: false,
        shiftTarget: 'current'
      });
    }

    function resetDataToOrigin(datum) {
      datum.data = datum.info.dimension === 1 ? cloneData1D(datum.originalData) : cloneData2D(datum.originalData);
      const {
        originalInfo
      } = datum;
      if (originalInfo) {
        datum.info = {
          ...originalInfo
        };
      }
    }
    // update the the part of the data that affected by applying the filters
    function updateData(datum) {
      if (datum.info.dimension === 1) {
        updatePeaks(datum);
        updateRanges(datum);
        updateIntegrals(datum);
      } else if (datum.info.dimension === 2) {
        updateZones(datum);
      }
    }
    /***
     * @param {object} Filters [{name:'',options:{}},{...}]
     */
    function applyFilter(datum, filters = [], options = {}) {
      const {
        forceReapply = false,
        filterIndex
      } = options;
      let isReduced = false;
      for (const filter of filters) {
        const filterOption = {
          name: filter.name,
          label: Filters[filter.name].name,
          value: filter.value
        };
        const previousFilter = lookupForFilter(datum, filter.name);
        const isDeleteAllow = 'isDeleteAllow' in filter ? filter.isDeleteAllow : true;
        if (previousFilter) {
          const reduceResult = Filters[filter.name].reduce(previousFilter.value, filterOption.value);
          if (reduceResult.once) {
            if (!isReduced) {
              isReduced = true;
            }
            if (reduceResult.reduce != null && reduceResult.reduce !== undefined) {
              replaceFilter(datum, previousFilter.id, reduceResult.reduce);
            }
          } else {
            addFilter(datum, filterOption, {
              isDeleteAllow,
              filterIndex
            });
          }
        } else {
          addFilter(datum, filterOption, {
            isDeleteAllow,
            filterIndex
          });
        }
      }
      if (forceReapply) {
        reapplyFilters(datum);
      } else if (isReduced) {
        if (filters.length === 1 && isLastFilter(datum, filters[0].name)) {
          Filters[filters[0].name].apply(datum, filters[0].value);
        } else {
          reapplyFilters(datum);
        }
      } else {
        for (const filter of filters) {
          Filters[filter.name].apply(datum, filter.value);
        }
      }
      updateData(datum);
    }
    function isLastFilter(datum, id) {
      const index = datum.filters.findIndex(f => f.name === id);
      if (datum.filters.length === index + 1) {
        return true;
      }
      return false;
    }
    function lookupForFilter(datum, filterName) {
      return datum.filters.find(f => f.name === filterName);
    }
    function reapplyFilters(datum, filters) {
      const _filters = filters || datum.filters;
      enableFilter(datum, null, null, _filters);
    }
    // id filter id
    function enableFilter(datum, id, checked, filters) {
      datum.filters = datum.filters.slice(0);
      if (id) {
        datum.filters = datum.filters.map(filter => {
          return {
            ...filter,
            flag: filter.id === id ? checked : filter.flag
          };
        });
      }
      resetDataToOrigin(datum);
      const _filters = filters || datum.filters;
      for (let filterIndex = 0; filterIndex < _filters.length; filterIndex++) {
        const filter = datum.filters[filterIndex];
        datum.filters[filterIndex] = {
          ...datum.filters[filterIndex]
        };
        if (filter.flag) {
          try {
            Filters[filter.name].apply(datum, filter.value);
          } catch (error) {
            datum.filters[filterIndex] = {
              ...datum.filters[filterIndex],
              error: error.message
            };
          }
        }
      }
    }
    function deleteFilter(datum, id) {
      // delete the specified filter otherwise delete all filters where isDeleteAllow is true
      datum.filters = datum.filters.slice(0).filter(filter => id ? filter.id !== id : !filter.isDeleteAllow);
      resetDataToOrigin(datum);
      for (let i = 0; i < datum.filters.length; i++) {
        datum.filters[i] = {
          ...datum.filters[i]
        };
        if (datum.filters[i].flag) {
          const {
            name,
            value
          } = datum.filters[i];
          try {
            Filters[name].apply(datum, value);
          } catch (error) {
            datum.filters[i] = {
              ...datum.filters[i],
              error: error.message
            };
          }
        }
      }
      updateData(datum);
    }
    function addFilter(datum, filterOptions, options = {}) {
      const {
        filterIndex,
        isDeleteAllow = true
      } = options;
      const id = v4();
      const filter = {
        ...filterOptions,
        id,
        flag: true,
        isDeleteAllow
      };
      if (typeof filterIndex === 'number' && filterIndex !== -1) {
        datum.filters = [...datum.filters.slice(0, filterIndex + 1), filter, ...datum.filters.slice(filterIndex + 1)];
      } else {
        datum.filters.push(filter);
      }
    }
    function replaceFilter(datum, filterID, value) {
      // datum.filters = datum.filters.slice(0);
      const index = datum.filters.findIndex(f => f.id === filterID);
      datum.filters[index] = {
        ...datum.filters[index],
        value
      };
    }

    var FiltersManager = /*#__PURE__*/Object.freeze({
        __proto__: null,
        applyFilter: applyFilter,
        lookupForFilter: lookupForFilter,
        reapplyFilters: reapplyFilters,
        enableFilter: enableFilter,
        deleteFilter: deleteFilter,
        addFilter: addFilter
    });

    exports.DatumKind = DatumKind;
    exports.Filters = Filters;
    exports.FiltersManager = FiltersManager;
    exports.MultiplicityPatterns = MultiplicityPatterns;
    exports.RangeUtilities = RangeUtilities;
    exports.SignalKinds = SignalKinds;
    exports.SignalKindsToInclude = SignalKindsToInclude;
    exports.XYAutoPeaksPickingWindowSizeError = XYAutoPeaksPickingWindowSizeError;
    exports.apodization = apodization$1;
    exports.apodizationFilter = apodizationFilter;
    exports.carbonImpurities = carbonImpurities;
    exports.checkIntegralKind = checkIntegralKind;
    exports.checkMultiplet = checkMultiplet;
    exports.checkMultiplicity = checkMultiplicity;
    exports.couplingACSPatterns = couplingACSPatterns;
    exports.couplingPatterns = couplingPatterns;
    exports.findMultiplet = findMultiplet;
    exports.get13CAssignments = get13CAssignments;
    exports.get1HAssignments = get1HAssignments;
    exports.getAssignments = getAssignments;
    exports.getDatabase = getDatabase;
    exports.getFrequency = getFrequency;
    exports.getShiftX = getShiftX;
    exports.getSpectrumErrorValue = getSpectrumErrorValue;
    exports.gyromagneticRatio = gyromagneticRatio;
    exports.impurities = impurities;
    exports.mapIntegrals = mapIntegrals;
    exports.mapPeaks = mapPeaks;
    exports.mapRanges = mapRanges;
    exports.markSolventPeaks = markSolventPeaks;
    exports.normalizeNucleus = normalizeNucleus;
    exports.optimizeSignals = optimizeSignals;
    exports.peakToXY = peakToXY;
    exports.peaksFilterImpurities = peaksFilterImpurities;
    exports.peaksToXY = peaksToXY;
    exports.predictAll = predictAll;
    exports.predictAllSpectra = predictAllSpectra;
    exports.predictCOSY = predictCOSY;
    exports.predictCarbon = predictCarbon;
    exports.predictHMBC = predictHMBC;
    exports.predictHSQC = predictHSQC;
    exports.predictProton = predictProton;
    exports.protonImpurities = protonImpurities;
    exports.rangeFromSignal = rangeFromSignal;
    exports.rangesToACS = rangesToACS;
    exports.rangesToXY = rangesToXY;
    exports.resurrect = resurrect;
    exports.signalJoinCouplings = signalJoinCouplings;
    exports.signalMultiplicityPattern = signalMultiplicityPattern;
    exports.signals2DToZ = signals2DToZ;
    exports.signalsToFID = signalsToFID;
    exports.signalsToRanges = signalsToRanges;
    exports.signalsToXY = signalsToXY;
    exports.simulate1D = simulate1D;
    exports.solventSuppression = solventSuppression;
    exports.splitPatterns = splitPatterns;
    exports.translateMultiplet = translateMultiplet;
    exports.updateIntegralsRelativeValues = updateIntegralsRelativeValues;
    exports.updateRanges = updateRanges;
    exports.updateRangesRelativeValues = updateRangesRelativeValues;
    exports.xyAutoPeaksPicking = xyAutoPeaksPicking;
    exports.xyAutoRangesPicking = xyAutoRangesPicking;
    exports.xyPeaksOptimization = xyPeaksOptimization;
    exports.xyzAutoSignalsPicking = xyzAutoSignalsPicking;
    exports.xyzAutoZonesPicking = xyzAutoZonesPicking;
    exports.xyzBidimensionalFFT = xyzBidimensionalFFT;
    exports.xyzJResAnalyzer = xyzJResAnalyzer;

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

}));
//# sourceMappingURL=nmr-processing.js.map
