global: replace most bps with ppm

This commit is contained in:
nym21
2026-07-20 18:28:12 +02:00
parent 3af673132a
commit 00984112d8
110 changed files with 3320 additions and 3220 deletions
+7 -7
View File
@@ -291,7 +291,7 @@ fn collect_instance_analyses(
///
/// Supports two cases:
/// 1. **Embedded discriminator**: a substring varies per instance within field_parts.
/// E.g., `ratio_pct99_bps` vs `ratio_pct1_bps` → template `ratio_{disc}_bps`
/// E.g., `ratio_pct99_ppm` vs `ratio_pct1_ppm` → template `ratio_{disc}_ppm`
/// 2. **Suffix discriminator**: a common suffix is appended to all field_parts.
/// E.g., `ratio_sd` vs `ratio_sd_4y` → template `ratio_sd{disc}`
fn try_detect_template(
@@ -307,11 +307,11 @@ fn try_detect_template(
return Some(mode);
}
// Strategy 2: embedded discriminator (e.g., ratio_pct99_bps vs ratio_pct1_bps)
// Strategy 2: embedded discriminator (e.g., ratio_pct99_ppm vs ratio_pct1_ppm)
try_embedded_disc(majority, fields)
}
/// Strategy 1: embedded discriminator (e.g., pct99 inside ratio_pct99_bps)
/// Strategy 1: embedded discriminator (e.g., pct99 inside ratio_pct99_ppm)
fn try_embedded_disc(
majority: &[&InstanceAnalysis],
fields: &[PatternField],
@@ -772,7 +772,7 @@ mod tests {
use std::collections::BTreeSet;
let fields = vec![
PatternField {
name: "bps".into(),
name: "ppm".into(),
rust_type: "T".into(),
json_type: "n".into(),
indexes: BTreeSet::new(),
@@ -796,7 +796,7 @@ mod tests {
let pct99 = InstanceAnalysis {
base: "realized_price".into(),
field_parts: [
("bps".into(), "ratio_pct99_bps".into()),
("ppm".into(), "ratio_pct99_ppm".into()),
("price".into(), "pct99".into()),
("ratio".into(), "ratio_pct99".into()),
]
@@ -808,7 +808,7 @@ mod tests {
let pct1 = InstanceAnalysis {
base: "realized_price".into(),
field_parts: [
("bps".into(), "ratio_pct1_bps".into()),
("ppm".into(), "ratio_pct1_ppm".into()),
("price".into(), "pct1".into()),
("ratio".into(), "ratio_pct1".into()),
]
@@ -821,7 +821,7 @@ mod tests {
assert!(mode.is_some());
match mode.unwrap() {
PatternMode::Templated { templates } => {
assert_eq!(templates.get("bps").unwrap(), "ratio_{disc}_bps");
assert_eq!(templates.get("ppm").unwrap(), "ratio_{disc}_ppm");
assert_eq!(templates.get("price").unwrap(), "{disc}");
assert_eq!(templates.get("ratio").unwrap(), "ratio_{disc}");
}
@@ -89,7 +89,7 @@ impl LanguageSyntax for JavaScriptSyntax {
format!("_m({}, '{}')", var_name, template)
} else {
// Template with {disc}: use nested _m for proper separator handling
// "ratio_{disc}_bps" → split on {disc} → _m(_m(acc, 'ratio'), disc) then _bps
// "ratio_{disc}_ppm" → split on {disc} → _m(_m(acc, 'ratio'), disc) then _ppm
// But this is complex. For embedded disc, use string interpolation.
// For suffix disc (ends with {disc}), use _m composition.
if let Some(static_part) = template.strip_suffix("{disc}") {
+2 -2
View File
@@ -107,7 +107,7 @@ pub trait LanguageSyntax {
/// - `"pct99"` (static) → `'pct99'` (JS) / `"pct99".to_string()` (Rust)
/// - `""` (empty) → `disc` (pass parent's disc through)
/// - `"p1sd{disc}"` (suffix) → `_m('p1sd', disc)` (composed)
/// - `"ratio_{disc}_bps"` (embedded) → `` `ratio_${disc}_bps` `` (template literal)
/// - `"ratio_{disc}_ppm"` (embedded) → `` `ratio_${disc}_ppm` `` (template literal)
fn disc_arg_expr(&self, template: &str) -> String;
/// Format a templated mode expression: substitute `{disc}` at runtime.
@@ -117,6 +117,6 @@ pub trait LanguageSyntax {
///
/// # Arguments
/// * `acc_var` - The accumulator variable (e.g., "acc")
/// * `template` - Template like `"ratio_{disc}_bps"` or `"{disc}"`
/// * `template` - Template like `"ratio_{disc}_ppm"` or `"{disc}"`
fn template_expr(&self, acc_var: &str, template: &str) -> String;
}
+1 -1
View File
@@ -26,7 +26,7 @@ pub enum PatternMode {
/// Fields construct series names using a template with a discriminator placeholder.
/// Factory takes two params: `acc` (base) and `disc` (discriminator).
/// Formula: `_m(acc, template.replace("{disc}", disc))`
/// Example: template `"ratio_{disc}_bps"` with disc `"pct99"` → `_m(acc, "ratio_pct99_bps")`
/// Example: template `"ratio_{disc}_ppm"` with disc `"pct99"` → `_m(acc, "ratio_pct99_ppm")`
Templated {
/// Maps field name to its template string containing `{disc}` placeholder
templates: BTreeMap<String, String>,
+1 -1
View File
@@ -128,7 +128,7 @@ impl StructuralPattern {
}
/// Extract the discriminator value by matching a template against a concrete string.
/// E.g., template `"ratio_{disc}_bps"` matched against `"ratio_pct99_bps"` yields `"pct99"`.
/// E.g., template `"ratio_{disc}_ppm"` matched against `"ratio_pct99_ppm"` yields `"pct99"`.
fn extract_disc(template: &str, value: &str) -> Option<String> {
let (prefix, suffix) = template.split_once("{disc}")?;
if value.starts_with(prefix) && value.ends_with(suffix) {