squiid_engine/
bucket.rs

1// items on the stack are called Buckets
2
3use std::{collections::HashMap, f64::consts, fmt::Display, sync::LazyLock};
4
5use rust_decimal::{prelude::FromPrimitive, Decimal, MathematicalOps};
6use rust_decimal_macros::dec;
7
8/// Types of constants
9#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
10pub enum ConstantTypes {
11    /// Pi
12    Pi,
13    /// Pi/2
14    HalfPi,
15    /// Pi/3
16    ThirdPi,
17    /// Pi/4
18    QuarterPi,
19    /// Pi/6
20    SixthPi,
21    /// Pi/8
22    EighthPi,
23    /// 2*pi
24    TwoPi,
25    /// Euler's number
26    E,
27    /// Speed of light
28    C,
29    /// Gravitational constant
30    G,
31    /// Golden ratio
32    Phi,
33}
34
35pub static CONSTANT_IDENTIFIERS: LazyLock<HashMap<&'static str, ConstantTypes>> =
36    LazyLock::new(|| {
37        HashMap::from([
38            ("#pi", ConstantTypes::Pi),
39            ("#e", ConstantTypes::E),
40            ("#tau", ConstantTypes::TwoPi),
41            ("#c", ConstantTypes::C),
42            ("#G", ConstantTypes::G),
43            ("#phi", ConstantTypes::Phi),
44            ("#halfpi", ConstantTypes::HalfPi),
45            ("#thirdpi", ConstantTypes::ThirdPi),
46            ("#quarterpi", ConstantTypes::QuarterPi),
47            ("#sixthpi", ConstantTypes::SixthPi),
48            ("#eighthpi", ConstantTypes::EighthPi),
49            ("#twopi", ConstantTypes::TwoPi),
50        ])
51    });
52
53/// Types of Buckets
54#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
55pub enum BucketTypes {
56    /// A floating point number. Also contains integers such as 3.0
57    Float,
58    /// A string
59    String,
60    /// A constant
61    Constant(ConstantTypes),
62    /// Undefined value, such as tan(pi/2)
63    // TODO: should undefined error out? in trig and stuff
64    Undefined,
65}
66
67/// Bucket contains the items that can be on the stack
68#[derive(Clone, Eq, PartialEq, Hash, Debug)]
69pub struct Bucket {
70    /// Bucket value. Will be None when undefined
71    pub value: Option<String>,
72    /// The type of the Bucket
73    pub bucket_type: BucketTypes,
74}
75
76impl Bucket {
77    /// Create a new undefined Bucket
78    pub fn new_undefined() -> Self {
79        Self {
80            value: None,
81            bucket_type: BucketTypes::Undefined,
82        }
83    }
84
85    /// Create a Bucket from a constant
86    pub fn from_constant(constant_type: ConstantTypes) -> Self {
87        let value = match constant_type {
88            ConstantTypes::Pi => consts::PI,
89            ConstantTypes::HalfPi => consts::FRAC_PI_2,
90            ConstantTypes::ThirdPi => consts::FRAC_PI_3,
91            ConstantTypes::QuarterPi => consts::FRAC_PI_4,
92            ConstantTypes::SixthPi => consts::FRAC_PI_6,
93            ConstantTypes::EighthPi => consts::FRAC_PI_8,
94            ConstantTypes::TwoPi => consts::TAU,
95            ConstantTypes::E => consts::E,
96            ConstantTypes::C => 299_792_458_f64,
97            ConstantTypes::G => 6.67430 * 10_f64.powf(-11_f64),
98            ConstantTypes::Phi => 1.618_033_988_749_895_f64,
99        }
100        .to_string();
101
102        Bucket {
103            value: Some(value),
104            bucket_type: BucketTypes::Constant(constant_type),
105        }
106    }
107
108    /// Sine
109    pub fn sin(&self) -> Option<Self> {
110        match &self.bucket_type {
111            BucketTypes::Constant(constant_type) => match constant_type {
112                ConstantTypes::E | ConstantTypes::C | ConstantTypes::G | ConstantTypes::Phi => {
113                    Some(Self::from(self.value.clone()?.parse::<f64>().ok()?.sin()))
114                }
115                ConstantTypes::Pi | ConstantTypes::TwoPi => Some(Self::from(0)),
116                ConstantTypes::HalfPi => Some(Self::from(1)),
117                ConstantTypes::QuarterPi => Some(Self::from(consts::FRAC_1_SQRT_2)),
118                ConstantTypes::EighthPi => Some(Self::from(consts::FRAC_PI_8.sin())),
119                ConstantTypes::SixthPi => Some(Self::from(0.5)),
120                ConstantTypes::ThirdPi => Some(Self::from(consts::FRAC_PI_3.sin())),
121            },
122            BucketTypes::Float => Some(Self::from(
123                Decimal::from_f64(self.value.clone()?.parse::<f64>().ok()?)?.checked_sin()?,
124            )),
125            BucketTypes::String | BucketTypes::Undefined => None,
126        }
127    }
128
129    /// Cosine
130    pub fn cos(&self) -> Option<Self> {
131        match &self.bucket_type {
132            BucketTypes::Constant(constant_type) => match constant_type {
133                ConstantTypes::E | ConstantTypes::C | ConstantTypes::G | ConstantTypes::Phi => {
134                    Some(Self::from(self.value.clone()?.parse::<f64>().ok()?.cos()))
135                }
136                ConstantTypes::Pi => Some(Self::from(-1)),
137                ConstantTypes::TwoPi => Some(Self::from(1)),
138                ConstantTypes::HalfPi => Some(Self::from(0)),
139                ConstantTypes::QuarterPi => Some(Self::from(consts::FRAC_1_SQRT_2)),
140                ConstantTypes::EighthPi => Some(Self::from(consts::FRAC_PI_8.cos())),
141                ConstantTypes::SixthPi => Some(Self::from(consts::FRAC_PI_6.cos())),
142                ConstantTypes::ThirdPi => Some(Self::from(0.5)),
143            },
144            BucketTypes::Float => Some(Self::from(
145                Decimal::from_f64(self.value.clone()?.parse::<f64>().ok()?)?.checked_cos()?,
146            )),
147            BucketTypes::String | BucketTypes::Undefined => None,
148        }
149    }
150
151    /// Tangent
152    pub fn tan(&self) -> Option<Self> {
153        match &self.bucket_type {
154            BucketTypes::Constant(constant_type) => match constant_type {
155                ConstantTypes::E | ConstantTypes::C | ConstantTypes::G | ConstantTypes::Phi => {
156                    Some(Self::from(self.value.clone()?.parse::<f64>().ok()?.tan()))
157                }
158                ConstantTypes::Pi | ConstantTypes::TwoPi => Some(Self::from(0)),
159                ConstantTypes::HalfPi => Some(Self::new_undefined()),
160                ConstantTypes::QuarterPi => Some(Self::from(1)),
161                ConstantTypes::EighthPi => Some(Self::from(consts::FRAC_PI_8.tan())),
162                ConstantTypes::SixthPi => Some(Self::from(consts::FRAC_PI_6.tan())),
163                ConstantTypes::ThirdPi => Some(Self::from(consts::FRAC_PI_3.tan())),
164            },
165            BucketTypes::Float => match &self.value {
166                Some(value) => {
167                    let float_value = value.parse::<f64>().ok()?;
168                    // check if equal to 3pi/2
169                    if float_value == (3.0 * consts::PI) / 2.0 {
170                        Some(Self::new_undefined())
171                    } else {
172                        Some(Self::from(
173                            Decimal::from_f64(self.value.clone()?.parse::<f64>().ok()?)?
174                                .checked_tan()?,
175                        ))
176                    }
177                }
178                None => None,
179            },
180            BucketTypes::String | BucketTypes::Undefined => None,
181        }
182    }
183
184    /// Cosecant
185    pub fn csc(&self) -> Option<Self> {
186        match &self.bucket_type {
187            BucketTypes::Constant(constant_type) => match constant_type {
188                // Compute:
189                // 1 / sin(value)
190                ConstantTypes::E | ConstantTypes::C | ConstantTypes::G | ConstantTypes::Phi => {
191                    Some(Self::from(
192                        dec!(1.0)
193                            / Decimal::from_f64(self.value.clone()?.parse::<f64>().ok()?)?
194                                .checked_sin()?,
195                    ))
196                }
197                ConstantTypes::Pi | ConstantTypes::TwoPi => Some(Self::new_undefined()),
198                ConstantTypes::HalfPi => Some(Self::from(1)),
199                ConstantTypes::QuarterPi => Some(Self::from(consts::SQRT_2)),
200                ConstantTypes::EighthPi => Some(Self::from(
201                    dec!(1.0) / Decimal::from_f64(consts::FRAC_PI_8.sin())?,
202                )),
203                ConstantTypes::SixthPi => Some(Self::from(2)),
204                ConstantTypes::ThirdPi => Some(Self::from(
205                    dec!(1.0) / Decimal::from_f64(consts::FRAC_PI_3.sin())?,
206                )),
207            },
208            BucketTypes::Float => match &self.value {
209                Some(value) => {
210                    let float_value = value.parse::<f64>().ok()?;
211                    if float_value == 0.0 {
212                        Some(Self::new_undefined())
213                    } else {
214                        Some(Self::from(
215                            dec!(1.0) / Decimal::from_f64(float_value)?.checked_sin()?,
216                        ))
217                    }
218                }
219                None => None,
220            },
221            BucketTypes::String | BucketTypes::Undefined => None,
222        }
223    }
224
225    /// Secant
226    pub fn sec(&self) -> Option<Self> {
227        match &self.bucket_type {
228            BucketTypes::Constant(constant_type) => match constant_type {
229                // Compute:
230                // 1 / cos(value)
231                ConstantTypes::E | ConstantTypes::C | ConstantTypes::G | ConstantTypes::Phi => {
232                    Some(Self::from(
233                        dec!(1.0)
234                            / Decimal::from_f64(self.value.clone()?.parse::<f64>().ok()?)?
235                                .checked_cos()?,
236                    ))
237                }
238                ConstantTypes::Pi => Some(Self::from(-1)),
239                ConstantTypes::TwoPi => Some(Self::from(1)),
240                ConstantTypes::HalfPi => Some(Self::new_undefined()),
241                ConstantTypes::QuarterPi => Some(Self::from(consts::SQRT_2)),
242                ConstantTypes::EighthPi => Some(Self::from(
243                    dec!(1.0) / Decimal::from_f64(consts::FRAC_PI_8.cos())?,
244                )),
245                ConstantTypes::SixthPi => Some(Self::from(
246                    dec!(1.0) / Decimal::from_f64(consts::FRAC_PI_6.cos())?,
247                )),
248                ConstantTypes::ThirdPi => Some(Self::from(2)),
249            },
250            BucketTypes::Float => match &self.value {
251                Some(value) => {
252                    let float_value = value.parse::<f64>().ok()?;
253
254                    // Handle sec(0) correctly, which should return 1
255                    if float_value == 0.0 {
256                        Some(Self::from(1)) // sec(0) = 1
257                    } else if float_value == (3.0 * consts::PI) / 2.0 {
258                        Some(Self::new_undefined()) // sec(3#pi/2) = undefined
259                    } else {
260                        Some(Self::from(
261                            dec!(1.0) / Decimal::from_f64(float_value)?.checked_cos()?,
262                        ))
263                    }
264                }
265                None => None,
266            },
267            BucketTypes::String | BucketTypes::Undefined => None,
268        }
269    }
270
271    /// Cotangent
272    pub fn cot(&self) -> Option<Self> {
273        match &self.bucket_type {
274            BucketTypes::Constant(constant_type) => match constant_type {
275                // Compute:
276                // 1 / tan(value)
277                ConstantTypes::E | ConstantTypes::C | ConstantTypes::G | ConstantTypes::Phi => {
278                    Some(Self::from(
279                        dec!(1.0)
280                            / Decimal::from_f64(self.value.clone()?.parse::<f64>().ok()?)?
281                                .checked_tan()?,
282                    ))
283                }
284                ConstantTypes::Pi | ConstantTypes::TwoPi => Some(Self::new_undefined()),
285                ConstantTypes::HalfPi => Some(Self::from(0)),
286                ConstantTypes::QuarterPi => Some(Self::from(1)),
287                ConstantTypes::EighthPi => Some(Self::from(
288                    dec!(1.0) / Decimal::from_f64(consts::FRAC_PI_8.tan())?,
289                )),
290                ConstantTypes::SixthPi => Some(Self::from(
291                    dec!(1.0) / Decimal::from_f64(consts::FRAC_PI_6.tan())?,
292                )),
293                ConstantTypes::ThirdPi => Some(Self::from(
294                    dec!(1.0) / Decimal::from_f64(consts::FRAC_PI_3.tan())?,
295                )),
296            },
297            BucketTypes::Float => match &self.value {
298                Some(value) => {
299                    let float_value = value.parse::<f64>().ok()?;
300                    if float_value == 0.0 {
301                        Some(Self::new_undefined())
302                    } else {
303                        Some(Self::from(
304                            dec!(1.0) / Decimal::from_f64(float_value)?.checked_tan()?,
305                        ))
306                    }
307                }
308                None => None,
309            },
310            BucketTypes::String | BucketTypes::Undefined => None,
311        }
312    }
313}
314
315// implementation of .to_string()
316impl Display for Bucket {
317    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
318        f.write_str(&match &self.value {
319            Some(value) => value.to_string(),
320            None => "Undefined".to_owned(),
321        })
322    }
323}
324
325/// Generate `From<>` implementations of floating points for [`Bucket`]
326macro_rules! generate_float_impl {
327    ( $($t:ty),* ) => {
328        $( impl From<$t> for Bucket {
329            fn from(value: $t) -> Self {
330                Self {
331                    value: Some(value.to_string()),
332                    bucket_type: BucketTypes::Float,
333                }
334            }
335        } ) *
336    };
337}
338
339/// Generate `From<>` implementations of integers for [`Bucket`]
340macro_rules! generate_int_impl {
341    ( $($t:ty),* ) => {
342        $( impl From<$t> for Bucket {
343            fn from(value: $t) -> Self {
344                Self {
345                    value: Some((value as f64).to_string()),
346                    bucket_type: BucketTypes::Float,
347                }
348            }
349        } ) *
350    };
351}
352
353generate_float_impl! {f32, f64}
354generate_int_impl! { u8, u16, u32, u64, i8, i16, i32, i64 }
355
356impl From<Decimal> for Bucket {
357    fn from(value: Decimal) -> Self {
358        Self {
359            value: Some(value.to_string()),
360            bucket_type: BucketTypes::Float,
361        }
362    }
363}
364
365impl From<String> for Bucket {
366    fn from(value: String) -> Self {
367        Self {
368            value: Some(value),
369            bucket_type: BucketTypes::String,
370        }
371    }
372}
373
374impl From<&str> for Bucket {
375    fn from(value: &str) -> Self {
376        Self {
377            value: Some(value.to_owned()),
378            bucket_type: BucketTypes::String,
379        }
380    }
381}