1use crate::origin::Origin;
4use crate::ty::{Mismatch, Ty};
5
6#[derive(Clone, Copy, Debug, PartialEq, Eq)]
7pub enum Code {
8 DomainMismatch,
9 SamplesInClosedForm,
10 RateConflict,
11 FrameMismatch,
12 WidthMismatch,
13 NonAffineSingular,
14 SeriesNotSummable,
15 LeftAlgebra,
16}
17
18impl Code {
19 pub fn as_str(self) -> &'static str {
20 match self {
21 Code::DomainMismatch => "type.domain_mismatch",
22 Code::SamplesInClosedForm => "type.samples_in_closed_form",
23 Code::RateConflict => "type.rate_conflict",
24 Code::FrameMismatch => "type.frame_mismatch",
25 Code::WidthMismatch => "type.width_mismatch",
26 Code::NonAffineSingular => "type.non_affine_singular",
27 Code::SeriesNotSummable => "type.series_not_summable",
28 Code::LeftAlgebra => "cast.left_algebra",
29 }
30 }
31}
32
33#[derive(Clone, Debug, PartialEq, Eq)]
34pub struct Refusal {
35 pub code: Code,
36 pub origin: Origin,
37 pub message: String,
38}
39
40impl Refusal {
41 pub fn new(code: Code, origin: Origin, message: impl Into<String>) -> Refusal {
42 Refusal {
43 code,
44 origin,
45 message: message.into(),
46 }
47 }
48}
49
50impl Refusal {
51 pub fn of_mismatch(m: Mismatch, origin: Origin, a: Ty, b: Ty) -> Refusal {
53 let (code, message) = match m {
54 Mismatch::Domain => (
55 Code::DomainMismatch,
56 "a closed form in t meets one in f. write fourier on the t side, or ifourier on \
57 the f side"
58 .to_string(),
59 ),
60 Mismatch::SamplesInClosedForm => (
61 Code::SamplesInClosedForm,
62 "this mixes a closed form and samples. write sample(...) on the closed form"
63 .to_string(),
64 ),
65 Mismatch::Rate => (
66 Code::RateConflict,
67 format!(
68 "{:?} Hz meets {:?} Hz; one rate per expression",
69 a.rate, b.rate
70 ),
71 ),
72 Mismatch::Frames => (
73 Code::FrameMismatch,
74 "two frame layouts meet in one expression".to_string(),
75 ),
76 Mismatch::Width => (
77 Code::WidthMismatch,
78 format!(
79 "{} components meet {}; an elementwise operation needs equal widths, or one \
80 side mono",
81 a.width, b.width
82 ),
83 ),
84 };
85 Refusal::new(code, origin, message)
86 }
87}
88
89#[derive(Clone, Copy, Debug, PartialEq, Eq)]
92pub enum Factor {
93 Value,
94 Amplitude,
95 Polynomial,
96 Exponential,
97 Gaussian,
98 Indicator,
99 Pole,
100 PrincipalValue,
101 Delta,
102}
103
104impl Factor {
105 pub fn as_str(self) -> &'static str {
106 match self {
107 Factor::Value => "this subterm",
108 Factor::Amplitude => "an amplitude",
109 Factor::Polynomial => "a polynomial",
110 Factor::Exponential => "an exponential",
111 Factor::Gaussian => "a Gaussian",
112 Factor::Indicator => "an indicator",
113 Factor::Pole => "a pole",
114 Factor::PrincipalValue => "a principal value",
115 Factor::Delta => "a delta",
116 }
117 }
118}
119
120#[derive(Clone, Copy, Debug, PartialEq, Eq)]
121pub struct AtomSketch {
122 pub first: Factor,
123 pub second: Option<Factor>,
124}
125
126impl AtomSketch {
127 pub fn of(first: Factor) -> AtomSketch {
128 AtomSketch {
129 first,
130 second: None,
131 }
132 }
133
134 pub fn pair(first: Factor, second: Factor) -> AtomSketch {
135 AtomSketch {
136 first,
137 second: Some(second),
138 }
139 }
140
141 pub fn describe(self) -> String {
142 match self.second {
143 Some(second) => format!("{} times {}", self.first.as_str(), second.as_str()),
144 None => self.first.as_str().to_string(),
145 }
146 }
147}
148
149#[derive(Clone, Copy, Debug, PartialEq, Eq)]
150pub enum LeftReason {
151 NotTempered,
152 GaussianTimesIndicator,
153 GaussianTimesPole,
154 PoleTimesIndicator,
155 PoleOrder(u16),
156 NonAffineArgument,
157 KeyedOnASignal,
158 Nonlinearity,
159 Reciprocal,
160 SeriesNotSummable,
161 SeriesNonUniform,
162 SeriesTimesSeries,
163 HilbertOfPolynomial,
164 NotInTable,
165 NoValue,
166 NotFinite,
167 Overflow,
168 Unsubstituted,
169}
170
171impl LeftReason {
172 pub fn clause(self) -> &'static str {
175 match self {
176 LeftReason::NotTempered => {
177 "an exponential growing on an unbounded side is not a tempered distribution"
178 }
179 LeftReason::GaussianTimesIndicator => {
180 "a Gaussian times an indicator leaves A: its dual is an error function"
181 }
182 LeftReason::GaussianTimesPole => {
183 "a Gaussian times a pole leaves A: its dual is a Faddeeva function"
184 }
185 LeftReason::PoleTimesIndicator => {
186 "a pole times an indicator leaves A: its dual is an exponential integral"
187 }
188 LeftReason::PoleOrder(_) => {
189 "A holds residues to total pole order 12, and a principal value only at order one"
190 }
191 LeftReason::NonAffineArgument => {
192 "an argument that is not affine in the free variable spreads to infinitely \
193 many lines"
194 }
195 LeftReason::KeyedOnASignal => {
196 "a hash keyed on a moving value is piecewise constant, and a piecewise \
197 constant has no finite atom sum"
198 }
199 LeftReason::Nonlinearity => "a bounded nonlinearity has no finite atom sum",
200 LeftReason::Reciprocal => {
201 "a reciprocal of a non-constant closed form is not an atom sum"
202 }
203 LeftReason::SeriesNotSummable => {
204 "a series converges in A only with polynomially bounded coefficients"
205 }
206 LeftReason::SeriesNonUniform => {
207 "a series index entering a Gaussian width or an indicator bound has no \
208 termwise dual"
209 }
210 LeftReason::SeriesTimesSeries => {
211 "two series multiply term by term only where one of them is a finite atom sum"
212 }
213 LeftReason::HilbertOfPolynomial => {
214 "the Hilbert transform of a polynomial has no tempered value"
215 }
216 LeftReason::NotInTable => {
217 "no rule of this Fourier dual rules version holds this atom; a later version may"
218 }
219 LeftReason::NoValue => {
220 "a division or a remainder by zero names no number, and no atom holds one"
221 }
222 LeftReason::NotFinite => {
223 "a rate, a width or an instant that is not a finite double names no number: \
224 it was divided by zero or it passed the largest double, and no atom holds one"
225 }
226 LeftReason::Overflow => {
227 "a growing exponential passes the largest double inside its window, so its \
228 transform has no finite weight; a decay read before its onset grows there, \
229 and a crop at the onset keeps it finite"
230 }
231 LeftReason::Unsubstituted => {
232 "a node or a parameter reaches the closed form unsubstituted; the graph resolves one \
233 before it normalizes"
234 }
235 }
236 }
237}
238
239#[derive(Clone, Copy, Debug, PartialEq, Eq)]
242pub struct Left {
243 pub origin: Origin,
244 pub sketch: AtomSketch,
245 pub reason: LeftReason,
246}
247
248impl Left {
249 pub fn new(origin: Origin, sketch: AtomSketch, reason: LeftReason) -> Left {
250 Left {
251 origin,
252 sketch,
253 reason,
254 }
255 }
256
257 pub fn refusal(self) -> Refusal {
258 Refusal::new(
259 Code::LeftAlgebra,
260 self.origin,
261 format!(
262 "{} left A. {}",
263 self.sketch.describe(),
264 self.reason.clause()
265 ),
266 )
267 }
268}