Skip to main content

sva_samples/
error.rs

1// Concern: declares the refusals the sampled half reaches, each with its code | Non-concern: the closed-form crate's own codes (sva-formula), rendering a diagnostic (sva-engine) | IO: none
2
3#[derive(Clone, Debug, PartialEq, Eq)]
4pub enum SampleError {
5    HopOutsideCola {
6        window: usize,
7        hop: usize,
8    },
9    WindowNotPowerOfTwo {
10        window: usize,
11    },
12    WidthMismatch {
13        left: usize,
14        right: usize,
15    },
16    ChannelOutOfRange {
17        k: usize,
18        width: usize,
19    },
20    /// A grid sized from the rate and the fundamental, past what one call may allocate.
21    GridTooLarge {
22        model: &'static str,
23        nodes: usize,
24        ceiling: usize,
25    },
26    /// A string no stable grid at this rate rings at the partials it was asked for.
27    StringPastRate {
28        model: &'static str,
29    },
30    /// A unison whose bridge row leaves its scheme's mass form not provably positive.
31    BridgeUnstable {
32        model: &'static str,
33    },
34    /// A contact whose force solve settled on no value at a sample.
35    ContactUnsettled {
36        model: &'static str,
37        sample: usize,
38    },
39    /// State held for other call sites than the ones it is handed to.
40    StateMismatch,
41}
42
43/// What a collapse refuses, per FORMAT 16.3.
44#[derive(Clone, Debug, PartialEq)]
45pub enum CollapseError {
46    NoHorizon,
47    EmptyBand {
48        ceiling: f64,
49        lowest: f64,
50    },
51    SingularInCt {
52        at: f64,
53        order: i32,
54    },
55    NotEvaluable(&'static str),
56    NestedSeries {
57        depth: usize,
58        terms: usize,
59        bound: usize,
60    },
61    LeftAlgebra(&'static str),
62    /// A form in `f` reaches the grid by one transform over a whole horizon.
63    NoBlockRow,
64}
65
66impl CollapseError {
67    pub fn code(&self) -> &'static str {
68        match self {
69            CollapseError::NoHorizon => "collapse.no_horizon",
70            CollapseError::EmptyBand { .. } => "collapse.empty_band",
71            CollapseError::SingularInCt { .. } => "collapse.singular_in_ct",
72            CollapseError::NotEvaluable(_) => "collapse.not_evaluable",
73            CollapseError::NestedSeries { .. } => "collapse.series_nesting",
74            CollapseError::LeftAlgebra(_) => "cast.left_algebra",
75            CollapseError::NoBlockRow => "collapse.no_block_row",
76        }
77    }
78}
79
80/// One repair per refusal: a window is what a missing horizon wants, and no window brings a
81/// line back under a ceiling.
82impl CollapseError {
83    pub fn help(&self) -> &'static str {
84        match self {
85            CollapseError::NoHorizon => "give the observation a window with --from and --to",
86            CollapseError::EmptyBand { ceiling, .. }
87                if *ceiling < sva_formula::AUDIBLE_CEILING_HZ =>
88            {
89                "this rate's own half is the ceiling: raise --sample-rate past twice the \
90                 lowest line"
91            }
92            CollapseError::EmptyBand { .. } => {
93                "the profile's 20 kHz ceiling is what no rate raises; read it with \
94                 `--as lines`, or bring the line into the band"
95            }
96            CollapseError::SingularInCt { .. } => {
97                "write it inside a convolution, or sample the closed form it multiplies"
98            }
99            CollapseError::NestedSeries { .. } => {
100                "write fewer levels, or a narrower band for the terms to clear"
101            }
102            CollapseError::NotEvaluable(_) | CollapseError::LeftAlgebra(_) => {
103                "write the subterm inside sample(...) to leave A deliberately"
104            }
105            CollapseError::NoBlockRow => {
106                "render it to a stated end instead of streaming it, or write it in t"
107            }
108        }
109    }
110}
111
112impl std::fmt::Display for CollapseError {
113    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
114        match self {
115            CollapseError::NoHorizon => {
116                write!(
117                    f,
118                    "a closed form collapses against a horizon. give --from and --to"
119                )
120            }
121            CollapseError::EmptyBand { ceiling, lowest } => write!(
122                f,
123                "every line is at or above the {ceiling} Hz ceiling, the lowest at {lowest} Hz"
124            ),
125            CollapseError::SingularInCt { at, order } => write!(
126                f,
127                "a singularity of order {order} at t = {at} has no value on the grid"
128            ),
129            CollapseError::NotEvaluable(what) => {
130                write!(f, "{what} has no value at a point")
131            }
132            CollapseError::NestedSeries {
133                depth,
134                terms,
135                bound,
136            } => write!(
137                f,
138                "a series nested {depth} deep takes {terms} terms at one instant, past the \
139                 {bound} one expansion holds"
140            ),
141            CollapseError::LeftAlgebra(clause) => write!(f, "the closed form left A. {clause}"),
142            CollapseError::NoBlockRow => write!(
143                f,
144                "a closed form in f reaches the grid by one transform over a whole horizon, \
145                 so no block of it is read alone"
146            ),
147        }
148    }
149}
150
151impl SampleError {
152    pub fn code(&self) -> &'static str {
153        match self {
154            SampleError::HopOutsideCola { .. } => "samples.hop_outside_cola",
155            SampleError::WindowNotPowerOfTwo { .. } => "samples.window_not_power_of_two",
156            SampleError::WidthMismatch { .. } => "type.width_mismatch",
157            SampleError::ChannelOutOfRange { .. } => "samples.channel_out_of_range",
158            SampleError::GridTooLarge { .. } => "samples.grid_too_large",
159            SampleError::StringPastRate { .. } => "samples.string_past_rate",
160            SampleError::BridgeUnstable { .. } => "samples.bridge_unstable",
161            SampleError::ContactUnsettled { .. } => "samples.contact_unsettled",
162            SampleError::StateMismatch => "samples.state_mismatch",
163        }
164    }
165}
166
167impl std::fmt::Display for SampleError {
168    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
169        match self {
170            SampleError::HopOutsideCola { window, hop } => write!(
171                f,
172                "a window of {window} at a hop of {hop} does not overlap-add to a constant. \
173                 write a hop that divides the window"
174            ),
175            SampleError::WindowNotPowerOfTwo { window } => {
176                write!(f, "a window of {window} is not a power of two")
177            }
178            SampleError::WidthMismatch { left, right } => write!(
179                f,
180                "{left} components meet {right}; an elementwise operation needs equal widths, \
181                 or one side mono"
182            ),
183            SampleError::ChannelOutOfRange { k, width } => {
184                write!(f, "component {k} of a value {width} wide")
185            }
186            SampleError::GridTooLarge {
187                model,
188                nodes,
189                ceiling,
190            } => write!(
191                f,
192                "`{model}` sizes a {nodes}-node grid at this rate, past the {ceiling} one \
193                 call may hold. ask for a higher fundamental, or a lower --sample-rate"
194            ),
195            SampleError::StringPastRate { model } => write!(
196                f,
197                "`{model}` has no stable grid at this rate whose first two partials ring where \
198                 asked. ask for a lower fundamental or `b`, or a higher --sample-rate"
199            ),
200            SampleError::BridgeUnstable { model } => write!(
201                f,
202                "`{model}`'s unison is not proven stable on its bridge at this rate: the mass \
203                 form its bridge row leaves is not provably positive. ask for a heavier bridge, \
204                 less frequency-dependent loss, or a higher --sample-rate"
205            ),
206            SampleError::ContactUnsettled { model, sample } => write!(
207                f,
208                "`{model}`'s contact force settled on no value at sample {sample}. ask for a \
209                 lower bow velocity or force, or a higher --sample-rate"
210            ),
211            SampleError::StateMismatch => write!(
212                f,
213                "the state held is for other call sites than these: a site that moved takes \
214                 only a chaigne_askenfelt string's motion, and only across a new release"
215            ),
216        }
217    }
218}