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}
31
32/// What a collapse refuses, per FORMAT 16.3.
33#[derive(Clone, Debug, PartialEq)]
34pub enum CollapseError {
35    NoHorizon,
36    EmptyBand {
37        ceiling: f64,
38        lowest: f64,
39    },
40    SingularInCt {
41        at: f64,
42        order: i32,
43    },
44    NotEvaluable(&'static str),
45    NestedSeries {
46        depth: usize,
47        terms: usize,
48        bound: usize,
49    },
50    LeftAlgebra(&'static str),
51}
52
53impl CollapseError {
54    pub fn code(&self) -> &'static str {
55        match self {
56            CollapseError::NoHorizon => "collapse.no_horizon",
57            CollapseError::EmptyBand { .. } => "collapse.empty_band",
58            CollapseError::SingularInCt { .. } => "collapse.singular_in_ct",
59            CollapseError::NotEvaluable(_) => "collapse.not_evaluable",
60            CollapseError::NestedSeries { .. } => "collapse.series_nesting",
61            CollapseError::LeftAlgebra(_) => "cast.left_algebra",
62        }
63    }
64}
65
66/// One repair per refusal: a window is what a missing horizon wants, and no window brings a
67/// line back under a ceiling.
68impl CollapseError {
69    pub fn help(&self) -> &'static str {
70        match self {
71            CollapseError::NoHorizon => "give the observation a window with --from and --to",
72            CollapseError::EmptyBand { ceiling, .. }
73                if *ceiling < sva_formula::AUDIBLE_CEILING_HZ =>
74            {
75                "this rate's own half is the ceiling: raise --sample-rate past twice the \
76                 lowest line"
77            }
78            CollapseError::EmptyBand { .. } => {
79                "the profile's 20 kHz ceiling is what no rate raises; read it with \
80                 `--as lines`, or bring the line into the band"
81            }
82            CollapseError::SingularInCt { .. } => {
83                "write it inside a convolution, or sample the closed form it multiplies"
84            }
85            CollapseError::NestedSeries { .. } => {
86                "write fewer levels, or a narrower band for the terms to clear"
87            }
88            CollapseError::NotEvaluable(_) | CollapseError::LeftAlgebra(_) => {
89                "write the subterm inside sample(...) to leave A deliberately"
90            }
91        }
92    }
93}
94
95impl std::fmt::Display for CollapseError {
96    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
97        match self {
98            CollapseError::NoHorizon => {
99                write!(
100                    f,
101                    "a closed form collapses against a horizon. give --from and --to"
102                )
103            }
104            CollapseError::EmptyBand { ceiling, lowest } => write!(
105                f,
106                "every line is at or above the {ceiling} Hz ceiling, the lowest at {lowest} Hz"
107            ),
108            CollapseError::SingularInCt { at, order } => write!(
109                f,
110                "a singularity of order {order} at t = {at} has no value on the grid"
111            ),
112            CollapseError::NotEvaluable(what) => {
113                write!(f, "{what} has no value at a point")
114            }
115            CollapseError::NestedSeries {
116                depth,
117                terms,
118                bound,
119            } => write!(
120                f,
121                "a series nested {depth} deep takes {terms} terms at one instant, past the \
122                 {bound} one expansion holds"
123            ),
124            CollapseError::LeftAlgebra(clause) => write!(f, "the closed form left A. {clause}"),
125        }
126    }
127}
128
129impl SampleError {
130    pub fn code(&self) -> &'static str {
131        match self {
132            SampleError::HopOutsideCola { .. } => "samples.hop_outside_cola",
133            SampleError::WindowNotPowerOfTwo { .. } => "samples.window_not_power_of_two",
134            SampleError::WidthMismatch { .. } => "type.width_mismatch",
135            SampleError::ChannelOutOfRange { .. } => "samples.channel_out_of_range",
136            SampleError::GridTooLarge { .. } => "samples.grid_too_large",
137            SampleError::StringPastRate { .. } => "samples.string_past_rate",
138        }
139    }
140}
141
142impl std::fmt::Display for SampleError {
143    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
144        match self {
145            SampleError::HopOutsideCola { window, hop } => write!(
146                f,
147                "a window of {window} at a hop of {hop} does not overlap-add to a constant. \
148                 write a hop that divides the window"
149            ),
150            SampleError::WindowNotPowerOfTwo { window } => {
151                write!(f, "a window of {window} is not a power of two")
152            }
153            SampleError::WidthMismatch { left, right } => write!(
154                f,
155                "{left} components meet {right}; an elementwise operation needs equal widths, \
156                 or one side mono"
157            ),
158            SampleError::ChannelOutOfRange { k, width } => {
159                write!(f, "component {k} of a value {width} wide")
160            }
161            SampleError::GridTooLarge {
162                model,
163                nodes,
164                ceiling,
165            } => write!(
166                f,
167                "`{model}` sizes a {nodes}-node grid at this rate, past the {ceiling} one \
168                 call may hold. ask for a higher fundamental, or a lower --sample-rate"
169            ),
170            SampleError::StringPastRate { model } => write!(
171                f,
172                "`{model}` has no stable grid at this rate whose first two partials ring where \
173                 asked. ask for a lower fundamental or `b`, or a higher --sample-rate"
174            ),
175        }
176    }
177}