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    /// A reading needs a sample this run has not computed yet.
40    ReadsAhead {
41        at: i64,
42    },
43    /// An exactly computed time passed what its integers hold.
44    UnreadablePosition,
45    /// A closed form read at one of the machine's instants has no value there.
46    FormulaUnevaluable {
47        at: i64,
48    },
49    /// A varying parameter outside the range its model holds, at one sample.
50    ArgumentOutOfRange {
51        model: &'static str,
52        name: &'static str,
53        /// The value's bits, which keep the error `Eq`.
54        bits: u64,
55        sample: u64,
56    },
57}
58
59/// What a collapse refuses, per FORMAT 16.3.
60#[derive(Clone, Debug, PartialEq)]
61pub enum CollapseError {
62    EmptyBand {
63        ceiling: f64,
64        lowest: f64,
65    },
66    SingularInCt {
67        at: f64,
68        order: i32,
69    },
70    NotEvaluable(&'static str),
71    NestedSeries {
72        depth: usize,
73        terms: usize,
74        bound: usize,
75    },
76    LeftAlgebra(&'static str),
77    /// A form in `f` reaches the grid by one transform over a whole extent.
78    NoBlockRow,
79}
80
81impl CollapseError {
82    pub fn code(&self) -> &'static str {
83        match self {
84            CollapseError::EmptyBand { .. } => "collapse.empty_band",
85            CollapseError::SingularInCt { .. } => "collapse.singular_in_ct",
86            CollapseError::NotEvaluable(_) => "collapse.not_evaluable",
87            CollapseError::NestedSeries { .. } => "collapse.series_nesting",
88            CollapseError::LeftAlgebra(_) => "cast.left_algebra",
89            CollapseError::NoBlockRow => "collapse.no_block_row",
90        }
91    }
92}
93
94/// One repair per refusal: no extent brings a line back under a ceiling.
95impl CollapseError {
96    pub fn help(&self) -> &'static str {
97        match self {
98            CollapseError::EmptyBand { ceiling, .. }
99                if *ceiling < sva_formula::AUDIBLE_CEILING_HZ =>
100            {
101                "this rate's own half is the ceiling: raise --rate past twice the \
102                 lowest line"
103            }
104            CollapseError::EmptyBand { .. } => {
105                "the profile's 20 kHz ceiling is what no rate raises; read it with \
106                 `--representation lines`, or bring the line into the band"
107            }
108            CollapseError::SingularInCt { .. } => {
109                "write it inside a convolution, or sample the closed form it multiplies"
110            }
111            CollapseError::NestedSeries { .. } => {
112                "write fewer levels, or a narrower band for the terms to clear"
113            }
114            CollapseError::NotEvaluable(_) | CollapseError::LeftAlgebra(_) => {
115                "write the subterm inside sample(...) to leave A deliberately"
116            }
117            CollapseError::NoBlockRow => {
118                "render it to a stated end instead of streaming it, or write it in t"
119            }
120        }
121    }
122}
123
124impl std::fmt::Display for CollapseError {
125    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
126        match self {
127            CollapseError::EmptyBand { ceiling, lowest } => write!(
128                f,
129                "every line is at or above the {ceiling} Hz ceiling, the lowest at {lowest} Hz"
130            ),
131            CollapseError::SingularInCt { at, order } => write!(
132                f,
133                "a singularity of order {order} at t = {at} has no value on the grid"
134            ),
135            CollapseError::NotEvaluable(what) => {
136                write!(f, "{what} has no value at a point")
137            }
138            CollapseError::NestedSeries {
139                depth,
140                terms,
141                bound,
142            } => write!(
143                f,
144                "a series nested {depth} deep takes {terms} terms at one instant, past the \
145                 {bound} one expansion holds"
146            ),
147            CollapseError::LeftAlgebra(clause) => write!(f, "the closed form left A. {clause}"),
148            CollapseError::NoBlockRow => write!(
149                f,
150                "a closed form in f reaches the grid by one transform over a whole extent, \
151                 so no block of it is read alone"
152            ),
153        }
154    }
155}
156
157impl SampleError {
158    pub fn code(&self) -> &'static str {
159        match self {
160            SampleError::HopOutsideCola { .. } => "samples.hop_outside_cola",
161            SampleError::WindowNotPowerOfTwo { .. } => "samples.window_not_power_of_two",
162            SampleError::WidthMismatch { .. } => "type.width_mismatch",
163            SampleError::ChannelOutOfRange { .. } => "samples.channel_out_of_range",
164            SampleError::GridTooLarge { .. } => "samples.grid_too_large",
165            SampleError::StringPastRate { .. } => "samples.string_past_rate",
166            SampleError::BridgeUnstable { .. } => "samples.bridge_unstable",
167            SampleError::ContactUnsettled { .. } => "samples.contact_unsettled",
168            SampleError::ArgumentOutOfRange { .. } => "samples.argument_out_of_range",
169            SampleError::ReadsAhead { .. } => "engine.reads_ahead",
170            SampleError::UnreadablePosition => "engine.unreadable_position",
171            SampleError::FormulaUnevaluable { .. } => "collapse.not_evaluable",
172        }
173    }
174}
175
176impl std::fmt::Display for SampleError {
177    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
178        match self {
179            SampleError::HopOutsideCola { window, hop } => write!(
180                f,
181                "a window of {window} at a hop of {hop} does not overlap-add to a constant. \
182                 write a hop that divides the window"
183            ),
184            SampleError::WindowNotPowerOfTwo { window } => {
185                write!(f, "a window of {window} is not a power of two")
186            }
187            SampleError::WidthMismatch { left, right } => write!(
188                f,
189                "{left} components meet {right}; an elementwise operation needs equal widths, \
190                 or one side mono"
191            ),
192            SampleError::ChannelOutOfRange { k, width } => {
193                write!(f, "component {k} of a value {width} wide")
194            }
195            SampleError::GridTooLarge {
196                model,
197                nodes,
198                ceiling,
199            } => write!(
200                f,
201                "`{model}` sizes a {nodes}-node grid at this rate, past the {ceiling} one \
202                 call may hold. ask for a higher fundamental, or a lower --rate"
203            ),
204            SampleError::StringPastRate { model } => write!(
205                f,
206                "`{model}` has no stable grid at this rate whose first two partials ring where \
207                 asked. ask for a lower fundamental or `b`, or a higher --rate"
208            ),
209            SampleError::BridgeUnstable { model } => write!(
210                f,
211                "`{model}`'s unison is not proven stable on its bridge at this rate: the mass \
212                 form its bridge row leaves is not provably positive. ask for a heavier bridge, \
213                 less frequency-dependent loss, or a higher --rate"
214            ),
215            SampleError::ContactUnsettled { model, sample } => write!(
216                f,
217                "`{model}`'s contact force settled on no value at sample {sample}. ask for a \
218                 lower bow velocity or force, or a higher --rate"
219            ),
220            SampleError::ReadsAhead { at } => write!(
221                f,
222                "a reading needs sample {at} of its source, which is not computed yet: a \
223                 stream reads no output ahead of where it stands"
224            ),
225            SampleError::UnreadablePosition => {
226                write!(f, "an exactly computed time passed what its integers hold")
227            }
228            SampleError::FormulaUnevaluable { at } => write!(
229                f,
230                "a closed form read at sample {at}'s instant has no value there"
231            ),
232            SampleError::ArgumentOutOfRange {
233                model,
234                name,
235                bits,
236                sample,
237            } => write!(
238                f,
239                "`{model}`'s `{name}` is {} at sample {sample}, outside the range it models",
240                f64::from_bits(*bits)
241            ),
242        }
243    }
244}