expr_solver/symbol/
f64.rs

1//! F64-specific symbol table implementation.
2//!
3//! This module provides standard f64 floating-point arithmetic with relaxed error handling.
4//! NaN and Inf values are allowed as results. Only domain errors that would cause panics
5//! are caught and returned as errors.
6
7use super::Symbol;
8use crate::symtable::SymTable;
9
10#[cfg(feature = "f64-floats")]
11impl SymTable {
12    /// Creates a symbol table with the standard library for f64 precision.
13    ///
14    /// This implementation uses standard f64 floating-point arithmetic.
15    /// NaN and Inf results are allowed and not treated as errors.
16    ///
17    /// ## Fixed arity functions
18    /// - `sin(x)` - Sine
19    /// - `cos(x)` - Cosine
20    /// - `tan(x)` - Tangent
21    /// - `asin(x)` - Arcsine (returns NaN for |x| > 1)
22    /// - `acos(x)` - Arccosine (returns NaN for |x| > 1)
23    /// - `atan(x)` - Arctangent
24    /// - `atan2(y, x)` - Two-argument arctangent
25    /// - `sinh(x)` - Hyperbolic sine
26    /// - `cosh(x)` - Hyperbolic cosine
27    /// - `tanh(x)` - Hyperbolic tangent
28    /// - `sqrt(x)` - Square root (returns NaN for x < 0)
29    /// - `cbrt(x)` - Cube root
30    /// - `pow(x, y)` - x raised to power y
31    /// - `log(x)` - Natural logarithm (returns NaN for x <= 0)
32    /// - `log2(x)` - Base-2 logarithm (returns NaN for x <= 0)
33    /// - `log10(x)` - Base-10 logarithm (returns NaN for x <= 0)
34    /// - `exp(x)` - e raised to power x
35    /// - `exp2(x)` - 2 raised to power x
36    /// - `abs(x)` - Absolute value
37    /// - `sign(x)` - Sign function (-1, 0, or 1)
38    /// - `floor(x)` - Floor function
39    /// - `ceil(x)` - Ceiling function
40    /// - `round(x)` - Round to nearest integer
41    /// - `trunc(x)` - Truncate to integer
42    /// - `fract(x)` - Fractional part
43    /// - `mod(x, y)` - Remainder of x/y
44    /// - `hypot(x, y)` - Euclidean distance sqrt(x²+y²)
45    /// - `clamp(x, min, max)` - Constrain value between bounds
46    ///
47    /// ## Variadic functions
48    /// - `min(x, ...)` - Minimum value
49    /// - `max(x, ...)` - Maximum value
50    /// - `sum(x, ...)` - Sum of values
51    /// - `avg(x, ...)` - Average of values
52    pub fn stdlib() -> Self {
53        Self::from_symbols(vec![
54            // Constants
55            Symbol::Const {
56                name: "pi".into(),
57                value: std::f64::consts::PI,
58                description: Some("π (3.14159...)".into()),
59                local: false,
60            },
61            Symbol::Const {
62                name: "e".into(),
63                value: std::f64::consts::E,
64                description: Some("Euler's number (2.71828...)".into()),
65                local: false,
66            },
67            Symbol::Const {
68                name: "tau".into(),
69                value: std::f64::consts::TAU,
70                description: Some("2π (6.28318...)".into()),
71                local: false,
72            },
73            Symbol::Const {
74                name: "ln2".into(),
75                value: std::f64::consts::LN_2,
76                description: Some("Natural logarithm of 2".into()),
77                local: false,
78            },
79            Symbol::Const {
80                name: "ln10".into(),
81                value: std::f64::consts::LN_10,
82                description: Some("Natural logarithm of 10".into()),
83                local: false,
84            },
85            Symbol::Const {
86                name: "sqrt2".into(),
87                value: std::f64::consts::SQRT_2,
88                description: Some("Square root of 2".into()),
89                local: false,
90            },
91            // Trigonometric functions
92            Symbol::Func {
93                name: "sin".into(),
94                args: 1,
95                variadic: false,
96                callback: |args| Ok(args[0].sin()),
97                description: Some("Sine".into()),
98                local: false,
99            },
100            Symbol::Func {
101                name: "cos".into(),
102                args: 1,
103                variadic: false,
104                callback: |args| Ok(args[0].cos()),
105                description: Some("Cosine".into()),
106                local: false,
107            },
108            Symbol::Func {
109                name: "tan".into(),
110                args: 1,
111                variadic: false,
112                callback: |args| Ok(args[0].tan()),
113                description: Some("Tangent".into()),
114                local: false,
115            },
116            Symbol::Func {
117                name: "asin".into(),
118                args: 1,
119                variadic: false,
120                callback: |args| Ok(args[0].asin()),
121                description: Some("Arcsine".into()),
122                local: false,
123            },
124            Symbol::Func {
125                name: "acos".into(),
126                args: 1,
127                variadic: false,
128                callback: |args| Ok(args[0].acos()),
129                description: Some("Arccosine".into()),
130                local: false,
131            },
132            Symbol::Func {
133                name: "atan".into(),
134                args: 1,
135                variadic: false,
136                callback: |args| Ok(args[0].atan()),
137                description: Some("Arctangent".into()),
138                local: false,
139            },
140            Symbol::Func {
141                name: "atan2".into(),
142                args: 2,
143                variadic: false,
144                callback: |args| Ok(args[0].atan2(args[1])),
145                description: Some("Two-argument arctangent".into()),
146                local: false,
147            },
148            Symbol::Func {
149                name: "sinh".into(),
150                args: 1,
151                variadic: false,
152                callback: |args| Ok(args[0].sinh()),
153                description: Some("Hyperbolic sine".into()),
154                local: false,
155            },
156            Symbol::Func {
157                name: "cosh".into(),
158                args: 1,
159                variadic: false,
160                callback: |args| Ok(args[0].cosh()),
161                description: Some("Hyperbolic cosine".into()),
162                local: false,
163            },
164            Symbol::Func {
165                name: "tanh".into(),
166                args: 1,
167                variadic: false,
168                callback: |args| Ok(args[0].tanh()),
169                description: Some("Hyperbolic tangent".into()),
170                local: false,
171            },
172            // Power and root functions
173            Symbol::Func {
174                name: "sqrt".into(),
175                args: 1,
176                variadic: false,
177                callback: |args| Ok(args[0].sqrt()),
178                description: Some("Square root".into()),
179                local: false,
180            },
181            Symbol::Func {
182                name: "cbrt".into(),
183                args: 1,
184                variadic: false,
185                callback: |args| Ok(args[0].cbrt()),
186                description: Some("Cube root".into()),
187                local: false,
188            },
189            Symbol::Func {
190                name: "pow".into(),
191                args: 2,
192                variadic: false,
193                callback: |args| Ok(args[0].powf(args[1])),
194                description: Some("x raised to power y".into()),
195                local: false,
196            },
197            // Logarithmic and exponential functions
198            Symbol::Func {
199                name: "log".into(),
200                args: 1,
201                variadic: false,
202                callback: |args| Ok(args[0].ln()),
203                description: Some("Natural logarithm".into()),
204                local: false,
205            },
206            Symbol::Func {
207                name: "log2".into(),
208                args: 1,
209                variadic: false,
210                callback: |args| Ok(args[0].log2()),
211                description: Some("Base-2 logarithm".into()),
212                local: false,
213            },
214            Symbol::Func {
215                name: "log10".into(),
216                args: 1,
217                variadic: false,
218                callback: |args| Ok(args[0].log10()),
219                description: Some("Base-10 logarithm".into()),
220                local: false,
221            },
222            Symbol::Func {
223                name: "exp".into(),
224                args: 1,
225                variadic: false,
226                callback: |args| Ok(args[0].exp()),
227                description: Some("e raised to power x".into()),
228                local: false,
229            },
230            Symbol::Func {
231                name: "exp2".into(),
232                args: 1,
233                variadic: false,
234                callback: |args| Ok(args[0].exp2()),
235                description: Some("2 raised to power x".into()),
236                local: false,
237            },
238            // Basic math functions
239            Symbol::Func {
240                name: "abs".into(),
241                args: 1,
242                variadic: false,
243                callback: |args| Ok(args[0].abs()),
244                description: Some("Absolute value".into()),
245                local: false,
246            },
247            Symbol::Func {
248                name: "sign".into(),
249                args: 1,
250                variadic: false,
251                callback: |args| {
252                    let x = args[0];
253                    if x > 0.0 {
254                        Ok(1.0)
255                    } else if x < 0.0 {
256                        Ok(-1.0)
257                    } else {
258                        Ok(0.0)
259                    }
260                },
261                description: Some("Sign function (-1, 0, or 1)".into()),
262                local: false,
263            },
264            Symbol::Func {
265                name: "floor".into(),
266                args: 1,
267                variadic: false,
268                callback: |args| Ok(args[0].floor()),
269                description: Some("Floor function".into()),
270                local: false,
271            },
272            Symbol::Func {
273                name: "ceil".into(),
274                args: 1,
275                variadic: false,
276                callback: |args| Ok(args[0].ceil()),
277                description: Some("Ceiling function".into()),
278                local: false,
279            },
280            Symbol::Func {
281                name: "round".into(),
282                args: 1,
283                variadic: false,
284                callback: |args| Ok(args[0].round()),
285                description: Some("Round to nearest integer".into()),
286                local: false,
287            },
288            Symbol::Func {
289                name: "trunc".into(),
290                args: 1,
291                variadic: false,
292                callback: |args| Ok(args[0].trunc()),
293                description: Some("Truncate to integer".into()),
294                local: false,
295            },
296            Symbol::Func {
297                name: "fract".into(),
298                args: 1,
299                variadic: false,
300                callback: |args| Ok(args[0].fract()),
301                description: Some("Fractional part".into()),
302                local: false,
303            },
304            Symbol::Func {
305                name: "mod".into(),
306                args: 2,
307                variadic: false,
308                callback: |args| Ok(args[0] % args[1]),
309                description: Some("Remainder of x/y".into()),
310                local: false,
311            },
312            Symbol::Func {
313                name: "hypot".into(),
314                args: 2,
315                variadic: false,
316                callback: |args| Ok(args[0].hypot(args[1])),
317                description: Some("Euclidean distance sqrt(x²+y²)".into()),
318                local: false,
319            },
320            Symbol::Func {
321                name: "clamp".into(),
322                args: 3,
323                variadic: false,
324                callback: |args| Ok(args[0].clamp(args[1].min(args[2]), args[2].max(args[1]))),
325                description: Some("Constrain value between bounds".into()),
326                local: false,
327            },
328            // Variadic functions
329            Symbol::Func {
330                name: "min".into(),
331                args: 1,
332                variadic: true,
333                callback: |args| Ok(args.iter().fold(f64::INFINITY, |acc, &x| acc.min(x))),
334                description: Some("Minimum value".into()),
335                local: false,
336            },
337            Symbol::Func {
338                name: "max".into(),
339                args: 1,
340                variadic: true,
341                callback: |args| Ok(args.iter().fold(f64::NEG_INFINITY, |acc, &x| acc.max(x))),
342                description: Some("Maximum value".into()),
343                local: false,
344            },
345            Symbol::Func {
346                name: "sum".into(),
347                args: 1,
348                variadic: true,
349                callback: |args| Ok(args.iter().sum()),
350                description: Some("Sum of values".into()),
351                local: false,
352            },
353            Symbol::Func {
354                name: "avg".into(),
355                args: 1,
356                variadic: true,
357                callback: |args| {
358                    let sum: f64 = args.iter().sum();
359                    let count = args.len() as f64;
360                    Ok(sum / count)
361                },
362                description: Some("Average of values".into()),
363                local: false,
364            },
365        ])
366    }
367}