tc/
func.rs

1use std::fmt::Display;
2
3#[derive(Debug, Clone, Copy, PartialEq)]
4pub enum Category {
5    General,
6    PowerLog,
7    Trigonometry,
8}
9
10impl Display for Category {
11    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
12        match self {
13            Category::General => write!(f, "General"),
14            Category::PowerLog => write!(f, "Power and Logarithms"),
15            Category::Trigonometry => write!(f, "Trigonometry"),
16        }
17    }
18}
19
20#[derive(Debug, Clone, Copy)]
21pub enum ArgCount {
22    One,
23    Two,
24    Atleast(u32),
25}
26
27impl ArgCount {
28    pub fn check(&self, count: usize) -> bool {
29        match self {
30            ArgCount::One => count == 1,
31            ArgCount::Two => count == 2,
32            ArgCount::Atleast(n) => count >= *n as usize,
33        }
34    }
35}
36
37impl Display for ArgCount {
38    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
39        match self {
40            // printed in error message
41            ArgCount::One => write!(f, "one argument"),
42            ArgCount::Two => write!(f, "two arguments"),
43            ArgCount::Atleast(n) => {
44                write!(f, "at least {n} argument{}", if *n > 1 { "s" } else { "" })
45            }
46        }
47    }
48}
49
50#[derive(Debug, Clone)]
51pub enum Args {
52    One(f64),
53    Two(f64, f64),
54    Dyn(Vec<f64>),
55}
56
57impl Args {
58    pub fn first(&self) -> f64 {
59        match self {
60            Args::One(v) => *v,
61            Args::Two(v, _) => *v,
62            Args::Dyn(v) => v[0],
63        }
64    }
65
66    pub fn second(&self) -> f64 {
67        match self {
68            Args::One(..) => unreachable!("Not enough arguments"),
69            Args::Two(_, v) => *v,
70            Args::Dyn(v) => v[1],
71        }
72    }
73
74    pub fn all(&self) -> &[f64] {
75        match self {
76            Args::One(..) => unreachable!("No dynamic arguments"),
77            Args::Two(..) => unreachable!("No dynamic arguments"),
78            Args::Dyn(v) => v.as_slice(),
79        }
80    }
81}
82
83#[allow(dead_code)]
84#[derive(Debug, Clone)]
85pub struct Func {
86    pub category: Category,
87    pub name: String,
88    pub help: String,
89    pub arg_count: ArgCount,
90    pub eval: fn(Args) -> f64,
91}
92
93pub fn all_funcs() -> Vec<Func> {
94    vec![
95        Func {
96            category: Category::General,
97            name: "floor".to_string(),
98            help: "floor value to lower integer".to_string(),
99            arg_count: ArgCount::One,
100            eval: |args| args.first().floor(),
101        },
102        Func {
103            category: Category::General,
104            name: "round".to_string(),
105            help: "round value to nearest integer".to_string(),
106            arg_count: ArgCount::One,
107            eval: |args| args.first().round(),
108        },
109        Func {
110            category: Category::General,
111            name: "ceil".to_string(),
112            help: "ceil value to upper integer".to_string(),
113            arg_count: ArgCount::One,
114            eval: |args| args.first().ceil(),
115        },
116        Func {
117            category: Category::General,
118            name: "trunc".to_string(),
119            help: "truncate value to its integer part (round towards zero)".to_string(),
120            arg_count: ArgCount::One,
121            eval: |args| args.first().trunc(),
122        },
123        Func {
124            category: Category::General,
125            name: "fract".to_string(),
126            help: "truncate value to its fractional part".to_string(),
127            arg_count: ArgCount::One,
128            eval: |args| args.first().fract(),
129        },
130        Func {
131            category: Category::General,
132            name: "abs".to_string(),
133            help: "compute absolute value".to_string(),
134            arg_count: ArgCount::One,
135            eval: |args| args.first().abs(),
136        },
137        Func {
138            category: Category::General,
139            name: "sign".to_string(),
140            help: "value sign (1 or -1)".to_string(),
141            arg_count: ArgCount::One,
142            eval: |args| args.first().signum(),
143        },
144        Func {
145            category: Category::General,
146            name: "min".to_string(),
147            help: "minimum of all arguments".to_string(),
148            arg_count: ArgCount::Atleast(1),
149            eval: |args| {
150                let args = args.all();
151                let mut min = args[0];
152                for arg in args.iter().skip(1) {
153                    min = min.min(*arg);
154                }
155                min
156            },
157        },
158        Func {
159            category: Category::General,
160            name: "max".to_string(),
161            help: "maximum of all arguments".to_string(),
162            arg_count: ArgCount::Atleast(1),
163            eval: |args| {
164                let args = args.all();
165                let mut max = args[0];
166                for arg in args.iter().skip(1) {
167                    max = max.max(*arg);
168                }
169                max
170            },
171        },
172        Func {
173            category: Category::PowerLog,
174            name: "pow".to_string(),
175            arg_count: ArgCount::Two,
176            help: "first argument raised to the power the second argument".to_string(),
177            eval: |args| args.first().powf(args.second()),
178        },
179        Func {
180            category: Category::PowerLog,
181            name: "sqrt".to_string(),
182            help: "square root".to_string(),
183            arg_count: ArgCount::One,
184            eval: |args| args.first().sqrt(),
185        },
186        Func {
187            category: Category::PowerLog,
188            name: "cbrt".to_string(),
189            help: "cubic root".to_string(),
190            arg_count: ArgCount::One,
191            eval: |args| args.first().cbrt(),
192        },
193        Func {
194            category: Category::PowerLog,
195            name: "exp".to_string(),
196            help: "exponential function (exp(x) = pow(e, x))".to_string(),
197            arg_count: ArgCount::One,
198            eval: |args| args.first().exp(),
199        },
200        Func {
201            category: Category::PowerLog,
202            name: "log".to_string(),
203            help: "log(b, x) is base b logarithm of x".to_string(),
204            arg_count: ArgCount::Two,
205            eval: |args| args.first().log(args.second()),
206        },
207        Func {
208            category: Category::PowerLog,
209            name: "ln".to_string(),
210            help: "natural logarithm".to_string(),
211            arg_count: ArgCount::One,
212            eval: |args| args.first().ln(),
213        },
214        Func {
215            category: Category::PowerLog,
216            name: "log2".to_string(),
217            help: "base 2 logarithm".to_string(),
218            arg_count: ArgCount::One,
219            eval: |args| args.first().log2(),
220        },
221        Func {
222            category: Category::PowerLog,
223            name: "log10".to_string(),
224            help: "base 10 logarithm".to_string(),
225            arg_count: ArgCount::One,
226            eval: |args| args.first().log10(),
227        },
228        Func {
229            category: Category::Trigonometry,
230            name: "sin".to_string(),
231            help: "sine function".to_string(),
232            arg_count: ArgCount::One,
233            eval: |args| args.first().sin(),
234        },
235        Func {
236            category: Category::Trigonometry,
237            name: "cos".to_string(),
238            help: "cosine function".to_string(),
239            arg_count: ArgCount::One,
240            eval: |args| args.first().cos(),
241        },
242        Func {
243            category: Category::Trigonometry,
244            name: "tan".to_string(),
245            help: "tangent function".to_string(),
246            arg_count: ArgCount::One,
247            eval: |args| args.first().tan(),
248        },
249        Func {
250            category: Category::Trigonometry,
251            name: "csc".to_string(),
252            help: "cosecante function (inverse of sine)".to_string(),
253            arg_count: ArgCount::One,
254            eval: |args| 1.0 / args.first().sin(),
255        },
256        Func {
257            category: Category::Trigonometry,
258            name: "sec".to_string(),
259            help: "secante function (inverse of cosine)".to_string(),
260            arg_count: ArgCount::One,
261            eval: |args| 1.0 / args.first().cos(),
262        },
263        Func {
264            category: Category::Trigonometry,
265            name: "cot".to_string(),
266            help: "cotangent function (inverse of tangent)".to_string(),
267            arg_count: ArgCount::One,
268            eval: |args| 1.0 / args.first().tan(),
269        },
270        Func {
271            category: Category::Trigonometry,
272            name: "asin".to_string(),
273            help: "arc sine function".to_string(),
274            arg_count: ArgCount::One,
275            eval: |args| args.first().asin(),
276        },
277        Func {
278            category: Category::Trigonometry,
279            name: "acos".to_string(),
280            help: "arc cosine function".to_string(),
281            arg_count: ArgCount::One,
282            eval: |args| args.first().acos(),
283        },
284        Func {
285            category: Category::Trigonometry,
286            name: "atan".to_string(),
287            help: "arc tangent function".to_string(),
288            arg_count: ArgCount::One,
289            eval: |args| args.first().atan(),
290        },
291        Func {
292            category: Category::Trigonometry,
293            name: "sinh".to_string(),
294            help: "hyperbolic sine function".to_string(),
295            arg_count: ArgCount::One,
296            eval: |args| args.first().sinh(),
297        },
298        Func {
299            category: Category::Trigonometry,
300            name: "cosh".to_string(),
301            help: "hyperbolic cosine function".to_string(),
302            arg_count: ArgCount::One,
303            eval: |args| args.first().cosh(),
304        },
305        Func {
306            category: Category::Trigonometry,
307            name: "tanh".to_string(),
308            help: "hyperbolic tangent function".to_string(),
309            arg_count: ArgCount::One,
310            eval: |args| args.first().tanh(),
311        },
312        Func {
313            category: Category::Trigonometry,
314            name: "asinh".to_string(),
315            help: "hyperbolic arc sine function".to_string(),
316            arg_count: ArgCount::One,
317            eval: |args| args.first().asinh(),
318        },
319        Func {
320            category: Category::Trigonometry,
321            name: "acosh".to_string(),
322            help: "hyperbolic arc cosine function".to_string(),
323            arg_count: ArgCount::One,
324            eval: |args| args.first().acosh(),
325        },
326        Func {
327            category: Category::Trigonometry,
328            name: "atanh".to_string(),
329            help: "hyperbolic arc tangent function".to_string(),
330            arg_count: ArgCount::One,
331            eval: |args| args.first().atanh(),
332        },
333        Func {
334            category: Category::Trigonometry,
335            name: "degs".to_string(),
336            help: "convert radians to degrees".to_string(),
337            arg_count: ArgCount::One,
338            eval: |args| args.first().to_degrees(),
339        },
340        Func {
341            category: Category::Trigonometry,
342            name: "rads".to_string(),
343            help: "convert degrees to radians".to_string(),
344            arg_count: ArgCount::One,
345            eval: |args| args.first().to_radians(),
346        },
347    ]
348}