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