nmr-processing 8.0.0 → 8.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/apodization/apodization.d.ts +22 -0
- package/lib/apodization/apodization.js +18 -0
- package/lib/apodization/apodization.js.map +1 -0
- package/lib/apodization/applyWindow.d.ts +28 -0
- package/lib/apodization/applyWindow.js +20 -0
- package/lib/apodization/applyWindow.js.map +1 -0
- package/lib/apodization/compose.d.ts +23 -0
- package/lib/apodization/compose.js +25 -0
- package/lib/apodization/compose.js.map +1 -0
- package/lib/apodization/getFunction.d.ts +2 -0
- package/lib/apodization/getFunction.js +18 -0
- package/lib/apodization/getFunction.js.map +1 -0
- package/lib/apodization/shapes/WindowFunctions.d.ts +12 -0
- package/lib/apodization/shapes/WindowFunctions.js +3 -0
- package/lib/apodization/shapes/WindowFunctions.js.map +1 -0
- package/lib/apodization/shapes/exponential.d.ts +11 -0
- package/lib/apodization/shapes/exponential.js +10 -0
- package/lib/apodization/shapes/exponential.js.map +1 -0
- package/lib/apodization/shapes/lorentzToGauss.d.ts +26 -0
- package/lib/apodization/shapes/lorentzToGauss.js +15 -0
- package/lib/apodization/shapes/lorentzToGauss.js.map +1 -0
- package/lib/apodization/utils/getData.d.ts +4 -0
- package/lib/apodization/utils/getData.js +21 -0
- package/lib/apodization/utils/getData.js.map +1 -0
- package/lib/assignment/utils/buildAssignments.d.ts +2 -2
- package/lib/assignment/utils/exploreTreeRec.js +1 -1
- package/lib/assignment/utils/exploreTreeRec.js.map +1 -1
- package/lib/assignment/utils/getAssignment/checkIDs.d.ts +1 -1
- package/lib/assignment/utils/getAssignment/checkIDs.js.map +1 -1
- package/lib/databases/DatabaseNMREntry.d.ts +2 -0
- package/lib/index.d.ts +5 -1
- package/lib/index.js +5 -1
- package/lib/index.js.map +1 -1
- package/lib/peaks/NMRPeak1D.d.ts +0 -1
- package/lib/peaks/peaksToRanges.d.ts +16 -0
- package/lib/peaks/peaksToRanges.js +3 -2
- package/lib/peaks/peaksToRanges.js.map +1 -1
- package/lib/peaks/solventSuppression.d.ts +6 -0
- package/lib/peaks/solventSuppression.js +158 -0
- package/lib/peaks/solventSuppression.js.map +1 -0
- package/lib/peaks/util/jAnalyzer.d.ts +1 -1
- package/lib/peaks/util/jAnalyzer.js +26 -31
- package/lib/peaks/util/jAnalyzer.js.map +1 -1
- package/lib/peaks/util/peakOptimizer.js +12 -16
- package/lib/peaks/util/peakOptimizer.js.map +1 -1
- package/lib/prediction/predictAllSpectra.js +7 -9
- package/lib/prediction/predictAllSpectra.js.map +1 -1
- package/lib/prediction/predictCarbon.js +1 -1
- package/lib/prediction/predictCarbon.js.map +1 -1
- package/lib/prediction/utils/queryByHOSE.js +1 -1
- package/lib/prediction/utils/queryByHOSE.js.map +1 -1
- package/lib/ranges/markSolventSignal.d.ts +3 -0
- package/lib/ranges/markSolventSignal.js +107 -0
- package/lib/ranges/markSolventSignal.js.map +1 -0
- package/lib/ranges/rangesToACS.js +8 -4
- package/lib/ranges/rangesToACS.js.map +1 -1
- package/lib/ranges/rangesToXY.js +27 -3
- package/lib/ranges/rangesToXY.js.map +1 -1
- package/lib/signals/addDummySignals.d.ts +2 -0
- package/lib/signals/addDummySignals.js +56 -0
- package/lib/signals/addDummySignals.js.map +1 -0
- package/lib/signals/hackSignalsToXY.js +2 -48
- package/lib/signals/hackSignalsToXY.js.map +1 -1
- package/lib/signals/simulation/getPauliMatrix.js.map +1 -1
- package/lib/signals/simulation/simulate1D.d.ts +1 -39
- package/lib/signals/simulation/simulate1D.js +13 -240
- package/lib/signals/simulation/simulate1D.js.map +1 -1
- package/lib/signals/simulation/simulateXYPeaks.d.ts +47 -0
- package/lib/signals/simulation/simulateXYPeaks.js +246 -0
- package/lib/signals/simulation/simulateXYPeaks.js.map +1 -0
- package/lib/signals/simulation/splitSpinSystem.js +11 -13
- package/lib/signals/simulation/splitSpinSystem.js.map +1 -1
- package/lib/utilities/getFrequency.d.ts +1 -1
- package/lib/utilities/getFrequency.js +4 -4
- package/lib/utilities/getFrequency.js.map +1 -1
- package/lib/utilities/rangeFromSignal.d.ts +9 -5
- package/lib/utilities/rangeFromSignal.js +7 -7
- package/lib/utilities/rangeFromSignal.js.map +1 -1
- package/lib/utilities/resurrectRange.js +4 -7
- package/lib/utilities/resurrectRange.js.map +1 -1
- package/lib/xyz/util/formatZone.d.ts +3 -0
- package/lib/xyz/util/formatZone.js +38 -0
- package/lib/xyz/util/formatZone.js.map +1 -0
- package/lib/xyz/xyzAutoZonesPicking.d.ts +1 -2
- package/lib/xyz/xyzAutoZonesPicking.js +2 -34
- package/lib/xyz/xyzAutoZonesPicking.js.map +1 -1
- package/lib/xyz/xyzJResAnalyzer.d.ts +6 -1
- package/lib/xyz/xyzJResAnalyzer.js +9 -8
- package/lib/xyz/xyzJResAnalyzer.js.map +1 -1
- package/lib-esm/apodization/apodization.js +14 -0
- package/lib-esm/apodization/apodization.js.map +1 -0
- package/lib-esm/apodization/applyWindow.js +16 -0
- package/lib-esm/apodization/applyWindow.js.map +1 -0
- package/lib-esm/apodization/compose.js +21 -0
- package/lib-esm/apodization/compose.js.map +1 -0
- package/lib-esm/apodization/getFunction.js +14 -0
- package/lib-esm/apodization/getFunction.js.map +1 -0
- package/lib-esm/apodization/shapes/WindowFunctions.js +2 -0
- package/lib-esm/apodization/shapes/WindowFunctions.js.map +1 -0
- package/lib-esm/apodization/shapes/exponential.js +6 -0
- package/lib-esm/apodization/shapes/exponential.js.map +1 -0
- package/lib-esm/apodization/shapes/lorentzToGauss.js +11 -0
- package/lib-esm/apodization/shapes/lorentzToGauss.js.map +1 -0
- package/lib-esm/apodization/utils/getData.js +17 -0
- package/lib-esm/apodization/utils/getData.js.map +1 -0
- package/lib-esm/assignment/utils/buildAssignments.js +3 -3
- package/lib-esm/assignment/utils/exploreTreeRec.js +1 -1
- package/lib-esm/assignment/utils/exploreTreeRec.js.map +1 -1
- package/lib-esm/assignment/utils/getAssignment/buildAssignments.js +3 -3
- package/lib-esm/assignment/utils/getAssignment/checkIDs.js.map +1 -1
- package/lib-esm/index.js +5 -1
- package/lib-esm/index.js.map +1 -1
- package/lib-esm/peaks/peaksToRanges.js +2 -2
- package/lib-esm/peaks/peaksToRanges.js.map +1 -1
- package/lib-esm/peaks/solventSuppression.js +154 -0
- package/lib-esm/peaks/solventSuppression.js.map +1 -0
- package/lib-esm/peaks/util/jAnalyzer.js +26 -31
- package/lib-esm/peaks/util/jAnalyzer.js.map +1 -1
- package/lib-esm/peaks/util/peakOptimizer.js +12 -16
- package/lib-esm/peaks/util/peakOptimizer.js.map +1 -1
- package/lib-esm/prediction/predictAllSpectra.js +7 -9
- package/lib-esm/prediction/predictAllSpectra.js.map +1 -1
- package/lib-esm/prediction/predictCarbon.js +1 -1
- package/lib-esm/prediction/predictCarbon.js.map +1 -1
- package/lib-esm/prediction/utils/queryByHOSE.js +1 -1
- package/lib-esm/prediction/utils/queryByHOSE.js.map +1 -1
- package/lib-esm/ranges/markSolventSignal.js +100 -0
- package/lib-esm/ranges/markSolventSignal.js.map +1 -0
- package/lib-esm/ranges/rangesToACS.js +8 -4
- package/lib-esm/ranges/rangesToACS.js.map +1 -1
- package/lib-esm/ranges/rangesToXY.js +27 -3
- package/lib-esm/ranges/rangesToXY.js.map +1 -1
- package/lib-esm/signals/addDummySignals.js +52 -0
- package/lib-esm/signals/addDummySignals.js.map +1 -0
- package/lib-esm/signals/hackSignalsToXY.js +2 -48
- package/lib-esm/signals/hackSignalsToXY.js.map +1 -1
- package/lib-esm/signals/simulation/getPauliMatrix.js.map +1 -1
- package/lib-esm/signals/simulation/simulate1D.js +14 -238
- package/lib-esm/signals/simulation/simulate1D.js.map +1 -1
- package/lib-esm/signals/simulation/simulateXYPeaks.js +239 -0
- package/lib-esm/signals/simulation/simulateXYPeaks.js.map +1 -0
- package/lib-esm/signals/simulation/splitSpinSystem.js +11 -13
- package/lib-esm/signals/simulation/splitSpinSystem.js.map +1 -1
- package/lib-esm/utilities/getFrequency.js +1 -1
- package/lib-esm/utilities/getFrequency.js.map +1 -1
- package/lib-esm/utilities/rangeFromSignal.js +7 -7
- package/lib-esm/utilities/rangeFromSignal.js.map +1 -1
- package/lib-esm/utilities/resurrectRange.js +4 -7
- package/lib-esm/utilities/resurrectRange.js.map +1 -1
- package/lib-esm/xyz/util/formatZone.js +34 -0
- package/lib-esm/xyz/util/formatZone.js.map +1 -0
- package/lib-esm/xyz/xyzAutoZonesPicking.js +1 -33
- package/lib-esm/xyz/xyzAutoZonesPicking.js.map +1 -1
- package/lib-esm/xyz/xyzJResAnalyzer.js +9 -8
- package/lib-esm/xyz/xyzJResAnalyzer.js.map +1 -1
- package/package.json +16 -14
- package/src/apodization/apodization.ts +34 -0
- package/src/apodization/applyWindow.ts +51 -0
- package/src/apodization/compose.ts +47 -0
- package/src/apodization/getFunction.ts +15 -0
- package/src/apodization/shapes/WindowFunctions.ts +14 -0
- package/src/apodization/shapes/exponential.ts +16 -0
- package/src/apodization/shapes/lorentzToGauss.ts +41 -0
- package/src/apodization/utils/getData.ts +15 -0
- package/src/assignment/utils/buildAssignments.ts +4 -4
- package/src/assignment/utils/exploreTreeRec.ts +1 -1
- package/src/assignment/utils/getAssignment/buildAssignments.ts +3 -3
- package/src/assignment/utils/getAssignment/checkIDs.ts +1 -1
- package/src/databases/DatabaseNMREntry.ts +3 -0
- package/src/index.ts +7 -1
- package/src/peaks/NMRPeak1D.ts +0 -1
- package/src/peaks/peaksToRanges.ts +2 -2
- package/src/peaks/solventSuppression.ts +214 -0
- package/src/peaks/util/jAnalyzer.ts +27 -31
- package/src/peaks/util/peakOptimizer.ts +11 -15
- package/src/prediction/predictAllSpectra.ts +6 -8
- package/src/prediction/predictCarbon.ts +1 -1
- package/src/prediction/utils/queryByHOSE.ts +1 -1
- package/src/ranges/markSolventSignal.ts +121 -0
- package/src/ranges/rangesToACS.ts +9 -10
- package/src/ranges/rangesToXY.ts +33 -4
- package/src/signals/addDummySignals.ts +77 -0
- package/src/signals/hackSignalsToXY.ts +2 -72
- package/src/signals/simulation/getPauliMatrix.ts +1 -1
- package/src/signals/simulation/simulate1D.ts +14 -319
- package/src/signals/simulation/simulateXYPeaks.ts +332 -0
- package/src/signals/simulation/splitSpinSystem.ts +10 -12
- package/src/utilities/getFrequency.ts +1 -1
- package/src/utilities/rangeFromSignal.ts +19 -11
- package/src/utilities/resurrectRange.ts +4 -7
- package/src/xyz/util/formatZone.ts +36 -0
- package/src/xyz/xyzAutoZonesPicking.ts +1 -35
- package/src/xyz/xyzJResAnalyzer.ts +14 -7
- package/lib/constants/gyromagneticRatio.d.ts +0 -6
- package/lib/constants/gyromagneticRatio.js +0 -26
- package/lib/constants/gyromagneticRatio.js.map +0 -1
- package/lib-esm/constants/gyromagneticRatio.js +0 -23
- package/lib-esm/constants/gyromagneticRatio.js.map +0 -1
- package/src/constants/gyromagneticRatio.ts +0 -49
|
@@ -0,0 +1,332 @@
|
|
|
1
|
+
import binarySearch from 'binary-search';
|
|
2
|
+
import { Matrix, EVD } from 'ml-matrix';
|
|
3
|
+
import type { Matrix as MatrixClassType } from 'ml-matrix';
|
|
4
|
+
import type { Shape1D } from 'ml-peak-shape-generator';
|
|
5
|
+
import { SparseMatrix } from 'ml-sparse-matrix';
|
|
6
|
+
|
|
7
|
+
import type { SpinSystem } from '../spinSystem';
|
|
8
|
+
|
|
9
|
+
import getPauliMatrix from './getPauliMatrix';
|
|
10
|
+
|
|
11
|
+
const smallValue = 1e-2;
|
|
12
|
+
|
|
13
|
+
export interface Simulate1DOptions {
|
|
14
|
+
/**
|
|
15
|
+
* The linewidth of the output spectrum, expresed in Hz.
|
|
16
|
+
* @default 1
|
|
17
|
+
*/
|
|
18
|
+
lineWidth?: number;
|
|
19
|
+
/**
|
|
20
|
+
* Maximum number of atoms on each cluster that can be considered to be simulated together. It affects the the quality and speed of the simulation.
|
|
21
|
+
* @default 8
|
|
22
|
+
*/
|
|
23
|
+
maxClusterSize?: number;
|
|
24
|
+
/**
|
|
25
|
+
* The frequency in Mhz of the fake spectrometer that records the spectrum.
|
|
26
|
+
* @default 400
|
|
27
|
+
*/
|
|
28
|
+
frequency?: number;
|
|
29
|
+
/**
|
|
30
|
+
* The low limit of the ordinate variable.
|
|
31
|
+
* @default 0
|
|
32
|
+
*/
|
|
33
|
+
from?: number;
|
|
34
|
+
/**
|
|
35
|
+
* The upper limit of the ordinate variable.
|
|
36
|
+
* @default 10
|
|
37
|
+
*/
|
|
38
|
+
to?: number;
|
|
39
|
+
/**
|
|
40
|
+
* Number of points of the output spectrum.
|
|
41
|
+
* @default 16K
|
|
42
|
+
*/
|
|
43
|
+
nbPoints?: number;
|
|
44
|
+
/**
|
|
45
|
+
* Shape options
|
|
46
|
+
* @default {kind:'gaussian'}
|
|
47
|
+
*/
|
|
48
|
+
shape?: Shape1D;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export function simulateXYPeaks(
|
|
52
|
+
/**
|
|
53
|
+
* The SpinSystem object to be simulated
|
|
54
|
+
*/
|
|
55
|
+
spinSystem: SpinSystem,
|
|
56
|
+
options: Simulate1DOptions = {},
|
|
57
|
+
) {
|
|
58
|
+
let {
|
|
59
|
+
lineWidth = 1,
|
|
60
|
+
maxClusterSize = 8,
|
|
61
|
+
frequency: frequencyMHz = 400,
|
|
62
|
+
} = options;
|
|
63
|
+
|
|
64
|
+
const chemicalShifts = spinSystem.chemicalShifts.slice();
|
|
65
|
+
for (let i = 0; i < chemicalShifts.length; i++) {
|
|
66
|
+
chemicalShifts[i] = chemicalShifts[i] * frequencyMHz;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const multiplicity = spinSystem.multiplicity;
|
|
70
|
+
|
|
71
|
+
const xyPeaks = [];
|
|
72
|
+
for (const cluster of spinSystem.clusters) {
|
|
73
|
+
let clusterFake = cluster.map((cluster) =>
|
|
74
|
+
cluster < 0 ? -cluster - 1 : cluster,
|
|
75
|
+
);
|
|
76
|
+
|
|
77
|
+
let weight = 1;
|
|
78
|
+
let sumI = 0;
|
|
79
|
+
let frequencies: number[] = [];
|
|
80
|
+
let intensities: number[] = [];
|
|
81
|
+
if (cluster.length > maxClusterSize) {
|
|
82
|
+
// This is a single spin, but the cluster exceeds the maxClusterSize criteria
|
|
83
|
+
// we use the simple multiplicity algorithm
|
|
84
|
+
// Add the central peak. It will be split with every single J coupling.
|
|
85
|
+
let index = 0;
|
|
86
|
+
while (cluster[index++] < 0);
|
|
87
|
+
index = cluster[index - 1];
|
|
88
|
+
frequencies.push(-chemicalShifts[index]);
|
|
89
|
+
for (let i = 0; i < cluster.length; i++) {
|
|
90
|
+
if (cluster[i] < 0) {
|
|
91
|
+
let jc = spinSystem.couplingConstants.get(index, clusterFake[i]) / 2;
|
|
92
|
+
let currentSize = frequencies.length;
|
|
93
|
+
for (let j = 0; j < currentSize; j++) {
|
|
94
|
+
frequencies.push(frequencies[j] + jc);
|
|
95
|
+
frequencies[j] -= jc;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
frequencies.sort((a, b) => a - b);
|
|
101
|
+
sumI = frequencies.length;
|
|
102
|
+
weight = 1;
|
|
103
|
+
|
|
104
|
+
for (let i = 0; i < sumI; i++) {
|
|
105
|
+
intensities.push(1);
|
|
106
|
+
}
|
|
107
|
+
} else {
|
|
108
|
+
const hamiltonian = getHamiltonian(
|
|
109
|
+
chemicalShifts,
|
|
110
|
+
spinSystem.couplingConstants,
|
|
111
|
+
multiplicity,
|
|
112
|
+
spinSystem.connectivity,
|
|
113
|
+
clusterFake,
|
|
114
|
+
);
|
|
115
|
+
const hamSize = hamiltonian.rows;
|
|
116
|
+
// TODO: add support for sparse matrix in matrix types.
|
|
117
|
+
// @ts-expect-error sparse matrix not supported
|
|
118
|
+
const evd = new EVD(hamiltonian);
|
|
119
|
+
const V = evd.eigenvectorMatrix;
|
|
120
|
+
const diagB = evd.realEigenvalues;
|
|
121
|
+
const assignmentMatrix = new SparseMatrix(hamSize, hamSize);
|
|
122
|
+
const multLen = cluster.length;
|
|
123
|
+
weight = 0;
|
|
124
|
+
for (let n = 0; n < multLen; n++) {
|
|
125
|
+
const L = getPauliMatrix(multiplicity[clusterFake[n]]);
|
|
126
|
+
|
|
127
|
+
let temp = 1;
|
|
128
|
+
for (let j = 0; j < n; j++) {
|
|
129
|
+
temp *= multiplicity[clusterFake[j]];
|
|
130
|
+
}
|
|
131
|
+
const A = SparseMatrix.eye(temp);
|
|
132
|
+
|
|
133
|
+
temp = 1;
|
|
134
|
+
for (let j = n + 1; j < multLen; j++) {
|
|
135
|
+
temp *= multiplicity[clusterFake[j]];
|
|
136
|
+
}
|
|
137
|
+
const B = SparseMatrix.eye(temp);
|
|
138
|
+
const tempMat = A.kroneckerProduct(L.m).kroneckerProduct(B);
|
|
139
|
+
if (cluster[n] >= 0) {
|
|
140
|
+
assignmentMatrix.add(tempMat.mul(cluster[n] + 1));
|
|
141
|
+
weight++;
|
|
142
|
+
} else {
|
|
143
|
+
assignmentMatrix.add(tempMat.mul(cluster[n]));
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
let rhoip = Matrix.zeros(hamSize, hamSize);
|
|
148
|
+
assignmentMatrix.forEachNonZero((i, j, v) => {
|
|
149
|
+
if (v > 0) {
|
|
150
|
+
for (let k = 0; k < V.columns; k++) {
|
|
151
|
+
let element = V.get(j, k);
|
|
152
|
+
if (element !== 0) {
|
|
153
|
+
rhoip.set(i, k, rhoip.get(i, k) + element);
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
return v;
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
let rhoip2 = rhoip.clone();
|
|
161
|
+
assignmentMatrix.forEachNonZero((i: number, j: number, v: number) => {
|
|
162
|
+
if (v < 0) {
|
|
163
|
+
for (let k = 0; k < V.columns; k++) {
|
|
164
|
+
let element = V.get(j, k);
|
|
165
|
+
if (element !== 0) {
|
|
166
|
+
rhoip2.set(i, k, rhoip2.get(i, k) + element);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
return v;
|
|
171
|
+
});
|
|
172
|
+
const tV = V.transpose();
|
|
173
|
+
|
|
174
|
+
rhoip = tV.mmul(rhoip);
|
|
175
|
+
const sparseRhoip = new SparseMatrix(rhoip.to2DArray(), {
|
|
176
|
+
threshold: smallValue,
|
|
177
|
+
});
|
|
178
|
+
triuTimesAbs(sparseRhoip, smallValue);
|
|
179
|
+
|
|
180
|
+
rhoip2 = tV.mmul(rhoip2);
|
|
181
|
+
const sparseRhoip2 = new SparseMatrix(rhoip2.to2DArray(), {
|
|
182
|
+
threshold: smallValue,
|
|
183
|
+
});
|
|
184
|
+
sparseRhoip2.forEachNonZero((i, j, v) => {
|
|
185
|
+
return v;
|
|
186
|
+
});
|
|
187
|
+
triuTimesAbs(sparseRhoip2, smallValue);
|
|
188
|
+
sparseRhoip2.forEachNonZero((i, j, v) => {
|
|
189
|
+
let val = rhoip.get(i, j);
|
|
190
|
+
val = Math.min(Math.abs(val), Math.abs(v));
|
|
191
|
+
val *= val;
|
|
192
|
+
|
|
193
|
+
sumI += val;
|
|
194
|
+
let valFreq = diagB[i] - diagB[j];
|
|
195
|
+
let insertIn = binarySearch(
|
|
196
|
+
frequencies,
|
|
197
|
+
valFreq,
|
|
198
|
+
(a: number, b: number) => a - b,
|
|
199
|
+
);
|
|
200
|
+
if (insertIn < 0) {
|
|
201
|
+
frequencies.splice(-1 - insertIn, 0, valFreq);
|
|
202
|
+
intensities.splice(-1 - insertIn, 0, val);
|
|
203
|
+
} else {
|
|
204
|
+
intensities[insertIn] += val;
|
|
205
|
+
}
|
|
206
|
+
});
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
const numFreq = frequencies.length;
|
|
210
|
+
|
|
211
|
+
if (numFreq > 0) {
|
|
212
|
+
weight /= sumI;
|
|
213
|
+
const diff = lineWidth / 64;
|
|
214
|
+
let valFreq = frequencies[0];
|
|
215
|
+
let inte = intensities[0];
|
|
216
|
+
let count = 1;
|
|
217
|
+
for (let i = 1; i < numFreq; i++) {
|
|
218
|
+
if (Math.abs(frequencies[i] - valFreq / count) < diff) {
|
|
219
|
+
inte += intensities[i];
|
|
220
|
+
valFreq += frequencies[i];
|
|
221
|
+
count++;
|
|
222
|
+
} else {
|
|
223
|
+
xyPeaks.push({
|
|
224
|
+
x: -valFreq / count / frequencyMHz,
|
|
225
|
+
y: inte * weight,
|
|
226
|
+
});
|
|
227
|
+
valFreq = frequencies[i];
|
|
228
|
+
inte = intensities[i];
|
|
229
|
+
count = 1;
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
xyPeaks.push({
|
|
234
|
+
x: -valFreq / count / frequencyMHz,
|
|
235
|
+
y: inte * weight,
|
|
236
|
+
});
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
return xyPeaks;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
function triuTimesAbs(A: SparseMatrix, val: number) {
|
|
244
|
+
A.forEachNonZero((i, j, v) => {
|
|
245
|
+
if (i > j) return 0;
|
|
246
|
+
if (Math.abs(v) <= val) return 0;
|
|
247
|
+
return v;
|
|
248
|
+
});
|
|
249
|
+
}
|
|
250
|
+
/**
|
|
251
|
+
* Create a hamiltonian matrix for the given spinsystem
|
|
252
|
+
* @param {Array} chemicalShifts - An array containing the chemical shift in Hz
|
|
253
|
+
* @param {Array} couplingConstants - An array containing the coupling constants in Hz
|
|
254
|
+
* @param {Array} multiplicity - An array specifiying the multiplicities of each scalar coupling
|
|
255
|
+
* @param {Array} conMatrix - A one step connectivity matrix for the given spin system
|
|
256
|
+
* @param {Array} cluster - An binary array specifiying the spins to be considered for this hamiltonial
|
|
257
|
+
* @return {object}
|
|
258
|
+
*/
|
|
259
|
+
function getHamiltonian(
|
|
260
|
+
chemicalShifts: number[],
|
|
261
|
+
couplingConstants: MatrixClassType,
|
|
262
|
+
multiplicity: number[],
|
|
263
|
+
conMatrix: MatrixClassType,
|
|
264
|
+
cluster: number[],
|
|
265
|
+
) {
|
|
266
|
+
let hamSize = 1;
|
|
267
|
+
for (const element of cluster) {
|
|
268
|
+
hamSize *= multiplicity[element];
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
const clusterHam = new SparseMatrix(hamSize, hamSize);
|
|
272
|
+
|
|
273
|
+
for (let pos = 0; pos < cluster.length; pos++) {
|
|
274
|
+
let n = cluster[pos];
|
|
275
|
+
|
|
276
|
+
const L = getPauliMatrix(multiplicity[n]);
|
|
277
|
+
|
|
278
|
+
let A1, B1;
|
|
279
|
+
let temp = 1;
|
|
280
|
+
for (let i = 0; i < pos; i++) {
|
|
281
|
+
temp *= multiplicity[cluster[i]];
|
|
282
|
+
}
|
|
283
|
+
A1 = SparseMatrix.eye(temp);
|
|
284
|
+
|
|
285
|
+
temp = 1;
|
|
286
|
+
for (let i = pos + 1; i < cluster.length; i++) {
|
|
287
|
+
temp *= multiplicity[cluster[i]];
|
|
288
|
+
}
|
|
289
|
+
B1 = SparseMatrix.eye(temp);
|
|
290
|
+
|
|
291
|
+
const alpha = chemicalShifts[n];
|
|
292
|
+
const kronProd = A1.kroneckerProduct(L.z).kroneckerProduct(B1);
|
|
293
|
+
clusterHam.add(kronProd.mul(alpha));
|
|
294
|
+
for (let pos2 = 0; pos2 < cluster.length; pos2++) {
|
|
295
|
+
const k = cluster[pos2];
|
|
296
|
+
if (conMatrix.get(n, k) === 1) {
|
|
297
|
+
const S = getPauliMatrix(multiplicity[k]);
|
|
298
|
+
|
|
299
|
+
let A2, B2;
|
|
300
|
+
let temp = 1;
|
|
301
|
+
for (let i = 0; i < pos2; i++) {
|
|
302
|
+
temp *= multiplicity[cluster[i]];
|
|
303
|
+
}
|
|
304
|
+
A2 = SparseMatrix.eye(temp);
|
|
305
|
+
|
|
306
|
+
temp = 1;
|
|
307
|
+
for (let i = pos2 + 1; i < cluster.length; i++) {
|
|
308
|
+
temp *= multiplicity[cluster[i]];
|
|
309
|
+
}
|
|
310
|
+
B2 = SparseMatrix.eye(temp);
|
|
311
|
+
|
|
312
|
+
const kron1 = A1.kroneckerProduct(L.x)
|
|
313
|
+
.kroneckerProduct(B1)
|
|
314
|
+
.mmul(A2.kroneckerProduct(S.x).kroneckerProduct(B2));
|
|
315
|
+
kron1.add(
|
|
316
|
+
A1.kroneckerProduct(L.y)
|
|
317
|
+
.kroneckerProduct(B1)
|
|
318
|
+
.mul(-1)
|
|
319
|
+
.mmul(A2.kroneckerProduct(S.y).kroneckerProduct(B2)),
|
|
320
|
+
);
|
|
321
|
+
kron1.add(
|
|
322
|
+
A1.kroneckerProduct(L.z)
|
|
323
|
+
.kroneckerProduct(B1)
|
|
324
|
+
.mmul(A2.kroneckerProduct(S.z).kroneckerProduct(B2)),
|
|
325
|
+
);
|
|
326
|
+
|
|
327
|
+
clusterHam.add(kron1.mul(couplingConstants.get(n, k) / 2));
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
return clusterHam;
|
|
332
|
+
}
|
|
@@ -80,19 +80,17 @@ function splitCluster(
|
|
|
80
80
|
}
|
|
81
81
|
if (count <= maxClusterSize) {
|
|
82
82
|
clusterList.push(members);
|
|
83
|
+
} else if (child.index < 0) {
|
|
84
|
+
splitCluster(child, clusterList, {
|
|
85
|
+
maxClusterSize,
|
|
86
|
+
force: true,
|
|
87
|
+
nSpins,
|
|
88
|
+
connectivity,
|
|
89
|
+
});
|
|
83
90
|
} else {
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
force: true,
|
|
88
|
-
nSpins,
|
|
89
|
-
connectivity,
|
|
90
|
-
});
|
|
91
|
-
} else {
|
|
92
|
-
// We have to threat this spin alone and use the resurrection algorithm instead of the simulation
|
|
93
|
-
members[child.index] = 2;
|
|
94
|
-
clusterList.push(members);
|
|
95
|
-
}
|
|
91
|
+
// We have to threat this spin alone and use the resurrection algorithm instead of the simulation
|
|
92
|
+
members[child.index] = 2;
|
|
93
|
+
clusterList.push(members);
|
|
96
94
|
}
|
|
97
95
|
} else {
|
|
98
96
|
splitCluster(child, clusterList, {
|
|
@@ -6,24 +6,32 @@ export interface RangeFromSignalOptions {
|
|
|
6
6
|
* nucleus
|
|
7
7
|
* @default '1h'
|
|
8
8
|
*/
|
|
9
|
-
nucleus
|
|
9
|
+
nucleus?: string;
|
|
10
10
|
/**
|
|
11
|
-
* frequency
|
|
11
|
+
* frequency observedhttps://www.wiley.com/en-us/NMR+Data+Processing-p-9780471039006#content-section
|
|
12
12
|
* @default 400
|
|
13
13
|
*/
|
|
14
|
-
frequency
|
|
14
|
+
frequency?: number;
|
|
15
|
+
/**
|
|
16
|
+
* tolerance to the halfWidth, default depends of the nucleus
|
|
17
|
+
*/
|
|
18
|
+
tolerance?: number;
|
|
15
19
|
}
|
|
16
20
|
|
|
17
|
-
export function rangeFromSignal(
|
|
21
|
+
export function rangeFromSignal(
|
|
22
|
+
signal: NMRSignal1D,
|
|
23
|
+
options: RangeFromSignalOptions,
|
|
24
|
+
) {
|
|
18
25
|
const { nucleus = '1h', frequency = 400 } = options;
|
|
19
26
|
const { tolerance = getTolerance(nucleus) / frequency } = options;
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
+
|
|
28
|
+
let halfWidth = 0;
|
|
29
|
+
for (const js of signal.js || []) {
|
|
30
|
+
const { coupling, multiplicity = 'd' } = js;
|
|
31
|
+
halfWidth += (couplingValues[multiplicity] * coupling) / frequency;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
halfWidth = tolerance + halfWidth / 2;
|
|
27
35
|
return {
|
|
28
36
|
from: signal.delta - halfWidth,
|
|
29
37
|
to: signal.delta + halfWidth,
|
|
@@ -46,13 +46,10 @@ export function resurrectRange(part: string, options: any = {}) {
|
|
|
46
46
|
signal.delta = from;
|
|
47
47
|
signal.multiplicity = multiplicity;
|
|
48
48
|
}
|
|
49
|
-
} else {
|
|
50
|
-
//
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
signal.delta = from;
|
|
54
|
-
signal.multiplicity = multiplicity;
|
|
55
|
-
}
|
|
49
|
+
} else if (!isARange) {
|
|
50
|
+
// a complex signal
|
|
51
|
+
signal.delta = from;
|
|
52
|
+
signal.multiplicity = multiplicity;
|
|
56
53
|
}
|
|
57
54
|
}
|
|
58
55
|
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { NMRSignal2D } from '../NMRSignal2D';
|
|
2
|
+
import { NMRZone } from '../NMRZone';
|
|
3
|
+
|
|
4
|
+
export function formatZones(signals: NMRSignal2D[]) {
|
|
5
|
+
let zones: NMRZone[] = [];
|
|
6
|
+
for (const signal of signals) {
|
|
7
|
+
let minMax1 = [Number.MAX_VALUE, 0];
|
|
8
|
+
let minMax2 = [Number.MAX_VALUE, 0];
|
|
9
|
+
for (const peak of signal.peaks || []) {
|
|
10
|
+
if (peak.minX < minMax1[0]) {
|
|
11
|
+
minMax1[0] = peak.minX;
|
|
12
|
+
}
|
|
13
|
+
if (peak.maxX > minMax1[1]) {
|
|
14
|
+
minMax1[1] = peak.maxX;
|
|
15
|
+
}
|
|
16
|
+
if (peak.minY < minMax2[0]) {
|
|
17
|
+
minMax2[0] = peak.minY;
|
|
18
|
+
}
|
|
19
|
+
if (peak.maxY > minMax2[1]) {
|
|
20
|
+
minMax2[1] = peak.maxY;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
zones.push({
|
|
24
|
+
x: {
|
|
25
|
+
from: minMax1[0],
|
|
26
|
+
to: minMax1[1],
|
|
27
|
+
},
|
|
28
|
+
y: {
|
|
29
|
+
from: minMax2[0],
|
|
30
|
+
to: minMax2[1],
|
|
31
|
+
},
|
|
32
|
+
signals: [signal],
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
return zones;
|
|
36
|
+
}
|
|
@@ -4,13 +4,13 @@ import * as matrixPeakFinders from 'ml-matrix-peaks-finder';
|
|
|
4
4
|
import type { Peak2D } from 'ml-matrix-peaks-finder';
|
|
5
5
|
import simpleClustering from 'ml-simple-clustering';
|
|
6
6
|
|
|
7
|
-
import { NMRZone } from '..';
|
|
8
7
|
import { determineRealTop } from '../peaks/util/determineRealTop';
|
|
9
8
|
import { getKernel } from '../peaks/util/getKernel';
|
|
10
9
|
import type { GetKernelOptions } from '../peaks/util/getKernel';
|
|
11
10
|
import * as PeakOptimizer from '../peaks/util/peakOptimizer';
|
|
12
11
|
|
|
13
12
|
import type { NMRSignal2D } from './NMRSignal2D';
|
|
13
|
+
import { formatZones } from './util/formatZone';
|
|
14
14
|
|
|
15
15
|
const smallFilter = [
|
|
16
16
|
[0, 0, 1, 2, 2, 2, 1, 0, 0],
|
|
@@ -183,40 +183,6 @@ export function xyzAutoZonesPicking(
|
|
|
183
183
|
return formatZones(signals);
|
|
184
184
|
}
|
|
185
185
|
|
|
186
|
-
function formatZones(signals: NMRSignal2D[]) {
|
|
187
|
-
let zones: NMRZone[] = [];
|
|
188
|
-
for (const signal of signals) {
|
|
189
|
-
let minMax1 = [Number.MAX_VALUE, 0];
|
|
190
|
-
let minMax2 = [Number.MAX_VALUE, 0];
|
|
191
|
-
for (const peak of signal.peaks || []) {
|
|
192
|
-
if (peak.minX < minMax1[0]) {
|
|
193
|
-
minMax1[0] = peak.minX;
|
|
194
|
-
}
|
|
195
|
-
if (peak.maxX > minMax1[1]) {
|
|
196
|
-
minMax1[1] = peak.maxX;
|
|
197
|
-
}
|
|
198
|
-
if (peak.minY < minMax2[0]) {
|
|
199
|
-
minMax2[0] = peak.minY;
|
|
200
|
-
}
|
|
201
|
-
if (peak.maxY > minMax2[1]) {
|
|
202
|
-
minMax2[1] = peak.maxY;
|
|
203
|
-
}
|
|
204
|
-
}
|
|
205
|
-
zones.push({
|
|
206
|
-
x: {
|
|
207
|
-
from: minMax1[0],
|
|
208
|
-
to: minMax1[1],
|
|
209
|
-
},
|
|
210
|
-
y: {
|
|
211
|
-
from: minMax2[0],
|
|
212
|
-
to: minMax2[1],
|
|
213
|
-
},
|
|
214
|
-
signals: [signal],
|
|
215
|
-
});
|
|
216
|
-
}
|
|
217
|
-
return zones;
|
|
218
|
-
}
|
|
219
|
-
|
|
220
186
|
/**
|
|
221
187
|
* This function converts a set of 2D-peaks in 2D-signals. Each signal could be composed
|
|
222
188
|
* of many 2D-peaks, and it has some additional information related to the NMR spectrum.
|
|
@@ -6,6 +6,7 @@ import jAnalyzer from '../peaks/util/jAnalyzer';
|
|
|
6
6
|
import type { MakeMandatory } from '../utilities/MakeMandatory';
|
|
7
7
|
|
|
8
8
|
import type { NMRSignal2D } from './NMRSignal2D';
|
|
9
|
+
import { formatZones } from './util/formatZone';
|
|
9
10
|
|
|
10
11
|
interface CompilePatternOptions {
|
|
11
12
|
observedFrequencies?: number[] | Float64Array;
|
|
@@ -25,6 +26,11 @@ interface XYZJResAnalyzerOptions extends CompilePatternOptions {
|
|
|
25
26
|
* @default 0
|
|
26
27
|
*/
|
|
27
28
|
reference?: number;
|
|
29
|
+
/**
|
|
30
|
+
* if it is true returns zones instead of signals 2D
|
|
31
|
+
* @default false
|
|
32
|
+
*/
|
|
33
|
+
getZones?: boolean;
|
|
28
34
|
}
|
|
29
35
|
|
|
30
36
|
type CompilePatternOptionsMandatory = MakeMandatory<
|
|
@@ -38,6 +44,7 @@ export function xyzJResAnalyzer(
|
|
|
38
44
|
) {
|
|
39
45
|
let {
|
|
40
46
|
reference = 0,
|
|
47
|
+
getZones = false,
|
|
41
48
|
referenceMaxShiftError = 0.08,
|
|
42
49
|
tolerances = [10, 100],
|
|
43
50
|
nuclei = ['1H', '1H'],
|
|
@@ -46,20 +53,20 @@ export function xyzJResAnalyzer(
|
|
|
46
53
|
jAxisKey: { jAxis: 'y', intensity: 'z' },
|
|
47
54
|
},
|
|
48
55
|
} = options;
|
|
49
|
-
let temporalSignals = compilePattern(signals, {
|
|
56
|
+
let temporalSignals = compilePattern([...signals], {
|
|
50
57
|
observedFrequencies,
|
|
51
58
|
tolerances,
|
|
52
59
|
nuclei,
|
|
53
60
|
jAnalyzer,
|
|
54
61
|
});
|
|
55
62
|
//check if the signal are symmetric around the reference
|
|
56
|
-
let
|
|
63
|
+
let signals2D = [];
|
|
57
64
|
for (const tempSignal of temporalSignals) {
|
|
58
65
|
let delta = tempSignal.y.delta;
|
|
59
66
|
if (Math.abs(delta - reference) > referenceMaxShiftError) continue;
|
|
60
|
-
|
|
67
|
+
signals2D.push(tempSignal);
|
|
61
68
|
}
|
|
62
|
-
return
|
|
69
|
+
return getZones ? formatZones(signals2D) : signals2D;
|
|
63
70
|
}
|
|
64
71
|
|
|
65
72
|
function compilePattern(
|
|
@@ -122,10 +129,10 @@ function compilePattern(
|
|
|
122
129
|
}
|
|
123
130
|
if (peaksO.length > 0) {
|
|
124
131
|
peaksO.reverse();
|
|
125
|
-
let
|
|
132
|
+
let signals2D = createSignals2D(peaksO, signalOptions);
|
|
126
133
|
|
|
127
|
-
for (const
|
|
128
|
-
newSignals.push(
|
|
134
|
+
for (const signal of signals2D) {
|
|
135
|
+
newSignals.push(signal);
|
|
129
136
|
}
|
|
130
137
|
}
|
|
131
138
|
}
|
|
@@ -1,6 +0,0 @@
|
|
|
1
|
-
export declare type Nuclei = '1H' | '2H' | '3H' | '3He' | '7Li' | '13C' | '14N' | '15N' | '17O' | '19F' | '23Na' | '27Al' | '29Si' | '31P' | '57Fe' | '63Cu' | '67Zn' | '129Xe';
|
|
2
|
-
declare type GyromagneticRatio = {
|
|
3
|
-
[key in Nuclei]: number;
|
|
4
|
-
};
|
|
5
|
-
export declare const gyromagneticRatio: GyromagneticRatio;
|
|
6
|
-
export {};
|
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
// sources:
|
|
3
|
-
// https://en.wikipedia.org/wiki/Gyromagnetic_ratio
|
|
4
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
5
|
-
exports.gyromagneticRatio = void 0;
|
|
6
|
-
exports.gyromagneticRatio = {
|
|
7
|
-
'1H': 267.52218744e6,
|
|
8
|
-
'2H': 41.065e6,
|
|
9
|
-
'3H': 285.3508e6,
|
|
10
|
-
'3He': -203.789e6,
|
|
11
|
-
'7Li': 103.962e6,
|
|
12
|
-
'13C': 67.28284e6,
|
|
13
|
-
'14N': 19.331e6,
|
|
14
|
-
'15N': -27.116e6,
|
|
15
|
-
'17O': -36.264e6,
|
|
16
|
-
'19F': 251.662e6,
|
|
17
|
-
'23Na': 70.761e6,
|
|
18
|
-
'27Al': 69.763e6,
|
|
19
|
-
'29Si': -53.19e6,
|
|
20
|
-
'31P': 108.291e6,
|
|
21
|
-
'57Fe': 8.681e6,
|
|
22
|
-
'63Cu': 71.118e6,
|
|
23
|
-
'67Zn': 16.767e6,
|
|
24
|
-
'129Xe': -73.997e6,
|
|
25
|
-
};
|
|
26
|
-
//# sourceMappingURL=gyromagneticRatio.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"gyromagneticRatio.js","sourceRoot":"","sources":["../../src/constants/gyromagneticRatio.ts"],"names":[],"mappings":";AAAA,WAAW;AACX,mDAAmD;;;AA4BtC,QAAA,iBAAiB,GAAsB;IAClD,IAAI,EAAE,cAAc;IACpB,IAAI,EAAE,QAAQ;IACd,IAAI,EAAE,UAAU;IAChB,KAAK,EAAE,CAAC,SAAS;IACjB,KAAK,EAAE,SAAS;IAChB,KAAK,EAAE,UAAU;IACjB,KAAK,EAAE,QAAQ;IACf,KAAK,EAAE,CAAC,QAAQ;IAChB,KAAK,EAAE,CAAC,QAAQ;IAChB,KAAK,EAAE,SAAS;IAChB,MAAM,EAAE,QAAQ;IAChB,MAAM,EAAE,QAAQ;IAChB,MAAM,EAAE,CAAC,OAAO;IAChB,KAAK,EAAE,SAAS;IAChB,MAAM,EAAE,OAAO;IACf,MAAM,EAAE,QAAQ;IAChB,MAAM,EAAE,QAAQ;IAChB,OAAO,EAAE,CAAC,QAAQ;CACnB,CAAC"}
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
// sources:
|
|
2
|
-
// https://en.wikipedia.org/wiki/Gyromagnetic_ratio
|
|
3
|
-
export const gyromagneticRatio = {
|
|
4
|
-
'1H': 267.52218744e6,
|
|
5
|
-
'2H': 41.065e6,
|
|
6
|
-
'3H': 285.3508e6,
|
|
7
|
-
'3He': -203.789e6,
|
|
8
|
-
'7Li': 103.962e6,
|
|
9
|
-
'13C': 67.28284e6,
|
|
10
|
-
'14N': 19.331e6,
|
|
11
|
-
'15N': -27.116e6,
|
|
12
|
-
'17O': -36.264e6,
|
|
13
|
-
'19F': 251.662e6,
|
|
14
|
-
'23Na': 70.761e6,
|
|
15
|
-
'27Al': 69.763e6,
|
|
16
|
-
'29Si': -53.19e6,
|
|
17
|
-
'31P': 108.291e6,
|
|
18
|
-
'57Fe': 8.681e6,
|
|
19
|
-
'63Cu': 71.118e6,
|
|
20
|
-
'67Zn': 16.767e6,
|
|
21
|
-
'129Xe': -73.997e6,
|
|
22
|
-
};
|
|
23
|
-
//# sourceMappingURL=gyromagneticRatio.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"gyromagneticRatio.js","sourceRoot":"","sources":["../../src/constants/gyromagneticRatio.ts"],"names":[],"mappings":"AAAA,WAAW;AACX,mDAAmD;AA4BnD,MAAM,CAAC,MAAM,iBAAiB,GAAsB;IAClD,IAAI,EAAE,cAAc;IACpB,IAAI,EAAE,QAAQ;IACd,IAAI,EAAE,UAAU;IAChB,KAAK,EAAE,CAAC,SAAS;IACjB,KAAK,EAAE,SAAS;IAChB,KAAK,EAAE,UAAU;IACjB,KAAK,EAAE,QAAQ;IACf,KAAK,EAAE,CAAC,QAAQ;IAChB,KAAK,EAAE,CAAC,QAAQ;IAChB,KAAK,EAAE,SAAS;IAChB,MAAM,EAAE,QAAQ;IAChB,MAAM,EAAE,QAAQ;IAChB,MAAM,EAAE,CAAC,OAAO;IAChB,KAAK,EAAE,SAAS;IAChB,MAAM,EAAE,OAAO;IACf,MAAM,EAAE,QAAQ;IAChB,MAAM,EAAE,QAAQ;IAChB,OAAO,EAAE,CAAC,QAAQ;CACnB,CAAC"}
|