1use core::fmt;
2use std::error::Error;
3
4#[derive(Debug, Clone, Copy, PartialEq)]
6pub enum CalculusError {
7 NonFinitePoint {
9 name: &'static str,
11 value: f64,
13 },
14 NonFiniteBound {
16 bound: &'static str,
18 value: f64,
20 },
21 NonFiniteStep(f64),
23 NonPositiveStep(f64),
25 NonFiniteTolerance(f64),
27 NegativeTolerance(f64),
29 ZeroSubintervals,
31 OddSubintervalCount(usize),
33 NonFiniteEvaluation {
35 input: f64,
37 value: f64,
39 },
40 LimitMismatch {
42 left: f64,
44 right: f64,
46 tolerance: f64,
48 },
49}
50
51impl CalculusError {
52 pub(crate) const fn validate_point(name: &'static str, value: f64) -> Result<f64, Self> {
53 if !value.is_finite() {
54 return Err(Self::NonFinitePoint { name, value });
55 }
56
57 Ok(value)
58 }
59
60 pub(crate) const fn validate_bound(bound: &'static str, value: f64) -> Result<f64, Self> {
61 if !value.is_finite() {
62 return Err(Self::NonFiniteBound { bound, value });
63 }
64
65 Ok(value)
66 }
67
68 pub(crate) const fn validate_step(step: f64) -> Result<f64, Self> {
69 if !step.is_finite() {
70 return Err(Self::NonFiniteStep(step));
71 }
72
73 if step <= 0.0 {
74 return Err(Self::NonPositiveStep(step));
75 }
76
77 Ok(step)
78 }
79
80 pub(crate) const fn validate_tolerance(tolerance: f64) -> Result<f64, Self> {
81 if !tolerance.is_finite() {
82 return Err(Self::NonFiniteTolerance(tolerance));
83 }
84
85 if tolerance < 0.0 {
86 return Err(Self::NegativeTolerance(tolerance));
87 }
88
89 Ok(tolerance)
90 }
91
92 pub(crate) const fn validate_subintervals(subintervals: usize) -> Result<usize, Self> {
93 if subintervals == 0 {
94 return Err(Self::ZeroSubintervals);
95 }
96
97 Ok(subintervals)
98 }
99
100 pub(crate) fn validate_even_subintervals(subintervals: usize) -> Result<usize, Self> {
101 Self::validate_subintervals(subintervals)?;
102
103 if !subintervals.is_multiple_of(2_usize) {
104 return Err(Self::OddSubintervalCount(subintervals));
105 }
106
107 Ok(subintervals)
108 }
109
110 pub(crate) const fn validate_evaluation(input: f64, value: f64) -> Result<f64, Self> {
111 if !value.is_finite() {
112 return Err(Self::NonFiniteEvaluation { input, value });
113 }
114
115 Ok(value)
116 }
117}
118
119impl fmt::Display for CalculusError {
120 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
121 match self {
122 Self::NonFinitePoint { name, value } => {
123 write!(formatter, "{name} point must be finite, got {value}")
124 },
125 Self::NonFiniteBound { bound, value } => {
126 write!(formatter, "{bound} bound must be finite, got {value}")
127 },
128 Self::NonFiniteStep(value) => {
129 write!(formatter, "step must be finite, got {value}")
130 },
131 Self::NonPositiveStep(value) => {
132 write!(formatter, "step must be positive, got {value}")
133 },
134 Self::NonFiniteTolerance(value) => {
135 write!(formatter, "tolerance must be finite, got {value}")
136 },
137 Self::NegativeTolerance(value) => {
138 write!(formatter, "tolerance must be non-negative, got {value}")
139 },
140 Self::ZeroSubintervals => {
141 write!(formatter, "subinterval count must be greater than zero")
142 },
143 Self::OddSubintervalCount(value) => write!(
144 formatter,
145 "Simpson integration requires an even number of subintervals, got {value}"
146 ),
147 Self::NonFiniteEvaluation { input, value } => write!(
148 formatter,
149 "function evaluation must be finite, got {value} at input {input}"
150 ),
151 Self::LimitMismatch {
152 left,
153 right,
154 tolerance,
155 } => write!(
156 formatter,
157 "left and right limit samples disagree beyond tolerance: left={left}, right={right}, tolerance={tolerance}"
158 ),
159 }
160 }
161}
162
163impl Error for CalculusError {}