seqeyes-python 0.2.10__tar.gz → 0.2.11__tar.gz
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.
- {seqeyes_python-0.2.10 → seqeyes_python-0.2.11}/PKG-INFO +1 -1
- {seqeyes_python-0.2.10 → seqeyes_python-0.2.11}/pyproject.toml +1 -1
- {seqeyes_python-0.2.10 → seqeyes_python-0.2.11}/src/seqeyes/__init__.py +1 -1
- {seqeyes_python-0.2.10 → seqeyes_python-0.2.11}/src/seqeyes/resources/pulseq-bundle.js +180 -84
- {seqeyes_python-0.2.10 → seqeyes_python-0.2.11}/.gitignore +0 -0
- {seqeyes_python-0.2.10 → seqeyes_python-0.2.11}/README.md +0 -0
- {seqeyes_python-0.2.10 → seqeyes_python-0.2.11}/examples/demo.ipynb +0 -0
- {seqeyes_python-0.2.10 → seqeyes_python-0.2.11}/examples/demo.py +0 -0
- {seqeyes_python-0.2.10 → seqeyes_python-0.2.11}/src/seqeyes/_plot.py +0 -0
- {seqeyes_python-0.2.10 → seqeyes_python-0.2.11}/src/seqeyes/_renderer.py +0 -0
- {seqeyes_python-0.2.10 → seqeyes_python-0.2.11}/src/seqeyes/resources/viewer.html +0 -0
- {seqeyes_python-0.2.10 → seqeyes_python-0.2.11}/src/seqeyes/resources/webview-bundle.js +0 -0
- {seqeyes_python-0.2.10 → seqeyes_python-0.2.11}/tests/conftest.py +0 -0
- {seqeyes_python-0.2.10 → seqeyes_python-0.2.11}/tests/debug_full.html +0 -0
- {seqeyes_python-0.2.10 → seqeyes_python-0.2.11}/tests/debug_minimal.html +0 -0
- {seqeyes_python-0.2.10 → seqeyes_python-0.2.11}/tests/debug_viewer.html +0 -0
- {seqeyes_python-0.2.10 → seqeyes_python-0.2.11}/tests/debug_viewer.py +0 -0
- {seqeyes_python-0.2.10 → seqeyes_python-0.2.11}/tests/integration_test.py +0 -0
- {seqeyes_python-0.2.10 → seqeyes_python-0.2.11}/tests/test_plot.py +0 -0
- {seqeyes_python-0.2.10 → seqeyes_python-0.2.11}/tests/test_renderer.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: seqeyes-python
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.11
|
|
4
4
|
Summary: Interactive Pulseq MRI sequence viewer for Jupyter — lightweight, beautiful, replaces seq.plot() in pypulseq.
|
|
5
5
|
Project-URL: Homepage, https://github.com/bughht/seqeyes_plugin
|
|
6
6
|
Project-URL: Repository, https://github.com/bughht/seqeyes_plugin
|
|
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "seqeyes-python"
|
|
7
|
-
version = "0.2.
|
|
7
|
+
version = "0.2.11"
|
|
8
8
|
description = "Interactive Pulseq MRI sequence viewer for Jupyter — lightweight, beautiful, replaces seq.plot() in pypulseq."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
license = {text = "MIT"}
|
|
@@ -53,7 +53,7 @@ var Pulseq = (() => {
|
|
|
53
53
|
});
|
|
54
54
|
|
|
55
55
|
// package.json
|
|
56
|
-
var version = "0.2.
|
|
56
|
+
var version = "0.2.11";
|
|
57
57
|
|
|
58
58
|
// src/pulseq/decompressor.ts
|
|
59
59
|
function decompressShape(compressed, numSamples) {
|
|
@@ -1784,9 +1784,7 @@ var Pulseq = (() => {
|
|
|
1784
1784
|
// src/pulseq/kspace.ts
|
|
1785
1785
|
var TRAJECTORY_TIME_ACCURACY_SEC = 1e-10;
|
|
1786
1786
|
var GRADIENT_ENDPOINT_TOLERANCE_SEC = 1e-12;
|
|
1787
|
-
|
|
1788
|
-
return TRAJECTORY_TIME_ACCURACY_SEC * Math.round(timeSec / TRAJECTORY_TIME_ACCURACY_SEC);
|
|
1789
|
-
}
|
|
1787
|
+
var POLYNOMIAL_SUPPORT_EPSILON_SEC = 1e-12;
|
|
1790
1788
|
function calculateKspace(blocks, gradientRaster, totalDuration, trajectoryDelay = 0, _options) {
|
|
1791
1789
|
if (!blocks.length || !gradientRaster || gradientRaster <= 0) return null;
|
|
1792
1790
|
const GR = gradientRaster;
|
|
@@ -1794,7 +1792,6 @@ var Pulseq = (() => {
|
|
|
1794
1792
|
const tacc = TRAJECTORY_TIME_ACCURACY_SEC;
|
|
1795
1793
|
const gradientSupport = _options?.gradientSupport ?? "endpoints";
|
|
1796
1794
|
const excT = [], refT = [];
|
|
1797
|
-
const gradTimes = [];
|
|
1798
1795
|
let totalAdcSamples = 0;
|
|
1799
1796
|
for (const b of blocks) {
|
|
1800
1797
|
if (b.adc) totalAdcSamples += b.adc.numSamples;
|
|
@@ -1807,9 +1804,6 @@ var Pulseq = (() => {
|
|
|
1807
1804
|
const adcT = new Float64Array(totalAdcSamples);
|
|
1808
1805
|
let adcIdx = 0;
|
|
1809
1806
|
for (const b of blocks) {
|
|
1810
|
-
collectGradientSupport(b.gx, gradTimes, gradientSupport);
|
|
1811
|
-
collectGradientSupport(b.gy, gradTimes, gradientSupport);
|
|
1812
|
-
collectGradientSupport(b.gz, gradTimes, gradientSupport);
|
|
1813
1807
|
if (b.rf) {
|
|
1814
1808
|
const iso = Number.isFinite(b.rf.centerTime) ? b.rf.centerTime : b.rf.startTime + b.rf.duration * 0.5;
|
|
1815
1809
|
const u = b.rf.use || "";
|
|
@@ -1824,11 +1818,12 @@ var Pulseq = (() => {
|
|
|
1824
1818
|
adcT[adcIdx++] = t0 + (s + 0.5) * dwell + trajectoryDelay;
|
|
1825
1819
|
}
|
|
1826
1820
|
}
|
|
1821
|
+
const gradientSeries = buildGlobalGradientSeries(blocks, GR, totalDuration);
|
|
1827
1822
|
const cand = [];
|
|
1828
1823
|
const pushC = (t) => {
|
|
1829
1824
|
if (isFinite(t) && t >= -tacc) cand.push(Math.max(0, tacc * Math.round(t / tacc)));
|
|
1830
1825
|
};
|
|
1831
|
-
for (const
|
|
1826
|
+
for (const series of gradientSeries) collectSeriesSupport(series, gradientSupport, pushC);
|
|
1832
1827
|
for (const t of excT) {
|
|
1833
1828
|
pushC(t);
|
|
1834
1829
|
pushC(t - RF);
|
|
@@ -1855,39 +1850,12 @@ var Pulseq = (() => {
|
|
|
1855
1850
|
if (N < 2) return null;
|
|
1856
1851
|
if (_options?.maxGridPoints && N > _options.maxGridPoints) return null;
|
|
1857
1852
|
const gx = new Float64Array(N), gy = new Float64Array(N), gz = new Float64Array(N);
|
|
1858
|
-
const
|
|
1859
|
-
let cum = 0;
|
|
1860
|
-
for (const b of blocks) {
|
|
1861
|
-
cum += b.duration;
|
|
1862
|
-
edges.push(canonicalTrajectoryTime(cum));
|
|
1863
|
-
}
|
|
1853
|
+
const cursors = [0, 0, 0];
|
|
1864
1854
|
for (let i = 0; i < N; i++) {
|
|
1865
1855
|
const t = grid[i];
|
|
1866
|
-
|
|
1867
|
-
|
|
1868
|
-
|
|
1869
|
-
const localX = gradVal(block.gx, t);
|
|
1870
|
-
const localY = gradVal(block.gy, t);
|
|
1871
|
-
const localZ = gradVal(block.gz, t);
|
|
1872
|
-
const rotated = rotateGradient(block, localX, localY, localZ);
|
|
1873
|
-
gx[i] = rotated[0];
|
|
1874
|
-
gy[i] = rotated[1];
|
|
1875
|
-
gz[i] = rotated[2];
|
|
1876
|
-
}
|
|
1877
|
-
}
|
|
1878
|
-
const kx = new Float64Array(N), ky = new Float64Array(N), kz = new Float64Array(N);
|
|
1879
|
-
for (let i = 1; i < N; i++) {
|
|
1880
|
-
const dt = grid[i] - grid[i - 1];
|
|
1881
|
-
if (dt <= 0) {
|
|
1882
|
-
kx[i] = kx[i - 1];
|
|
1883
|
-
ky[i] = ky[i - 1];
|
|
1884
|
-
kz[i] = kz[i - 1];
|
|
1885
|
-
continue;
|
|
1886
|
-
}
|
|
1887
|
-
const gxm = 0.5 * (gx[i - 1] + gx[i]), gym = 0.5 * (gy[i - 1] + gy[i]), gzm = 0.5 * (gz[i - 1] + gz[i]);
|
|
1888
|
-
kx[i] = kx[i - 1] + gxm * dt;
|
|
1889
|
-
ky[i] = ky[i - 1] + gym * dt;
|
|
1890
|
-
kz[i] = kz[i - 1] + gzm * dt;
|
|
1856
|
+
gx[i] = sampleSeries(gradientSeries[0], t, cursors, 0);
|
|
1857
|
+
gy[i] = sampleSeries(gradientSeries[1], t, cursors, 1);
|
|
1858
|
+
gz[i] = sampleSeries(gradientSeries[2], t, cursors, 2);
|
|
1891
1859
|
}
|
|
1892
1860
|
const eIdx = [], rIdx = [];
|
|
1893
1861
|
for (const t of excT) {
|
|
@@ -1900,37 +1868,52 @@ var Pulseq = (() => {
|
|
|
1900
1868
|
}
|
|
1901
1869
|
eIdx.sort((a, b) => a - b);
|
|
1902
1870
|
rIdx.sort((a, b) => a - b);
|
|
1903
|
-
const
|
|
1904
|
-
|
|
1905
|
-
for (const i of
|
|
1906
|
-
|
|
1907
|
-
|
|
1908
|
-
|
|
1909
|
-
|
|
1910
|
-
|
|
1911
|
-
|
|
1912
|
-
|
|
1913
|
-
|
|
1914
|
-
|
|
1915
|
-
|
|
1916
|
-
|
|
1917
|
-
|
|
1918
|
-
|
|
1919
|
-
|
|
1920
|
-
|
|
1921
|
-
|
|
1922
|
-
|
|
1923
|
-
|
|
1924
|
-
|
|
1925
|
-
|
|
1926
|
-
|
|
1927
|
-
|
|
1928
|
-
|
|
1929
|
-
|
|
1930
|
-
|
|
1931
|
-
|
|
1932
|
-
|
|
1933
|
-
|
|
1871
|
+
const excitationAt = new Uint8Array(N);
|
|
1872
|
+
const refocusingAt = new Uint8Array(N);
|
|
1873
|
+
for (const i of eIdx) excitationAt[i] = 1;
|
|
1874
|
+
for (const i of rIdx) refocusingAt[i] = 1;
|
|
1875
|
+
const kx = new Float64Array(N), ky = new Float64Array(N), kz = new Float64Array(N);
|
|
1876
|
+
let cx = 0, cy = 0, cz = 0;
|
|
1877
|
+
if (refocusingAt[0] && !excitationAt[0]) {
|
|
1878
|
+
kx[0] = -kx[0];
|
|
1879
|
+
ky[0] = -ky[0];
|
|
1880
|
+
kz[0] = -kz[0];
|
|
1881
|
+
}
|
|
1882
|
+
for (let i = 1; i < N; i++) {
|
|
1883
|
+
const dt = grid[i] - grid[i - 1];
|
|
1884
|
+
if (dt <= 0) {
|
|
1885
|
+
kx[i] = kx[i - 1];
|
|
1886
|
+
ky[i] = ky[i - 1];
|
|
1887
|
+
kz[i] = kz[i - 1];
|
|
1888
|
+
continue;
|
|
1889
|
+
}
|
|
1890
|
+
const dx = 0.5 * (gx[i - 1] + gx[i]) * dt;
|
|
1891
|
+
const dy = 0.5 * (gy[i - 1] + gy[i]) * dt;
|
|
1892
|
+
const dz = 0.5 * (gz[i - 1] + gz[i]) * dt;
|
|
1893
|
+
const yx = dx - cx, yy = dy - cy, yz = dz - cz;
|
|
1894
|
+
const nx = kx[i - 1] + yx, ny = ky[i - 1] + yy, nz = kz[i - 1] + yz;
|
|
1895
|
+
cx = nx - kx[i - 1] - yx;
|
|
1896
|
+
cy = ny - ky[i - 1] - yy;
|
|
1897
|
+
cz = nz - kz[i - 1] - yz;
|
|
1898
|
+
kx[i] = nx;
|
|
1899
|
+
ky[i] = ny;
|
|
1900
|
+
kz[i] = nz;
|
|
1901
|
+
if (excitationAt[i]) {
|
|
1902
|
+
kx[i] = 0;
|
|
1903
|
+
ky[i] = 0;
|
|
1904
|
+
kz[i] = 0;
|
|
1905
|
+
cx = 0;
|
|
1906
|
+
cy = 0;
|
|
1907
|
+
cz = 0;
|
|
1908
|
+
} else if (refocusingAt[i]) {
|
|
1909
|
+
kx[i] = -kx[i];
|
|
1910
|
+
ky[i] = -ky[i];
|
|
1911
|
+
kz[i] = -kz[i];
|
|
1912
|
+
cx = -cx;
|
|
1913
|
+
cy = -cy;
|
|
1914
|
+
cz = -cz;
|
|
1915
|
+
}
|
|
1916
|
+
}
|
|
1934
1917
|
const kxP = new Float64Array(kx), kyP = new Float64Array(ky), kzP = new Float64Array(kz);
|
|
1935
1918
|
for (const i of eIdx) {
|
|
1936
1919
|
if (i > 0) {
|
|
@@ -1948,13 +1931,135 @@ var Pulseq = (() => {
|
|
|
1948
1931
|
}
|
|
1949
1932
|
return { ktraj: [kxP, kyP, kzP], t_ktraj: new Float64Array(grid), ktraj_adc: [kxA, kyA, kzA], t_adc: new Float64Array(adcT) };
|
|
1950
1933
|
}
|
|
1951
|
-
function
|
|
1952
|
-
if (
|
|
1934
|
+
function collectSeriesSupport(series, mode, push) {
|
|
1935
|
+
if (series.times.length < 2) return;
|
|
1953
1936
|
if (mode === "all") {
|
|
1954
|
-
for (
|
|
1937
|
+
for (const time of series.times) push(time);
|
|
1938
|
+
return;
|
|
1939
|
+
}
|
|
1940
|
+
for (const time of series.requiredSupport) push(time);
|
|
1941
|
+
}
|
|
1942
|
+
function buildGlobalGradientSeries(blocks, gradientRaster, totalDuration) {
|
|
1943
|
+
const output = [
|
|
1944
|
+
{ times: [], values: [], requiredSupport: [] },
|
|
1945
|
+
{ times: [], values: [], requiredSupport: [] },
|
|
1946
|
+
{ times: [], values: [], requiredSupport: [] }
|
|
1947
|
+
];
|
|
1948
|
+
for (const block of blocks) {
|
|
1949
|
+
for (let axis = 0; axis < 3; axis++) {
|
|
1950
|
+
const piece = physicalGradientPiece(block, axis);
|
|
1951
|
+
if (piece.times.length) appendGradientPiece(output[axis], piece, gradientRaster);
|
|
1952
|
+
}
|
|
1953
|
+
}
|
|
1954
|
+
for (const series of output) {
|
|
1955
|
+
if (!series.times.length) continue;
|
|
1956
|
+
const first = series.times[0];
|
|
1957
|
+
const last = series.times[series.times.length - 1];
|
|
1958
|
+
if (first > 0) {
|
|
1959
|
+
series.times.unshift(-POLYNOMIAL_SUPPORT_EPSILON_SEC, first - POLYNOMIAL_SUPPORT_EPSILON_SEC);
|
|
1960
|
+
series.values.unshift(0, 0);
|
|
1961
|
+
series.requiredSupport.push(-POLYNOMIAL_SUPPORT_EPSILON_SEC, first - POLYNOMIAL_SUPPORT_EPSILON_SEC);
|
|
1962
|
+
}
|
|
1963
|
+
if (last < totalDuration) {
|
|
1964
|
+
series.times.push(last + POLYNOMIAL_SUPPORT_EPSILON_SEC, totalDuration + POLYNOMIAL_SUPPORT_EPSILON_SEC);
|
|
1965
|
+
series.values.push(0, 0);
|
|
1966
|
+
series.requiredSupport.push(last + POLYNOMIAL_SUPPORT_EPSILON_SEC, totalDuration + POLYNOMIAL_SUPPORT_EPSILON_SEC);
|
|
1967
|
+
}
|
|
1968
|
+
}
|
|
1969
|
+
return output;
|
|
1970
|
+
}
|
|
1971
|
+
function physicalGradientPiece(block, axis) {
|
|
1972
|
+
const gradients = [block.gx, block.gy, block.gz];
|
|
1973
|
+
const hasGradient = gradients.some((g) => g && g.type !== "none" && g.timePoints.length >= 2);
|
|
1974
|
+
if (!hasGradient) return { times: [], values: [], requiredSupport: [] };
|
|
1975
|
+
if (!block.rotation?.values) {
|
|
1976
|
+
const gradient = gradients[axis];
|
|
1977
|
+
if (!gradient || gradient.type === "none" || gradient.timePoints.length < 2) {
|
|
1978
|
+
return { times: [], values: [], requiredSupport: [] };
|
|
1979
|
+
}
|
|
1980
|
+
return {
|
|
1981
|
+
times: Array.from(gradient.timePoints),
|
|
1982
|
+
values: Array.from(gradient.waveform),
|
|
1983
|
+
requiredSupport: []
|
|
1984
|
+
};
|
|
1985
|
+
}
|
|
1986
|
+
const times = [];
|
|
1987
|
+
for (const gradient of gradients) {
|
|
1988
|
+
if (!gradient || gradient.type === "none") continue;
|
|
1989
|
+
for (const time of gradient.timePoints) times.push(time);
|
|
1990
|
+
}
|
|
1991
|
+
times.sort((a, b) => a - b);
|
|
1992
|
+
const uniqueTimes = [];
|
|
1993
|
+
for (const time of times) {
|
|
1994
|
+
if (!uniqueTimes.length || time - uniqueTimes[uniqueTimes.length - 1] > GRADIENT_ENDPOINT_TOLERANCE_SEC) {
|
|
1995
|
+
uniqueTimes.push(time);
|
|
1996
|
+
}
|
|
1997
|
+
}
|
|
1998
|
+
return {
|
|
1999
|
+
times: uniqueTimes,
|
|
2000
|
+
values: uniqueTimes.map((time) => {
|
|
2001
|
+
const rotated = rotateGradient(
|
|
2002
|
+
block,
|
|
2003
|
+
gradVal(block.gx, time),
|
|
2004
|
+
gradVal(block.gy, time),
|
|
2005
|
+
gradVal(block.gz, time)
|
|
2006
|
+
);
|
|
2007
|
+
return rotated[axis];
|
|
2008
|
+
}),
|
|
2009
|
+
requiredSupport: []
|
|
2010
|
+
};
|
|
2011
|
+
}
|
|
2012
|
+
function appendGradientPiece(target, piece, gradientRaster) {
|
|
2013
|
+
if (!piece.times.length) return;
|
|
2014
|
+
target.requiredSupport.push(piece.times[0], piece.times[piece.times.length - 1]);
|
|
2015
|
+
if (!target.times.length) {
|
|
2016
|
+
target.times.push(...piece.times);
|
|
2017
|
+
target.values.push(...piece.values);
|
|
1955
2018
|
return;
|
|
1956
2019
|
}
|
|
1957
|
-
|
|
2020
|
+
const lastIndex = target.times.length - 1;
|
|
2021
|
+
const previousTime = target.times[lastIndex];
|
|
2022
|
+
const firstTime = piece.times[0];
|
|
2023
|
+
if (previousTime + gradientRaster < firstTime) {
|
|
2024
|
+
if (target.values[lastIndex] !== 0) {
|
|
2025
|
+
if (Math.abs(target.values[lastIndex]) > 1e-6) {
|
|
2026
|
+
target.times.push(previousTime + gradientRaster * 0.5);
|
|
2027
|
+
target.values.push(0);
|
|
2028
|
+
target.requiredSupport.push(previousTime + gradientRaster * 0.5);
|
|
2029
|
+
} else {
|
|
2030
|
+
target.values[lastIndex] = 0;
|
|
2031
|
+
}
|
|
2032
|
+
}
|
|
2033
|
+
if (piece.values[0] !== 0) {
|
|
2034
|
+
if (Math.abs(piece.values[0]) > 1e-6) {
|
|
2035
|
+
target.times.push(firstTime - gradientRaster * 0.5);
|
|
2036
|
+
target.values.push(0);
|
|
2037
|
+
target.requiredSupport.push(firstTime - gradientRaster * 0.5);
|
|
2038
|
+
} else {
|
|
2039
|
+
piece.values[0] = 0;
|
|
2040
|
+
}
|
|
2041
|
+
}
|
|
2042
|
+
}
|
|
2043
|
+
let start = 0;
|
|
2044
|
+
const currentLast = target.times[target.times.length - 1];
|
|
2045
|
+
while (start < piece.times.length && piece.times[start] <= currentLast) start++;
|
|
2046
|
+
for (let i = start; i < piece.times.length; i++) {
|
|
2047
|
+
target.times.push(piece.times[i]);
|
|
2048
|
+
target.values.push(piece.values[i]);
|
|
2049
|
+
}
|
|
2050
|
+
}
|
|
2051
|
+
function sampleSeries(series, time, cursors, axis) {
|
|
2052
|
+
const n = series.times.length;
|
|
2053
|
+
if (!n || time < series.times[0] || time > series.times[n - 1]) return 0;
|
|
2054
|
+
let cursor = Math.min(cursors[axis], n - 2);
|
|
2055
|
+
while (cursor + 1 < n && series.times[cursor + 1] < time) cursor++;
|
|
2056
|
+
cursors[axis] = cursor;
|
|
2057
|
+
if (cursor + 1 >= n) return series.values[n - 1];
|
|
2058
|
+
const t0 = series.times[cursor], t1 = series.times[cursor + 1];
|
|
2059
|
+
const v0 = series.values[cursor], v1 = series.values[cursor + 1];
|
|
2060
|
+
if (time <= t0 || t1 <= t0) return v0;
|
|
2061
|
+
if (time >= t1) return v1;
|
|
2062
|
+
return v0 + (v1 - v0) * (time - t0) / (t1 - t0);
|
|
1958
2063
|
}
|
|
1959
2064
|
function gradVal(g, t) {
|
|
1960
2065
|
if (!g || g.type === "none") return 0;
|
|
@@ -1974,15 +2079,6 @@ var Pulseq = (() => {
|
|
|
1974
2079
|
if (s <= 0) return wf[lo];
|
|
1975
2080
|
return wf[lo] + (wf[hi] - wf[lo]) * (t - tp[lo]) / s;
|
|
1976
2081
|
}
|
|
1977
|
-
function blockIdx(t, edges) {
|
|
1978
|
-
let lo = 0, hi = edges.length - 1;
|
|
1979
|
-
while (lo < hi) {
|
|
1980
|
-
const m = lo + hi >> 1;
|
|
1981
|
-
if (edges[m] <= t + 1e-12) lo = m + 1;
|
|
1982
|
-
else hi = m;
|
|
1983
|
-
}
|
|
1984
|
-
return Math.max(0, lo - 1);
|
|
1985
|
-
}
|
|
1986
2082
|
function timeIdx(t, g) {
|
|
1987
2083
|
let lo = 0, hi = g.length;
|
|
1988
2084
|
while (lo < hi) {
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|