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
54struct 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 "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 "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 "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 "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}