quantized-lab 0.8.0__py3-none-any.whl
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.
- quantized/__init__.py +11 -0
- quantized/__main__.py +14 -0
- quantized/api.py +220 -0
- quantized/app.py +206 -0
- quantized/calc/__init__.py +8 -0
- quantized/calc/_clipfit.py +76 -0
- quantized/calc/_natural_neighbor.py +219 -0
- quantized/calc/aggregate.py +159 -0
- quantized/calc/backgrounds.py +353 -0
- quantized/calc/baseline.py +349 -0
- quantized/calc/batch_fit.py +148 -0
- quantized/calc/constants.py +27 -0
- quantized/calc/corrections.py +192 -0
- quantized/calc/crystallography.py +400 -0
- quantized/calc/diffusion.py +120 -0
- quantized/calc/electrical.py +248 -0
- quantized/calc/electrochemistry.py +176 -0
- quantized/calc/element_data.json +1 -0
- quantized/calc/element_data.py +59 -0
- quantized/calc/errors.py +246 -0
- quantized/calc/figure.py +417 -0
- quantized/calc/figure_break.py +152 -0
- quantized/calc/figure_categorical.py +156 -0
- quantized/calc/figure_corner.py +229 -0
- quantized/calc/figure_facets.py +127 -0
- quantized/calc/figure_field.py +137 -0
- quantized/calc/figure_hitmap.py +116 -0
- quantized/calc/figure_labels.py +62 -0
- quantized/calc/figure_map.py +287 -0
- quantized/calc/figure_overrides.py +159 -0
- quantized/calc/figure_page.py +266 -0
- quantized/calc/figure_scale.py +125 -0
- quantized/calc/figure_statplots.py +167 -0
- quantized/calc/figure_styles.py +131 -0
- quantized/calc/figure_ternary.py +239 -0
- quantized/calc/figure_ticks.py +217 -0
- quantized/calc/fit_autoguess.py +156 -0
- quantized/calc/fit_bootstrap.py +163 -0
- quantized/calc/fit_bumps.py +258 -0
- quantized/calc/fit_constraints.py +114 -0
- quantized/calc/fit_equation.py +264 -0
- quantized/calc/fit_findxy.py +80 -0
- quantized/calc/fit_models.py +195 -0
- quantized/calc/fit_models_special.py +189 -0
- quantized/calc/fit_odr.py +99 -0
- quantized/calc/fit_scan.py +243 -0
- quantized/calc/fit_stats.py +215 -0
- quantized/calc/fitting.py +199 -0
- quantized/calc/formula.py +90 -0
- quantized/calc/global_curve_fit.py +305 -0
- quantized/calc/global_fit.py +181 -0
- quantized/calc/interp2d.py +260 -0
- quantized/calc/linecut.py +269 -0
- quantized/calc/magnetic.py +414 -0
- quantized/calc/magnetometry.py +464 -0
- quantized/calc/map.py +228 -0
- quantized/calc/mcmc.py +177 -0
- quantized/calc/optics.py +228 -0
- quantized/calc/pawley.py +251 -0
- quantized/calc/peak_batch.py +128 -0
- quantized/calc/peak_fit.py +259 -0
- quantized/calc/peak_integrate.py +104 -0
- quantized/calc/peak_multifit.py +260 -0
- quantized/calc/peak_track.py +134 -0
- quantized/calc/peaks.py +298 -0
- quantized/calc/peakshapes.py +85 -0
- quantized/calc/plotting.py +147 -0
- quantized/calc/processing.py +232 -0
- quantized/calc/qspace.py +48 -0
- quantized/calc/reductions.py +155 -0
- quantized/calc/reductions_fft.py +383 -0
- quantized/calc/refl_sld_presets.json +1 -0
- quantized/calc/reflectivity.py +80 -0
- quantized/calc/registry.py +303 -0
- quantized/calc/relaxation.py +119 -0
- quantized/calc/report.py +253 -0
- quantized/calc/report_emit.py +227 -0
- quantized/calc/resample.py +142 -0
- quantized/calc/rsm.py +91 -0
- quantized/calc/rsm_analyze.py +245 -0
- quantized/calc/semiconductor.py +488 -0
- quantized/calc/sld.py +131 -0
- quantized/calc/sld_formula.py +138 -0
- quantized/calc/spectral.py +357 -0
- quantized/calc/statplots.py +214 -0
- quantized/calc/stats.py +399 -0
- quantized/calc/stats_anova2.py +202 -0
- quantized/calc/stats_anova_ext.py +338 -0
- quantized/calc/stats_dist.py +196 -0
- quantized/calc/stats_glm.py +245 -0
- quantized/calc/stats_multivar.py +289 -0
- quantized/calc/stats_roc.py +157 -0
- quantized/calc/stats_survival.py +261 -0
- quantized/calc/stats_tests.py +380 -0
- quantized/calc/substrates.py +181 -0
- quantized/calc/superconductor.py +359 -0
- quantized/calc/surface_fit.py +290 -0
- quantized/calc/surface_models.py +156 -0
- quantized/calc/thermal.py +119 -0
- quantized/calc/thin_film.py +425 -0
- quantized/calc/unit_convert.py +259 -0
- quantized/calc/units.py +80 -0
- quantized/calc/vacuum.py +290 -0
- quantized/calc/xray.py +169 -0
- quantized/cli.py +214 -0
- quantized/datastruct.py +153 -0
- quantized/io/__init__.py +11 -0
- quantized/io/_hdf5_layout.py +308 -0
- quantized/io/_jcamp_asdf.py +135 -0
- quantized/io/_xrdml_scan.py +291 -0
- quantized/io/base.py +82 -0
- quantized/io/bruker_brml.py +177 -0
- quantized/io/bruker_raw.py +158 -0
- quantized/io/cif.py +266 -0
- quantized/io/consolidated.py +122 -0
- quantized/io/delimited.py +222 -0
- quantized/io/excel.py +135 -0
- quantized/io/hdf5.py +192 -0
- quantized/io/import_filters.py +178 -0
- quantized/io/import_preview.py +262 -0
- quantized/io/jcamp.py +179 -0
- quantized/io/lakeshore.py +163 -0
- quantized/io/ncnr.py +278 -0
- quantized/io/netcdf.py +195 -0
- quantized/io/opus.py +231 -0
- quantized/io/origin.py +346 -0
- quantized/io/origin_com.py +194 -0
- quantized/io/origin_project/__init__.py +221 -0
- quantized/io/origin_project/annotation_marks.py +288 -0
- quantized/io/origin_project/container.py +262 -0
- quantized/io/origin_project/curve_style_color.py +359 -0
- quantized/io/origin_project/figure_geometry.py +108 -0
- quantized/io/origin_project/figure_layers.py +333 -0
- quantized/io/origin_project/figure_text.py +258 -0
- quantized/io/origin_project/figures.py +210 -0
- quantized/io/origin_project/figures_opju.py +440 -0
- quantized/io/origin_project/notes.py +302 -0
- quantized/io/origin_project/opj.py +459 -0
- quantized/io/origin_project/opj_curves.py +297 -0
- quantized/io/origin_project/opj_shapes.py +148 -0
- quantized/io/origin_project/opju.py +146 -0
- quantized/io/origin_project/opju_axis_real_form.py +418 -0
- quantized/io/origin_project/opju_axis_specimen_form.py +167 -0
- quantized/io/origin_project/opju_codec.py +370 -0
- quantized/io/origin_project/opju_curves.py +497 -0
- quantized/io/origin_project/opju_curves_allcols.py +258 -0
- quantized/io/origin_project/opju_figure_curves.py +302 -0
- quantized/io/origin_project/opju_figure_text.py +245 -0
- quantized/io/origin_project/opju_reports.py +129 -0
- quantized/io/origin_project/origin_richtext.py +145 -0
- quantized/io/origin_project/preview.py +132 -0
- quantized/io/origin_project/templates.py +314 -0
- quantized/io/origin_project/tree.py +379 -0
- quantized/io/origin_project/tree_opju.py +228 -0
- quantized/io/origin_project/windows.py +238 -0
- quantized/io/origin_project/windows_opju.py +393 -0
- quantized/io/origin_project/writer.py +156 -0
- quantized/io/origin_project/writer_blocks.py +282 -0
- quantized/io/qd.py +380 -0
- quantized/io/refl1d.py +132 -0
- quantized/io/registry.py +210 -0
- quantized/io/report_export.py +347 -0
- quantized/io/rigaku.py +100 -0
- quantized/io/sims.py +398 -0
- quantized/io/spc.py +311 -0
- quantized/io/xrd_csv.py +308 -0
- quantized/io/xrdml.py +394 -0
- quantized/jobs.py +173 -0
- quantized/plugins/__init__.py +50 -0
- quantized/plugins/contract.py +111 -0
- quantized/plugins/loader.py +394 -0
- quantized/plugins/steps.py +90 -0
- quantized/routes/__init__.py +7 -0
- quantized/routes/_bookcache.py +62 -0
- quantized/routes/_export_common.py +27 -0
- quantized/routes/_payload.py +58 -0
- quantized/routes/_uploadcache.py +59 -0
- quantized/routes/aggregate.py +46 -0
- quantized/routes/baseline.py +210 -0
- quantized/routes/books.py +117 -0
- quantized/routes/calc.py +56 -0
- quantized/routes/corrections.py +78 -0
- quantized/routes/crystallography.py +80 -0
- quantized/routes/diffusion.py +58 -0
- quantized/routes/electrical.py +101 -0
- quantized/routes/electrochemistry.py +83 -0
- quantized/routes/export.py +280 -0
- quantized/routes/export_facets.py +83 -0
- quantized/routes/export_figures.py +471 -0
- quantized/routes/export_page.py +125 -0
- quantized/routes/fitting.py +379 -0
- quantized/routes/fitting_bumps.py +97 -0
- quantized/routes/import_template.py +97 -0
- quantized/routes/import_wizard.py +150 -0
- quantized/routes/jobs_api.py +59 -0
- quantized/routes/magnetic.py +135 -0
- quantized/routes/magnetometry.py +133 -0
- quantized/routes/optics.py +98 -0
- quantized/routes/parsers.py +281 -0
- quantized/routes/peaks.py +184 -0
- quantized/routes/plot.py +103 -0
- quantized/routes/reductions.py +121 -0
- quantized/routes/reference.py +58 -0
- quantized/routes/reflectivity.py +91 -0
- quantized/routes/report_export.py +119 -0
- quantized/routes/rsm.py +136 -0
- quantized/routes/samples.py +32 -0
- quantized/routes/semiconductor.py +207 -0
- quantized/routes/sld.py +42 -0
- quantized/routes/spectral.py +54 -0
- quantized/routes/statplots.py +99 -0
- quantized/routes/stats.py +418 -0
- quantized/routes/stats_design.py +321 -0
- quantized/routes/substrates.py +46 -0
- quantized/routes/superconductor.py +139 -0
- quantized/routes/thermal.py +57 -0
- quantized/routes/thin_film.py +153 -0
- quantized/routes/vacuum.py +113 -0
- quantized/routes/xray.py +32 -0
- quantized/samples/demo_vsm.csv +42 -0
- quantized/server_launch.py +251 -0
- quantized/web/assets/JetBrainsMono-Bold-CUogYd9I.woff2 +0 -0
- quantized/web/assets/JetBrainsMono-Regular-CA-Os4ii.woff2 +0 -0
- quantized/web/assets/index-BHmmCL-x.js +27 -0
- quantized/web/assets/index-BiZzN7J6.css +1 -0
- quantized/web/index.html +13 -0
- quantized/web/loading.html +69 -0
- quantized_lab-0.8.0.dist-info/METADATA +122 -0
- quantized_lab-0.8.0.dist-info/RECORD +233 -0
- quantized_lab-0.8.0.dist-info/WHEEL +4 -0
- quantized_lab-0.8.0.dist-info/entry_points.txt +4 -0
- quantized_lab-0.8.0.dist-info/licenses/LICENSE +201 -0
- quantized_lab-0.8.0.dist-info/licenses/NOTICE +11 -0
|
@@ -0,0 +1,418 @@
|
|
|
1
|
+
"""``.opju`` (CPYUA) real-corpus-form axis record decoding (item 33).
|
|
2
|
+
|
|
3
|
+
Split out of ``figures_opju.py`` to stay under the repo's 500-line
|
|
4
|
+
god-module ceiling; this is purely an extraction, not a new subsystem — see
|
|
5
|
+
``figures_opju.py``'s module docstring for the full "Real-corpus form"
|
|
6
|
+
narrative (record grammar, value-token encodings) this implements:
|
|
7
|
+
|
|
8
|
+
```
|
|
9
|
+
03 00 00 1f layer anchor
|
|
10
|
+
[optional flag token] 1-2 bytes, skipped (see below)
|
|
11
|
+
[X from] [X to] [X step] value tokens; ``from`` elided when 0.0
|
|
12
|
+
81 <id> <plen> 00 00 01 … separator (layer geometry; plen VARIES
|
|
13
|
+
and is only a search-window hint)
|
|
14
|
+
[Y from] [Y to] [Y step] value tokens (tagged/RLE only)
|
|
15
|
+
81 <id> <plen> 00 00 01 … end separator
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
Value tokens: **tagged compact** (``8T nn <nn bytes>``, tag ``0x81..0x8f``,
|
|
19
|
+
payload = the double's BE top-``nn`` reversed), **RLE-compressed literal**
|
|
20
|
+
(a byte-run inside the 8 LE double bytes collapses to a ``c2``/``c3``
|
|
21
|
+
escape — ``c2`` = a run of exactly 5 repeated bytes, ``c3`` = exactly 6; the
|
|
22
|
+
byte after the repeated byte is a context/tag byte, skipped, not a count),
|
|
23
|
+
and **bare compact** (1-3 significant bytes, no tag, right after a flag
|
|
24
|
+
token). The optional X flag token (``89 01``/``89 18``/``97 03``/``91 09`` =
|
|
25
|
+
2 bytes; a bare ``91`` before a run-first RLE value = 1 byte; absent when
|
|
26
|
+
the record opens with a tagged value) is skipped via a deterministic length
|
|
27
|
+
rule; its semantics stay undecoded.
|
|
28
|
+
|
|
29
|
+
**Y-axis scale flag — solved 2026-07-04** (the rf_* 4-file by-construction
|
|
30
|
+
oracle: ``rf_linlin``/``rf_logx``/``rf_logy``/``rf_loglog.opju``, the SAME
|
|
31
|
+
single-curve graph with identical custom ranges ``x=[0.2,20]``/
|
|
32
|
+
``y=[50,2000]``, differing only in ``layer.x.type``/``layer.y.type``). The
|
|
33
|
+
end separator's geometry payload is followed by a fixed 4-byte layer-style
|
|
34
|
+
marker ``00 10 10 00``; the 2 bytes immediately before it are an exact Y
|
|
35
|
+
lin/log flag -- ``01 00`` linear, ``08 01`` log10 -- independent of the
|
|
36
|
+
geometry payload's own (variable) shape/length and of X's own type/encoding
|
|
37
|
+
(which shifts the marker's absolute position but never the flag's value or
|
|
38
|
+
its relationship to the marker). Validated exact against all 14 real-corpus
|
|
39
|
+
anchors (RockingCurve's 3 log-Y layers read ``08 01``; XAS 3 + UnpolPlots 4
|
|
40
|
+
+ "Fixed Lambdas SI" 4, all linear-Y, read ``01 00``) -- see
|
|
41
|
+
``docs/origin_project_format.md`` §6.2 for the full byte-level trail. This
|
|
42
|
+
mirrors the independently-discovered ``.opj`` flag (``figure_layers.py``'s
|
|
43
|
+
``_y_scale_flag``, same two byte values, different fixed offset) -- strong
|
|
44
|
+
cross-container corroboration that both are real, not coincidental.
|
|
45
|
+
|
|
46
|
+
**X-axis scale flag — solved 2026-07-06** (the same rf_* oracle, byte-diffed
|
|
47
|
+
pairwise: ``rf_logx`` vs ``rf_linlin`` and ``rf_loglog`` vs ``rf_logy`` differ
|
|
48
|
+
ONLY in X): the geometry payload between the two separators ENDS with an
|
|
49
|
+
X-scale field carrying the very same two byte values as the Y flag --
|
|
50
|
+
``01`` = linear, ``08 01`` = log10 -- immediately before the Y span's first
|
|
51
|
+
token, with 0-2 trailing ``00`` pad bytes in between (pad count varies with
|
|
52
|
+
the Y token encoding; the flag bytes never do). ``_real_x_log_flag`` reads it
|
|
53
|
+
by scanning backward from the decoded Y-span start over the ``00`` pads.
|
|
54
|
+
Corpus-proof (see ``docs/origin_re/ORIGIN_CONVENTIONS.md`` §6.2): all 9
|
|
55
|
+
by-construction specimens read exactly (rf quad, fig_linx/logx/xylog via the
|
|
56
|
+
same bytes on the specimen path, ``axis_custom``), the real-corpus log-x
|
|
57
|
+
graph ``Fixed Lambdas SI!Graph6`` (2 layers, GT ``layer.x.type=2``, a 3.8x
|
|
58
|
+
span the decade heuristic mislabels linear) reads ``08 01``, and every
|
|
59
|
+
GT-linear real record (~70 across Hc2/RockingCurve/XAS/UnpolPlots/Fixed
|
|
60
|
+
Lambdas) reads ``01`` -- zero false positives. Six Hc2 records end ``02``
|
|
61
|
+
instead (an unrecognized value, possibly another scale type): those return
|
|
62
|
+
``None`` and keep the decade heuristic -- never guessed. (The ``85 02 f0
|
|
63
|
+
3f`` sequence once suspected to be a scale flag is in fact a tagged
|
|
64
|
+
``y_from = 1.0``; the real flag sits before the Y span, not inside it.)
|
|
65
|
+
|
|
66
|
+
**Panel-layer records (``03 00 00 5f`` anchor) + bare-literal Y spans.**
|
|
67
|
+
Multi-layer panel/composite windows (Fixed Lambdas SI ``Graph5``/``Graph6``,
|
|
68
|
+
RockingCurve/UnpolPlots ``Graph3``) anchor their per-layer records with
|
|
69
|
+
``03 00 00 5f`` (= ``1f | 0x40``), not ``1f`` -- same record grammar inside.
|
|
70
|
+
Fixed Lambdas' panel layers additionally encode their Y spans as bare 8-byte
|
|
71
|
+
LE literals (elsewhere Y is tagged/RLE only), so the Y scan gets a
|
|
72
|
+
LAST-RESORT bare retry that is accepted ONLY where `_real_x_log_flag`
|
|
73
|
+
authenticates the Y-span start (the exact flag bytes right before it) --
|
|
74
|
+
binary noise cannot satisfy both the flag pattern and a unique exact fill,
|
|
75
|
+
so previously-decoding records parse byte-identically (retry-only, like
|
|
76
|
+
``_SEP_WIDE``).
|
|
77
|
+
"""
|
|
78
|
+
|
|
79
|
+
from __future__ import annotations
|
|
80
|
+
|
|
81
|
+
import re
|
|
82
|
+
import struct
|
|
83
|
+
|
|
84
|
+
__all__: list[str] = [] # internal to the .opju figures subsystem; imported by name
|
|
85
|
+
|
|
86
|
+
# `8x <id> <plen> 00 00 01` separates the X span from the layer-geometry
|
|
87
|
+
# payload and the geometry from the Y span. Two lead bytes exist: `81`
|
|
88
|
+
# (the original form, every record validated through 2026-07-04) and `80`
|
|
89
|
+
# (found 2026-07-05 on Hc2's Graph8/Graph12-family records, always paired
|
|
90
|
+
# with a final span token carrying one trailing subfield byte -- see
|
|
91
|
+
# `_real_tagged_trailer`). The strict `81` form is always tried first and
|
|
92
|
+
# the wide form only as a retry, so previously-decoding records parse
|
|
93
|
+
# byte-identically (see `_parse_real_record`).
|
|
94
|
+
_SEP_STRICT = re.compile(rb"\x81..\x00\x00\x01", re.DOTALL)
|
|
95
|
+
_SEP_WIDE = re.compile(rb"[\x80\x81]..\x00\x00\x01", re.DOTALL)
|
|
96
|
+
# Specimen-form record markers (moved with _parse_specimen_record from
|
|
97
|
+
# figures_opju, 2026-07-06).
|
|
98
|
+
_Y_TRANSITION = bytes([0x81, 0x04, 0x06, 0x00, 0x00, 0x01, 0xC3, 0x66])
|
|
99
|
+
_STEP_TAG = bytes([0x83, 0x02])
|
|
100
|
+
|
|
101
|
+
_TAG_SEARCH_SPAN = 2_000 # max bytes allowed between an anchor and its separator/marker
|
|
102
|
+
# Y may start up to this many bytes past the nominal payload end. 7, not 6:
|
|
103
|
+
# the geometry payload runs 11 bytes past `y_lo` when plen=5 (Hc2 Graph1 /
|
|
104
|
+
# the Graph3 6-curve family, oracle-verified), which the old 6-byte scan
|
|
105
|
+
# missed by exactly one.
|
|
106
|
+
_Y_START_SCAN = 7
|
|
107
|
+
|
|
108
|
+
# Y-scale flag: the fixed layer-style marker that follows the end separator's
|
|
109
|
+
# geometry payload, and the 2-byte flag immediately before it (see module docstring).
|
|
110
|
+
_Y_STYLE_MARKER = bytes([0x00, 0x10, 0x10, 0x00])
|
|
111
|
+
_Y_LIN_FLAG = bytes([0x01, 0x00])
|
|
112
|
+
_Y_LOG_FLAG = bytes([0x08, 0x01])
|
|
113
|
+
_Y_FLAG_SEARCH_SPAN = 150 # observed within ~10-20 bytes in every instance seen
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
def _plausible(v: float) -> bool:
|
|
117
|
+
return v == v and (v == 0.0 or 1e-9 <= abs(v) <= 1e9)
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
def _decode_compact(chunk: bytes) -> float | None:
|
|
121
|
+
"""1-3 significant bytes: BE top-N of the double, stored reversed."""
|
|
122
|
+
n = len(chunk)
|
|
123
|
+
be = bytes(reversed(chunk)) + b"\x00" * (8 - n)
|
|
124
|
+
v = struct.unpack(">d", be)[0]
|
|
125
|
+
return v if _plausible(v) else None
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
def _decode_raw8(chunk: bytes) -> float | None:
|
|
129
|
+
if len(chunk) != 8:
|
|
130
|
+
return None
|
|
131
|
+
v = struct.unpack("<d", chunk)[0]
|
|
132
|
+
return v if _plausible(v) else None
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
def _real_tagged(b: bytes, p: int, end: int) -> tuple[float, int] | None:
|
|
136
|
+
"""``8T nn <nn bytes>``: payload reversed is the double's BE top-``nn``."""
|
|
137
|
+
if p + 2 > end:
|
|
138
|
+
return None
|
|
139
|
+
tag, nn = b[p], b[p + 1]
|
|
140
|
+
if not (0x81 <= tag <= 0x8F) or not (1 <= nn <= 8) or p + 2 + nn > end:
|
|
141
|
+
return None
|
|
142
|
+
v = _decode_compact(b[p + 2 : p + 2 + nn])
|
|
143
|
+
return (v, 2 + nn) if v is not None else None
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
def _real_rle(b: bytes, p: int, end: int) -> tuple[float, int] | None:
|
|
147
|
+
"""RLE-compressed 8-byte literal: ``c2`` = run of 5, ``c3`` = run of 6.
|
|
148
|
+
|
|
149
|
+
Lead form (marker at ``p+1``) and run-first form (marker at ``p``); the
|
|
150
|
+
byte after the repeated byte is a context/tag byte and is skipped; literal
|
|
151
|
+
suffix bytes complete the 8-byte LE double (see the module docstring).
|
|
152
|
+
"""
|
|
153
|
+
for lead_len in (1, 0):
|
|
154
|
+
mpos = p + lead_len
|
|
155
|
+
if mpos + 3 > end:
|
|
156
|
+
continue
|
|
157
|
+
marker = b[mpos]
|
|
158
|
+
if marker == 0xC2:
|
|
159
|
+
run = 5
|
|
160
|
+
elif marker == 0xC3:
|
|
161
|
+
run = 6
|
|
162
|
+
else:
|
|
163
|
+
continue
|
|
164
|
+
suffix_len = 8 - lead_len - run
|
|
165
|
+
tok_end = mpos + 3 + suffix_len
|
|
166
|
+
if suffix_len < 0 or tok_end > end:
|
|
167
|
+
continue
|
|
168
|
+
raw = b[p : p + lead_len] + bytes([b[mpos + 1]]) * run + b[mpos + 3 : tok_end]
|
|
169
|
+
v = struct.unpack("<d", raw)[0]
|
|
170
|
+
if _plausible(v):
|
|
171
|
+
return (v, tok_end - p)
|
|
172
|
+
return None
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
def _real_bare8(b: bytes, p: int, end: int) -> tuple[float, int] | None:
|
|
176
|
+
"""Bare 8-byte LE literal. A leading byte in the tag range ``0x81..0x8f``
|
|
177
|
+
that failed to decode as a tagged value marks a flag/control position, not
|
|
178
|
+
a literal — no corpus literal starts with such a byte."""
|
|
179
|
+
if p + 8 > end or 0x81 <= b[p] <= 0x8F:
|
|
180
|
+
return None
|
|
181
|
+
v = _decode_raw8(b[p : p + 8])
|
|
182
|
+
return (v, 8) if v is not None else None
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
def _real_tagged_trailer(b: bytes, p: int, end: int) -> tuple[float, int] | None:
|
|
186
|
+
"""``8T nn <nn-1 value bytes> <trailer>`` — a tagged compact whose payload
|
|
187
|
+
carries ONE trailing subfield byte (a small int, ``02``/``04`` observed).
|
|
188
|
+
|
|
189
|
+
Found 2026-07-05 on Hc2's ``80``-lead-separator records: the span's FINAL
|
|
190
|
+
(step) token gains one payload byte and the separator lead flips 81->80
|
|
191
|
+
in lockstep (e.g. ``83 03 14 40 02`` = step 5.0 + trailer 02 on Graph8,
|
|
192
|
+
vs ``83 02 14 40`` on the byte-identical Graph4). Only ever offered for
|
|
193
|
+
the last token of a span (see ``_real_fills``) so it cannot re-split
|
|
194
|
+
``from``/``to`` values, and the trailer is constrained to a small int.
|
|
195
|
+
"""
|
|
196
|
+
if p + 2 > end:
|
|
197
|
+
return None
|
|
198
|
+
tag, nn = b[p], b[p + 1]
|
|
199
|
+
if not (0x81 <= tag <= 0x8F) or not (2 <= nn <= 8) or p + 2 + nn > end:
|
|
200
|
+
return None
|
|
201
|
+
if not 0x01 <= b[p + 2 + nn - 1] <= 0x0F:
|
|
202
|
+
return None
|
|
203
|
+
v = _decode_compact(b[p + 2 : p + 2 + nn - 1])
|
|
204
|
+
return (v, 2 + nn) if v is not None else None
|
|
205
|
+
|
|
206
|
+
|
|
207
|
+
def _real_candidates(
|
|
208
|
+
b: bytes, p: int, end: int, bare: bool, last: bool = False
|
|
209
|
+
) -> list[tuple[float, int]]:
|
|
210
|
+
out: list[tuple[float, int]] = []
|
|
211
|
+
t = _real_tagged(b, p, end)
|
|
212
|
+
if t is not None:
|
|
213
|
+
out.append(t)
|
|
214
|
+
r = _real_rle(b, p, end)
|
|
215
|
+
if r is not None:
|
|
216
|
+
out.append(r)
|
|
217
|
+
if last:
|
|
218
|
+
tt = _real_tagged_trailer(b, p, end)
|
|
219
|
+
if tt is not None:
|
|
220
|
+
out.append(tt)
|
|
221
|
+
if bare:
|
|
222
|
+
w = _real_bare8(b, p, end)
|
|
223
|
+
if w is not None:
|
|
224
|
+
out.append(w)
|
|
225
|
+
if p < end and not (0x81 <= b[p] <= 0x8F):
|
|
226
|
+
for k in (1, 2, 3): # tag-less compact (seen right after a flag token)
|
|
227
|
+
if p + k <= end:
|
|
228
|
+
v = _decode_compact(b[p : p + k])
|
|
229
|
+
if v is not None:
|
|
230
|
+
out.append((v, k))
|
|
231
|
+
return out
|
|
232
|
+
|
|
233
|
+
|
|
234
|
+
def _real_fills(b: bytes, pos: int, end: int, n: int, bare: bool) -> list[tuple[float, ...]]:
|
|
235
|
+
"""All ways to place exactly ``n`` value tokens filling ``[pos, end)``.
|
|
236
|
+
|
|
237
|
+
The final token position (``n == 1``) additionally admits the
|
|
238
|
+
trailing-subfield tagged form (`_real_tagged_trailer`) — the step slot
|
|
239
|
+
only, so ``from``/``to`` decoding is never affected by it."""
|
|
240
|
+
if n == 0:
|
|
241
|
+
return [()] if pos == end else []
|
|
242
|
+
out: list[tuple[float, ...]] = []
|
|
243
|
+
for v, consumed in _real_candidates(b, pos, end, bare, last=n == 1):
|
|
244
|
+
for rest in _real_fills(b, pos + consumed, end, n - 1, bare):
|
|
245
|
+
out.append((v, *rest))
|
|
246
|
+
return out
|
|
247
|
+
|
|
248
|
+
|
|
249
|
+
def real_form_bare_pair(b: bytes, start: int, end: int) -> tuple[float, float] | None:
|
|
250
|
+
"""``(from, to)`` from two REAL-FORM value tokens exactly filling
|
|
251
|
+
``[start, end)`` — the "hybrid" axis record (§13.2 #13): a specimen-form
|
|
252
|
+
skeleton (``_Y_TRANSITION`` marker + ``7b 40`` X-scale filler) whose X/Y
|
|
253
|
+
span nevertheless stores real-corpus RLE/tagged value tokens, produced when
|
|
254
|
+
a plotted graph is later customized (``layer.plotN.*`` edits) instead of
|
|
255
|
+
ranged. An optional 1-2 byte leading flag (``0x81..0x8f``) precedes the
|
|
256
|
+
first value; try each skip and accept only a UNIQUE two-token exact fill
|
|
257
|
+
(fail-closed on ambiguity — never guessed). The tokens themselves are the
|
|
258
|
+
same oracle-validated ``_real_rle``/``_real_tagged`` used by the real form,
|
|
259
|
+
so a hit reproduces Origin's own range (verified: symbol_kinds.opju X span
|
|
260
|
+
``89 c3 33 03 eb 3f 9a c2 99 02 18 40`` -> (0.85, 6.15), matching COM)."""
|
|
261
|
+
accepted: set[tuple[float, float]] = set()
|
|
262
|
+
for skip in (0, 1, 2):
|
|
263
|
+
if start + skip > end:
|
|
264
|
+
continue
|
|
265
|
+
fills = _real_fills(b, start + skip, end, 2, bare=False)
|
|
266
|
+
pairs = {(f[0], f[1]) for f in fills}
|
|
267
|
+
if len(pairs) == 1:
|
|
268
|
+
accepted |= pairs
|
|
269
|
+
return accepted.pop() if len(accepted) == 1 else None
|
|
270
|
+
|
|
271
|
+
|
|
272
|
+
def _real_span_pair(
|
|
273
|
+
b: bytes, start: int, end: int, bare: bool
|
|
274
|
+
) -> tuple[float, float, float | None] | None:
|
|
275
|
+
"""``[from, to, step]`` (n=3) else ``[to, step]`` with from elided (n=2),
|
|
276
|
+
exact-fill; accepted only when the fill set at that arity is unique on
|
|
277
|
+
the ``(from, to)`` pair (the historical acceptance rule — §13.2 #8 added
|
|
278
|
+
the STEP as a third returned value: the tick increment, ``None`` when
|
|
279
|
+
the accepted fills disagree on it)."""
|
|
280
|
+
for n in (3, 2):
|
|
281
|
+
fills = _real_fills(b, start, end, n, bare)
|
|
282
|
+
pairs = {(f[0], f[1]) if n == 3 else (0.0, f[0]) for f in fills}
|
|
283
|
+
if len(pairs) == 1:
|
|
284
|
+
fr, to = pairs.pop()
|
|
285
|
+
steps = {f[2] if n == 3 else f[1] for f in fills}
|
|
286
|
+
return (fr, to, steps.pop() if len(steps) == 1 else None)
|
|
287
|
+
if len(pairs) > 1:
|
|
288
|
+
return None # ambiguous: drop, never guess
|
|
289
|
+
return None
|
|
290
|
+
|
|
291
|
+
|
|
292
|
+
def _real_x_flag_len(b: bytes, p: int, end: int) -> int:
|
|
293
|
+
"""Deterministic length of the optional X flag token (see module docstring):
|
|
294
|
+
0 when the record opens with a tagged value, 1 for a bare ``91`` before a
|
|
295
|
+
run-first RLE value, else 2 (every other observed flag is 2 bytes)."""
|
|
296
|
+
if _real_tagged(b, p, end) is not None:
|
|
297
|
+
return 0
|
|
298
|
+
if p + 1 < end and b[p] == 0x91 and b[p + 1] in (0xC2, 0xC3):
|
|
299
|
+
return 1
|
|
300
|
+
return 2
|
|
301
|
+
|
|
302
|
+
|
|
303
|
+
def _real_x_log_flag(b: bytes, y_lo: int, y_start: int) -> bool | None:
|
|
304
|
+
"""Exact X-axis lin/log flag: the geometry payload's trailing field,
|
|
305
|
+
read backward from the Y-span start over 0-2 ``00`` pad bytes --
|
|
306
|
+
``01`` = linear, ``08 01`` = log10 (the same byte values as the Y flag;
|
|
307
|
+
see the module docstring for the rf_*-diff derivation and the corpus
|
|
308
|
+
proof). Any other tail returns ``None`` (decade heuristic; e.g. the six
|
|
309
|
+
Hc2 records whose field reads ``02``) -- never guessed."""
|
|
310
|
+
q = y_start
|
|
311
|
+
while q > y_lo and b[q - 1] == 0x00:
|
|
312
|
+
q -= 1
|
|
313
|
+
if q - y_lo >= 2 and b[q - 2 : q] == b"\x08\x01":
|
|
314
|
+
return True
|
|
315
|
+
if q - y_lo >= 1 and b[q - 1] == 0x01 and (q - y_lo < 2 or b[q - 2] != 0x08):
|
|
316
|
+
return False
|
|
317
|
+
return None
|
|
318
|
+
|
|
319
|
+
|
|
320
|
+
def _real_y_log_flag(b: bytes, sep_start: int, window_end: int) -> bool | None:
|
|
321
|
+
"""Exact Y-axis lin/log flag for the real-corpus form.
|
|
322
|
+
|
|
323
|
+
Pinned from a 4-file by-construction oracle (``rf_linlin``/``rf_logx``/
|
|
324
|
+
``rf_logy``/``rf_loglog.opju`` -- the same single-curve graph with
|
|
325
|
+
identical custom ranges, differing only in ``layer.x.type``/
|
|
326
|
+
``layer.y.type``): the two bytes right before the fixed ``00 10 10 00``
|
|
327
|
+
layer-style marker that follows the end separator are ``01 00`` for a
|
|
328
|
+
linear Y axis and ``08 01`` for log10, regardless of X's own type or
|
|
329
|
+
encoding (which shifts the marker's absolute position but never its
|
|
330
|
+
value or the flag's relationship to it). Validated exact against all 14
|
|
331
|
+
real-corpus anchors (RockingCurve's 3 log-Y layers read ``08 01``; XAS
|
|
332
|
+
3 + UnpolPlots 4 + "Fixed Lambdas SI" 4, all linear-Y, read ``01 00``).
|
|
333
|
+
Any other value, or no marker found within the search span, returns
|
|
334
|
+
``None`` so the decade heuristic takes over -- never guessed.
|
|
335
|
+
"""
|
|
336
|
+
start = sep_start + 6 # past the separator's own `81 <id> <plen> 00 00 01`
|
|
337
|
+
mpos = b.find(_Y_STYLE_MARKER, start, min(window_end, start + _Y_FLAG_SEARCH_SPAN))
|
|
338
|
+
if mpos < start + 2:
|
|
339
|
+
return None
|
|
340
|
+
flag = b[mpos - 2 : mpos]
|
|
341
|
+
if flag == _Y_LIN_FLAG:
|
|
342
|
+
return False
|
|
343
|
+
if flag == _Y_LOG_FLAG:
|
|
344
|
+
return True
|
|
345
|
+
return None
|
|
346
|
+
|
|
347
|
+
|
|
348
|
+
def _parse_real_record(
|
|
349
|
+
b: bytes, p: int, window_end: int
|
|
350
|
+
) -> tuple[float, float, float, float, bool | None, bool | None, float | None, float | None] | None:
|
|
351
|
+
"""Real-corpus axis record at anchor payload ``p``:
|
|
352
|
+
``(xf, xt, yf, yt, x_log, y_log, x_step, y_step)`` -- the last two are
|
|
353
|
+
the tick increments (None when undecoded); ``x_log``/``y_log`` are the exact
|
|
354
|
+
flags from ``_real_x_log_flag``/``_real_y_log_flag`` when isolatable,
|
|
355
|
+
else ``None`` (caller falls back to the decade heuristic).
|
|
356
|
+
|
|
357
|
+
Tried with the strict ``81``-lead separator first (every record
|
|
358
|
+
validated through 2026-07-04 parses byte-identically), then retried
|
|
359
|
+
with the wide ``[80|81]`` lead that Hc2's Graph8/Graph12-family records
|
|
360
|
+
need -- a record only ever reaches the wide pass after failing the
|
|
361
|
+
strict one, so the retry can add parses but never change one."""
|
|
362
|
+
for sep_re in (_SEP_STRICT, _SEP_WIDE):
|
|
363
|
+
got = _parse_real_record_sep(b, p, window_end, sep_re)
|
|
364
|
+
if got is not None:
|
|
365
|
+
return got
|
|
366
|
+
return None
|
|
367
|
+
|
|
368
|
+
|
|
369
|
+
def _parse_real_record_sep(
|
|
370
|
+
b: bytes, p: int, window_end: int, sep_re: re.Pattern[bytes]
|
|
371
|
+
) -> tuple[float, float, float, float, bool | None, bool | None, float | None, float | None] | None:
|
|
372
|
+
"""One separator-form attempt of `_parse_real_record` (see above)."""
|
|
373
|
+
m1 = sep_re.search(b, p, min(window_end, p + _TAG_SEARCH_SPAN))
|
|
374
|
+
if m1 is None:
|
|
375
|
+
return None
|
|
376
|
+
x_start = p + _real_x_flag_len(b, p, m1.start())
|
|
377
|
+
if x_start >= m1.start():
|
|
378
|
+
return None
|
|
379
|
+
xpair = _real_span_pair(b, x_start, m1.start(), bare=True)
|
|
380
|
+
if xpair is None:
|
|
381
|
+
return None
|
|
382
|
+
plen = b[m1.start() + 2]
|
|
383
|
+
y_lo = m1.start() + 6
|
|
384
|
+
m2 = sep_re.search(b, y_lo + plen, min(window_end, y_lo + plen + _TAG_SEARCH_SPAN))
|
|
385
|
+
y_hi = m2.start() if m2 else min(window_end, y_lo + plen + 200)
|
|
386
|
+
y_scan_hi = min(y_lo + plen + _Y_START_SCAN, y_hi)
|
|
387
|
+
# plen is a hint only: Y can start inside or a few bytes past the nominal
|
|
388
|
+
# geometry payload, so scan for the first uniquely exact-filling start.
|
|
389
|
+
# Bare 8-byte literals are excluded from the first pass (a mis-aligned
|
|
390
|
+
# scan start could decode plausible junk) and retried last-resort ONLY
|
|
391
|
+
# where the exact X-scale flag authenticates the Y-span start (the
|
|
392
|
+
# Fixed Lambdas panel-layer form -- see the module docstring). In the
|
|
393
|
+
# retry a start pointing AT a ``00`` pad byte is not a candidate: the
|
|
394
|
+
# true span begins at the first non-``00`` after the flag, and letting a
|
|
395
|
+
# bare literal absorb the pads decodes a wrong-but-plausible value
|
|
396
|
+
# (measured: Fixed Lambdas Graph5 layer 2's y_from, -0.0488.. for -0.05).
|
|
397
|
+
for bare in (False, True):
|
|
398
|
+
for y_start in range(y_lo, y_scan_hi):
|
|
399
|
+
if bare and (
|
|
400
|
+
(y_start < y_hi and b[y_start] == 0x00)
|
|
401
|
+
or _real_x_log_flag(b, y_lo, y_start) is None
|
|
402
|
+
):
|
|
403
|
+
continue
|
|
404
|
+
ypair = _real_span_pair(b, y_start, y_hi, bare=bare)
|
|
405
|
+
if ypair is not None:
|
|
406
|
+
x_log = _real_x_log_flag(b, y_lo, y_start)
|
|
407
|
+
y_log = _real_y_log_flag(b, m2.start(), window_end) if m2 else None
|
|
408
|
+
return (
|
|
409
|
+
xpair[0], xpair[1], ypair[0], ypair[1], x_log, y_log,
|
|
410
|
+
xpair[2], ypair[2],
|
|
411
|
+
)
|
|
412
|
+
return None
|
|
413
|
+
|
|
414
|
+
|
|
415
|
+
# Specimen-form + hybrid record parsing now lives in
|
|
416
|
+
# ``opju_axis_specimen_form.py`` (2026-07-06, the 500-line guard); it imports
|
|
417
|
+
# the value-token readers above. ``_parse_real_record`` here remains the
|
|
418
|
+
# real-corpus-form entry point.
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
"""Specimen-form + hybrid Origin ``.opju`` axis-record parsing.
|
|
2
|
+
|
|
3
|
+
Split from ``opju_axis_real_form.py`` (2026-07-06, the 500-line god-module
|
|
4
|
+
guard). Same axis-record domain as the real form: this file owns the
|
|
5
|
+
DEFAULT-DIALOG ("specimen") record grammar and the LAST-RESORT hybrid parser,
|
|
6
|
+
while ``opju_axis_real_form`` owns the real-corpus token grammar and the
|
|
7
|
+
real-form record parser. The two share the low-level value-token readers
|
|
8
|
+
(imported below) so a decode stays byte-identical whichever entry point runs.
|
|
9
|
+
|
|
10
|
+
Three record forms, tried in this order by ``figures_opju``:
|
|
11
|
+
|
|
12
|
+
1. specimen form (``_parse_specimen_record``) — the default plot-dialog
|
|
13
|
+
layout: literal / tag-compact value tokens around a ``_Y_TRANSITION``
|
|
14
|
+
marker, with an exact ``7b 40``-family X-scale flag;
|
|
15
|
+
2. real-corpus form (``opju_axis_real_form._parse_real_record``) — RLE/tagged
|
|
16
|
+
tokens with ``81/80`` separators and geometry-tail scale flags;
|
|
17
|
+
3. hybrid (``parse_hybrid_record``) — a specimen skeleton whose spans hold
|
|
18
|
+
real-form value tokens (a plotted-then-``layer.plotN.*``-customized graph).
|
|
19
|
+
Gated behind BOTH 1 and 2 failing, so it can never change a record they
|
|
20
|
+
already decode; fail-closed on any ambiguous span (never a guessed range).
|
|
21
|
+
|
|
22
|
+
Pure leaf: bytes in → floats out. No fastapi/pydantic/routes imports.
|
|
23
|
+
"""
|
|
24
|
+
|
|
25
|
+
from __future__ import annotations
|
|
26
|
+
|
|
27
|
+
from quantized.io.origin_project.opju_axis_real_form import (
|
|
28
|
+
_STEP_TAG,
|
|
29
|
+
_TAG_SEARCH_SPAN,
|
|
30
|
+
_Y_TRANSITION,
|
|
31
|
+
_decode_compact,
|
|
32
|
+
_decode_raw8,
|
|
33
|
+
real_form_bare_pair,
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
__all__ = ["parse_hybrid_record"]
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def _value_candidates(b: bytes, pos: int, end: int) -> list[tuple[float, int]]:
|
|
40
|
+
"""Every plausible ``(value, bytes_consumed)`` parse starting at ``pos``.
|
|
41
|
+
|
|
42
|
+
The bare (no-tag) raw8 shape is rejected when ``pos`` itself starts with a
|
|
43
|
+
byte in the real-form flag-token range ``0x81..0x8f`` (mirroring
|
|
44
|
+
``_real_bare8``'s identical guard): a genuine specimen-form literal never
|
|
45
|
+
starts there, but a real-form flag token (e.g. ``89 01`` before an
|
|
46
|
+
RLE-compressed value) does, and would otherwise misdecode as a plausible-
|
|
47
|
+
looking bare double -- the false positive that made the rf_* oracle
|
|
48
|
+
quad's linear-X records (whose 8 leading bytes are flag+RLE, not a
|
|
49
|
+
literal) parse via the specimen path with a wrong ``x_from`` and a
|
|
50
|
+
type-byte reading that (unlike the true real-form flag) carries no Y
|
|
51
|
+
information at all."""
|
|
52
|
+
avail = end - pos
|
|
53
|
+
out: list[tuple[float, int]] = []
|
|
54
|
+
if avail >= 8 and not (pos < end and 0x81 <= b[pos] <= 0x8F):
|
|
55
|
+
v = _decode_raw8(b[pos : pos + 8])
|
|
56
|
+
if v is not None:
|
|
57
|
+
out.append((v, 8))
|
|
58
|
+
if avail >= 10:
|
|
59
|
+
v = _decode_raw8(b[pos + 2 : pos + 10])
|
|
60
|
+
if v is not None:
|
|
61
|
+
out.append((v, 10))
|
|
62
|
+
for k in (1, 2, 3):
|
|
63
|
+
if avail >= 2 + k:
|
|
64
|
+
v = _decode_compact(b[pos + 2 : pos + 2 + k])
|
|
65
|
+
if v is not None:
|
|
66
|
+
out.append((v, 2 + k))
|
|
67
|
+
return out
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def _parse_pair(b: bytes, pos: int, end: int) -> tuple[float, float] | None:
|
|
71
|
+
"""Decode ``(from, to)`` from the byte span ``[pos, end)``, or ``None``.
|
|
72
|
+
|
|
73
|
+
Every admissible split (``from`` elided, or ``from``+``to`` both present)
|
|
74
|
+
is tried; accepted only if exactly one split consumes the span exactly with
|
|
75
|
+
two plausible values.
|
|
76
|
+
"""
|
|
77
|
+
candidates: set[tuple[float, float]] = set()
|
|
78
|
+
for v, n in _value_candidates(b, pos, end): # from elided (== 0.0): one token = "to"
|
|
79
|
+
if pos + n == end:
|
|
80
|
+
candidates.add((0.0, v))
|
|
81
|
+
for vf, nf in _value_candidates(b, pos, end): # from present, then to
|
|
82
|
+
p2 = pos + nf
|
|
83
|
+
for vt, nt in _value_candidates(b, p2, end):
|
|
84
|
+
if p2 + nt == end:
|
|
85
|
+
candidates.add((vf, vt))
|
|
86
|
+
return candidates.pop() if len(candidates) == 1 else None
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
def _parse_specimen_record(
|
|
90
|
+
b: bytes, p: int
|
|
91
|
+
) -> tuple[float, float, float, float, int, bool | None] | None:
|
|
92
|
+
"""Specimen-form axis record at anchor payload ``p``:
|
|
93
|
+
``(xf, xt, yf, yt, type_byte, x_log)``.
|
|
94
|
+
|
|
95
|
+
``x_log`` is the exact X-scale flag inside the "filler" after the type
|
|
96
|
+
byte -- really ``7b 40`` + ``01`` (linear) / ``08 01`` (log10), the same
|
|
97
|
+
field the real form carries; ``None`` keeps the type-byte/heuristic path.
|
|
98
|
+
``y_start`` stays at the historical +3 skip: a log X's extra ``08`` byte
|
|
99
|
+
is absorbed by ``_parse_pair``'s 2-byte tag-skip candidate."""
|
|
100
|
+
ytrans = b.find(_Y_TRANSITION, p, min(len(b), p + _TAG_SEARCH_SPAN))
|
|
101
|
+
if ytrans < 0:
|
|
102
|
+
return None
|
|
103
|
+
xstep = b.rfind(_STEP_TAG, p, ytrans)
|
|
104
|
+
if xstep < 0:
|
|
105
|
+
return None
|
|
106
|
+
xpair = _parse_pair(b, p, xstep)
|
|
107
|
+
if xpair is None:
|
|
108
|
+
return None
|
|
109
|
+
if ytrans + len(_Y_TRANSITION) >= len(b): # marker at EOF — no type byte to read
|
|
110
|
+
return None
|
|
111
|
+
tb = ytrans + len(_Y_TRANSITION)
|
|
112
|
+
type_byte = b[tb]
|
|
113
|
+
x_log: bool | None = None
|
|
114
|
+
if b[tb + 1 : tb + 4] == b"\x7b\x40\x01":
|
|
115
|
+
x_log = False
|
|
116
|
+
elif b[tb + 1 : tb + 5] == b"\x7b\x40\x08\x01":
|
|
117
|
+
x_log = True
|
|
118
|
+
y_start = tb + 1 + 3 # + type byte + "7b 40 ..." filler (see docstring)
|
|
119
|
+
ystep = b.find(_STEP_TAG, y_start, min(len(b), y_start + _TAG_SEARCH_SPAN))
|
|
120
|
+
if ystep < 0:
|
|
121
|
+
return None
|
|
122
|
+
ypair = _parse_pair(b, y_start, ystep)
|
|
123
|
+
if ypair is None:
|
|
124
|
+
return None
|
|
125
|
+
return (*xpair, *ypair, type_byte, x_log)
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
def parse_hybrid_record(
|
|
129
|
+
b: bytes, p: int
|
|
130
|
+
) -> tuple[float, float, float, float, int, bool | None] | None:
|
|
131
|
+
"""LAST-RESORT axis record: ``(xf, xt, yf, yt, type_byte, x_log)``.
|
|
132
|
+
|
|
133
|
+
Fires ONLY when neither the specimen form nor the real form parsed (§13.2
|
|
134
|
+
#13). Shape: a specimen-form skeleton (``_Y_TRANSITION`` + ``_STEP_TAG``
|
|
135
|
+
markers, ``7b 40``-family X-scale filler) whose X and Y spans hold
|
|
136
|
+
real-corpus RLE/tagged value tokens instead of specimen-form literals --
|
|
137
|
+
what a plotted-then-customized (``layer.plotN.*``) graph writes. Reuses the
|
|
138
|
+
specimen skeleton for span boundaries and the type byte / ``7b 40`` filler
|
|
139
|
+
for the scale flag, but decodes each span with the oracle-validated
|
|
140
|
+
real-form token machinery (``real_form_bare_pair``). Because it is gated
|
|
141
|
+
behind BOTH other parsers failing, it can never change a record they
|
|
142
|
+
already decode. Fail-closed: any span that doesn't yield a unique pair
|
|
143
|
+
returns ``None`` (never a guessed range)."""
|
|
144
|
+
ytrans = b.find(_Y_TRANSITION, p, min(len(b), p + _TAG_SEARCH_SPAN))
|
|
145
|
+
if ytrans < 0 or ytrans + len(_Y_TRANSITION) >= len(b):
|
|
146
|
+
return None
|
|
147
|
+
xstep = b.rfind(_STEP_TAG, p, ytrans)
|
|
148
|
+
if xstep < 0:
|
|
149
|
+
return None
|
|
150
|
+
xpair = real_form_bare_pair(b, p, xstep)
|
|
151
|
+
if xpair is None:
|
|
152
|
+
return None
|
|
153
|
+
tb = ytrans + len(_Y_TRANSITION)
|
|
154
|
+
type_byte = b[tb]
|
|
155
|
+
x_log: bool | None = None
|
|
156
|
+
if b[tb + 1 : tb + 4] == b"\x7b\x40\x01":
|
|
157
|
+
x_log = False
|
|
158
|
+
elif b[tb + 1 : tb + 5] == b"\x7b\x40\x08\x01":
|
|
159
|
+
x_log = True
|
|
160
|
+
y_start = tb + 1 + 3
|
|
161
|
+
ystep = b.find(_STEP_TAG, y_start, min(len(b), y_start + _TAG_SEARCH_SPAN))
|
|
162
|
+
if ystep < 0:
|
|
163
|
+
return None
|
|
164
|
+
ypair = real_form_bare_pair(b, y_start, ystep)
|
|
165
|
+
if ypair is None:
|
|
166
|
+
return None
|
|
167
|
+
return (*xpair, *ypair, type_byte, x_log)
|