From b5d194f678df3594db1e9ff7d239ce62edb46ccf Mon Sep 17 00:00:00 2001 From: Jan Petykiewicz Date: Sat, 18 Apr 2026 07:06:30 -0700 Subject: [PATCH] Rehost selected-year bucket companion bands --- README.md | 5 +- .../rt3-1.06/selected-year-bucket-ladder.json | 20 ++ crates/rrt-runtime/src/import.rs | 6 + crates/rrt-runtime/src/runtime.rs | 171 ++++++++++++++++-- crates/rrt-runtime/src/summary.rs | 84 +++++++++ docs/README.md | 4 + docs/rehost-queue.md | 13 +- docs/runtime-rehost-plan.md | 5 +- .../py/extract_selected_year_bucket_ladder.py | 38 ++++ 9 files changed, 326 insertions(+), 20 deletions(-) diff --git a/README.md b/README.md index 8274753..98f89ee 100644 --- a/README.md +++ b/README.md @@ -129,7 +129,10 @@ living only as ad hoc world flags. The selected-year seam is now doing the same thing: the checked-in `0x00433bd0` year ladder now drives a derived selected-year bucket scalar in runtime restore state, and the economic-tuning mirror `[world+0x0bde]` now rebuilds from tuning lane `0` instead of freezing one stale load-time -word. +word. That same checked-in owner family now also rebuilds the direct bucket trio +`[world+0x65/+0x69/+0x6d]`, the complement trio `[world+0x71/+0x75/+0x79]`, and the scaled +companion trio `[world+0x7d/+0x81/+0x85]` from the selected-year bucket scalar instead of +preserving stale save-time residue. Those bankruptcy branches now follow the grounded owner semantics too: they stamp the bankruptcy year and halve live bond principals in place instead of treating bankruptcy as a liquidation path. The same save-native live bond-slot surface now also carries per-slot maturity years all the way diff --git a/artifacts/exports/rt3-1.06/selected-year-bucket-ladder.json b/artifacts/exports/rt3-1.06/selected-year-bucket-ladder.json index fe02742..f098ea1 100644 --- a/artifacts/exports/rt3-1.06/selected-year-bucket-ladder.json +++ b/artifacts/exports/rt3-1.06/selected-year-bucket-ladder.json @@ -5,6 +5,26 @@ "pair_count": 21, "terminal_scalar_virtual_address": "0x005f3a24", "terminal_scalar_value": 123.0, + "direct_lane_multipliers": [ + 0.9, + 1.05, + 0.7 + ], + "complement_formula": { + "divisor": 50.0, + "multiplier": 0.65, + "bias": 0.35, + "scale": 0.001190483570098877, + "floor": 0.0001, + "build_106_multiplier": 1.15, + "cap": 0.15 + }, + "scaled_companion_formula": { + "numerator": 50.0, + "multiplier": 0.7, + "bias": 0.3, + "scale": 75.0 + }, "entries": [ { "year": 1800, diff --git a/crates/rrt-runtime/src/import.rs b/crates/rrt-runtime/src/import.rs index 8f32304..1570a82 100644 --- a/crates/rrt-runtime/src/import.rs +++ b/crates/rrt-runtime/src/import.rs @@ -982,6 +982,12 @@ fn project_save_slice_components( .cached_available_locomotive_rating_value_f32_text .clone() }), + selected_year_bucket_direct_lane_raw_u32: Vec::new(), + selected_year_bucket_direct_lane_value_f32_text: Vec::new(), + selected_year_bucket_complement_lane_raw_u32: Vec::new(), + selected_year_bucket_complement_lane_value_f32_text: Vec::new(), + selected_year_bucket_scaled_companion_lane_raw_u32: Vec::new(), + selected_year_bucket_scaled_companion_lane_value_f32_text: Vec::new(), selected_year_bucket_scalar_raw_u32: None, selected_year_bucket_scalar_value_f32_text: None, selected_year_gap_scalar_raw_u32: None, diff --git a/crates/rrt-runtime/src/runtime.rs b/crates/rrt-runtime/src/runtime.rs index a12f076..97499e8 100644 --- a/crates/rrt-runtime/src/runtime.rs +++ b/crates/rrt-runtime/src/runtime.rs @@ -1423,6 +1423,18 @@ pub struct RuntimeWorldRestoreState { #[serde(default)] pub selected_year_bucket_scalar_value_f32_text: Option, #[serde(default)] + pub selected_year_bucket_direct_lane_raw_u32: Vec, + #[serde(default)] + pub selected_year_bucket_direct_lane_value_f32_text: Vec, + #[serde(default)] + pub selected_year_bucket_complement_lane_raw_u32: Vec, + #[serde(default)] + pub selected_year_bucket_complement_lane_value_f32_text: Vec, + #[serde(default)] + pub selected_year_bucket_scaled_companion_lane_raw_u32: Vec, + #[serde(default)] + pub selected_year_bucket_scaled_companion_lane_value_f32_text: Vec, + #[serde(default)] pub selected_year_gap_scalar_raw_u32: Option, #[serde(default)] pub selected_year_gap_scalar_value_f32_text: Option, @@ -2423,6 +2435,37 @@ impl RuntimeState { self.world_restore.selected_year_bucket_scalar_raw_u32 = Some(value.to_bits()); self.world_restore .selected_year_bucket_scalar_value_f32_text = Some(format!("{value:.6}")); + if let Some(bands) = runtime_selected_year_bucket_bands_from_scalar(value) { + self.world_restore.selected_year_bucket_direct_lane_raw_u32 = + bands.direct.iter().map(|lane| lane.to_bits()).collect(); + self.world_restore + .selected_year_bucket_direct_lane_value_f32_text = bands + .direct + .iter() + .map(|lane| format!("{lane:.6}")) + .collect(); + self.world_restore + .selected_year_bucket_complement_lane_raw_u32 = + bands.complement.iter().map(|lane| lane.to_bits()).collect(); + self.world_restore + .selected_year_bucket_complement_lane_value_f32_text = bands + .complement + .iter() + .map(|lane| format!("{lane:.6}")) + .collect(); + self.world_restore + .selected_year_bucket_scaled_companion_lane_raw_u32 = bands + .scaled_companion + .iter() + .map(|lane| lane.to_bits()) + .collect(); + self.world_restore + .selected_year_bucket_scaled_companion_lane_value_f32_text = bands + .scaled_companion + .iter() + .map(|lane| format!("{lane:.6}")) + .collect(); + } } if let Some(value) = runtime_world_selected_year_gap_scalar_from_year_word(year_word) { self.world_restore.selected_year_gap_scalar_raw_u32 = Some(value.to_bits()); @@ -2455,6 +2498,9 @@ impl RuntimeState { #[derive(Debug, Clone, Deserialize)] struct CheckedInSelectedYearBucketLadderArtifact { + direct_lane_multipliers: Vec, + complement_formula: CheckedInSelectedYearBucketComplementFormula, + scaled_companion_formula: CheckedInSelectedYearBucketScaledCompanionFormula, entries: Vec, } @@ -2464,21 +2510,44 @@ struct CheckedInSelectedYearBucketLadderEntry { value: f32, } -fn checked_in_selected_year_bucket_ladder() -> &'static [CheckedInSelectedYearBucketLadderEntry] { - static LADDER: OnceLock> = OnceLock::new(); - LADDER - .get_or_init(|| { - serde_json::from_str::(include_str!( - "../../../artifacts/exports/rt3-1.06/selected-year-bucket-ladder.json" - )) - .expect("checked-in selected-year bucket ladder should parse") - .entries - }) - .as_slice() +#[derive(Debug, Clone, Deserialize)] +struct CheckedInSelectedYearBucketComplementFormula { + divisor: f32, + multiplier: f32, + bias: f32, + scale: f32, + floor: f32, + build_106_multiplier: f32, + cap: f32, +} + +#[derive(Debug, Clone, Deserialize)] +struct CheckedInSelectedYearBucketScaledCompanionFormula { + numerator: f32, + multiplier: f32, + bias: f32, + scale: f32, +} + +#[derive(Debug, Clone)] +struct RuntimeSelectedYearBucketBands { + direct: [f32; 3], + complement: [f32; 3], + scaled_companion: [f32; 3], +} + +fn checked_in_selected_year_bucket_ladder() -> &'static CheckedInSelectedYearBucketLadderArtifact { + static LADDER: OnceLock = OnceLock::new(); + LADDER.get_or_init(|| { + serde_json::from_str::(include_str!( + "../../../artifacts/exports/rt3-1.06/selected-year-bucket-ladder.json" + )) + .expect("checked-in selected-year bucket ladder should parse") + }) } pub fn runtime_world_selected_year_bucket_scalar_from_year_word(year_word: u32) -> Option { - let ladder = checked_in_selected_year_bucket_ladder(); + let ladder = &checked_in_selected_year_bucket_ladder().entries; if ladder.is_empty() { return None; } @@ -2500,6 +2569,41 @@ pub fn runtime_world_selected_year_bucket_scalar_from_year_word(year_word: u32) ladder.last().map(|entry| entry.value) } +fn runtime_selected_year_bucket_bands_from_scalar( + scalar: f32, +) -> Option { + let artifact = checked_in_selected_year_bucket_ladder(); + if artifact.direct_lane_multipliers.len() != 3 { + return None; + } + let direct = [ + scalar * artifact.direct_lane_multipliers[0], + scalar * artifact.direct_lane_multipliers[1], + scalar * artifact.direct_lane_multipliers[2], + ]; + let mut complement = [0.0f32; 3]; + let mut scaled_companion = [0.0f32; 3]; + for (index, direct_value) in direct.iter().copied().enumerate() { + let mut x = (((direct_value / artifact.complement_formula.divisor) + * artifact.complement_formula.multiplier) + + artifact.complement_formula.bias) + * artifact.complement_formula.scale; + x = x.max(artifact.complement_formula.floor); + x *= artifact.complement_formula.build_106_multiplier; + x = x.min(artifact.complement_formula.cap); + complement[index] = 1.0 - x; + scaled_companion[index] = (((artifact.scaled_companion_formula.numerator / direct_value) + * artifact.scaled_companion_formula.multiplier) + + artifact.scaled_companion_formula.bias) + * artifact.scaled_companion_formula.scale; + } + Some(RuntimeSelectedYearBucketBands { + direct, + complement, + scaled_companion, + }) +} + pub fn runtime_world_selected_year_gap_scalar_from_year_word(year_word: u32) -> Option { let normalized = (year_word as f64 - 1850.0) / 150.0; if !normalized.is_finite() { @@ -5134,6 +5238,12 @@ mod tests { economic_tuning_lane_value_f32_text: Vec::new(), selected_year_bucket_scalar_raw_u32: None, selected_year_bucket_scalar_value_f32_text: None, + selected_year_bucket_direct_lane_raw_u32: Vec::new(), + selected_year_bucket_direct_lane_value_f32_text: Vec::new(), + selected_year_bucket_complement_lane_raw_u32: Vec::new(), + selected_year_bucket_complement_lane_value_f32_text: Vec::new(), + selected_year_bucket_scaled_companion_lane_raw_u32: Vec::new(), + selected_year_bucket_scaled_companion_lane_value_f32_text: Vec::new(), selected_year_gap_scalar_raw_u32: None, selected_year_gap_scalar_value_f32_text: None, absolute_counter_restore_kind: Some( @@ -8136,6 +8246,13 @@ mod tests { runtime_world_selected_year_bucket_scalar_from_year_word(2000), Some(123.0) ); + let bands = runtime_selected_year_bucket_bands_from_scalar(25.0) + .expect("selected-year bucket companion bands"); + assert!((bands.direct[0] - 22.5).abs() < 1e-6); + assert!((bands.direct[1] - 26.25).abs() < 1e-5); + assert!((bands.direct[2] - 17.5).abs() < 1e-6); + assert!((bands.complement[0] - 0.999121).abs() < 1e-6); + assert!((bands.scaled_companion[0] - 139.16667).abs() < 1e-4); assert_eq!( runtime_world_selected_year_gap_scalar_from_year_word(1830), Some((1.0f32 / 3.0).clamp(1.0 / 3.0, 1.0)) @@ -8224,6 +8341,36 @@ mod tests { .as_deref(), Some("70.000000") ); + assert_eq!( + state + .world_restore + .selected_year_bucket_direct_lane_value_f32_text, + vec![ + "63.000000".to_string(), + "73.500000".to_string(), + "49.000000".to_string() + ] + ); + assert_eq!( + state + .world_restore + .selected_year_bucket_complement_lane_value_f32_text, + vec![ + "0.998400".to_string(), + "0.998213".to_string(), + "0.998649".to_string() + ] + ); + assert_eq!( + state + .world_restore + .selected_year_bucket_scaled_companion_lane_value_f32_text, + vec![ + "64.166672".to_string(), + "58.214291".to_string(), + "76.071426".to_string() + ] + ); assert_eq!( state.world_restore.selected_year_gap_scalar_raw_u32, Some(((50.0f32 / 150.0).clamp(1.0 / 3.0, 1.0)).to_bits()) diff --git a/crates/rrt-runtime/src/summary.rs b/crates/rrt-runtime/src/summary.rs index 36a20ef..34bd15e 100644 --- a/crates/rrt-runtime/src/summary.rs +++ b/crates/rrt-runtime/src/summary.rs @@ -82,6 +82,12 @@ pub struct RuntimeSummary { pub world_restore_cached_available_locomotive_rating_value_f32_text: Option, pub world_restore_selected_year_bucket_scalar_raw_u32: Option, pub world_restore_selected_year_bucket_scalar_value_f32_text: Option, + pub world_restore_selected_year_bucket_direct_lane_count: usize, + pub world_restore_selected_year_bucket_direct_lane_value_f32_text: Vec, + pub world_restore_selected_year_bucket_complement_lane_count: usize, + pub world_restore_selected_year_bucket_complement_lane_value_f32_text: Vec, + pub world_restore_selected_year_bucket_scaled_companion_lane_count: usize, + pub world_restore_selected_year_bucket_scaled_companion_lane_value_f32_text: Vec, pub world_restore_selected_year_gap_scalar_raw_u32: Option, pub world_restore_selected_year_gap_scalar_value_f32_text: Option, pub world_restore_absolute_counter_restore_kind: Option, @@ -480,6 +486,30 @@ impl RuntimeSummary { .world_restore .selected_year_bucket_scalar_value_f32_text .clone(), + world_restore_selected_year_bucket_direct_lane_count: state + .world_restore + .selected_year_bucket_direct_lane_raw_u32 + .len(), + world_restore_selected_year_bucket_direct_lane_value_f32_text: state + .world_restore + .selected_year_bucket_direct_lane_value_f32_text + .clone(), + world_restore_selected_year_bucket_complement_lane_count: state + .world_restore + .selected_year_bucket_complement_lane_raw_u32 + .len(), + world_restore_selected_year_bucket_complement_lane_value_f32_text: state + .world_restore + .selected_year_bucket_complement_lane_value_f32_text + .clone(), + world_restore_selected_year_bucket_scaled_companion_lane_count: state + .world_restore + .selected_year_bucket_scaled_companion_lane_raw_u32 + .len(), + world_restore_selected_year_bucket_scaled_companion_lane_value_f32_text: state + .world_restore + .selected_year_bucket_scaled_companion_lane_value_f32_text + .clone(), world_restore_selected_year_gap_scalar_raw_u32: state .world_restore .selected_year_gap_scalar_raw_u32, @@ -1818,6 +1848,36 @@ mod tests { cached_available_locomotive_rating_value_f32_text: Some("20.000000".to_string()), selected_year_bucket_scalar_raw_u32: Some(25.0f32.to_bits()), selected_year_bucket_scalar_value_f32_text: Some("25.000000".to_string()), + selected_year_bucket_direct_lane_raw_u32: vec![ + 22.5f32.to_bits(), + 26.25f32.to_bits(), + 17.5f32.to_bits(), + ], + selected_year_bucket_direct_lane_value_f32_text: vec![ + "22.500000".to_string(), + "26.250000".to_string(), + "17.500000".to_string(), + ], + selected_year_bucket_complement_lane_raw_u32: vec![ + 0.999121f32.to_bits(), + 0.998998f32.to_bits(), + 0.999210f32.to_bits(), + ], + selected_year_bucket_complement_lane_value_f32_text: vec![ + "0.999121".to_string(), + "0.998998".to_string(), + "0.999210".to_string(), + ], + selected_year_bucket_scaled_companion_lane_raw_u32: vec![ + 139.16667f32.to_bits(), + 122.5f32.to_bits(), + 171.42857f32.to_bits(), + ], + selected_year_bucket_scaled_companion_lane_value_f32_text: vec![ + "139.166672".to_string(), + "122.500000".to_string(), + "171.428574".to_string(), + ], selected_year_gap_scalar_raw_u32: Some(0x3eaaaaab), selected_year_gap_scalar_value_f32_text: Some("0.333333".to_string()), ..RuntimeWorldRestoreState::default() @@ -1982,6 +2042,30 @@ mod tests { .as_deref(), Some("25.000000") ); + assert_eq!( + summary.world_restore_selected_year_bucket_direct_lane_count, + 3 + ); + assert_eq!( + summary.world_restore_selected_year_bucket_direct_lane_value_f32_text, + vec!["22.500000", "26.250000", "17.500000"] + ); + assert_eq!( + summary.world_restore_selected_year_bucket_complement_lane_count, + 3 + ); + assert_eq!( + summary.world_restore_selected_year_bucket_complement_lane_value_f32_text, + vec!["0.999121", "0.998998", "0.999210"] + ); + assert_eq!( + summary.world_restore_selected_year_bucket_scaled_companion_lane_count, + 3 + ); + assert_eq!( + summary.world_restore_selected_year_bucket_scaled_companion_lane_value_f32_text, + vec!["139.166672", "122.500000", "171.428574"] + ); assert_eq!(summary.world_restore_economic_tuning_lane_count, 6); assert_eq!( summary.world_restore_economic_tuning_lane_value_f32_text, diff --git a/docs/README.md b/docs/README.md index 06d6064..43a591f 100644 --- a/docs/README.md +++ b/docs/README.md @@ -177,6 +177,10 @@ The highest-value next passes are now: drives a derived selected-year bucket scalar in runtime restore state, and the economic-tuning mirror `[world+0x0bde]` now rebuilds from tuning lane `0` instead of freezing one stale load-time word +- that same selected-year owner family now also rebuilds the direct bucket trio + `[world+0x65/+0x69/+0x6d]`, the complement trio `[world+0x71/+0x75/+0x79]`, and the scaled + companion trio `[world+0x7d/+0x81/+0x85]` from the bucket scalar instead of preserving stale + save-time residue - the project rule on the remaining closure work is now explicit too: when one runtime-facing field is still ambiguous, prefer rehosting the owning source state or real reader/setter family first instead of guessing another derived leaf field from neighboring raw offsets diff --git a/docs/rehost-queue.md b/docs/rehost-queue.md index 52354fb..bac2200 100644 --- a/docs/rehost-queue.md +++ b/docs/rehost-queue.md @@ -9,12 +9,9 @@ Working rule: ## Next -- Rehost the next selected-year periodic-boundary world seam under - `simulation_service_periodic_boundary_work`, extending the now-grounded selected-year bucket - scalar into the direct bucket trio `[world+0x65/+0x69/+0x6d]` and any safe follow-on companion - lanes rooted in `0x00433bd0`. -- Expand the selected-year world-owner surface beyond the stepped calendar, gap scalar, - bucket-scalar, mirror, and locomotive-policy lanes when the owning reader/rebuild family is +- Rehost the next periodic-boundary world seam after the selected-year bucket family, favoring + owner-state rebuilds that can advance shellless simulation without freezing load-time residue. +- Keep widening selected-year world-owner state only when a full owning reader/rebuild family is grounded strongly enough to avoid one-off leaf guesses. ## In Progress @@ -51,6 +48,10 @@ Working rule: and runtime restore state now derives both the selected-year bucket scalar and the `[world+0x0bde]` economic-tuning mirror from owner-family inputs instead of preserving stale load-time residue. +- That same selected-year owner family now also rebuilds the direct bucket trio + `[world+0x65/+0x69/+0x6d]`, the complement trio `[world+0x71/+0x75/+0x79]`, and the scaled + companion trio `[world+0x7d/+0x81/+0x85]` from the checked-in `0x00433bd0` artifact instead of + preserving stale save-time residue. - Company cash, confiscation, and major governance effects now write through owner state instead of drifting from market/cache readers. - Company credit rating, prime rate, book value per share, investor confidence, and management diff --git a/docs/runtime-rehost-plan.md b/docs/runtime-rehost-plan.md index deb1bed..292adc3 100644 --- a/docs/runtime-rehost-plan.md +++ b/docs/runtime-rehost-plan.md @@ -223,7 +223,10 @@ the fixed world block, so the `All Steam/Diesel/Electric Locos Avail.` descripto through owner state instead of living only as mirrored world flags. The selected-year seam now follows the same owner rule: the checked-in `0x00433bd0` year ladder now drives a derived selected-year bucket scalar in runtime restore state, and the economic-tuning mirror `[world+0x0bde]` -now rebuilds from tuning lane `0` instead of freezing one stale load-time word. The same owned company annual-finance state +now rebuilds from tuning lane `0` instead of freezing one stale load-time word. That same +checked-in owner family now also rebuilds the direct bucket trio `[world+0x65/+0x69/+0x6d]`, the +complement trio `[world+0x71/+0x75/+0x79]`, and the scaled companion trio `[world+0x7d/+0x81/+0x85]` +from the bucket scalar instead of preserving stale save-time residue. The same owned company annual-finance state now also drives a shared company market reader seam for stock-capital, salary, bonus, and the full two-word current/prior issue-calendar tuples, which is a better base for shellless finance simulation than summary-only helpers. That same owned annual-finance state now also derives elapsed diff --git a/tools/py/extract_selected_year_bucket_ladder.py b/tools/py/extract_selected_year_bucket_ladder.py index 8a89e90..d63850e 100644 --- a/tools/py/extract_selected_year_bucket_ladder.py +++ b/tools/py/extract_selected_year_bucket_ladder.py @@ -10,6 +10,18 @@ IMAGE_BASE = 0x400000 PAIRED_YEAR_VALUE_TABLE_VA = 0x005F3980 PAIR_COUNT = 21 TERMINAL_SCALAR_VA = 0x005F3A24 +DIRECT_MULTIPLIER_VAS = [0x005C8888, 0x005C9ED8, 0x005C8680] +COMPLEMENT_DIVISOR_VA = 0x005C8A20 +COMPLEMENT_MULTIPLIER_VA = 0x005C8DA8 +COMPLEMENT_BIAS_VA = 0x005C8618 +COMPLEMENT_SCALE_VA = 0x005C9ED0 +COMPLEMENT_FLOOR_VA = 0x005C88A0 +COMPLEMENT_BUILD_106_MULTIPLIER_VA = 0x005C8878 +COMPLEMENT_CAP_VA = 0x005C8988 +SCALED_COMPANION_NUMERATOR_VA = 0x005C8A20 +SCALED_COMPANION_MULTIPLIER_VA = 0x005C8680 +SCALED_COMPANION_BIAS_VA = 0x005C88C8 +SCALED_COMPANION_SCALE_VA = 0x005C9EC8 def read_u32_table(blob: bytes, va: int, count: int) -> list[int]: @@ -20,6 +32,14 @@ def read_u32_table(blob: bytes, va: int, count: int) -> list[int]: return [struct.unpack(" float: + offset = va - IMAGE_BASE + data = blob[offset : offset + 8] + if len(data) != 8: + raise ValueError(f"f64 at {va:#x} truncated") + return struct.unpack(" dict[str, object]: raw_pairs = read_u32_table(exe_bytes, PAIRED_YEAR_VALUE_TABLE_VA, PAIR_COUNT * 2) terminal_scalar_raw = read_u32_table(exe_bytes, TERMINAL_SCALAR_VA, 1)[0] @@ -33,6 +53,24 @@ def build_artifact(exe_bytes: bytes) -> dict[str, object]: "pair_count": PAIR_COUNT, "terminal_scalar_virtual_address": f"0x{TERMINAL_SCALAR_VA:08x}", "terminal_scalar_value": float(terminal_scalar_raw), + "direct_lane_multipliers": [ + read_f64(exe_bytes, va) for va in DIRECT_MULTIPLIER_VAS + ], + "complement_formula": { + "divisor": read_f64(exe_bytes, COMPLEMENT_DIVISOR_VA), + "multiplier": read_f64(exe_bytes, COMPLEMENT_MULTIPLIER_VA), + "bias": read_f64(exe_bytes, COMPLEMENT_BIAS_VA), + "scale": read_f64(exe_bytes, COMPLEMENT_SCALE_VA), + "floor": read_f64(exe_bytes, COMPLEMENT_FLOOR_VA), + "build_106_multiplier": read_f64(exe_bytes, COMPLEMENT_BUILD_106_MULTIPLIER_VA), + "cap": read_f64(exe_bytes, COMPLEMENT_CAP_VA), + }, + "scaled_companion_formula": { + "numerator": read_f64(exe_bytes, SCALED_COMPANION_NUMERATOR_VA), + "multiplier": read_f64(exe_bytes, SCALED_COMPANION_MULTIPLIER_VA), + "bias": read_f64(exe_bytes, SCALED_COMPANION_BIAS_VA), + "scale": read_f64(exe_bytes, SCALED_COMPANION_SCALE_VA), + }, "entries": entries, }