clients: snapshot

This commit is contained in:
nym21
2026-01-14 09:37:43 +01:00
parent 25a0ebe51e
commit 922a0abb60
11 changed files with 24404 additions and 3752 deletions

View File

@@ -123,6 +123,9 @@ pub struct PatternBaseResult {
/// Whether this instance uses suffix mode (common prefix) or prefix mode (common suffix).
/// Used to check compatibility with the pattern's mode.
pub is_suffix_mode: bool,
/// The field parts (suffix in suffix mode, prefix in prefix mode) for each field.
/// Used to check if instance field parts match the pattern's field parts.
pub field_parts: HashMap<String, String>,
}
/// Get the metric base for a pattern instance by analyzing direct children.
@@ -141,6 +144,7 @@ pub fn get_pattern_instance_base(node: &TreeNode) -> PatternBaseResult {
base: String::new(),
has_outlier: false,
is_suffix_mode: true, // default
field_parts: HashMap::new(),
};
}
@@ -150,6 +154,7 @@ pub fn get_pattern_instance_base(node: &TreeNode) -> PatternBaseResult {
base: result.base,
has_outlier: result.has_outlier,
is_suffix_mode: result.is_suffix_mode,
field_parts: result.field_parts,
};
}
@@ -168,6 +173,7 @@ pub fn get_pattern_instance_base(node: &TreeNode) -> PatternBaseResult {
base: result.base,
has_outlier: true,
is_suffix_mode: result.is_suffix_mode,
field_parts: result.field_parts,
};
}
}
@@ -179,14 +185,16 @@ pub fn get_pattern_instance_base(node: &TreeNode) -> PatternBaseResult {
base: String::new(),
has_outlier: false,
is_suffix_mode: true, // default
field_parts: HashMap::new(),
}
}
/// Result of try_find_base: base name, has_outlier flag, and is_suffix_mode flag.
/// Result of try_find_base: base name, has_outlier flag, is_suffix_mode flag, and field_parts.
struct FindBaseResult {
base: String,
has_outlier: bool,
is_suffix_mode: bool,
field_parts: HashMap<String, String>,
}
/// Try to find a common base from child names using prefix/suffix detection.
@@ -197,20 +205,52 @@ fn try_find_base(child_names: &[(String, String)], is_outlier_attempt: bool) ->
// Try common prefix first (suffix mode)
if let Some(prefix) = find_common_prefix(&leaf_names) {
let base = prefix.trim_end_matches('_').to_string();
let mut field_parts = HashMap::new();
for (field_name, leaf_name) in child_names {
// Compute the suffix part for this field
let suffix = if leaf_name == &base {
String::new()
} else {
leaf_name
.strip_prefix(&prefix)
.unwrap_or(leaf_name)
.to_string()
};
field_parts.insert(field_name.clone(), suffix);
}
return Some(FindBaseResult {
base,
has_outlier: is_outlier_attempt,
is_suffix_mode: true,
field_parts,
});
}
// Try common suffix (prefix mode)
if let Some(suffix) = find_common_suffix(&leaf_names) {
let base = suffix.trim_start_matches('_').to_string();
let mut field_parts = HashMap::new();
for (field_name, leaf_name) in child_names {
// Compute the prefix part for this field
let prefix_part = leaf_name
.strip_suffix(&suffix)
.map(|s| {
if s.is_empty() {
String::new()
} else if s.ends_with('_') {
s.to_string()
} else {
format!("{}_", s)
}
})
.unwrap_or_default();
field_parts.insert(field_name.clone(), prefix_part);
}
return Some(FindBaseResult {
base,
has_outlier: is_outlier_attempt,
is_suffix_mode: false,
field_parts,
});
}

View File

@@ -55,17 +55,20 @@ pub fn prepare_tree_node<'a>(
.map(|(f, _)| f.clone())
.collect();
// Skip if this matches a parameterizable pattern AND has no outlier AND mode matches
// Skip if this matches a parameterizable pattern AND has no outlier AND field parts match
let base_result = get_pattern_instance_base(node);
let mode_matches = pattern_lookup
let pattern_fully_matches = pattern_lookup
.get(&fields)
.and_then(|name| metadata.find_pattern(name))
.is_none_or(|p| p.is_suffix_mode() == base_result.is_suffix_mode);
.is_some_and(|p| {
p.is_suffix_mode() == base_result.is_suffix_mode
&& p.field_parts_match(&base_result.field_parts)
});
if let Some(pattern_name) = pattern_lookup.get(&fields)
&& pattern_name != name
&& metadata.is_parameterizable(pattern_name)
&& !base_result.has_outlier
&& mode_matches
&& pattern_fully_matches
{
return None;
}
@@ -89,16 +92,23 @@ pub fn prepare_tree_node<'a>(
.as_ref()
.is_some_and(|cf| metadata.matches_pattern(cf));
// Check if the pattern mode matches the instance mode
let mode_matches = child_fields
// Check if the pattern mode AND field parts match the instance
let pattern_fully_matches = child_fields
.as_ref()
.and_then(|cf| metadata.find_pattern_by_fields(cf))
.is_none_or(|p| p.is_suffix_mode() == base_result.is_suffix_mode);
.is_some_and(|p| {
// Mode must match (suffix vs prefix)
if p.is_suffix_mode() != base_result.is_suffix_mode {
return false;
}
// Field parts must also match
p.field_parts_match(&base_result.field_parts)
});
// should_inline determines if we generate an inline struct type
// We inline if: it's a branch AND (doesn't match any pattern OR mode doesn't match OR has outlier)
// We inline if: it's a branch AND (doesn't match any pattern OR pattern doesn't fully match OR has outlier)
let should_inline =
!is_leaf && (!matches_any_pattern || !mode_matches || base_result.has_outlier);
!is_leaf && (!matches_any_pattern || !pattern_fully_matches || base_result.has_outlier);
// Inline type name (only used when should_inline is true)
let inline_type_name = if should_inline {

View File

@@ -1,6 +1,6 @@
//! Structural pattern and field types.
use std::collections::BTreeSet;
use std::collections::{BTreeSet, HashMap};
use brk_types::Index;
@@ -47,6 +47,30 @@ impl StructuralPattern {
pub fn is_suffix_mode(&self) -> bool {
matches!(&self.mode, Some(PatternMode::Suffix { .. }))
}
/// Check if the given instance field parts match this pattern's field parts.
/// Returns true if all field parts in the pattern match the instance's field parts.
pub fn field_parts_match(&self, instance_field_parts: &HashMap<String, String>) -> bool {
match &self.mode {
Some(PatternMode::Suffix { relatives }) => {
// For each field in the pattern, check if the instance has the same suffix
relatives.iter().all(|(field_name, pattern_suffix)| {
instance_field_parts
.get(field_name)
.is_some_and(|instance_suffix| instance_suffix == pattern_suffix)
})
}
Some(PatternMode::Prefix { prefixes }) => {
// For each field in the pattern, check if the instance has the same prefix
prefixes.iter().all(|(field_name, pattern_prefix)| {
instance_field_parts
.get(field_name)
.is_some_and(|instance_prefix| instance_prefix == pattern_prefix)
})
}
None => false, // Non-parameterizable patterns don't use field parts
}
}
}
/// A field in a structural pattern.

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -379,11 +379,11 @@ export function createChartElement({
if (metric) {
signals.createEffect(index, (index) => {
// Get timestamp metric from tree based on index type
// timestampFixed has height only, timestamp has date-based indexes
// timestampMonotonic has height only, timestamp has date-based indexes
/** @type {AnyMetricPattern} */
const timeMetric =
index === "height"
? brk.metrics.blocks.time.timestampFixed
? brk.metrics.blocks.time.timestampMonotonic
: brk.metrics.blocks.time.timestamp;
/** @type {AnyMetricPattern} */
const valuesMetric = metric;

View File

@@ -379,34 +379,34 @@ export function createCostBasisPercentilesSeries(ctx, list, useGroupName) {
const percentiles = tree.costBasis.percentiles;
return [
line({
metric: percentiles.costBasisPct10,
metric: percentiles.pct10,
name: useGroupName ? `${name} p10` : "p10",
color,
unit: Unit.usd,
defaultActive: false,
}),
line({
metric: percentiles.costBasisPct25,
metric: percentiles.pct25,
name: useGroupName ? `${name} p25` : "p25",
color,
unit: Unit.usd,
defaultActive: false,
}),
line({
metric: percentiles.costBasisPct50,
metric: percentiles.pct50,
name: useGroupName ? `${name} p50` : "p50",
color,
unit: Unit.usd,
}),
line({
metric: percentiles.costBasisPct75,
metric: percentiles.pct75,
name: useGroupName ? `${name} p75` : "p75",
color,
unit: Unit.usd,
defaultActive: false,
}),
line({
metric: percentiles.costBasisPct90,
metric: percentiles.pct90,
name: useGroupName ? `${name} p90` : "p90",
color,
unit: Unit.usd,

View File

@@ -64,7 +64,7 @@ export function createInvestingSection(ctx, { dca, lookback, returns }) {
["10y", "_10y"],
]).map(([id, key]) => {
const name = periodIdToName(id, true);
const priceAgo = lookback.priceAgo[key];
const priceAgo = lookback[key];
const priceReturns = returns.priceReturns[key];
const dcaCostBasis = dca.periodAveragePrice[key];
const dcaReturns = dca.periodReturns[key];
@@ -181,14 +181,15 @@ export function createInvestingSection(ctx, { dca, lookback, returns }) {
{
name: "Returns",
title: "DCA Returns by Year",
bottom: dcaClasses.map(({ year, color, defaultActive, returns }) =>
baseline({
metric: returns,
name: `${year}`,
color,
defaultActive,
unit: Unit.percentage,
}),
bottom: dcaClasses.map(
({ year, color, defaultActive, returns }) =>
baseline({
metric: returns,
name: `${year}`,
color,
defaultActive,
unit: Unit.percentage,
}),
),
},
{

View File

@@ -118,18 +118,6 @@ export function fromBitcoin(colors, pattern, title, color) {
return [
{ metric: pattern.base, title, color: color ?? colors.default },
{ metric: pattern.average, title: "Average", defaultActive: false },
{
metric: pattern.sum,
title: `${title} (sum)`,
color: colors.red,
defaultActive: false,
},
{
metric: pattern.cumulative,
title: `${title} (cum.)`,
color: colors.cyan,
defaultActive: false,
},
{
metric: pattern.max,
title: "Max",
@@ -333,20 +321,6 @@ export function fromFullnessPattern(colors, pattern, title, unit) {
unit,
defaultActive: false,
},
{
metric: pattern.sum,
title: `${title} sum`,
color: colors.blue,
unit,
defaultActive: false,
},
{
metric: pattern.cumulative,
title: `${title} cumulative`,
color: colors.indigo,
unit,
defaultActive: false,
},
{
metric: pattern.min,
title: `${title} min`,

View File

@@ -11,5 +11,5 @@
"lib": ["DOM", "DOM.Iterable", "ESNext", "WebWorker"],
"skipLibCheck": true,
},
"exclude": ["assets", "./scripts/modules", "../../modules"],
"exclude": ["assets", "./scripts/modules"],
}