tc_transact/public/
number.rs

1use safecast::{CastFrom, TryCastFrom, TryCastInto};
2use std::marker::PhantomData;
3
4use tc_error::*;
5use tc_value::{Float, Number, NumberClass, NumberInstance, Trigonometry, Value};
6use tcgeneric::PathSegment;
7
8use super::{GetHandler, Handler, PostHandler, Route, StateInstance};
9
10struct Dual<F> {
11    op: F,
12}
13
14impl<F> Dual<F> {
15    fn new(op: F) -> Self {
16        Self { op }
17    }
18}
19
20impl<'a, State, F> Handler<'a, State> for Dual<F>
21where
22    State: StateInstance,
23    F: Fn(Number) -> TCResult<Number> + Send + 'a,
24    Number: TryCastFrom<State>,
25    Value: TryCastFrom<State>,
26{
27    fn get<'b>(self: Box<Self>) -> Option<GetHandler<'a, 'b, State::Txn, State>>
28    where
29        'b: 'a,
30    {
31        Some(Box::new(|_txn, value| {
32            Box::pin(async move {
33                let value = value.try_cast_into(|v| TCError::unexpected(v, "a Number"))?;
34                (self.op)(value).map(Value::Number).map(State::from)
35            })
36        }))
37    }
38
39    fn post<'b>(self: Box<Self>) -> Option<PostHandler<'a, 'b, State::Txn, State>>
40    where
41        'b: 'a,
42    {
43        Some(Box::new(|_txn, mut params| {
44            Box::pin(async move {
45                let value: Number = params.require("r")?;
46                params.expect_empty()?;
47
48                (self.op)(value).map(Value::Number).map(State::from)
49            })
50        }))
51    }
52}
53
54// TODO: should this be more general, like `DualWithDefaultArg`?
55struct Log {
56    n: Number,
57}
58
59impl Log {
60    fn new(n: Number) -> Self {
61        Self { n }
62    }
63}
64
65impl<'a, State: StateInstance> Handler<'a, State> for Log
66where
67    Value: TryCastFrom<State>,
68{
69    fn get<'b>(self: Box<Self>) -> Option<GetHandler<'a, 'b, State::Txn, State>>
70    where
71        'b: 'a,
72    {
73        Some(Box::new(|_txn, value| {
74            Box::pin(async move {
75                if self.n == Number::from(0) {
76                    return Err(bad_request!("the logarithm of zero is undefined"));
77                }
78
79                let log = if value.is_none() {
80                    Ok(self.n.ln())
81                } else {
82                    let base: Number =
83                        value.try_cast_into(|v| TCError::unexpected(v, "a Number"))?;
84
85                    if base.class().is_complex() {
86                        Err(bad_request!("invalid base {} for log", base))
87                    } else {
88                        let base = Float::cast_from(base);
89                        Ok(self.n.log(base))
90                    }
91                }?;
92
93                Ok(Value::Number(log).into())
94            })
95        }))
96    }
97
98    fn post<'b>(self: Box<Self>) -> Option<PostHandler<'a, 'b, State::Txn, State>>
99    where
100        'b: 'a,
101    {
102        Some(Box::new(|_txn, mut params| {
103            Box::pin(async move {
104                let base: Value = params.or_default("r")?;
105                params.expect_empty()?;
106
107                let log = if base.is_none() {
108                    self.n.ln()
109                } else {
110                    let base: Number =
111                        base.try_cast_into(|v| bad_request!("invalid base {v:?} for log"))?;
112
113                    if base.class().is_complex() {
114                        return Err(bad_request!("log does not support a complex base {}", base));
115                    }
116
117                    let base = Float::cast_from(base);
118                    self.n.log(base)
119                };
120
121                Ok(Value::Number(log).into())
122            })
123        }))
124    }
125}
126
127struct Unary<State, F> {
128    name: &'static str,
129    op: F,
130    state: PhantomData<State>,
131}
132
133impl<State, F> Unary<State, F> {
134    fn new(name: &'static str, op: F) -> Self {
135        Self {
136            name,
137            op,
138            state: PhantomData,
139        }
140    }
141}
142
143impl<'a, State, F> Handler<'a, State> for Unary<State, F>
144where
145    State: StateInstance,
146    F: Fn() -> Number + Send + 'a,
147{
148    fn get<'b>(self: Box<Self>) -> Option<GetHandler<'a, 'b, State::Txn, State>>
149    where
150        'b: 'a,
151    {
152        Some(Box::new(|_txn, value| {
153            Box::pin(async move {
154                if value.is_some() {
155                    return Err(bad_request!(
156                        "{} does not have any parameters (found {:?})",
157                        self.name,
158                        value
159                    ));
160                }
161
162                Ok(State::from(Value::from((self.op)())))
163            })
164        }))
165    }
166}
167
168impl<State: StateInstance> Route<State> for Number
169where
170    Number: TryCastFrom<State>,
171    Value: TryCastFrom<State>,
172{
173    fn route<'a>(&'a self, path: &'a [PathSegment]) -> Option<Box<dyn Handler<'a, State> + 'a>> {
174        if path.len() != 1 {
175            return None;
176        }
177
178        let handler: Box<dyn Handler<'a, State> + 'a> = match path[0].as_str() {
179            // basic math
180            "abs" => Box::new(Unary::new("abs", move || self.abs())),
181            "add" => Box::new(Dual::new(move |other| Ok(*self + other))),
182            "and" => Box::new(Dual::new(move |other| Ok(self.and(other)))),
183            "div" => Box::new(Dual::new(move |other: Number| {
184                if other == other.class().zero() {
185                    Err(bad_request!("cannot divide by zero"))
186                } else {
187                    Ok(*self / other)
188                }
189            })),
190            "exp" => Box::new(Unary::new("exp", move || self.exp())),
191            "ln" => Box::new(Unary::new("ln", move || self.ln())),
192            "log" => Box::new(Log::new(*self)),
193            "mod" => Box::new(Dual::new(move |other| Ok(*self % other))),
194            "mul" => Box::new(Dual::new(move |other| Ok(*self * other))),
195            "round" => Box::new(Unary::new("round", move || self.round())),
196            "sub" => Box::new(Dual::new(move |other| Ok(*self - other))),
197            "pow" => Box::new(Dual::new(move |other| Ok(self.pow(other)))),
198
199            // comparison
200            "gt" => Box::new(Dual::new(move |other| Ok((*self > other).into()))),
201            "ge" => Box::new(Dual::new(move |other| Ok((*self >= other).into()))),
202            "lt" => Box::new(Dual::new(move |other| Ok((*self < other).into()))),
203            "le" => Box::new(Dual::new(move |other| Ok((*self <= other).into()))),
204            "not" => Box::new(Unary::new("not", move || self.not())),
205            "or" => Box::new(Dual::new(move |other| Ok(self.or(other)))),
206            "xor" => Box::new(Dual::new(move |other| Ok(self.xor(other)))),
207
208            // trigonometry
209            "asin" => Box::new(Unary::new("abs", move || self.asin())),
210            "sin" => Box::new(Unary::new("sin", move || self.sin())),
211            "asinh" => Box::new(Unary::new("asinh", move || self.asinh())),
212            "sinh" => Box::new(Unary::new("sinh", move || self.sinh())),
213
214            "acos" => Box::new(Unary::new("acos", move || self.acos())),
215            "cos" => Box::new(Unary::new("cos", move || self.cos())),
216            "acosh" => Box::new(Unary::new("acosh", move || self.acosh())),
217            "cosh" => Box::new(Unary::new("cosh", move || self.cosh())),
218
219            "atan" => Box::new(Unary::new("atan", move || self.atan())),
220            "tan" => Box::new(Unary::new("tan", move || self.tan())),
221            "atanh" => Box::new(Unary::new("atanh", move || self.atanh())),
222            "tanh" => Box::new(Unary::new("tanh", move || self.tanh())),
223
224            // complex
225            "imag" => match self {
226                Number::Complex(c) => Box::new(Unary::new("imag", move || c.im().into())),
227                _real => Box::new(Unary::new("imag", || Number::from(0.0f32))),
228            },
229            "real" => match self {
230                Number::Complex(c) => Box::new(Unary::new("real", move || c.re().into())),
231                real => Box::new(Unary::new("real", move || *real)),
232            },
233
234            _ => return None,
235        };
236
237        Some(handler)
238    }
239}