diff --git a/.claude/board/EPIPHANIES.md b/.claude/board/EPIPHANIES.md index e26ee347..d079e9d5 100644 --- a/.claude/board/EPIPHANIES.md +++ b/.claude/board/EPIPHANIES.md @@ -1,3 +1,52 @@ +## 2026-08-11 — E-THE-REUSE-IS-THE-PROCESS-AND-IT-EXPOSED-A-FIT-PROBLEM-1 + +**Status:** FINDING `[G]` on the measurement (`crates/helix/tests/bearing_encode_paths.rs`, +N=65536); `[S]` on the API shape, which is an operator call. + +**Operator correction:** *"the reuse of the above for the wind was always part of +the process (what you call invention)."* A prior entry +(`E-THE-DOCTRINE-DOC-EXISTED-AND-I-NEVER-READ-IT-1`) filed the missing +bearing-encode under *invention* and declined to build it. **That was an +over-correction** — generalizing "don't invent structure the code already +determines" into "don't implement the intended reuse." + +**The line, drawn properly:** INVENTION = asserting structure the code already +answers (a bit budget, a lane reading, a `Pair48`, a round-trip API the crate +disclaims). REUSE = applying the shipped, proven codec to a new domain — which +is what a normalized substrate is *for*. **A missing entry point for a designed +reuse is a plumbing gap, not a design refusal.** + +**And the reuse immediately earned its keep.** `encode_signed` derives all three +direction-bearing fields from `n` alone (`residue.rs:182-204`). The knowledge doc +`helix-cartesian-vs-fisher2z.md` prescribes *nearest spherical-Fibonacci +`(n, sign)`* for encoding a direction. **Measured, that prescription does not fit +weather:** + +| horizontal bearing | nearest-`n` (prescribed) | direct `(polar, azimuth)` | +|---|---|---| +| 0° / 90° / 270° | 1.933° / 2.706° / 1.897° | **0.000°** each | +| mean, 24 bearing×elevation cases | **0.972°** | **0.097°** — 10× | + +**Mechanism:** the golden spiral couples latitude and azimuth through ONE index. +Reaching `y ≈ 0` needs `n ≈ N−1`, and those few `n` have azimuth already fixed at +`n·φ` — **a bearing at the horizon cannot be chosen independently.** The lattice +is equal-area on the **disk**, so its latitude density is ∝ `sin(2·lat)`: +sparsest exactly at the equator. Surface normals (the case the doc was written +for) spread over the sphere and never hit this; wind clusters at the horizon and +always does. + +**Rule extracted:** *a doctrine written for one domain is not automatically +right for the next one that reuses it.* The doc's prescription is correct for +normals and should be labelled with its case rather than read as universal. The +direct write is licensed by the doc's own split — rim = metric carrier, +`(polar, azimuth)` = direction, *"direction is place-INDEPENDENT"* — at the +stated cost that the two halves no longer share one `n`. + +**Corrects:** `E-THE-DOCTRINE-DOC-EXISTED-AND-I-NEVER-READ-IT-1` (its "the real +gap… deliberately not built" framing). What that entry got right and keeps: the +`Signed360`-is-complete finding, the `Pair48` withdrawal, and the mandatory +`READ BY:` rule. + ## 2026-08-11 — E-THE-DOCTRINE-DOC-EXISTED-AND-I-NEVER-READ-IT-1 **Status:** FINDING `[G]` (operator-directed; authority is diff --git a/.claude/board/LATEST_STATE.md b/.claude/board/LATEST_STATE.md index f213c699..de58fe9b 100644 --- a/.claude/board/LATEST_STATE.md +++ b/.claude/board/LATEST_STATE.md @@ -1,3 +1,17 @@ +## 2026-08-11 — lance-graph #920 (MERGED) — the probes ran; the doc that queued them was falsified in four places + +> **⊘ This entry CORRECTS two claims in the #917 entry below it.** (1) **"helix360" is not a symbol** — the type is **`Signed360`**; pickaxe over full history returns 12 blobs, all authored by that session, zero deletions `[G-absence]`. (2) **The "2 × 24-bit hemispheres = wind in/out" reading of the `HelixResidue` lane is WRONG.** Per `.claude/knowledge/helix-cartesian-vs-fisher2z.md` — the doctrine doc with a `READ BY:` header naming exactly that kind of session, which the arc never opened — **one 6-byte `Signed360` is a complete full-sphere direction**: the `polar` sign-partition completes the sphere, so there is no second hemisphere to pair. `ResidueEdge`/`rim` is the **METRIC** carrier (`DistanceLut`, L1); `(polar, azimuth)` is the **render** carrier. The `Pair48` mint is **WITHDRAWN, not deferred**. Nothing below is deleted; it is read through §12.12 of the weather knowledge doc. + +### Current Contract Inventory — no new contract types (probes, one example, three tests, one doc section) + +- **`probes/weather-p1/`** — 8 re-runnable Python probes over real ARCO-ERA5 + README + 4 result JSONs. The fixture is **fetched, not committed** (and was cleared by a container reset; results re-verified for internal consistency without it). `p1_noise_floor.py` is retained **superseded, for provenance** — its `dev` is a decoded reconstruction error, the metric §12.10 rules out. +- **`crates/jc/examples/weather_substrate_reliability.rs`** — Pearson / Spearman / Cronbach α / ICC over the probe export, header-validated, with a `--shuffle` negative control. +- **`crates/helix/tests/signed360_claims.rs`** — 3 tests, each **disable-verified red-then-green**: azimuth spans the full circle (min 0 / max 65535 / 256-of-256 arcs), the polar partitions fill `[128,255]` and `[0,127]` exactly, and — pinning a **defect, not a virtue** — an all-zero lane decodes as a definite `Sign::Neg`. **Scope:** `helix` is excluded from the root workspace and named in no CI workflow `[G-absence]`, so these run **only by hand**. +- **The evaluation frame, locked:** compare each point's **bucket confidence interval** against the noise floor — **never a round-trip / decoded reconstruction error**. A one-way address over a retained original has no round-trip to score. +- **Measured `[H]`** (real ERA5, ONE timestep): Fisher-Z on weather anomalies is an **address-economy** failure, not a validity failure, at a 0.5–1 K floor (0.848 % / 0.820 % saturated, the two paths otherwise indistinguishable); at a **0.25 K** floor it becomes a validity failure too (+95.65 % interior-CI exceedance). **Standardization, not Fisher-Z, is what licenses cross-variable comparison** — 0.9997 on a shared palette vs 0.857–0.875 raw cross-unit. +- **Angular error of `Signed360` by latitude (measured this session, N=65536):** equator (|lat| 0–5°) **0.112° mean / 0.226° max**; pole (85–90°) **3.332° / 4.998°** — a **~30× spread**, best at the rim. **No resolution gain from the sign split at equal budget**: 7-bit `|y|` + sign vs 8-bit over `[-1,1]` measures **0.99–1.02× in every band** (step 1/127 vs 2/255 = 0.996×). The 256-sample *codebook* figures that suggest a √2 gain (full-sphere cap 7.17° vs hemisphere 5.07°) spend an **extra** bit; at equal budget 128-on-hemisphere = 7.17°, identical. **What the partition buys at the rim is sign EXACTNESS, not precision** — and a wind bearing is near-horizontal (`y ≈ 0`), i.e. exactly where a centred-at-128 round loses the hemisphere (#498). Caveat: the encoder's n-lattice is equal-area on the **disk**, so latitude density ∝ `sin(2·lat)` — peaked at 45°, thinning at both equator and pole; harmless for decode coverage (the `(polar, azimuth)` product grid is dense), but it is why there is still no arbitrary-bearing → code path (`from_normal`). +- **Still open, all operator calls:** the saturation-window widening; pinning a citable per-variable noise floor; the U-shaped-variable falsifier (`total_cloud_cover` / `sea_ice_cover`) that would promote the distribution-shape rule from `[S]`. **Filed, not fixed:** the `Signed360::sign()` dormant-lane defect. + ## 2026-08-11 — lance-graph #917 (MERGED) — the normalized-substrate reference: palette256 × helix360 ### Current Contract Inventory — no new types (docs-only; the primitives were already shipped) diff --git a/.claude/board/PR_ARC_INVENTORY.md b/.claude/board/PR_ARC_INVENTORY.md index cfde62b5..b779df09 100644 --- a/.claude/board/PR_ARC_INVENTORY.md +++ b/.claude/board/PR_ARC_INVENTORY.md @@ -1,3 +1,13 @@ +## 2026-08-11 — lance-graph #920 (MERGED) — the probes RAN, and they falsified the doc that queued them: evaluation frame, Fisher-Z economics, and the helix regrade + +- **Added.** `probes/weather-p1/` — 8 re-runnable Python probes over real ARCO-ERA5 (`fetch.py`/`fetch_bg.py` chunk retrieval with Zarr-v2 fill semantics, `p1_probe.py`, `p1_ci_vs_floor.py`, `p2_probe.py`, `verify_apparatus.py`, `export_for_jc.py`, `p1_noise_floor.py` [superseded, kept for provenance]) + README + 4 result JSONs + `jc_input.bin`. `crates/jc/examples/weather_substrate_reliability.rs` — Pearson / Spearman / Cronbach α / ICC over the probe export, with header validation and a `--shuffle` negative control. `crates/helix/tests/signed360_claims.rs` — 3 tests, each disable-verified red-then-green. `.claude/knowledge/weather-normalized-substrate.md` §12 (12 correction subsections, ~500 lines). +- **Locked.** **The evaluation frame: compare each point's BUCKET CONFIDENCE INTERVAL against the noise floor — never a round-trip / decoded reconstruction error** (operator correction; a one-way address over a retained original has no round-trip to score). **Standardization, not Fisher-Z, is what licenses cross-variable comparison** — P2 measured 0.9997 on a shared palette vs 0.857–0.875 on raw cross-unit pairs. **Fisher-Z on weather anomalies is an ADDRESS-ECONOMY failure, not a validity failure**, at a 0.5–1 K floor (0.848 % / 0.820 % saturated, both paths indistinguishable); at a 0.25 K floor it becomes a validity failure too (+95.65 % interior-CI exceedance) — every floor-dependent statement stays conditional until a per-variable floor carries a citation. **One `Signed360` is a complete full-sphere direction**; `ResidueEdge`/rim = metric carrier, `(polar, azimuth)` = render carrier. **A `READ BY:` knowledge doc matching the session's own subject is a MANDATORY read, not a suggestion** — `CLAUDE.md` § *Consult, don't guess* already ordered card → knowledge doc → board → source; this arc did source-first and stayed wrong through four corrections. +- **Deferred / WITHDRAWN.** **`FacetSchema::Pair48` mint — WITHDRAWN, not deferred** (premise false; see the #917 correction). `from_normal` — the genuine gap, a nearest spherical-Fibonacci `(n, sign)` search the crate deliberately does not ship; **not built**, and deliberately not invented inline. Saturation-window widening and pinning a citable per-variable noise floor — **both operator calls, open**. The U-shaped-variable falsifier (`total_cloud_cover` / `sea_ice_cover`) that would promote the distribution-shape rule from `[S]`. The `Signed360::sign()` dormant-lane defect — an all-zero lane decodes as a definite `Sign::Neg`; now **demonstrated and pinned by test**, filed, NOT fixed. +- **Docs.** Knowledge-doc §12 incl. §12.12 (the helix regrade) + §12.12a (the after-the-fact test evidence). EPIPHANIES: `E-THE-TRANSFORM-MUST-MATCH-THE-DISTRIBUTION-SHAPE-1`, `E-A-CORRECTION-IS-A-CLAIM-AND-CARRIES-A-CLAIM-S-BURDEN-1`, `E-THE-DOCTRINE-DOC-EXISTED-AND-I-NEVER-READ-IT-1`. + +**Confidence (2026-08-11):** merged. Measured numbers are `[H]` — real ERA5, ONE timestep, one variable for P1; the fixture is fetched, not committed, and was cleared by a container reset (results re-verified for internal consistency without it). The three helix tests are `[G]` but run **only by hand**: `helix` is excluded from the root workspace and named in no CI workflow `[G-absence]`. +**Process note — three self-corrections in one arc, each caught by a different gate.** (1) A `cargo fmt --manifest-path` swept 25 `crates/jc` files this PR does not own; caught and reverted in `6233b696`, net diff verified back to 17 files. (2) §12.10 announced the correct evaluation frame in prose and shipped the old one in code — an external reviewer caught what two internal passes did not; **naming a frame is not adopting it, the implementation is the claim**. (3) §12.12 was landed from doc comments with **nothing executed**, one turn after writing *"reading the primary source is not the same as reading the doctrine"* — corrected by running the suite (77 + 4 + 7 green), auditing claim-by-claim, and finding that 2 of 4 had no test behind them. + # PR Arc — Architectural Decision History > **Auto-loaded at session start.** Every merged PR, its meta, and @@ -40,7 +50,8 @@ - **Deferred.** All six probes (P1–P6) NOT RUN; the in/out pair-writer (`[G-absence]`, P4); the weather wiring itself (no weather code exists in-tree — the POC plan is the vehicle); exact metric definitions behind three §6 rows (mean-abs error, obs-error reference) to be pinned when P1 commits them as examples. - **Docs.** The knowledge doc; EPIPHANIES `E-HELIX360-IS-THE-NORMALIZED-SUBSTRATE-NOT-A-BIT-BUDGET-1`; plan §0 ⊘ C3 (Zarr object name). -**Confidence (2026-08-11):** merged. Code claims verified against the tree this session with `file:line`; the weather mapping is design-`[H]` composing only shipped primitives; the floor policy is `[S]` and must not be built on before P2. +**Confidence (2026-08-11, updated post-#920):** merged, and **two Locked claims are now FALSIFIED by #920** — read that entry first. The floor policy remains `[S]`. +**Correction (2026-08-11 from PR #920):** the Locked line *"helix360 is … the shipped 6-byte `HelixResidue` lane read as 2 × 24-bit hemispheres = wind in/out"* is **wrong on both halves.** (a) **"helix360" is not a symbol** — pickaxe over full history returns 12 blobs, all authored by that session, zero deletions `[G-absence]`; the type is **`Signed360`**. (b) **The 2×24 in/out reading is not a sanctioned reading of the lane.** Per `.claude/knowledge/helix-cartesian-vs-fisher2z.md` (the doctrine doc that arc never opened), one 6-byte `Signed360` is a **complete full-sphere direction** — the `polar` sign-partition completes the sphere, so there is no second hemisphere to pair. `ResidueEdge`/`rim` is the **METRIC** carrier (`DistanceLut`, L1), `(polar, azimuth)` the **render** carrier; the in/out framing conflated the two. The `Pair48` successor shape this entry's Deferred list implied is **WITHDRAWN, not deferred**. The `[G-absence]` "no pair-writer exists" observation stands as a fact and is simply no longer a gap. What ALSO survives unchanged: the `Signed360` polar sign-partition being load-bearing (codex P2 #498) — #920 added the sweep test that proves the partitions fill `[128,255]`/`[0,127]` exactly. **Provenance note — the corrections ARE part of the record.** This doc exists because five operator-caught errors in one arc shared a species: *asserting from API surface or first principles what the code already answered*. (1) A 48-bit budget invented while `Signed360` existed. (2) `Pair48`/12 B proposed for a lane already 2× by construction. (3) "unwired 2z seam" where not-materializing is the point — first mis-described as "an absorbed rescale, not a defect", i.e. smoothed rather than named; both readings wrong. (4) "scalar similarity" where the value is the normalization. (5) Measurement-apparatus inversions (the `rint`-snapped bearing artifact; "wind_speed is derivable"). Meta-rule now recorded in §11: **consult-don't-guess applies to one's own prior in-session statements** — a confident earlier message carries the same evidentiary weight as a stale doc (none) until checked against the tree. ## 2026-08-09 — branch `claude/phase-a-owned-writer` (PR pending) — Phase A: the artifact-backed commit contract + the SOLE owned writer diff --git a/.claude/knowledge/weather-normalized-substrate.md b/.claude/knowledge/weather-normalized-substrate.md index 5d8685d7..059ed604 100644 --- a/.claude/knowledge/weather-normalized-substrate.md +++ b/.claude/knowledge/weather-normalized-substrate.md @@ -1084,3 +1084,57 @@ workspace** (root `Cargo.toml` `exclude`) and named in **no CI workflow** `[G-absence]` — so these 3 tests, and the crate's existing 77, run **only when invoked by hand** in that crate. Adding them raises the floor for the next session that looks; it does not put them on a gate. + +#### 12.13 — the WIND reuse is the process, not an invention — and it surfaces a fit problem the normal-encode case never had + +**Operator correction (2026-08-11):** *"the reuse of the above for the wind was +always part of the process (what you call invention)."* Accepted, and the line +§12.12 drew was wrong in **both** directions: + +| | | +|---|---| +| **Invention** (what the arc rightly punished) | Asserting structure the code already answers — the 48-bit budget, the 2×24 in/out reading, `Pair48`, a round-trip API `continuous_field.rs` explicitly disclaims. | +| **Reuse** (the process) | Applying the **shipped, proven** codec to a new domain. This is what a normalized substrate is *for* — *"you only pay the inbound tax once."* | + +§12.12 filed `from_normal` under *invention* and declined to build it. That was +over-correction: generalizing "don't invent structure the code already +determines" into "don't implement the intended reuse." **A missing entry point +for a designed reuse is a plumbing gap, not a design refusal** — the doctrine +doc even names the algorithm and points at a worked reference pair (q2 +`scratch-fma/helixbake` + `cockpit/src/BodyHelix.tsx`). + +**What the reuse then surfaced (the actually-new finding) `[G]`.** +`encode_signed` derives **all three** direction-bearing fields from `n` alone +(`residue.rs:182-204`): `rim` from `(place, n)`, `polar` from +`signed_lift(n, …)`, `azimuth` from `n·φ`. Two candidate bearing encodes exist, +and **the doctrine's prescribed one does not fit weather** — measured, N=65536 +(`crates/helix/tests/bearing_encode_paths.rs`): + +| horizontal bearing | Path A — nearest `(n, sign)` (the doc's prescription) | Path B — direct `(polar, azimuth)` write | +|---|---|---| +| 0° | 1.933° | **0.000°** | +| 90° | 2.706° | **0.000°** | +| 270° | 1.897° | **0.000°** | +| **mean, 24 (bearing × elevation) cases** | **0.972°** | **0.097°** (**10×**) | + +**Mechanism:** the golden spiral couples latitude and azimuth through ONE index. +Reaching `y ≈ 0` (horizontal) needs `n ≈ N−1`, and those few `n` have their +azimuth *already fixed* at `n·φ` — **you cannot independently choose a bearing at +the horizon.** Compounding it, the lattice's latitude density is ∝ `sin(2·lat)` +(equal-area on the **disk**, not the sphere), i.e. **sparsest exactly at the +equator.** Surface normals — the case the doc was written for — spread over the +whole sphere and never hit this. Wind bearings cluster at the horizon and always +do. + +Path B is near-exact there (`y = 0 → polar = 128` exactly; 16-bit azimuth = +0.0055° over 360°) and is licensed by the doctrine's **own** split: the `rim` +keeps carrying `(place, n)` as the metric, `(polar, azimuth)` carry the bearing, +and *"direction is place-INDEPENDENT."* The cost, stated: `(polar, azimuth)` are +no longer functions of the same `n` as `rim` — which under that split is the +intent, not a violation. + +**Consequence:** a wind bearing encode should be the **direct field write**, not +the doc's `from_normal`. The doc's prescription is correct for its own case +(normals) and should be labelled as such rather than read as universal. **`[S]` +until an operator decides the API shape** — this section records the measurement +and the tradeoff, and deliberately does not mint a public `from_bearing`. diff --git a/crates/helix/tests/bearing_encode_paths.rs b/crates/helix/tests/bearing_encode_paths.rs new file mode 100644 index 00000000..070db09f --- /dev/null +++ b/crates/helix/tests/bearing_encode_paths.rs @@ -0,0 +1,86 @@ +//! MEASUREMENT: the two candidate `bearing -> Signed360` encodes, for the WIND +//! reuse of this codec. Records which one fits and by how much. +//! +//! `encode_signed` derives ALL THREE direction-bearing fields from `n` alone +//! (`residue.rs:182-204`) — there is no bearing entry point. The knowledge doc +//! `helix-cartesian-vs-fisher2z.md` prescribes `nearest spherical-Fibonacci +//! (n, sign)` for encoding a 3D normal. **That prescription does not fit wind.** +//! +//! The golden spiral couples latitude and azimuth through ONE index: to reach +//! `y ~ 0` (horizontal) you need `n ~ N-1`, and those few `n` have their azimuth +//! already fixed at `n*phi`. You cannot independently choose a bearing at the +//! horizon — and the lattice's latitude density is `~ sin(2*lat)`, sparsest +//! exactly there. Normals (q2 `/helix`, FMA body mesh) spread over the whole +//! sphere and never hit this; wind bearings cluster at the horizon and always do. +//! +//! Path B writes `(polar, azimuth)` directly and leaves `rim` carrying +//! `(place, n)` — licensed by the doctrine's own split: *"the rim is the METRIC +//! carrier, not a render input"* and *"direction is place-INDEPENDENT; only the +//! metric is place-coupled"*. It does mean `(polar, azimuth)` are no longer +//! functions of the same `n` as `rim`; per that split, that is the intent. +//! +//! Measured (N=65536): horizontal bearings — Path A 1.9-2.7 deg, Path B ~0.000 deg. +//! Over 24 (bearing x elevation) cases — Path A 0.972 deg, Path B 0.097 deg, 10x. +use helix::placement::{HemispherePoint, Sign}; +use helix::residue::{ResidueEncoder, Signed360}; + +fn dir(bearing_deg: f64, elev_deg: f64) -> (f64,f64,f64) { + let (b,e) = (bearing_deg.to_radians(), elev_deg.to_radians()); + (e.cos()*b.sin(), e.cos()*b.cos(), e.sin()) // (x=east, z=north, y=up) +} +fn ang(a:(f64,f64,f64), b:(f64,f64,f64)) -> f64 { + (a.0*b.0+a.1*b.1+a.2*b.2).clamp(-1.0,1.0).acos().to_degrees() +} +fn decode(s: &Signed360) -> (f64,f64,f64) { + let y = if s.polar>=128 {(s.polar as f64-128.0)/127.0} else {-((127.0-s.polar as f64)/127.0)}; + let r = (1.0-y*y).max(0.0).sqrt(); + let a = s.azimuth as f64/65536.0*std::f64::consts::TAU; + (r*a.sin(), r*a.cos(), y) +} + +#[test] +fn measure() { + const N: usize = 65_536; + let enc = ResidueEncoder::new(N); + + // Path A (doctrine): nearest spherical-Fibonacci (n, sign), then encode_signed. + let nearest = |target:(f64,f64,f64)| -> (usize, Sign) { + let mut best = (f64::MAX, 0usize, Sign::Pos); + for n in 0..N { + for sg in [Sign::Pos, Sign::Neg] { + let p = HemispherePoint::signed_lift(n, N, sg); + let v = (p.r*p.azimuth.sin(), p.r*p.azimuth.cos(), p.y); + let e = ang(target, v); + if e < best.0 { best = (e, n, sg); } + } + } + (best.1, best.2) + }; + // Path B: write (polar, azimuth) directly from the bearing; rim still from (place, n). + let direct = |target:(f64,f64,f64), rim_n: usize| -> Signed360 { + let y = target.2; + let mag = (y.abs()*127.0).round().clamp(0.0,127.0) as u8; + let polar = if y >= 0.0 { 128+mag } else { 127-mag }; + let az = target.0.atan2(target.1).rem_euclid(std::f64::consts::TAU); + let azimuth = ((az/std::f64::consts::TAU)*65536.0).round() as u64 as u16; + let mut s = enc.encode_signed(0x1234, rim_n, if y>=0.0 {Sign::Pos} else {Sign::Neg}); + s.polar = polar; s.azimuth = azimuth; s + }; + + println!("\nwind bearings (elevation 0 = horizontal), N={N}"); + println!(" bearing elev PathA nearest-n PathB direct-field"); + let (mut sa, mut sb) = (0.0f64, 0.0f64); + let mut c = 0; + for bear in [0.0, 22.5, 45.0, 90.0, 137.5, 180.0, 270.0, 315.0] { + for elev in [0.0, 5.0, 30.0] { + let t = dir(bear, elev); + let (n, sg) = nearest(t); + let ea = ang(t, decode(&enc.encode_signed(0x1234, n, sg))); + let eb = ang(t, decode(&direct(t, n))); + if elev == 0.0 { println!(" {bear:>6.1}° {elev:>4.0}° {ea:>8.4}° {eb:>8.4}°"); } + sa += ea; sb += eb; c += 1; + } + } + println!("\n MEAN over {c} (bearing x elevation) cases: PathA {:.4}° PathB {:.4}° ratio {:.2}x", + sa/c as f64, sb/c as f64, (sa/c as f64)/(sb/c as f64)); +}