1use std::ops::{AddAssign, BitAnd, Div, Rem, SubAssign};
3
4use crate::{
5 arith::NumberValue,
6 core::{
7 cons::Cons,
8 gc::Context,
9 object::{Number, NumberType, Object},
10 },
11};
12use anyhow::Result;
13use anyhow::anyhow;
14use num_bigint::BigInt;
15use num_integer::Integer;
16use num_traits::{FromPrimitive, Signed, ToPrimitive, Zero};
17
18use rune_macros::defun;
19
20#[inline(always)]
21fn coerce(arg: Number) -> f64 {
22 match arg.untag() {
23 NumberType::Int(i) => i as f64,
24 NumberType::Float(f) => **f,
25 NumberType::Big(b) => b.to_f64().unwrap(), }
27}
28
29fn double_integer_scale(d: f64) -> i64 {
37 let exponent = frexp_f(d).1 - 1;
38
39 if f64::MIN_EXP as i64 - 1 <= exponent && exponent < i64::MAX {
40 f64::MANTISSA_DIGITS as i64 - 1 - exponent
41 } else {
42 let x = f64::MANTISSA_DIGITS as i64 - f64::MIN_EXP as i64;
43 let c = if exponent == i64::MAX { 1 } else { 0 };
44 if d.is_nan() { x + 2 } else { x + c }
45 }
46}
47
48fn rescale_for_division(n: NumberValue, nscale: i64, dscale: i64) -> Result<BigInt> {
49 let mut result = match n {
50 NumberValue::Int(i) => BigInt::from(i),
51 NumberValue::Float(f) => {
52 if (f64::MANTISSA_DIGITS as i64 - f64::MIN_EXP as i64) < nscale {
53 return Err(anyhow!("Overflow error"));
54 }
55 BigInt::from_f64(libm::scalbn(f, nscale as i32)).expect("Conversion error")
56 }
57 NumberValue::Big(b) => b,
58 };
59
60 if nscale < dscale {
61 let power = (dscale - nscale) * f64::RADIX.ilog2() as i64;
62 result *= BigInt::from(2).pow(power as u32);
63 }
64
65 Ok(result)
66}
67
68fn rounding_driver(
69 n: NumberValue,
70 d: Option<NumberValue>,
71 double_round: fn(f64) -> f64,
72 int_divide: fn(i64, i64) -> i64,
73 bigum_divide: fn(BigInt, BigInt) -> BigInt,
74) -> Result<NumberValue> {
75 let d = match d {
76 None => {
77 return Ok(match n {
78 NumberValue::Float(f) => NumberValue::Float(double_round(f)).coerce_integer(),
79 other => other,
80 });
81 }
82 Some(d) if d.is_zero() => {
83 return Err(anyhow!("(arith-error)"));
84 }
85 Some(d) => d,
86 };
87
88 match (n, d) {
89 (NumberValue::Int(n), NumberValue::Int(d)) => Ok(NumberValue::Int(int_divide(n, d))),
90 (n, d) => {
91 let dscale = match d {
92 NumberValue::Float(f) => double_integer_scale(f),
93 _ => 0,
94 };
95
96 let nscale = match n {
97 NumberValue::Float(f) => double_integer_scale(f),
98 _ => 0,
99 };
100
101 if dscale == f64::MANTISSA_DIGITS as i64 - f64::MIN_EXP as i64 + 1 && nscale < dscale {
105 return Ok(NumberValue::Int(0));
106 }
107
108 let num = rescale_for_division(n, nscale, dscale)?;
109 let denom = rescale_for_division(d, dscale, nscale)?;
110 Ok(NumberValue::Big(bigum_divide(num, denom)).coerce_integer())
111 }
112 }
113}
114
115fn round2<T>(num: T, den: T) -> T
116where
117 T: Div<Output = T>
118 + Rem<Output = T>
119 + PartialOrd
120 + num_traits::Zero
121 + num_traits::Signed
122 + BitAnd<Output = T>
123 + AddAssign
124 + SubAssign
125 + Clone,
126{
127 let mut q: T = num.clone() / den.clone();
133 let r: T = num.clone() % den.clone();
134 let neg_d = den < T::zero();
135 let neg_r = r < T::zero();
136 let abs_r: T = r.abs();
137 let abs_r1: T = den.abs() - abs_r.clone();
138
139 let increment = if (q.clone() & T::one()) == T::one() { T::zero() } else { T::one() };
140 if abs_r1 < abs_r + increment {
141 if neg_d == neg_r {
142 q += T::one();
143 } else {
144 q -= T::one();
145 }
146 }
147 q
148}
149
150const LIMBS_LIMIT: usize = 2147483642;
151
152fn checked_pow(base: BigInt, exp: u32) -> Result<BigInt> {
153 let nbase = base.bits() as usize; let n = nbase * exp as usize; if n > LIMBS_LIMIT {
159 return Err(anyhow!("Overflow error".to_string()));
160 }
161 Ok(base.pow(exp))
162}
163
164#[defun]
165fn floor(num: Number, divisor: Option<Number>) -> Result<NumberValue> {
166 rounding_driver(
167 num.val(),
168 divisor.map(|d| d.val()),
169 |f| f.floor(),
170 |n, d| num_integer::Integer::div_floor(&n, &d),
171 |n, d| n.div_floor(&d),
172 )
173}
174
175#[defun]
176fn ceiling(num: Number, divisor: Option<Number>) -> Result<NumberValue> {
177 rounding_driver(
178 num.val(),
179 divisor.map(|d| d.val()),
180 |f| f.ceil(),
181 |n, d| num_integer::Integer::div_ceil(&n, &d),
182 |n, d| n.div_ceil(&d),
183 )
184}
185
186#[defun]
187fn round(num: Number, divisor: Option<Number>) -> Result<NumberValue> {
188 rounding_driver(
189 num.val(),
190 divisor.map(|d| d.val()), |f| f.round(),
192 round2,
193 round2,
194 )
195}
196
197#[defun]
198fn truncate(num: Number, divisor: Option<Number>) -> Result<NumberValue> {
199 rounding_driver(
200 num.val(),
201 divisor.map(|d| d.val()), |f| f,
203 |n, d| n.div(&d),
204 |n, d| n.div(&d),
205 )
206}
207
208#[defun]
209fn fceiling(num: f64) -> f64 {
210 num.ceil()
211}
212
213#[defun]
214fn ffloor(num: f64) -> f64 {
215 num.floor()
216}
217
218#[defun]
219fn fround(num: f64) -> f64 {
220 num.round()
221}
222
223#[defun]
224fn ftruncate(num: f64) -> f64 {
225 num.trunc()
226}
227
228#[defun]
229fn float(arg: Number) -> NumberValue {
230 NumberValue::Float(coerce(arg))
231}
232
233#[defun]
234fn asin(arg: Number) -> f64 {
235 coerce(arg).asin()
236}
237
238#[defun]
239fn acos(arg: Number) -> f64 {
240 coerce(arg).acos()
241}
242
243#[defun]
244fn atan(arg: Number, x: Option<f64>) -> f64 {
245 if let Some(x) = x { coerce(arg).atan2(x) } else { coerce(arg).atan() }
246}
247
248#[defun]
249fn cos(arg: Number) -> f64 {
250 coerce(arg).cos()
251}
252
253#[defun]
254fn sin(arg: Number) -> f64 {
255 coerce(arg).sin()
256}
257
258#[defun]
259fn tan(arg: Number) -> f64 {
260 coerce(arg).tan()
261}
262
263#[defun]
264fn isnan(arg: Number) -> bool {
265 match arg.untag() {
266 NumberType::Float(f) => f.is_nan(),
267 _ => false,
268 }
269}
270
271#[defun]
272fn copysign(x: f64, y: f64) -> f64 {
273 x.copysign(y)
274}
275
276#[defun]
277fn exp(arg: Number) -> f64 {
278 coerce(arg).exp()
279}
280
281#[defun]
282fn expt(x: Number, y: Number) -> NumberValue {
283 match (x.untag(), y.untag()) {
285 (NumberType::Float(_), _) | (_, NumberType::Float(_)) => {
286 return NumberValue::Float(coerce(x).powf(coerce(y)));
287 }
288 (_, _) => {}
289 };
290
291 let bx = match x.untag() {
293 NumberType::Int(x) => BigInt::from(x),
294 NumberType::Big(b) => (*b).clone(),
295 NumberType::Float(x) => {
296 return NumberValue::Float(x.powf(coerce(y)));
297 }
298 };
299
300 let by = match y.untag() {
301 NumberType::Int(x) => u32::try_from(x).ok(),
302 NumberType::Big(b) => b.to_u32(),
303 NumberType::Float(y) => {
304 return NumberValue::Float(coerce(x).powf(**y));
305 }
306 };
307
308 if let Some(y) = by {
309 let result = checked_pow(bx, y).unwrap();
310 return NumberValue::Big(result).coerce_integer();
311 }
312 NumberValue::Float(coerce(x).powf(coerce(y)))
313}
314
315#[defun]
316fn log(arg: Number, base: Option<f64>) -> f64 {
317 if let Some(base) = base {
318 coerce(arg).log(base)
319 } else {
320 coerce(arg).log(std::f64::consts::E)
321 }
322}
323
324#[defun]
325fn sqrt(arg: Number) -> f64 {
326 coerce(arg).sqrt()
327}
328
329#[defun]
330fn abs(arg: Number) -> NumberValue {
331 match arg.untag() {
332 NumberType::Int(i) => NumberValue::Int(i.abs()),
333 NumberType::Float(f) => NumberValue::Float(f.abs()),
334 NumberType::Big(b) => NumberValue::Big(b.abs()),
335 }
336}
337
338#[defun]
339fn ldexp(s: Number, e: i64) -> f64 {
340 coerce(s) * 2f64.powi(e as i32)
341}
342
343#[defun]
344fn logb(arg: Number) -> i64 {
345 let l2 = coerce(arg).log2();
346 l2.floor() as i64
348}
349
350#[defun]
351fn frexp<'ob>(x: Number, cx: &'ob Context) -> Object<'ob> {
352 let f = coerce(x);
353 let (significand, exponent) = frexp_f(f);
354 Cons::new(significand, exponent, cx).into()
355}
356
357fn frexp_f(f: f64) -> (f64, i64) {
358 let (x, exp) = libm::frexp(f);
359 (x, exp as i64)
360}