• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

mattwparas / steel / 19643200669

24 Nov 2025 05:24PM UTC coverage: 49.283% (-0.03%) from 49.314%
19643200669

Pull #574

github

web-flow
Merge a59c922d4 into ed9e0c34e
Pull Request #574: Add bitwise operators and hash code function

7 of 51 new or added lines in 3 files covered. (13.73%)

18 existing lines in 4 files now uncovered.

15943 of 32350 relevant lines covered (49.28%)

3471394.66 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

56.13
/crates/steel-core/src/primitives/numbers.rs
1
use crate::gc::Gc;
2
use crate::rvals::{IntoSteelVal, Result, SteelComplex, SteelVal};
3
use crate::{steelerr, stop, throw};
4
use num_bigint::BigInt;
5
use num_integer::Integer;
6
use num_rational::{BigRational, Ratio, Rational32};
7
use num_traits::{pow::Pow, CheckedAdd, CheckedMul, Euclid, One, Signed, ToPrimitive, Zero};
8
use std::cmp::Ordering;
9
use std::ops::{BitAnd, BitOr, BitXor, Neg};
10

11
/// Checks if the given value is a number
12
///
13
/// (number? value) -> boolean?
14
///
15
/// * value : any - The value to check
16
///
17
/// # Examples
18
/// ```scheme
19
/// > (number? 42) ;; => #t
20
/// > (number? "hello") ;; => #f
21
/// > (number? 'symbol) ;; => #f
22
/// ```
23
#[steel_derive::function(name = "number?", constant = true)]
24
pub fn numberp(value: &SteelVal) -> bool {
267,040,544✔
25
    matches!(
34,616✔
26
        value,
267,040,544✔
27
        SteelVal::IntV(_)
28
            | SteelVal::BigNum(_)
29
            | SteelVal::Rational(_)
30
            | SteelVal::BigRational(_)
31
            | SteelVal::NumV(_)
32
            | SteelVal::Complex(_)
33
    )
34
}
35

36
/// Checks if the given value is a complex number
37
///
38
/// (complex? value) -> boolean?
39
///
40
/// * value : any - The value to check
41
///
42
/// # Examples
43
/// ```scheme
44
/// > (complex? 3+4i) ;; => #t
45
/// > (complex? 42) ;; => #t
46
/// > (complex? "hello") ;; => #f
47
/// ```
48
#[steel_derive::function(name = "complex?", constant = true)]
49
pub fn complexp(value: &SteelVal) -> bool {
×
50
    numberp(value)
×
51
}
52

53
/// Checks if the given value is a real number
54
///
55
/// (real? value) -> boolean?
56
///
57
/// * value : any - The value to check
58
///
59
/// # Examples
60
/// ```scheme
61
/// > (real? 42) ;; => #t
62
/// > (real? 3+4i) ;; => #f
63
/// > (real? "hello") ;; => #f
64
/// ```
65
#[steel_derive::function(name = "real?", constant = true)]
66
pub fn realp(value: &SteelVal) -> bool {
112,506✔
67
    matches!(
3✔
68
        value,
112,506✔
69
        SteelVal::IntV(_)
70
            | SteelVal::BigNum(_)
71
            | SteelVal::Rational(_)
72
            | SteelVal::BigRational(_)
73
            | SteelVal::NumV(_)
74
    )
75
}
76

77
/// Returns #t if obj is a rational number, #f otherwise.
78
/// Rational numbers are numbers that can be expressed as the quotient of two numbers.
79
/// For example, 3/4, -5/2, 0.25, and 0 are rational numbers.
80
///
81
/// (rational? value) -> bool?
82
///
83
/// * value : any - The value to check
84
///
85
/// Examples:
86
/// ```scheme
87
/// > (rational? (/ 0.0)) ;; => #f
88
/// > (rational? 3.5) ;; => #t
89
/// > (rational? 6/10) ;; => #t
90
/// > (rational? +nan.0) ;; => #f
91
/// ```
92
#[steel_derive::function(name = "rational?", constant = true)]
93
fn rationalp(value: &SteelVal) -> bool {
5✔
94
    match value {
5✔
95
        SteelVal::IntV(_)
96
        | SteelVal::BigNum(_)
97
        | SteelVal::Rational(_)
98
        | SteelVal::BigRational(_) => true,
2✔
99
        SteelVal::NumV(n) => n.is_finite(),
9✔
100
        _ => false,
×
101
    }
102
}
103

104
/// Checks if the given value is an integer, an alias for `integer?`
105
///
106
/// (int? value) -> boolean?
107
///
108
/// * value : any - The value to check
109
///
110
/// # Examples
111
/// ```scheme
112
/// > (int? 42) ;; => #t
113
/// > (int? 3.14) ;; => #f
114
/// > (int? "hello") ;; => #f
115
/// ```
116
#[steel_derive::function(name = "int?", constant = true)]
117
fn intp(value: &SteelVal) -> bool {
116✔
118
    match value {
5✔
119
        SteelVal::IntV(_) | SteelVal::BigNum(_) => true,
89✔
120
        SteelVal::NumV(n) if n.fract() == 0.0 => true,
17✔
121
        _ => false,
26✔
122
    }
123
}
124

125
/// Checks if the given value is an integer, an alias for `int?`
126
///
127
/// (integer? value) -> boolean?
128
///
129
/// * value : any - The value to check
130
///
131
/// # Examples
132
/// ```scheme
133
/// > (integer? 42) ;; => #t
134
/// > (integer? 3.14) ;; => #f
135
/// > (integer? "hello") ;; => #f
136
/// ```
137
#[steel_derive::function(name = "integer?", constant = true)]
138
fn integerp(value: &SteelVal) -> bool {
4✔
139
    intp(value)
8✔
140
}
141

142
/// Checks if the given value is an exact integer
143
///
144
/// (exact-integer? value) -> boolean?
145
///
146
/// * value : any - The value to check
147
///
148
/// # Examples
149
/// ```scheme
150
/// > (exact-integer? 42) ;; => #t
151
/// > (exact-integer? -42) ;; => #t
152
/// > (exact-integer? 4.0) ;; => #f
153
/// ```
154
#[steel_derive::function(name = "exact-integer?", constant = true)]
155
fn exact_integerp(value: &SteelVal) -> bool {
4✔
156
    matches!(value, SteelVal::IntV(_) | SteelVal::BigNum(_))
7✔
157
}
158

159
/// Checks if the given value is a floating-point number
160
///
161
/// (float? value) -> boolean?
162
///
163
/// * value : any - The value to check
164
///
165
/// # Examples
166
/// ```scheme
167
/// > (float? 42) ;; => #f
168
/// > (float? 3.14) ;; => #t
169
/// > (float? #t) ;; => #f
170
/// ```
171
#[steel_derive::function(name = "float?", constant = true)]
172
fn floatp(value: &SteelVal) -> bool {
2✔
173
    matches!(value, SteelVal::NumV(_))
3✔
174
}
175

176
/// Returns `#t` if the real number is Nan.
177
///
178
/// (nan? value) -> boolean?
179
///
180
/// * value : real? - The value to check
181
///
182
/// ```scheme
183
/// (nan? +nan.0) => #t
184
/// (nan? 100000) => #f
185
/// ```
186
#[steel_derive::function(name = "nan?", constant = true)]
187
fn nanp(value: &SteelVal) -> Result<SteelVal> {
5✔
188
    match value {
5✔
189
        SteelVal::NumV(n) => n.is_nan().into_steelval(),
12✔
190
        // The following types are numbers but can not be nan.
191
        SteelVal::IntV(_)
192
        | SteelVal::Rational(_)
193
        | SteelVal::BigNum(_)
194
        | SteelVal::BigRational(_) => false.into_steelval(),
2✔
195
        _ => steelerr!(TypeMismatch => "nan? expected real number"),
×
196
    }
197
}
198

199
/// Checks if the given real number is zero.
200
///
201
/// (zero? num) -> boolean?
202
///
203
/// * num : real? - The number to check for zero.
204
///
205
/// # Examples
206
/// ```scheme
207
/// > (zero? 0) ;; => #t
208
/// > (zero? 0.0) ;; => #t
209
/// > (zero? 0.1) ;; => #f
210
/// ```
211
#[steel_derive::function(name = "zero?", constant = true)]
212
fn zerop(value: &SteelVal) -> Result<SteelVal> {
×
213
    match value {
×
214
        SteelVal::NumV(x) => x.is_zero().into_steelval(),
×
215
        SteelVal::IntV(0) => true.into_steelval(),
×
216
        // The following types are numbers, but are casted to NumV or IntV if they are 0 by their
217
        // into_steelval implementation.
218
        SteelVal::IntV(_)
219
        | SteelVal::Rational(_)
220
        | SteelVal::BigNum(_)
221
        | SteelVal::BigRational(_)
222
        | SteelVal::Complex(_) => false.into_steelval(),
×
223
        _ => steelerr!(TypeMismatch => "zero? expected number"),
×
224
    }
225
}
226

227
/// Checks if the given real number is positive.
228
///
229
/// (positive? num) -> boolean?
230
///
231
/// * num : real? - The real number to check for positivity.
232
///
233
/// # Examples
234
/// ```scheme
235
/// > (positive? 0) ;; => #f
236
/// > (positive? 1) ;; => #t
237
/// > (positive? -1) ;; => #f
238
/// ```
239
#[steel_derive::function(name = "positive?", constant = true)]
240
fn positivep(value: &SteelVal) -> Result<SteelVal> {
7✔
241
    match value {
7✔
242
        SteelVal::NumV(n) => n.is_positive().into_steelval(),
8✔
243
        SteelVal::IntV(n) => n.is_positive().into_steelval(),
12✔
244
        SteelVal::Rational(n) => n.is_positive().into_steelval(),
8✔
245
        SteelVal::BigNum(n) => n.is_positive().into_steelval(),
×
246
        SteelVal::BigRational(n) => n.is_positive().into_steelval(),
×
247
        _ => steelerr!(TypeMismatch => "positive? expected real number"),
×
248
    }
249
}
250

251
/// Checks if the given real number is negative.
252
///
253
/// (negative? num) -> boolean?
254
///
255
/// * num : real? - The real number to check for negativity.
256
///
257
/// # Examples
258
/// ```scheme
259
/// > (negative? 0) ;; => #f
260
/// > (negative? 1) ;; => #f
261
/// > (negative? -1) ;; => #t
262
/// ```
263
#[steel_derive::function(name = "negative?", constant = true)]
264
fn negativep(value: &SteelVal) -> Result<SteelVal> {
11✔
265
    match value {
11✔
266
        SteelVal::NumV(n) => n.is_negative().into_steelval(),
8✔
267
        SteelVal::IntV(n) => n.is_negative().into_steelval(),
28✔
268
        SteelVal::Rational(n) => n.is_negative().into_steelval(),
8✔
269
        SteelVal::BigNum(n) => n.is_negative().into_steelval(),
×
270
        SteelVal::BigRational(n) => n.is_negative().into_steelval(),
×
271
        _ => steelerr!(TypeMismatch => "negative? expected real number"),
×
272
    }
273
}
274

275
/// Subtracts the given numbers.
276
///
277
/// (- . nums) -> number?
278
///
279
/// * nums : number? - The numbers to subtract. Must have at least one number.
280
///
281
/// # Examples
282
/// ```scheme
283
/// > (- 5 3) ;; => 2
284
/// > (- 10 3 2) ;; => 5
285
/// > (- -5) ;; => 5
286
/// ```
287
#[steel_derive::native(name = "-", constant = true, arity = "AtLeast(1)")]
288
pub fn subtract_primitive(args: &[SteelVal]) -> Result<SteelVal> {
20,061,669✔
289
    ensure_args_are_numbers("-", args)?;
60,185,008✔
290
    match args {
20,061,668✔
291
        [] => steelerr!(ArityMismatch => "- requires at least one argument"),
20,061,668✔
292
        [x] => negate(x),
448,566✔
293
        [x, ys @ ..] => {
39,824,292✔
294
            let y = negate(&add_primitive_no_check(ys)?)?;
79,648,584✔
295
            add_two(x, &y)
59,736,438✔
296
        }
297
    }
298
}
299

300
#[inline(always)]
301
fn add_primitive_no_check(args: &[SteelVal]) -> Result<SteelVal> {
19,912,146✔
302
    match args {
19,912,146✔
303
        [] => 0.into_steelval(),
19,912,146✔
304
        [x] => x.clone().into_steelval(),
79,648,568✔
305
        [x, y] => add_two(x, y),
10✔
306
        [x, y, zs @ ..] => {
6✔
307
            let mut res = add_two(x, y)?;
8✔
308
            for z in zs {
6✔
309
                res = add_two(&res, z)?;
8✔
310
            }
311
            res.into_steelval()
4✔
312
        }
313
    }
314
}
315

316
/// Adds the given numbers.
317
///
318
/// (+ . nums) -> number?
319
///
320
/// * nums : number? - The numbers to add. Can have any number of arguments including zero.
321
///
322
/// # Examples
323
/// ```scheme
324
/// > (+ 5 3) ;; => 8
325
/// > (+ 10 3 2) ;; => 15
326
/// > (+) ;; => 0
327
/// ```
328
#[steel_derive::native(name = "+", constant = true, arity = "AtLeast(0)")]
329
pub fn add_primitive(args: &[SteelVal]) -> Result<SteelVal> {
74,109,397✔
330
    ensure_args_are_numbers("+", args)?;
222,328,193✔
331
    match args {
74,109,395✔
332
        [] => 0.into_steelval(),
74,109,397✔
333
        [x] => x.clone().into_steelval(),
12✔
334
        [x, y] => add_two(x, y),
365,078,010✔
335
        [x, y, zs @ ..] => {
3,281,364✔
336
            let mut res = add_two(x, y)?;
4,375,152✔
337
            for z in zs {
4,365,166✔
338
                res = add_two(&res, z)?;
6,542,756✔
339
            }
340
            res.into_steelval()
2,187,576✔
341
        }
342
    }
343
}
344

345
/// Multiplies the given numbers.
346
///
347
/// (* . nums) -> number?
348
///
349
/// * nums : number? - The numbers to multiply. Can have any number of arguments including zero.
350
///
351
/// # Examples
352
/// ```scheme
353
/// > (* 5 3) ;; => 15
354
/// > (* 10 3 2) ;; => 60
355
/// > (*) ;; => 1
356
/// ```
357
#[steel_derive::native(name = "*", constant = true, arity = "AtLeast(0)")]
358
pub fn multiply_primitive(args: &[SteelVal]) -> Result<SteelVal> {
31,055,662✔
359
    ensure_args_are_numbers("*", args)?;
93,166,986✔
360
    multiply_primitive_impl(args)
62,111,324✔
361
}
362

363
/// Simultaneously returns the quotient and the arithmetic remainder of a truncated
364
/// integer division of a given numerator *n* by a given denominator *m*.
365
///
366
/// Equivalent to `(values (truncate-quotient n m) (truncate-remainder n m))`,
367
/// but may be computed more efficiently.
368
///
369
/// (truncate/ n m) -> (integer? integer?)
370
///
371
/// * n : integer?
372
/// * m : integer?
373
///
374
/// # Examples
375
///
376
/// ```scheme
377
/// > (truncate/ 5 2) ;; => (2 1)
378
/// > (truncate/ -5 2) ;; => (-2 -1)
379
/// > (truncate/ 5 -2) ;; => (-2 1)
380
/// > (truncate/ -5 -2) ;; => (2 -1)
381
/// ```
382
#[steel_derive::native(name = "truncate/", constant = true, arity = "Exact(2)")]
383
pub fn truncate_slash(args: &[SteelVal]) -> Result<SteelVal> {
15✔
384
    match (&args[0], &args[1]) {
30✔
385
        (SteelVal::NumV(l), SteelVal::IntV(0) | SteelVal::NumV(0.0)) if l.fract() == 0.0 => {
4✔
386
            steelerr!(Generic => "truncate/: division by zero")
×
387
        }
388
        (SteelVal::IntV(_) | SteelVal::BigNum(_), SteelVal::IntV(0) | SteelVal::NumV(0.0)) => {
2✔
389
            steelerr!(Generic => "truncate/: division by zero")
×
390
        }
391
        // prevent panic due to overflow
392
        (SteelVal::IntV(l @ isize::MIN), SteelVal::IntV(r @ -1)) => {
2✔
393
            let l = BigInt::from(*l);
3✔
394
            (&l / r, l % r).into_steelval()
3✔
395
        }
396
        (SteelVal::IntV(l), SteelVal::IntV(r)) => (l / r, l % r).into_steelval(),
25✔
397
        (SteelVal::BigNum(l), SteelVal::IntV(r)) => {
2✔
398
            (l.as_ref() / r, l.as_ref() % r).into_steelval()
5✔
399
        }
400
        (SteelVal::IntV(l), SteelVal::BigNum(r)) => {
×
401
            (l / r.as_ref(), l % r.as_ref()).into_steelval()
×
402
        }
403
        (SteelVal::BigNum(l), SteelVal::BigNum(r)) => {
×
404
            l.as_ref().div_rem(r.as_ref()).into_steelval()
×
405
        }
406
        (SteelVal::NumV(l), SteelVal::NumV(r)) if l.fract() == 0.0 && r.fract() == 0.0 => {
24✔
407
            ((l / r).trunc(), l % r).into_steelval()
16✔
408
        }
409
        (SteelVal::NumV(l), SteelVal::IntV(r)) if l.fract() == 0.0 => {
10✔
410
            ((l / *r as f64).trunc(), l % *r as f64).into_steelval()
8✔
411
        }
412
        (SteelVal::IntV(l), SteelVal::NumV(r)) if r.fract() == 0.0 => {
10✔
413
            ((*l as f64 / r).trunc(), *l as f64 % r).into_steelval()
8✔
414
        }
415
        (SteelVal::NumV(l), SteelVal::BigNum(r)) if l.fract() == 0.0 => {
×
416
            let r = r.to_f64().unwrap();
×
417
            ((l / r).trunc(), l % r).into_steelval()
×
418
        }
419
        (SteelVal::BigNum(l), SteelVal::NumV(r)) if r.fract() == 0.0 => {
×
420
            let l = l.to_f64().unwrap();
×
421
            ((l / r).trunc(), l % r).into_steelval()
×
422
        }
423
        _ => steelerr!(TypeMismatch => "truncate/ only supports integers"),
×
424
    }
425
}
426

427
/// Returns the quotient of a truncated integer division of a given numerator *n*
428
/// by a given denominator *m*.
429
///
430
/// (truncate-quotient n m) -> integer?
431
///
432
/// * n : integer? - The numerator.
433
/// * m : integer? - The denominator.
434
///
435
/// # Examples
436
///
437
/// ```scheme
438
/// > (truncate-quotient 5 2) ;; => 2
439
/// > (truncate-quotient -5 2) ;; => -2
440
/// > (truncate-quotient 5 -2) ;; => -2
441
/// > (truncate-quotient -5 -2) ;; => 2
442
/// ```
443
#[steel_derive::native(name = "truncate-quotient", constant = true, arity = "Exact(2)")]
444
pub fn truncate_quotient(args: &[SteelVal]) -> Result<SteelVal> {
6,106,467✔
445
    match (&args[0], &args[1]) {
12,212,934✔
446
        (SteelVal::NumV(l), SteelVal::IntV(0) | SteelVal::NumV(0.0)) if l.fract() == 0.0 => {
4✔
447
            steelerr!(Generic => "truncate-quotient: division by zero")
×
448
        }
449
        (SteelVal::IntV(_) | SteelVal::BigNum(_), SteelVal::IntV(0) | SteelVal::NumV(0.0)) => {
2✔
450
            steelerr!(Generic => "truncate-quotient: division by zero")
×
451
        }
452
        // prevent panic due to overflow
453
        (SteelVal::IntV(l @ isize::MIN), SteelVal::IntV(r @ -1)) => {
2✔
454
            (BigInt::from(*l) / r).into_steelval()
3✔
455
        }
456
        (SteelVal::IntV(l), SteelVal::IntV(r)) => (l / r).into_steelval(),
23,395,028✔
457
        (SteelVal::BigNum(l), SteelVal::IntV(r)) => (l.as_ref() / r).into_steelval(),
953,005✔
458
        (SteelVal::IntV(l), SteelVal::BigNum(r)) => (l / r.as_ref()).into_steelval(),
9,500✔
459
        (SteelVal::BigNum(l), SteelVal::BigNum(r)) => (l.as_ref() / r.as_ref()).into_steelval(),
391,200✔
460
        (SteelVal::NumV(l), SteelVal::NumV(r)) if l.fract() == 0.0 && r.fract() == 0.0 => {
24✔
461
            (l / r).trunc().into_steelval()
12✔
462
        }
463
        (SteelVal::NumV(l), SteelVal::IntV(r)) if l.fract() == 0.0 => {
10✔
464
            (l / *r as f64).trunc().into_steelval()
6✔
465
        }
466
        (SteelVal::IntV(l), SteelVal::NumV(r)) if r.fract() == 0.0 => {
10✔
467
            (*l as f64 / r).trunc().into_steelval()
6✔
468
        }
469
        (SteelVal::NumV(l), SteelVal::BigNum(r)) if l.fract() == 0.0 => {
×
470
            (l / r.to_f64().unwrap()).trunc().into_steelval()
×
471
        }
472
        (SteelVal::BigNum(l), SteelVal::NumV(r)) if r.fract() == 0.0 => {
×
473
            (l.to_f64().unwrap() / r).trunc().into_steelval()
×
474
        }
475
        _ => steelerr!(TypeMismatch => "truncate-quotient only supports integers"),
×
476
    }
477
}
478

479
/// Returns the arithmetic remainder of a truncated integer division of a given
480
/// numerator *n* by a given denominator *m*.
481
///
482
/// The return value of this procedure has the same sign as the numerator.
483
///
484
/// (truncate-remainder n m) -> integer?
485
///
486
/// * n : integer? - The numerator.
487
/// * m : integer? - The denominator.
488
///
489
/// # Examples
490
///
491
/// ```scheme
492
/// > (truncate-remainder 5 2) ;; => 1
493
/// > (truncate-remainder -5 2) ;; => -1
494
/// > (truncate-remainder 5 -2) ;; => 1
495
/// > (truncate-remainder -5 -2) ;; => -1
496
/// ```
497
#[steel_derive::native(name = "truncate-remainder", constant = true, arity = "Exact(2)")]
498
pub fn truncate_remainder(args: &[SteelVal]) -> Result<SteelVal> {
9,920,603✔
499
    match (&args[0], &args[1]) {
19,841,206✔
500
        (SteelVal::NumV(l), SteelVal::IntV(0) | SteelVal::NumV(0.0)) if l.fract() == 0.0 => {
4✔
501
            steelerr!(Generic => "truncate-remainder: division by zero")
×
502
        }
503
        (SteelVal::IntV(_) | SteelVal::BigNum(_), SteelVal::IntV(0) | SteelVal::NumV(0.0)) => {
2✔
504
            steelerr!(Generic => "truncate-remainder: division by zero")
×
505
        }
506
        // prevent panic due to overflow
507
        (SteelVal::IntV(l @ isize::MIN), SteelVal::IntV(r @ -1)) => {
2✔
508
            (BigInt::from(*l) % r).into_steelval()
3✔
509
        }
510
        (SteelVal::IntV(l), SteelVal::IntV(r)) => (l % r).into_steelval(),
39,682,372✔
511
        (SteelVal::IntV(l), SteelVal::BigNum(r)) => (l % r.as_ref()).into_steelval(),
×
512
        (SteelVal::BigNum(l), SteelVal::IntV(r)) => (l.as_ref() % r).into_steelval(),
5✔
513
        (SteelVal::BigNum(l), SteelVal::BigNum(r)) => (l.as_ref() % r.as_ref()).into_steelval(),
×
514
        (SteelVal::NumV(l), SteelVal::NumV(r)) if l.fract() == 0.0 && r.fract() == 0.0 => {
24✔
515
            (l % r).into_steelval()
8✔
516
        }
517
        (SteelVal::NumV(l), SteelVal::IntV(r)) if l.fract() == 0.0 => {
10✔
518
            (l % *r as f64).into_steelval()
4✔
519
        }
520
        (SteelVal::IntV(l), SteelVal::NumV(r)) if r.fract() == 0.0 => {
10✔
521
            (*l as f64 % r).into_steelval()
4✔
522
        }
523
        (SteelVal::NumV(l), SteelVal::BigNum(r)) if l.fract() == 0.0 => {
×
524
            (l % r.to_f64().unwrap()).into_steelval()
×
525
        }
526
        (SteelVal::BigNum(l), SteelVal::NumV(r)) if r.fract() == 0.0 => {
×
527
            (l.to_f64().unwrap() % r).into_steelval()
×
528
        }
529
        _ => steelerr!(TypeMismatch => "truncate-remainder only supports integers"),
×
530
    }
531
}
532

533
/// Simultaneously returns the quotient and the arithmetic remainder of a floored
534
/// integer division of a given numerator *n* by a given denominator *m*.
535
///
536
/// (floor/ n m) -> (integer? integer?)
537
///
538
/// * n : integer?
539
/// * m : integer?
540
///
541
/// # Examples
542
///
543
/// ```scheme
544
/// > (floor/ 5 2) ;; => (2 1)
545
/// > (floor/ -5 2) ;; => (-3 1)
546
/// > (floor/ 5 -2) ;; => (-3 -1)
547
/// > (floor/ -5 -2) ;; => (2 -1)
548
/// ```
549
#[steel_derive::native(name = "floor/", constant = true, arity = "Exact(2)")]
550
pub fn floor_slash(args: &[SteelVal]) -> Result<SteelVal> {
15✔
551
    match (&args[0], &args[1]) {
30✔
552
        (SteelVal::NumV(l), SteelVal::IntV(0) | SteelVal::NumV(0.0)) if l.fract() == 0.0 => {
4✔
553
            steelerr!(Generic => "floor/: division by zero")
×
554
        }
555
        (SteelVal::IntV(_) | SteelVal::BigNum(_), SteelVal::IntV(0) | SteelVal::NumV(0.0)) => {
2✔
556
            steelerr!(Generic => "floor/: division by zero")
×
557
        }
558
        // prevent panic due to overflow
559
        (SteelVal::IntV(l @ isize::MIN), SteelVal::IntV(r @ -1)) => BigInt::from(*l)
3✔
560
            .div_mod_floor(&BigInt::from(*r))
2✔
561
            .into_steelval(),
562
        (SteelVal::IntV(l), SteelVal::IntV(r)) => l.div_mod_floor(r).into_steelval(),
30✔
563
        (SteelVal::IntV(l), SteelVal::BigNum(r)) => {
×
564
            BigInt::from(*l).div_mod_floor(r).into_steelval()
×
565
        }
566
        (SteelVal::BigNum(l), SteelVal::IntV(r)) => {
2✔
567
            l.div_mod_floor(&BigInt::from(*r)).into_steelval()
4✔
568
        }
569
        (SteelVal::BigNum(l), SteelVal::BigNum(r)) => l.div_mod_floor(r).into_steelval(),
×
570
        (SteelVal::NumV(l), SteelVal::NumV(r)) if l.fract() == 0.0 && r.fract() == 0.0 => {
24✔
571
            ((l / r).floor(), float_rem_floor(*l, *r)).into_steelval()
20✔
572
        }
573
        (SteelVal::NumV(l), SteelVal::IntV(r)) if l.fract() == 0.0 => {
10✔
574
            ((l / *r as f64).floor(), float_rem_floor(*l, *r as f64)).into_steelval()
10✔
575
        }
576
        (SteelVal::IntV(l), SteelVal::NumV(r)) if r.fract() == 0.0 => {
10✔
577
            ((*l as f64 / r).floor(), float_rem_floor(*l as f64, *r)).into_steelval()
10✔
578
        }
579
        (SteelVal::NumV(l), SteelVal::BigNum(r)) if l.fract() == 0.0 => {
×
580
            let r = r.to_f64().unwrap();
×
581
            ((l / r).floor(), float_rem_floor(*l, r)).into_steelval()
×
582
        }
583
        (SteelVal::BigNum(l), SteelVal::NumV(r)) if r.fract() == 0.0 => {
×
584
            let l = l.to_f64().unwrap();
×
585
            ((l / r).floor(), float_rem_floor(l, *r)).into_steelval()
×
586
        }
587
        _ => steelerr!(TypeMismatch => "floor/ only supports integers"),
×
588
    }
589
}
590

591
/// Returns the quotient of a floored integer division of a given numerator *n*
592
/// by a given denominator *m*.
593
///
594
/// Equivalent to `(values (floor-quotient n m) (floor-remainder n m))`, but
595
/// may be computed more efficiently.
596
///
597
/// (floor-quotient n m) -> integer?
598
///
599
/// * n : integer?
600
/// * m : integer?
601
///
602
/// # Examples
603
///
604
/// ```scheme
605
/// > (floor-quotient 5 2) ;; => 2
606
/// > (floor-quotient -5 2) ;; => -3
607
/// > (floor-quotient 5 -2) ;; => -3
608
/// > (floor-quotient -5 -2) ;; => 2
609
/// ```
610
#[steel_derive::native(name = "floor-quotient", constant = true, arity = "Exact(2)")]
611
pub fn floor_quotient(args: &[SteelVal]) -> Result<SteelVal> {
15✔
612
    match (&args[0], &args[1]) {
30✔
613
        (SteelVal::NumV(l), SteelVal::IntV(0) | SteelVal::NumV(0.0)) if l.fract() == 0.0 => {
4✔
614
            steelerr!(Generic => "floor-quotient: division by zero")
×
615
        }
616
        (SteelVal::IntV(_) | SteelVal::BigNum(_), SteelVal::IntV(0) | SteelVal::NumV(0.0)) => {
2✔
617
            steelerr!(Generic => "floor-quotient: division by zero")
×
618
        }
619
        // prevent panic due to overflow
620
        (SteelVal::IntV(l @ isize::MIN), SteelVal::IntV(r @ -1)) => BigInt::from(*l)
3✔
621
            .div_floor(&BigInt::from(*r))
2✔
622
            .into_steelval(),
623
        (SteelVal::IntV(l), SteelVal::IntV(r)) => l.div_floor(r).into_steelval(),
30✔
624
        (SteelVal::IntV(l), SteelVal::BigNum(r)) => BigInt::from(*l).div_floor(r).into_steelval(),
×
625
        (SteelVal::BigNum(l), SteelVal::IntV(r)) => l.div_floor(&BigInt::from(*r)).into_steelval(),
6✔
626
        (SteelVal::BigNum(l), SteelVal::BigNum(r)) => l.div_floor(r).into_steelval(),
×
627
        (SteelVal::NumV(l), SteelVal::NumV(r)) if l.fract() == 0.0 && r.fract() == 0.0 => {
24✔
628
            (l / r).floor().into_steelval()
12✔
629
        }
630
        (SteelVal::NumV(l), SteelVal::IntV(r)) if l.fract() == 0.0 => {
10✔
631
            (l / *r as f64).floor().into_steelval()
6✔
632
        }
633
        (SteelVal::IntV(l), SteelVal::NumV(r)) if r.fract() == 0.0 => {
10✔
634
            (*l as f64 / r).floor().into_steelval()
6✔
635
        }
636
        (SteelVal::NumV(l), SteelVal::BigNum(r)) if l.fract() == 0.0 => {
×
637
            (l / r.to_f64().unwrap()).floor().into_steelval()
×
638
        }
639
        (SteelVal::BigNum(l), SteelVal::NumV(r)) if r.fract() == 0.0 => {
×
640
            (l.to_f64().unwrap() / r).floor().into_steelval()
×
641
        }
642
        _ => steelerr!(TypeMismatch => "floor-quotient only supports integers"),
×
643
    }
644
}
645

646
fn float_rem_floor(lhs: f64, rhs: f64) -> f64 {
16✔
647
    // Algorithm taken from num-integer, which itself takes it from
648
    // https://www.microsoft.com/en-us/research/wp-content/uploads/2016/02/divmodnote-letter.pdf
649
    let r = lhs % rhs;
32✔
650
    if (r > 0.0 && rhs < 0.0) || (r < 0.0 && rhs > 0.0) {
44✔
651
        r + rhs
8✔
652
    } else {
653
        r
8✔
654
    }
655
}
656

657
/// Returns the arithmetic remainder of a floored integer division of a given
658
/// numerator *n* by a given denominator *m*.
659
///
660
/// The return value of this procedure has the same sign as the denominator.
661
///
662
/// (floor-remainder n m) -> integer?
663
///
664
/// * n : integer?
665
/// * m : integer?
666
///
667
/// # Examples
668
///
669
/// ```scheme
670
/// > (floor-remainder 5 2) ;; => 1
671
/// > (floor-remainder -5 2) ;; => 1
672
/// > (floor-remainder 5 -2) ;; => -1
673
/// > (floor-remainder -5 -2) ;; => -1
674
/// ```
675
#[steel_derive::native(name = "floor-remainder", constant = true, arity = "Exact(2)")]
676
pub fn floor_remainder(args: &[SteelVal]) -> Result<SteelVal> {
31✔
677
    match (&args[0], &args[1]) {
62✔
678
        (SteelVal::NumV(l), SteelVal::IntV(0) | SteelVal::NumV(0.0)) if l.fract() == 0.0 => {
4✔
679
            steelerr!(Generic => "floor-remainder: division by zero")
×
680
        }
681
        (SteelVal::IntV(_) | SteelVal::BigNum(_), SteelVal::IntV(0) | SteelVal::NumV(0.0)) => {
2✔
682
            steelerr!(Generic => "floor-remainder: division by zero")
×
683
        }
684
        // prevent panic due to overflow
685
        (SteelVal::IntV(l @ isize::MIN), SteelVal::IntV(r @ -1)) => BigInt::from(*l)
3✔
686
            .mod_floor(&BigInt::from(*r))
2✔
687
            .into_steelval(),
688
        (SteelVal::IntV(l), SteelVal::IntV(r)) => l.mod_floor(r).into_steelval(),
126✔
689
        (SteelVal::IntV(l), SteelVal::BigNum(r)) => BigInt::from(*l).mod_floor(r).into_steelval(),
×
690
        (SteelVal::BigNum(l), SteelVal::IntV(r)) => l.mod_floor(&BigInt::from(*r)).into_steelval(),
6✔
691
        (SteelVal::BigNum(l), SteelVal::BigNum(r)) => l.mod_floor(r).into_steelval(),
×
692
        (SteelVal::NumV(l), SteelVal::NumV(r)) if l.fract() == 0.0 && r.fract() == 0.0 => {
24✔
693
            float_rem_floor(*l, *r).into_steelval()
16✔
694
        }
695
        (SteelVal::NumV(l), SteelVal::IntV(r)) if l.fract() == 0.0 => {
10✔
696
            float_rem_floor(*l, *r as f64).into_steelval()
8✔
697
        }
698
        (SteelVal::IntV(l), SteelVal::NumV(r)) if r.fract() == 0.0 => {
10✔
699
            float_rem_floor(*l as f64, *r).into_steelval()
8✔
700
        }
701
        (SteelVal::NumV(l), SteelVal::BigNum(r)) if l.fract() == 0.0 => {
×
702
            float_rem_floor(*l, r.to_f64().unwrap()).into_steelval()
×
703
        }
704
        (SteelVal::BigNum(l), SteelVal::NumV(r)) if r.fract() == 0.0 => {
×
705
            float_rem_floor(l.to_f64().unwrap(), *r).into_steelval()
×
706
        }
707
        _ => steelerr!(TypeMismatch => "floor-remainder only supports integers"),
×
708
    }
709
}
710

711
/// Simultaneously returns the quotient and the arithmetic remainder of a euclidean
712
/// integer division of a given numerator *n* by a given denominator *m*.
713
///
714
/// Equivalent to `(values (euclidean-quotient n m) (euclidean-remainder n m))`,
715
/// but may be computed more efficiently.
716
///
717
/// (euclidean/ n m) -> (integer? integer?)
718
///
719
/// * n : integer?
720
/// * m : integer?
721
///
722
/// # Examples
723
///
724
/// ```scheme
725
/// > (euclidean/ 5 2) ;; => (2 1)
726
/// > (euclidean/ -5 2) ;; => (-3 1)
727
/// > (euclidean/ 5 -2) ;; => (-2 1)
728
/// > (euclidean/ -5 -2) ;; => (3 1)
729
/// ```
730
#[steel_derive::native(name = "euclidean/", constant = true, arity = "Exact(2)")]
731
pub fn euclidean_slash(args: &[SteelVal]) -> Result<SteelVal> {
15✔
732
    match (&args[0], &args[1]) {
30✔
733
        (SteelVal::NumV(l), SteelVal::IntV(0) | SteelVal::NumV(0.0)) if l.fract() == 0.0 => {
4✔
734
            steelerr!(Generic => "euclidean/: division by zero")
×
735
        }
736
        (SteelVal::IntV(_) | SteelVal::BigNum(_), SteelVal::IntV(0) | SteelVal::NumV(0.0)) => {
2✔
737
            steelerr!(Generic => "euclidean/: division by zero")
×
738
        }
739
        // prevent panic due to overflow
740
        (SteelVal::IntV(l @ isize::MIN), SteelVal::IntV(r @ -1)) => BigInt::from(*l)
3✔
741
            .div_rem_euclid(&BigInt::from(*r))
2✔
742
            .into_steelval(),
743
        (SteelVal::IntV(l), SteelVal::IntV(r)) => l.div_rem_euclid(r).into_steelval(),
30✔
744
        (SteelVal::IntV(l), SteelVal::BigNum(r)) => {
×
745
            BigInt::from(*l).div_rem_euclid(r).into_steelval()
×
746
        }
747
        (SteelVal::BigNum(l), SteelVal::IntV(r)) => {
2✔
748
            l.div_rem_euclid(&BigInt::from(*r)).into_steelval()
4✔
749
        }
750
        (SteelVal::BigNum(l), SteelVal::BigNum(r)) => l.div_rem_euclid(r).into_steelval(),
×
751
        (SteelVal::NumV(l), SteelVal::NumV(r)) if l.fract() == 0.0 && r.fract() == 0.0 => {
24✔
752
            l.div_rem_euclid(r).into_steelval()
16✔
753
        }
754
        (SteelVal::NumV(l), SteelVal::IntV(r)) if l.fract() == 0.0 => {
10✔
755
            l.div_rem_euclid(&(*r as f64)).into_steelval()
8✔
756
        }
757
        (SteelVal::IntV(l), SteelVal::NumV(r)) if r.fract() == 0.0 => {
10✔
758
            (*l as f64).div_rem_euclid(r).into_steelval()
8✔
759
        }
760
        (SteelVal::NumV(l), SteelVal::BigNum(r)) if l.fract() == 0.0 => {
×
761
            l.div_rem_euclid(&r.to_f64().unwrap()).into_steelval()
×
762
        }
763
        (SteelVal::BigNum(l), SteelVal::NumV(r)) if r.fract() == 0.0 => {
×
764
            l.to_f64().unwrap().div_rem_euclid(r).into_steelval()
×
765
        }
766
        _ => steelerr!(TypeMismatch => "euclidean/ only supports integers"),
×
767
    }
768
}
769

770
/// Returns the quotient of a euclidean integer division of a given numerator *n*
771
/// by a given denominator *m*.
772
///
773
/// (euclidean-quotient n m) -> integer?
774
///
775
/// * n : integer?
776
/// * m : integer?
777
///
778
/// # Examples
779
///
780
/// ```scheme
781
/// > (euclidean-quotient 5 2) ;; => 2
782
/// > (euclidean-quotient -5 2) ;; => -3
783
/// > (euclidean-quotient 5 -2) ;; => -2
784
/// > (euclidean-quotient -5 -2) ;; => 3
785
/// ```
786
#[steel_derive::native(name = "euclidean-quotient", constant = true, arity = "Exact(2)")]
787
pub fn euclidean_quotient(args: &[SteelVal]) -> Result<SteelVal> {
15✔
788
    match (&args[0], &args[1]) {
30✔
789
        (SteelVal::NumV(l), SteelVal::IntV(0) | SteelVal::NumV(0.0)) if l.fract() == 0.0 => {
4✔
790
            steelerr!(Generic => "euclidean-quotient: division by zero")
×
791
        }
792
        (SteelVal::IntV(_) | SteelVal::BigNum(_), SteelVal::IntV(0) | SteelVal::NumV(0.0)) => {
2✔
793
            steelerr!(Generic => "euclidean-quotient: division by zero")
×
794
        }
795
        // prevent panic due to overflow
796
        (SteelVal::IntV(l @ isize::MIN), SteelVal::IntV(r @ -1)) => BigInt::from(*l)
3✔
797
            .div_euclid(&BigInt::from(*r))
2✔
798
            .into_steelval(),
799
        (SteelVal::IntV(l), SteelVal::IntV(r)) => l.div_euclid(r).into_steelval(),
30✔
800
        (SteelVal::IntV(l), SteelVal::BigNum(r)) => BigInt::from(*l).div_euclid(r).into_steelval(),
×
801
        (SteelVal::BigNum(l), SteelVal::IntV(r)) => l.div_euclid(&BigInt::from(*r)).into_steelval(),
6✔
802
        (SteelVal::BigNum(l), SteelVal::BigNum(r)) => l.div_euclid(r).into_steelval(),
×
803
        (SteelVal::NumV(l), SteelVal::NumV(r)) if l.fract() == 0.0 && r.fract() == 0.0 => {
24✔
804
            l.div_euclid(r).into_steelval()
16✔
805
        }
806
        (SteelVal::NumV(l), SteelVal::IntV(r)) if l.fract() == 0.0 => {
10✔
807
            (*l).div_euclid(*r as f64).into_steelval()
8✔
808
        }
809
        (SteelVal::IntV(l), SteelVal::NumV(r)) if r.fract() == 0.0 => {
10✔
810
            (*l as f64).div_euclid(*r).into_steelval()
8✔
811
        }
812
        (SteelVal::NumV(l), SteelVal::BigNum(r)) if l.fract() == 0.0 => {
×
813
            (*l).div_euclid(r.to_f64().unwrap()).into_steelval()
×
814
        }
815
        (SteelVal::BigNum(l), SteelVal::NumV(r)) if r.fract() == 0.0 => {
×
816
            l.to_f64().unwrap().div_euclid(*r).into_steelval()
×
817
        }
818
        _ => steelerr!(TypeMismatch => "euclidean-quotient only supports integers"),
×
819
    }
820
}
821

822
/// Returns the arithmetic remainder of a euclidean integer division of a given
823
/// numerator *n* by a given denominator *m*.
824
///
825
/// The return value of this procedure is always positive.
826
///
827
/// (euclidean-remainder n m) -> integer?
828
///
829
/// * n : integer?
830
/// * m : integer?
831
///
832
/// # Examples
833
///
834
/// ```scheme
835
/// > (euclidean-remainder 5 2) ;; => 1
836
/// > (euclidean-remainder -5 2) ;; => 1
837
/// > (euclidean-remainder 5 -2) ;; => 1
838
/// > (euclidean-remainder -5 -2) ;; => 1
839
/// ```
840
#[steel_derive::native(name = "euclidean-remainder", constant = true, arity = "Exact(2)")]
841
pub fn euclidean_remainder(args: &[SteelVal]) -> Result<SteelVal> {
15✔
842
    match (&args[0], &args[1]) {
30✔
843
        (SteelVal::NumV(l), SteelVal::IntV(0) | SteelVal::NumV(0.0)) if l.fract() == 0.0 => {
4✔
844
            steelerr!(Generic => "euclidean-remainder: division by zero")
×
845
        }
846
        (SteelVal::IntV(_) | SteelVal::BigNum(_), SteelVal::IntV(0) | SteelVal::NumV(0.0)) => {
2✔
847
            steelerr!(Generic => "euclidean-remainder: division by zero")
×
848
        }
849
        // prevent panic due to overflow
850
        (SteelVal::IntV(l @ isize::MIN), SteelVal::IntV(r @ -1)) => BigInt::from(*l)
3✔
851
            .rem_euclid(&BigInt::from(*r))
2✔
852
            .into_steelval(),
853
        (SteelVal::IntV(l), SteelVal::IntV(r)) => l.rem_euclid(r).into_steelval(),
30✔
854
        (SteelVal::IntV(l), SteelVal::BigNum(r)) => BigInt::from(*l).rem_euclid(r).into_steelval(),
×
855
        (SteelVal::BigNum(l), SteelVal::IntV(r)) => l.rem_euclid(&BigInt::from(*r)).into_steelval(),
6✔
856
        (SteelVal::BigNum(l), SteelVal::BigNum(r)) => l.rem_euclid(r).into_steelval(),
×
857
        (SteelVal::NumV(l), SteelVal::NumV(r)) if l.fract() == 0.0 && r.fract() == 0.0 => {
24✔
858
            l.rem_euclid(r).into_steelval()
16✔
859
        }
860
        (SteelVal::NumV(l), SteelVal::IntV(r)) if l.fract() == 0.0 => {
10✔
861
            (*l).rem_euclid(*r as f64).into_steelval()
8✔
862
        }
863
        (SteelVal::IntV(l), SteelVal::NumV(r)) if r.fract() == 0.0 => {
10✔
864
            (*l as f64).rem_euclid(*r).into_steelval()
8✔
865
        }
866
        (SteelVal::NumV(l), SteelVal::BigNum(r)) if l.fract() == 0.0 => {
×
867
            (*l).rem_euclid(r.to_f64().unwrap()).into_steelval()
×
868
        }
869
        (SteelVal::BigNum(l), SteelVal::NumV(r)) if r.fract() == 0.0 => {
×
870
            l.to_f64().unwrap().rem_euclid(*r).into_steelval()
×
871
        }
872
        _ => steelerr!(TypeMismatch => "euclidean-remainder only supports integers"),
×
873
    }
874
}
875

876
/// Returns the quotient of a truncated integer division of a given numerator *n*
877
/// by a given denominator *m*.
878
///
879
/// This procedure is an alias of `truncate-quotient`.
880
///
881
/// (quotient n m) -> integer?
882
///
883
/// * n : integer? - The numerator.
884
/// * m : integer? - The denominator.
885
///
886
/// # Examples
887
///
888
/// ```scheme
889
/// > (quotient 5 2) ;; => 2
890
/// > (quotient -5 2) ;; => -2
891
/// > (quotient 5 -2) ;; => -2
892
/// > (quotient -5 -2) ;; => 2
893
/// ```
894
#[steel_derive::native(name = "quotient", constant = true, arity = "Exact(2)")]
895
pub fn quotient(args: &[SteelVal]) -> Result<SteelVal> {
6,106,452✔
896
    truncate_quotient(args)
12,212,904✔
897
}
898

899
/// Returns the arithmetic remainder of a truncated integer division of a given
900
/// numerator *n* by a given denominator *m*.
901
///
902
/// The return value of this procedure has the same sign as the numerator.
903
///
904
/// This procedure is an alias of `truncate-remainder`.
905
///
906
/// (remainder n m) -> integer?
907
///
908
/// * n : integer?
909
/// * m : integer?
910
///
911
/// # Examples
912
///
913
/// ```scheme
914
/// > (remainder 5 2) ;; => 1
915
/// > (remainder -5 2) ;; => -1
916
/// > (remainder 5 -2) ;; => 1
917
/// > (remainder -5 -2) ;; => -1
918
/// ```
919
#[steel_derive::native(name = "remainder", constant = true, arity = "Exact(2)")]
920
pub fn remainder(args: &[SteelVal]) -> Result<SteelVal> {
9,920,588✔
921
    truncate_remainder(args)
19,841,176✔
922
}
923

924
/// Returns the arithmetic remainder of a floored integer division of a given
925
/// numerator *n* by a given denominator *m*.
926
///
927
/// The return value of this procedure has the same sign as the denominator.
928
///
929
/// This procedure is an alias of `floor-remainder`.
930
///
931
/// (modulo n m) -> integer?
932
///
933
/// * n : integer?
934
/// * m : integer?
935
///
936
/// # Examples
937
///
938
/// ```scheme
939
/// > (modulo 5 2) ;; => 1
940
/// > (modulo -5 2) ;; => 1
941
/// > (modulo 5 -2) ;; => -1
942
/// > (modulo -5 -2) ;; => -1
943
/// ```
944
#[steel_derive::native(name = "modulo", constant = true, arity = "Exact(2)")]
945
pub fn modulo(args: &[SteelVal]) -> Result<SteelVal> {
16✔
946
    floor_remainder(args)
32✔
947
}
948

949
/// Returns the sine value of the input angle, measured in radians.
950
///
951
/// (sin n) -> number?
952
///
953
/// * n : number? - The input angle, in radians.
954
///
955
/// # Examples
956
/// ```scheme
957
/// > (sin 0) ;; => 0
958
/// > (sin 1) ;; => 0.8414709848078965
959
/// > (sin 2.0) ;; => 0.9092974268256817
960
/// > (sin 3.14) ;; => 0.0015926529164868282
961
/// ```
962
#[steel_derive::function(name = "sin", constant = true)]
963
pub fn sin(arg: &SteelVal) -> Result<SteelVal> {
40,484✔
964
    match arg {
40,484✔
965
        SteelVal::IntV(i) => (*i as f64).sin(),
×
966
        SteelVal::BigNum(i) => i.to_f64().unwrap().sin(),
×
967
        SteelVal::NumV(n) => n.sin(),
121,452✔
968
        SteelVal::Rational(r) => (*r.numer() as f32 / *r.denom() as f32).sin() as f64,
×
969
        _ => stop!(TypeMismatch => "sin expects a number, found: {}", arg),
×
970
    }
971
    .into_steelval()
972
}
973

974
/// Returns the cosine value of the input angle, measured in radians.
975
///
976
/// (cos n) -> number?
977
///
978
/// * n : number? - The input angle, in radians.
979
///
980
/// # Examples
981
/// ```scheme
982
/// > (cos 0) ;; => 1
983
/// > (cos 1) ;; => 0.5403023058681398
984
/// > (cos 2.0) ;; => -0.4161468365471424
985
/// > (cos 3.14) ;; => -0.9999987317275395
986
/// ```
987
#[steel_derive::function(name = "cos", constant = true)]
988
pub fn cos(arg: &SteelVal) -> Result<SteelVal> {
40,184✔
989
    match arg {
40,184✔
990
        SteelVal::IntV(i) => (*i as f64).cos(),
×
991
        SteelVal::BigNum(i) => i.to_f64().unwrap().cos(),
×
992
        SteelVal::NumV(n) => n.cos(),
120,552✔
993
        SteelVal::Rational(r) => (*r.numer() as f32 / *r.denom() as f32).cos() as f64,
×
994
        _ => stop!(TypeMismatch => "cos expects a number, found: {}", arg),
×
995
    }
996
    .into_steelval()
997
}
998

999
/// Returns the tangent value of the input angle, measured in radians.
1000
///
1001
/// (tan n) -> number?
1002
///
1003
/// * n : number? - The input angle, in radians.
1004
///
1005
/// # Examples
1006
/// ```scheme
1007
/// > (tan 0) ;; => 0
1008
/// > (tan 1) ;; => 1.557407724654902
1009
/// > (tan 2.0) ;; => -2.185039863261519
1010
/// > (tan 3.14) ;; => -0.0015926549364072232
1011
/// ```
1012
#[steel_derive::function(name = "tan", constant = true)]
1013
pub fn tan(arg: &SteelVal) -> Result<SteelVal> {
×
1014
    match arg {
×
1015
        SteelVal::IntV(i) => (*i as f64).tan(),
×
1016
        SteelVal::BigNum(i) => i.to_f64().unwrap().tan(),
×
1017
        SteelVal::NumV(n) => n.tan(),
×
1018
        SteelVal::Rational(r) => (*r.numer() as f32 / *r.denom() as f32).tan() as f64,
×
1019
        _ => stop!(TypeMismatch => "tan expects a number, found: {}", arg),
×
1020
    }
1021
    .into_steelval()
1022
}
1023

1024
/// Returns the arcsine, or inverse sine, of a value; output is in radians.
1025
///
1026
/// (asin n) -> number?
1027
///
1028
/// * n : number? - The input value is the sine of the angle you want and must be from -1 to 1.
1029
///
1030
/// # Examples
1031
/// ```scheme
1032
/// > (asin -1) ;; => -1.5707963267948966
1033
/// > (asin 0) ;; => 0
1034
/// > (asin 0.5) ;; => 0.5235987755982988
1035
/// > (asin 2) ;; => +nan.0
1036
/// ```
1037
#[steel_derive::function(name = "asin", constant = true)]
1038
pub fn asin(arg: &SteelVal) -> Result<SteelVal> {
×
1039
    match arg {
×
1040
        SteelVal::IntV(i) => (*i as f64).asin(),
×
1041
        SteelVal::BigNum(i) => i.to_f64().unwrap().asin(),
×
1042
        SteelVal::NumV(n) => n.asin(),
×
1043
        SteelVal::Rational(r) => (*r.numer() as f32 / *r.denom() as f32).asin() as f64,
×
1044
        _ => stop!(TypeMismatch => "asin expects a number, found: {}", arg),
×
1045
    }
1046
    .into_steelval()
1047
}
1048

1049
/// Returns the arccosine, or inverse cosine, of a value; output is in radians.
1050
///
1051
/// (acos n) -> number?
1052
///
1053
/// * n : number? - The input value is the cosine of the angle you want and must be from -1 to 1.
1054
///
1055
/// # Examples
1056
/// ```scheme
1057
/// > (acos -1) ;; => 3.141592653589793
1058
/// > (acos 0) ;; => 1.5707963267948966
1059
/// > (acos 0.5) ;; => 1.0471975511965976
1060
/// > (acos 2) ;; => +nan.0
1061
/// ```
1062
#[steel_derive::function(name = "acos", constant = true)]
1063
pub fn acos(arg: &SteelVal) -> Result<SteelVal> {
2✔
1064
    match arg {
2✔
1065
        SteelVal::IntV(i) => (*i as f64).acos(),
6✔
1066
        SteelVal::BigNum(i) => i.to_f64().unwrap().acos(),
×
1067
        SteelVal::NumV(n) => n.acos(),
×
1068
        SteelVal::Rational(r) => (*r.numer() as f32 / *r.denom() as f32).acos() as f64,
×
1069
        _ => stop!(TypeMismatch => "acos expects a number, found: {}", arg),
×
1070
    }
1071
    .into_steelval()
1072
}
1073

1074
/// Returns the arctangent, or inverse tangent, of a value; output is in radians.
1075
///
1076
/// (atan n) -> number?
1077
///
1078
/// * n : number? - The input value is the tangent of the angle you want.
1079
///
1080
/// # Examples
1081
/// ```scheme
1082
/// > (atan -1) ;; => -0.7853981633974483
1083
/// > (atan 0) ;; => 0
1084
/// > (atan 0.5) ;; => 0.46364760900080615
1085
/// > (atan 2) ;; => 1.1071487177940906
1086
/// ```
1087
#[steel_derive::function(name = "atan", constant = true)]
1088
pub fn atan(arg: &SteelVal) -> Result<SteelVal> {
40,184✔
1089
    match arg {
40,184✔
1090
        SteelVal::IntV(i) => (*i as f64).atan(),
×
1091
        SteelVal::BigNum(i) => i.to_f64().unwrap().atan(),
×
1092
        SteelVal::NumV(n) => n.atan(),
120,552✔
1093
        SteelVal::Rational(r) => (*r.numer() as f32 / *r.denom() as f32).atan() as f64,
×
1094
        _ => stop!(TypeMismatch => "atan expects a number, found: {}", arg),
×
1095
    }
1096
    .into_steelval()
1097
}
1098

1099
/// Divides the given numbers.
1100
///
1101
/// (/ . nums) -> number?
1102
///
1103
/// * nums : number? - The numbers to divide. Must have at least one number.
1104
///
1105
/// # Examples
1106
/// ```scheme
1107
/// > (/ 10 2) ;; => 5
1108
/// > (/ 10 2 2.0) ;; => 2.5
1109
/// > (/ 1 3.0) ;; => 0.3333333333333333
1110
/// > (/ 1 3) ;; => 1/3
1111
/// ```
1112
#[steel_derive::native(name = "/", constant = true, arity = "AtLeast(1)")]
1113
pub fn divide_primitive(args: &[SteelVal]) -> Result<SteelVal> {
7,299,493✔
1114
    ensure_args_are_numbers("/", args)?;
21,898,479✔
1115
    let recip = |x: &SteelVal| -> Result<SteelVal> {
14,598,986✔
1116
        match x {
7,299,493✔
1117
            SteelVal::IntV(n) => match i32::try_from(*n) {
14,378,276✔
1118
                Ok(0) => {
1119
                    stop!(Generic => "/: division by zero")
2✔
1120
                }
1121
                Ok(n) => Rational32::new(1, n).into_steelval(),
28,756,548✔
1122
                Err(_) => BigRational::new(BigInt::from(1), BigInt::from(*n)).into_steelval(),
×
1123
            },
1124
            SteelVal::NumV(n) => n.recip().into_steelval(),
441,392✔
1125
            SteelVal::Rational(r) => r.recip().into_steelval(),
8✔
1126
            SteelVal::BigRational(r) => r.recip().into_steelval(),
×
1127
            SteelVal::BigNum(n) => BigRational::new(1.into(), n.as_ref().clone()).into_steelval(),
24✔
1128
            SteelVal::Complex(c) => complex_reciprocal(c),
3✔
1129
            unexpected => {
×
1130
                steelerr!(TypeMismatch => "/ expects a number, but found: {:?}", unexpected)
×
1131
            }
1132
        }
1133
    };
1134
    match &args {
7,299,493✔
1135
        [] => steelerr!(ArityMismatch => "/ requires at least one argument"),
7,299,493✔
1136
        [x] => recip(x),
6,022✔
1137
        // TODO: Provide custom implementation to optimize by joining the multiply and recip calls.
1138
        [x, y] => multiply_two(x, &recip(y)?),
36,482,405✔
1139
        [x, ys @ ..] => {
2✔
1140
            let d = multiply_primitive_impl(ys)?;
3✔
1141
            multiply_two(x, &recip(&d)?)
3✔
1142
        }
1143
    }
1144
}
1145

1146
/// Checks if the given value is exact.
1147
///
1148
/// (exact? val) -> boolean?
1149
///
1150
/// * val : any - The value to check for exactness.
1151
///
1152
/// # Examples
1153
/// ```scheme
1154
/// > (exact? 42) ;; => #t
1155
/// > (exact? 3.14) ;; => #f
1156
/// > (exact? "hello") ;; => #f
1157
/// ```
1158
#[steel_derive::function(name = "exact?", constant = true)]
1159
pub fn exactp(value: &SteelVal) -> bool {
10✔
1160
    match value {
10✔
1161
        SteelVal::IntV(_)
1162
        | SteelVal::BigNum(_)
1163
        | SteelVal::Rational(_)
1164
        | SteelVal::BigRational(_) => true,
6✔
1165
        SteelVal::Complex(x) => exactp(&x.re) && exactp(&x.im),
8✔
1166
        _ => false,
2✔
1167
    }
1168
}
1169

1170
/// Checks if the given value is inexact.
1171
///
1172
/// (inexact? val) -> boolean?
1173
///
1174
/// * val : any - The value to check for inexactness.
1175
///
1176
/// # Examples
1177
/// ```scheme
1178
/// > (inexact? 42) ;; => #f
1179
/// > (inexact? 3.14) ;; => #t
1180
/// ```
1181
#[steel_derive::function(name = "inexact?", constant = true)]
1182
pub fn inexactp(value: &SteelVal) -> bool {
12✔
1183
    match value {
12✔
1184
        SteelVal::NumV(_) => true,
4✔
1185
        SteelVal::Complex(x) => inexactp(&x.re) || inexactp(&x.im),
8✔
1186
        _ => false,
6✔
1187
    }
1188
}
1189

1190
fn number_to_float(number: &SteelVal) -> Result<f64> {
×
1191
    let res = match number {
×
1192
        SteelVal::IntV(i) => *i as f64,
×
1193
        SteelVal::Rational(f) => f.to_f64().unwrap(),
×
1194
        SteelVal::BigRational(f) => f.to_f64().unwrap(),
×
1195
        SteelVal::NumV(n) => *n,
×
1196
        SteelVal::BigNum(n) => n.to_f64().unwrap(),
×
1197
        _ => stop!(TypeMismatch => "number->float expects a real number, found: {}", number),
×
1198
    };
1199
    Ok(res)
×
1200
}
1201

1202
/// Converts a number to an inexact number.
1203
///
1204
/// (inexact num) -> number?
1205
///
1206
/// * num : number? - The number to convert from exact to inexact.
1207
///
1208
/// # Examples
1209
/// ```scheme
1210
/// > (inexact 10) ;; => 10
1211
/// > (inexact 1/2) ;; => 0.5
1212
/// > (inexact 1+2i) ;; => 1+2i
1213
/// ```
1214
#[steel_derive::function(name = "inexact", alias = "exact->inexact", constant = true)]
1215
fn inexact(number: &SteelVal) -> Result<SteelVal> {
122,670✔
1216
    match number {
122,670✔
1217
        SteelVal::IntV(i) => (*i as f64).into_steelval(),
367,950✔
1218
        SteelVal::Rational(f) => f.to_f64().unwrap().into_steelval(),
95✔
1219
        SteelVal::BigRational(f) => f.to_f64().unwrap().into_steelval(),
×
1220
        SteelVal::NumV(n) => n.into_steelval(),
3✔
1221
        SteelVal::BigNum(n) => Ok(SteelVal::NumV(n.to_f64().unwrap())),
×
1222
        SteelVal::Complex(x) => SteelComplex::new(inexact(&x.re)?, inexact(&x.im)?).into_steelval(),
×
1223
        _ => steelerr!(TypeMismatch => "exact->inexact expects a number type, found: {}", number),
×
1224
    }
1225
}
1226

1227
/// Converts a number to an exact number.
1228
///
1229
/// (exact num) -> number?
1230
///
1231
/// * num : number? - The value to convert to exact.
1232
///
1233
/// # Examples
1234
/// ```scheme
1235
/// > (exact 10.0) ;; => 10
1236
/// > (exact 1.5) ;; => 3/2
1237
/// > (exact 1.5+2.5i) ;; => 3/2+5/2i
1238
/// ```
1239
#[steel_derive::function(name = "exact", alias = "inexact->exact", constant = true)]
1240
pub fn exact(number: &SteelVal) -> Result<SteelVal> {
20,005✔
1241
    match number {
20,005✔
1242
        SteelVal::IntV(_)
1243
        | SteelVal::Rational(_)
1244
        | SteelVal::BigRational(_)
1245
        | SteelVal::BigNum(_) => Ok(number.clone()),
2✔
1246
        SteelVal::NumV(x) => {
20,003✔
1247
            if x.fract() == 0. {
20,003✔
1248
                (*x as isize).into_steelval()
40,004✔
1249
            } else {
1250
                BigRational::from_float(*x)
2✔
1251
                    .ok_or_else(throw!(ConversionError => "no exact representation for {}", x))?
2✔
1252
                    .into_steelval()
1253
            }
1254
        }
1255
        SteelVal::Complex(x) => SteelComplex::new(exact(&x.re)?, exact(&x.im)?).into_steelval(),
×
1256
        _ => steelerr!(TypeMismatch => "exact->inexact expects a number type, found: {}", number),
×
1257
    }
1258
}
1259

1260
fn finitep_impl(number: &SteelVal) -> Result<bool> {
7✔
1261
    match number {
5✔
1262
        SteelVal::NumV(x) if x.is_nan() || x.is_infinite() => Ok(false),
29✔
1263
        SteelVal::IntV(_)
1264
        | SteelVal::NumV(_)
1265
        | SteelVal::BigNum(_)
1266
        | SteelVal::Rational(_)
1267
        | SteelVal::BigRational(_) => Ok(true),
3✔
1268
        SteelVal::Complex(x) => Ok(finitep_impl(&x.re)? && finitep_impl(&x.im)?),
×
1269
        _ => steelerr!(TypeMismatch => "finite? expects a number, found: {}", number),
×
1270
    }
1271
}
1272

1273
/// Returns `#t` if the given number is finite.
1274
///
1275
/// (finite? number) -> boolean?
1276
///
1277
/// * number : number? - The number to check for finiteness.
1278
///
1279
/// # Examples
1280
/// ```scheme
1281
/// > (finite? 42) ;; => #t
1282
/// > (finite? 0.1) ;; => #t
1283
/// > (finite? +inf.0) ;; => #f
1284
/// > (finite? -inf.0) ;; => #f
1285
/// > (finite? +nan.0) ;; => #f
1286
/// ```
1287
#[steel_derive::function(name = "finite?", constant = true)]
1288
fn finitep(number: &SteelVal) -> Result<SteelVal> {
7✔
1289
    finitep_impl(number).into_steelval()
21✔
1290
}
1291

1292
fn infinitep_impl(number: &SteelVal) -> Result<bool> {
5✔
1293
    match number {
3✔
1294
        SteelVal::NumV(x) if x.is_infinite() => Ok(true),
13✔
1295
        SteelVal::IntV(_)
1296
        | SteelVal::NumV(_)
1297
        | SteelVal::BigNum(_)
1298
        | SteelVal::Rational(_)
1299
        | SteelVal::BigRational(_) => Ok(false),
3✔
1300
        SteelVal::Complex(n) => Ok(infinitep_impl(&n.re)? || infinitep_impl(&n.im)?),
×
1301
        _ => steelerr!(TypeMismatch => "exact->inexact expects a real number, found: {}", number),
×
1302
    }
1303
}
1304

1305
/// Returns `#t` if the given number is infinite.
1306
///
1307
/// (infinite? number) -> boolean?
1308
///
1309
/// * number : number? - The number to check for infiniteness.
1310
///
1311
/// # Examples
1312
/// ```scheme
1313
/// > (infinite? 42) ;; => #f
1314
/// > (infinite? -nan.0) ;; => #f
1315
/// > (infinite? +inf.0) ;; => #t
1316
/// ```
1317
#[steel_derive::function(name = "infinite?", constant = true)]
1318
fn infinitep(number: &SteelVal) -> Result<SteelVal> {
5✔
1319
    infinitep_impl(number)?.into_steelval()
15✔
1320
}
1321

1322
/// Computes the absolute value of the given number.
1323
///
1324
/// (abs number) -> number?
1325
///
1326
/// * number : number? - The number to compute the absolute value of.
1327
///
1328
/// # Examples
1329
/// ```scheme
1330
/// > (abs 42) ;; => 42
1331
/// > (abs -42) ;; => 42
1332
/// > (abs 0) ;; => 0
1333
/// ```
1334
#[steel_derive::function(name = "abs", constant = true)]
1335
fn abs(number: &SteelVal) -> Result<SteelVal> {
17✔
1336
    match number {
17✔
1337
        SteelVal::IntV(i) => Ok(SteelVal::IntV(i.abs())),
30✔
1338
        SteelVal::NumV(n) => Ok(SteelVal::NumV(n.abs())),
2✔
1339
        SteelVal::Rational(f) => f.abs().into_steelval(),
4✔
1340
        SteelVal::BigRational(f) => f.abs().into_steelval(),
×
1341
        SteelVal::BigNum(n) => n.as_ref().abs().into_steelval(),
×
1342
        _ => steelerr!(TypeMismatch => "abs expects a real number, found: {}", number),
×
1343
    }
1344
}
1345

1346
/// Retrieves the denominator of the given rational number.
1347
///
1348
/// (denominator number) -> integer?
1349
///
1350
/// * number : number? - The rational number to retrieve the denominator from.
1351
///
1352
/// # Examples
1353
/// ```scheme
1354
/// > (denominator 1/2) ;; => 2
1355
/// > (denominator 3/4) ;; => 4
1356
/// > (denominator 4) ;; => 1
1357
/// ```
1358
#[steel_derive::function(name = "denominator", constant = true)]
1359
fn denominator(number: &SteelVal) -> Result<SteelVal> {
2✔
1360
    match number {
2✔
1361
        SteelVal::IntV(_) | SteelVal::BigNum(_) => 1.into_steelval(),
1✔
1362
        SteelVal::NumV(_) => {
1363
            steelerr!(TypeMismatch => "denominator not supported for number {}", number)
×
1364
        }
1365
        SteelVal::Rational(f) => f.denom().into_steelval(),
3✔
1366
        SteelVal::BigRational(f) => f.denom().clone().into_steelval(),
×
1367
        _ => steelerr!(TypeMismatch => "ceiling expects a real number, found: {}", number),
×
1368
    }
1369
}
1370

1371
// TODO: Add support for BigNum.
1372
/// Raises the left operand to the power of the right operand.
1373
///
1374
/// (expt base exponent) -> number?
1375
///
1376
/// * base : number? - The base number.
1377
/// * exponent : number? - The exponent to raise the base to.
1378
///
1379
/// # Examples
1380
/// ```scheme
1381
/// > (expt 2 3) ;; => 8
1382
/// > (expt 2.0 0.5) ;; => 1.4142135623730951
1383
/// > (expt 9 0.5) ;; => 3
1384
/// ```
1385
#[steel_derive::function(name = "expt", constant = true)]
1386
fn expt(left: &SteelVal, right: &SteelVal) -> Result<SteelVal> {
433,027✔
1387
    match (left, right) {
866,054✔
1388
        (SteelVal::IntV(l), SteelVal::IntV(r)) if *r >= 0 => {
1,732,088✔
1389
            match u32::try_from(*r).ok().and_then(|r| l.checked_pow(r)) {
3,031,154✔
1390
                Some(val) => val.into_steelval(),
1,260,060✔
1391
                None => BigInt::from(*l).pow(*r as usize).into_steelval(),
65,010✔
1392
            }
1393
        }
1394
        // r is negative here
1395
        (SteelVal::IntV(l), SteelVal::IntV(r)) => {
×
1396
            if l.is_zero() {
×
1397
                stop!(Generic => "expt: 0 cannot be raised to a negative power");
×
1398
            }
1399

1400
            let r = r.unsigned_abs();
×
1401
            // a^-b = 1/(a^b)
1402
            match (u32::try_from(r).ok())
×
1403
                .and_then(|r| l.checked_pow(r))
×
1404
                .and_then(|l| i32::try_from(l).ok())
×
1405
            {
1406
                Some(val) => Rational32::new_raw(1, val).into_steelval(),
×
1407
                None => {
1408
                    BigRational::new_raw(BigInt::from(1), BigInt::from(*l).pow(r)).into_steelval()
×
1409
                }
1410
            }
1411
        }
1412
        (SteelVal::IntV(l), SteelVal::NumV(r)) => (*l as f64).powf(*r).into_steelval(),
6✔
1413
        (SteelVal::IntV(l), SteelVal::Rational(r)) => {
2✔
1414
            (*l as f64).powf(r.to_f64().unwrap()).into_steelval()
6✔
1415
        }
1416
        (SteelVal::IntV(l), SteelVal::BigNum(r)) => {
×
1417
            if l.is_zero() {
×
1418
                stop!(Generic => "expt: 0 cannot be raised to a negative power");
×
1419
            }
1420

1421
            let expt = BigInt::from(*l).pow(r.magnitude());
×
1422
            match r.sign() {
×
1423
                num_bigint::Sign::Plus | num_bigint::Sign::NoSign => expt.into_steelval(),
×
1424
                num_bigint::Sign::Minus => {
1425
                    BigRational::new_raw(BigInt::from(1), expt).into_steelval()
×
1426
                }
1427
            }
1428
        }
1429
        (SteelVal::IntV(l), SteelVal::BigRational(r)) => {
×
1430
            (*l as f64).powf(r.to_f64().unwrap()).into_steelval()
×
1431
        }
1432
        (SteelVal::NumV(l), SteelVal::NumV(r)) => Ok(SteelVal::NumV(l.powf(*r))),
4✔
1433
        (SteelVal::NumV(l), SteelVal::IntV(r)) => match i32::try_from(*r) {
×
1434
            Ok(r) => l.powi(r).into_steelval(),
×
1435
            Err(_) => l.powf(*r as f64).into_steelval(),
×
1436
        },
1437
        (SteelVal::NumV(l), SteelVal::Rational(r)) => l.powf(r.to_f64().unwrap()).into_steelval(),
×
1438
        (SteelVal::NumV(l), SteelVal::BigNum(r)) => l.powf(r.to_f64().unwrap()).into_steelval(),
×
1439
        (SteelVal::NumV(l), SteelVal::BigRational(r)) => {
×
1440
            l.powf(r.to_f64().unwrap()).into_steelval()
×
1441
        }
1442
        (SteelVal::Rational(l), SteelVal::Rational(r)) => l
×
1443
            .to_f64()
1444
            .unwrap()
1445
            .powf(r.to_f64().unwrap())
×
1446
            .into_steelval(),
1447
        (SteelVal::Rational(l), SteelVal::NumV(r)) => l.to_f64().unwrap().powf(*r).into_steelval(),
8✔
1448
        (SteelVal::Rational(l), SteelVal::IntV(r)) => match i32::try_from(*r) {
3✔
1449
            Ok(r) => l.pow(r).into_steelval(),
5✔
1450
            Err(_) => {
1451
                let base = BigRational::new(BigInt::from(*l.numer()), BigInt::from(*l.denom()));
×
1452
                let exp = BigInt::from(*r);
×
1453
                base.pow(exp).into_steelval()
×
1454
            }
1455
        },
1456
        (SteelVal::Rational(l), SteelVal::BigNum(r)) => l
×
1457
            .to_f64()
1458
            .unwrap()
1459
            .powf(r.to_f64().unwrap())
×
1460
            .into_steelval(),
1461
        (SteelVal::Rational(l), SteelVal::BigRational(r)) => l
×
1462
            .to_f64()
1463
            .unwrap()
1464
            .powf(r.to_f64().unwrap())
×
1465
            .into_steelval(),
1466
        (SteelVal::BigNum(l), SteelVal::BigNum(r)) => {
×
1467
            let expt = l.as_ref().clone().pow(r.magnitude());
×
1468
            match r.sign() {
×
1469
                num_bigint::Sign::NoSign | num_bigint::Sign::Plus => expt.into_steelval(),
×
1470
                num_bigint::Sign::Minus => {
1471
                    BigRational::new_raw(BigInt::from(1), expt).into_steelval()
×
1472
                }
1473
            }
1474
        }
1475
        (SteelVal::BigNum(l), SteelVal::IntV(r)) => match *r {
×
1476
            0 => 1.into_steelval(),
×
1477
            r if r < 0 => {
×
1478
                BigRational::new_raw(BigInt::from(1), l.as_ref().clone().pow(r.unsigned_abs()))
×
1479
                    .into_steelval()
1480
            }
1481
            r => l.as_ref().clone().pow(r as usize).into_steelval(),
×
1482
        },
1483
        (SteelVal::BigNum(l), SteelVal::NumV(r)) => l.to_f64().unwrap().powf(*r).into_steelval(),
×
1484
        (SteelVal::BigNum(l), SteelVal::Rational(r)) => l
×
1485
            .to_f64()
1486
            .unwrap()
1487
            .powf(r.to_f64().unwrap())
×
1488
            .into_steelval(),
1489
        (SteelVal::BigNum(l), SteelVal::BigRational(r)) => l
×
1490
            .to_f64()
1491
            .unwrap()
1492
            .powf(r.to_f64().unwrap())
×
1493
            .into_steelval(),
1494
        (SteelVal::BigRational(l), SteelVal::Rational(r)) => l
×
1495
            .to_f64()
1496
            .unwrap()
1497
            .powf(r.to_f64().unwrap())
×
1498
            .into_steelval(),
1499
        (SteelVal::BigRational(l), SteelVal::NumV(r)) => {
×
1500
            l.to_f64().unwrap().powf(*r).into_steelval()
×
1501
        }
1502
        (SteelVal::BigRational(l), SteelVal::IntV(r)) => match i32::try_from(*r) {
×
1503
            Ok(r) => l.as_ref().pow(r).into_steelval(),
×
1504
            Err(_) => {
1505
                let exp = BigInt::from(*r);
×
1506
                l.as_ref().clone().pow(exp).into_steelval()
×
1507
            }
1508
        },
1509
        (SteelVal::BigRational(l), SteelVal::BigNum(r)) => l
×
1510
            .to_f64()
1511
            .unwrap()
1512
            .powf(r.to_f64().unwrap())
×
1513
            .into_steelval(),
1514
        (SteelVal::BigRational(l), SteelVal::BigRational(r)) => l
×
1515
            .to_f64()
1516
            .unwrap()
1517
            .powf(r.to_f64().unwrap())
×
1518
            .into_steelval(),
1519
        (l, r) => {
×
1520
            steelerr!(TypeMismatch => "expt expected two numbers but found {} and {}", l, r)
×
1521
        }
1522
    }
1523
}
1524

1525
/// Returns Euler’s number raised to the power of z.
1526
///
1527
/// (exp z) -> number?
1528
///
1529
/// * z : number? - The number to raise e to the power of.
1530
///
1531
/// # Examples
1532
/// ```scheme
1533
/// > (exp 0) ;; => 1
1534
/// > (exp 2) ;; => 7.38905609893065
1535
/// > (exp 1.5) ;; => 4.4816890703380645
1536
/// ```
1537
#[steel_derive::function(name = "exp", constant = true)]
1538
fn exp(left: &SteelVal) -> Result<SteelVal> {
2✔
1539
    match left {
1✔
1540
        SteelVal::IntV(0) => Ok(SteelVal::IntV(1)),
1✔
1541
        SteelVal::IntV(l) if *l < i32::MAX as isize => {
3✔
1542
            Ok(SteelVal::NumV(std::f64::consts::E.powi(*l as i32)))
1✔
1543
        }
1544
        maybe_number => match number_to_float(maybe_number) {
×
1545
            Ok(n) => Ok(SteelVal::NumV(std::f64::consts::E.powf(n))),
×
1546
            Err(_) => steelerr!(Generic => "exp expected a real number"),
×
1547
        },
1548
    }
1549
}
1550

1551
/// Retrieves the numerator of the given rational number.
1552
///
1553
/// (numerator number) -> number?
1554
///
1555
/// * number : number? - The rational number to retrieve the numerator from.
1556
///
1557
/// # Examples
1558
/// ```scheme
1559
/// > (numerator 3/4) ;; => 3
1560
/// > (numerator 5/2) ;; => 5
1561
/// > (numerator -2) ;; => -2
1562
/// ```
1563
#[steel_derive::function(name = "numerator", constant = true)]
1564
fn numerator(number: &SteelVal) -> Result<SteelVal> {
2✔
1565
    match number {
2✔
1566
        SteelVal::IntV(x) => x.into_steelval(),
3✔
1567
        SteelVal::Rational(x) => (*x.numer() as isize).into_steelval(),
3✔
1568
        SteelVal::BigNum(x) => Ok(SteelVal::BigNum(x.clone())),
×
1569
        SteelVal::BigRational(x) => (x.numer().clone()).into_steelval(),
×
1570
        _ => steelerr!(Generic => "numerator expects an integer or rational number"),
×
1571
    }
1572
}
1573

1574
/// Rounds the given number down to the nearest integer not larger than it.
1575
///
1576
/// (floor number) -> number?
1577
///
1578
/// * number : real? - The number to compute the floor for.
1579
///
1580
/// # Examples
1581
/// ```scheme
1582
/// > (floor 3.14) ;; => 3
1583
/// > (floor 4.99) ;; => 4
1584
/// > (floor -2.5) ;; => -3
1585
/// ```
1586
#[steel_derive::function(name = "floor", constant = true)]
1587
fn floor(number: &SteelVal) -> Result<SteelVal> {
10,008✔
1588
    match number {
10,008✔
1589
        SteelVal::NumV(x) => Ok(SteelVal::NumV(x.floor())),
20,006✔
1590
        SteelVal::IntV(x) => x.into_steelval(),
9✔
1591
        SteelVal::Rational(x) => x.floor().into_steelval(),
8✔
1592
        SteelVal::BigNum(x) => Ok(SteelVal::BigNum(x.clone())),
×
1593
        SteelVal::BigRational(x) => x.floor().into_steelval(),
×
1594
        _ => steelerr!(Generic => "floor expects a real number, found: {}", number),
×
1595
    }
1596
}
1597

1598
/// Rounds the given number up to the nearest integer not less than it.
1599
///
1600
/// (ceiling number) -> integer?
1601
///
1602
/// * number : real? - The number to round up.
1603
///
1604
/// # Examples
1605
/// ```scheme
1606
/// > (ceiling 42) ;; => 42
1607
/// > (ceiling 42.1) ;; => 43
1608
/// > (ceiling -42.1) ;; => -42
1609
/// ```
1610
#[steel_derive::function(name = "ceiling", constant = true)]
1611
fn ceiling(number: &SteelVal) -> Result<SteelVal> {
6✔
1612
    match number {
6✔
1613
        n @ SteelVal::IntV(_) | n @ SteelVal::BigNum(_) => Ok(n.clone()),
2✔
1614
        SteelVal::NumV(n) => Ok(SteelVal::NumV(n.ceil())),
6✔
1615
        SteelVal::Rational(f) => f.ceil().into_steelval(),
8✔
1616
        SteelVal::BigRational(f) => f.ceil().into_steelval(),
×
1617
        _ => steelerr!(TypeMismatch => "ceiling expects a real number, found: {}", number),
×
1618
    }
1619
}
1620

1621
/// Rounds the given number to the nearest integer, whose absolute value is not
1622
/// larger than it.
1623
///
1624
/// (truncate number) -> integer?
1625
///
1626
/// * number : real? - The number to truncate.
1627
///
1628
/// # Examples
1629
///
1630
/// ```scheme
1631
/// > (truncate 42) ;; => 42
1632
/// > (truncate 42.1) ;; => 42
1633
/// > (truncate -42.1) ;; => -42
1634
/// ```
1635
#[steel_derive::function(name = "truncate", constant = true)]
1636
pub fn truncate(arg: &SteelVal) -> Result<SteelVal> {
30,006✔
1637
    match arg {
30,006✔
1638
        SteelVal::NumV(n) => n.trunc().into_steelval(),
120,012✔
1639
        SteelVal::IntV(i) => Ok(SteelVal::IntV(*i)),
2✔
1640
        SteelVal::Rational(ratio) => ratio.trunc().into_steelval(),
8✔
1641
        SteelVal::BigNum(gc) => Ok(SteelVal::BigNum(gc.clone())),
×
1642
        SteelVal::BigRational(gc) => gc.trunc().into_steelval(),
×
1643
        _ => stop!(TypeMismatch => "truncate expects a real number, found: {}", arg),
×
1644
    }
1645
}
1646

1647
/// Rounds to the nearest integer. Rounds half-way cases to even.
1648
///
1649
/// Reimplementation of https://github.com/rust-num/num-rational/pull/141,
1650
/// while that one isn't merged yet.
1651
fn rational_round_ties_even<T>(num: &Ratio<T>) -> Ratio<T>
5✔
1652
where
1653
    T: Zero + One + Integer + Clone,
1654
{
1655
    let zero: Ratio<T> = Zero::zero();
15✔
1656
    let one: T = One::one();
15✔
1657
    let two: T = one.clone() + one.clone();
25✔
1658

1659
    // Find unsigned fractional part of rational number
1660
    let mut fractional = num.fract();
15✔
1661
    if fractional < zero {
5✔
1662
        fractional = zero - fractional
×
1663
    };
1664

1665
    // Compare the unsigned fractional part with 1/2
1666
    let half = Ratio::new_raw(one, two);
20✔
1667
    match fractional.cmp(&half) {
10✔
1668
        Ordering::Greater => {
×
1669
            let one: Ratio<T> = One::one();
3✔
1670
            if *num >= Zero::zero() {
1✔
1671
                num.trunc() + one
2✔
1672
            } else {
1673
                num.trunc() - one
×
1674
            }
1675
        }
1676
        Ordering::Equal => {
×
1677
            let trunc = num.trunc();
9✔
1678
            if trunc.numer().is_even() {
6✔
1679
                trunc
2✔
1680
            } else {
1681
                let one: Ratio<T> = One::one();
3✔
1682
                if *num >= Zero::zero() {
1✔
1683
                    num.trunc() + one
2✔
1684
                } else {
1685
                    num.trunc() - one
×
1686
                }
1687
            }
1688
        }
1689
        Ordering::Less => num.trunc(),
2✔
1690
    }
1691
}
1692

1693
/// Rounds the given number to the nearest integer, rounding half-way cases to
1694
/// the even number.
1695
///
1696
/// (round number) -> number?
1697
///
1698
/// * number : real? - The number to round.
1699
///
1700
/// # Examples
1701
/// ```scheme
1702
/// > (round 3.14) ;; => 3
1703
/// > (round 4.6) ;; => 5
1704
/// > (round 2.5) ;; => 2
1705
/// > (round 3.5) ;; => 4
1706
/// > (round -2.5) ;; => -2
1707
/// ```
1708
#[steel_derive::function(name = "round", constant = true)]
1709
fn round(number: &SteelVal) -> Result<SteelVal> {
32✔
1710
    match number {
32✔
1711
        SteelVal::IntV(i) => i.into_steelval(),
57✔
1712
        SteelVal::NumV(n) => n.round_ties_even().into_steelval(),
28✔
1713
        SteelVal::Rational(f) => rational_round_ties_even(f).into_steelval(),
20✔
1714
        SteelVal::BigRational(f) => rational_round_ties_even(f).into_steelval(),
×
1715
        SteelVal::BigNum(n) => Ok(SteelVal::BigNum(n.clone())),
2✔
1716
        _ => steelerr!(TypeMismatch => "round expects a real number, found: {}", number),
×
1717
    }
1718
}
1719

1720
/// Computes the square of the given number.
1721
///
1722
/// (square number) -> number?
1723
///
1724
/// * number : number? - The number to square.
1725
///
1726
/// # Examples
1727
/// ```scheme
1728
/// > (square 5) ;; => 25
1729
/// > (square -3) ;; => 9
1730
/// > (square 2.5) ;; => 6.25
1731
/// ```
1732
#[steel_derive::function(name = "square", constant = true)]
1733
fn square(number: &SteelVal) -> Result<SteelVal> {
38,116✔
1734
    if !numberp(number) {
38,116✔
1735
        stop!(TypeMismatch => "square expects a number, found: {:?}", number)
×
1736
    }
1737
    multiply_two(number, number)
114,348✔
1738
}
1739

1740
/// Computes the square root of the given number.
1741
///
1742
/// (sqrt number) -> number?
1743
///
1744
/// * number : number? - The number to compute the square root for.
1745
///
1746
/// # Examples
1747
/// ```scheme
1748
/// > (sqrt 4) ;; => 2
1749
/// > (sqrt 2) ;; => 1.4142135623730951
1750
/// > (sqrt -1) ;; => 0+1i
1751
/// ```
1752
#[steel_derive::function(name = "sqrt", constant = true)]
1753
fn sqrt(number: &SteelVal) -> Result<SteelVal> {
69,621✔
1754
    match number {
69,621✔
1755
        SteelVal::NumV(x) => {
69,601✔
1756
            if x.is_negative() {
139,202✔
1757
                let imag = x.neg().sqrt();
×
1758
                SteelComplex::new(0.0.into_steelval()?, imag.into_steelval()?).into_steelval()
×
1759
            } else {
1760
                x.sqrt().into_steelval()
208,803✔
1761
            }
1762
        }
1763
        SteelVal::IntV(x) => {
15✔
1764
            if x.is_negative() {
30✔
1765
                let sqrt = (*x as f64).abs().sqrt();
×
1766
                if sqrt as isize as f64 == sqrt {
×
1767
                    SteelComplex::new(0.into_steelval()?, (sqrt as isize).into_steelval()?)
×
1768
                        .into_steelval()
1769
                } else {
1770
                    SteelComplex::new(0.into_steelval()?, sqrt.into_steelval()?).into_steelval()
×
1771
                }
1772
            } else {
1773
                let sqrt = (*x as f64).sqrt();
45✔
1774
                if sqrt as isize as f64 == sqrt {
15✔
1775
                    (sqrt as isize).into_steelval()
30✔
1776
                } else {
1777
                    sqrt.into_steelval()
×
1778
                }
1779
            }
1780
        }
1781
        SteelVal::Rational(x) => {
1✔
1782
            let n = x.numer().abs();
3✔
1783
            let d = *x.denom();
2✔
1784
            let n_sqrt = (n as f64).sqrt();
3✔
1785
            let d_sqrt = (d as f64).sqrt();
3✔
1786
            let sqrt = if n_sqrt as i32 as f64 == n_sqrt && d_sqrt as i32 as f64 == d_sqrt {
3✔
1787
                Rational32::new(n_sqrt as i32, d_sqrt as i32).into_steelval()?
4✔
1788
            } else {
1789
                (n_sqrt / d_sqrt).into_steelval()?
×
1790
            };
1791
            if x.is_negative() {
2✔
1792
                let re = if exactp(&sqrt) {
×
1793
                    0.into_steelval()?
×
1794
                } else {
1795
                    0.0.into_steelval()?
×
1796
                };
1797
                SteelComplex::new(re, sqrt).into_steelval()
×
1798
            } else {
1799
                Ok(sqrt)
1✔
1800
            }
1801
        }
1802
        SteelVal::BigNum(n) => {
×
1803
            let sqrt = n.as_ref().to_f64().unwrap().sqrt();
×
1804
            if n.as_ref().is_negative() {
×
1805
                SteelComplex::new(0.0.into_steelval()?, sqrt.into_steelval()?).into_steelval()
×
1806
            } else {
1807
                sqrt.into_steelval()
×
1808
            }
1809
        }
1810
        SteelVal::BigRational(n) => {
×
1811
            let sqrt = n.as_ref().to_f64().unwrap().sqrt();
×
1812
            if n.as_ref().is_negative() {
×
1813
                SteelComplex::new(0.0.into_steelval()?, sqrt.into_steelval()?).into_steelval()
×
1814
            } else {
1815
                sqrt.into_steelval()
×
1816
            }
1817
        }
1818
        SteelVal::Complex(n) => {
4✔
1819
            let z_mag = magnitude(number)?;
12✔
1820
            let half = Rational32::new(1, 2).into_steelval()?;
12✔
1821
            let re = sqrt(&multiply_two(&add_two(&z_mag, &n.re)?, &half)?)?;
28✔
1822
            let im = sqrt(&multiply_two(&add_two(&z_mag, &negate(&n.re)?)?, &half)?)?;
32✔
1823
            if negativep(&n.im)? == SteelVal::BoolV(true) {
12✔
1824
                SteelComplex::new(re, negate(&im)?).into_steelval()
10✔
1825
            } else {
1826
                SteelComplex::new(re, im).into_steelval()
8✔
1827
            }
1828
        }
1829
        _ => steelerr!(TypeMismatch => "sqrt expected a number"),
×
1830
    }
1831
}
1832

1833
/// Create a complex number with `re` as the real part and `im` as the imaginary part.
1834
///
1835
/// (make-rectangular re im) -> number?
1836
///
1837
/// - re : real?
1838
/// - im : real?
1839
#[steel_derive::function(name = "make-rectangular", constant = true)]
1840
pub fn make_rectangular(re: &SteelVal, im: &SteelVal) -> Result<SteelVal> {
56,250✔
1841
    ensure_arg_is_real("make-rectangular", re)?;
168,750✔
1842
    ensure_arg_is_real("make-rectangular", im)?;
168,750✔
1843

1844
    SteelComplex {
1845
        re: re.clone(),
168,750✔
1846
        im: im.clone(),
56,250✔
1847
    }
1848
    .into_steelval()
1849
}
1850

1851
/// Make a complex number out of a magnitude `r` and an angle `θ`, so that the result is `r * (cos θ + i sin θ)`
1852
///
1853
/// (make-polar r θ) -> number?
1854
///
1855
/// - r : real?
1856
/// - theta : real?
1857
#[steel_derive::function(name = "make-polar", constant = true)]
1858
pub fn make_polar(r: &SteelVal, theta: &SteelVal) -> Result<SteelVal> {
×
1859
    ensure_arg_is_real("make-polar", r)?;
×
1860
    ensure_arg_is_real("make-polar", theta)?;
×
1861

1862
    let re = multiply_primitive(&[r.clone(), cos(theta)?])?;
×
1863
    let im = multiply_primitive(&[r.clone(), sin(theta)?])?;
×
1864
    SteelComplex { re, im }.into_steelval()
1865
}
1866

1867
fn ensure_arg_is_real(op: &str, arg: &SteelVal) -> Result<()> {
112,500✔
1868
    if !realp(arg) {
112,500✔
1869
        stop!(TypeMismatch => "{op} expects a real number, found: {:?}", arg);
×
1870
    } else {
1871
        Ok(())
112,500✔
1872
    }
1873
}
1874

1875
/// Returns the real part of a number
1876
///
1877
/// (real-part number) -> number?
1878
///
1879
/// # Examples
1880
/// ```scheme
1881
/// > (real-part 3+4i) ;; => 3
1882
/// > (real-part 42) ;; => 42
1883
/// ```
1884
#[steel_derive::function(name = "real-part", constant = true)]
1885
pub fn real_part(value: &SteelVal) -> Result<SteelVal> {
1,703,532✔
1886
    match value {
1,703,532✔
1887
        val @ SteelVal::IntV(_)
1✔
1888
        | val @ SteelVal::BigNum(_)
×
1889
        | val @ SteelVal::Rational(_)
×
1890
        | val @ SteelVal::BigRational(_)
×
1891
        | val @ SteelVal::NumV(_) => Ok(val.clone()),
1✔
1892
        SteelVal::Complex(complex) => Ok(complex.re.clone()),
3,407,062✔
1893
        _ => steelerr!(TypeMismatch => "real-part expects a number, found {}", value),
×
1894
    }
1895
}
1896

1897
/// Returns the imaginary part of a number
1898
///
1899
/// (imag-part number) -> number?
1900
///
1901
/// # Examples
1902
/// ```scheme
1903
/// > (imag-part 3+4i) ;; => 4
1904
/// > (imag-part 42) ;; => 0
1905
/// ```
1906
#[steel_derive::function(name = "imag-part", constant = true)]
1907
pub fn imag_part(value: &SteelVal) -> Result<SteelVal> {
1,703,532✔
1908
    match value {
1,703,532✔
1909
        SteelVal::IntV(_)
1910
        | SteelVal::BigNum(_)
1911
        | SteelVal::Rational(_)
1912
        | SteelVal::BigRational(_)
1913
        | SteelVal::NumV(_) => Ok(SteelVal::IntV(0)),
1✔
1914
        SteelVal::Complex(complex) => Ok(complex.im.clone()),
3,407,062✔
1915
        _ => steelerr!(TypeMismatch => "imag-part expects a number, found {}", value),
×
1916
    }
1917
}
1918

1919
/// Computes the magnitude of the given number.
1920
///
1921
/// (magnitude number) -> number?
1922
///
1923
/// * number : number? - The number to compute the magnitude for.
1924
///
1925
/// # Examples
1926
/// ```scheme
1927
/// > (magnitude 3+4i) ;; => 5
1928
/// > (magnitude 5) ;; => 5
1929
/// > (magnitude -5) ;; => 5
1930
/// ```
1931
#[steel_derive::function(name = "magnitude", constant = true)]
1932
fn magnitude(number: &SteelVal) -> Result<SteelVal> {
8✔
1933
    match number {
8✔
1934
        SteelVal::NumV(x) => x.abs().into_steelval(),
4✔
1935
        SteelVal::IntV(x) => x.abs().into_steelval(),
4✔
1936
        SteelVal::Rational(x) => x.abs().into_steelval(),
4✔
1937
        SteelVal::BigNum(x) => x.as_ref().abs().into_steelval(),
×
1938
        SteelVal::BigRational(x) => x.as_ref().abs().into_steelval(),
×
1939
        SteelVal::Complex(x) => {
5✔
1940
            let c_squared = add_two(&square(&x.re)?, &square(&x.im)?)?;
30✔
1941
            sqrt(&c_squared)
10✔
1942
        }
1943
        _ => steelerr!(TypeMismatch => "magnitude expects a number, found {}", number),
×
1944
    }
1945
}
1946

1947
/// Computes the angle `θ` of a complex number `z` where `z = r * (cos θ + i sin θ)` and `r` is the magnitude.
1948
///
1949
/// (angle number) -> number?
1950
///
1951
/// - number : number?
1952
#[steel_derive::function(name = "angle", constant = true)]
1953
pub fn angle(number: &SteelVal) -> Result<SteelVal> {
×
1954
    let (re, im) = match number {
×
1955
        re @ SteelVal::NumV(_)
×
1956
        | re @ SteelVal::IntV(_)
×
1957
        | re @ SteelVal::Rational(_)
×
1958
        | re @ SteelVal::BigNum(_) => (re, &SteelVal::IntV(0)),
×
1959
        SteelVal::Complex(complex) => (&complex.re, &complex.im),
×
1960
        _ => stop!(TypeMismatch => "angle expects a number, found {}", number),
×
1961
    };
1962

1963
    atan2(im, re)
×
1964
}
1965

1966
/// Computes the quadratic arctan of `y` and `x`
1967
fn atan2(y: &SteelVal, x: &SteelVal) -> Result<SteelVal> {
×
1968
    let as_f64 = |arg: &_| match arg {
×
1969
        SteelVal::NumV(arg) => Ok(*arg),
×
1970
        SteelVal::IntV(arg) => Ok(*arg as f64),
×
1971
        SteelVal::Rational(arg) => Ok(*arg.numer() as f64 / *arg.denom() as f64),
×
1972
        SteelVal::BigNum(arg) => Ok(arg.to_f64().unwrap()),
×
1973
        _ => steelerr!(TypeMismatch => "atan2 expects a number, found {}", arg),
×
1974
    };
1975

1976
    let y = as_f64(y)?;
×
1977
    let x = as_f64(x)?;
×
1978
    if y == 0. && x == 0. {
×
1979
        // as this is currently only used for `angle`, make the error
1980
        // message a little better by saying `angle` instead of `atan2`
1981
        stop!(Generic => "angle: undefined for zero");
×
1982
    }
1983

1984
    f64::atan2(y, x).into_steelval()
×
1985
}
1986

1987
/// Computes the natural logarithm of the given number.
1988
///
1989
/// (log number [base]) -> number?
1990
///
1991
/// * number : number? - The number to compute the logarithm for.
1992
/// * base : number? - The base of the logarithm. If not provided, defaults to Euler's number (e).
1993
///
1994
/// # Examples
1995
/// ```scheme
1996
/// > (log 10) ;; => 2.302585092994046
1997
/// > (log 100 10) ;; => 2
1998
/// > (log 27 3) ;; => 3
1999
/// ```
2000
#[steel_derive::native(name = "log", arity = "Range(1,2)")]
2001
fn log(args: &[SteelVal]) -> Result<SteelVal> {
6✔
2002
    let first = &args[0];
12✔
2003
    let base = args
12✔
2004
        .get(1)
2005
        .cloned()
2006
        .unwrap_or(SteelVal::NumV(std::f64::consts::E));
12✔
2007

2008
    match (first, &base) {
12✔
2009
        (SteelVal::IntV(1), _) => Ok(SteelVal::IntV(0)),
1✔
2010
        (SteelVal::IntV(_) | SteelVal::NumV(_), SteelVal::IntV(1)) => {
2011
            steelerr!(Generic => "log: divide by zero with args: {} and {}", first, base)
×
2012
        }
2013
        (SteelVal::IntV(arg), SteelVal::NumV(n)) => Ok(SteelVal::NumV((*arg as f64).log(*n))),
4✔
2014
        (SteelVal::IntV(arg), SteelVal::IntV(base)) => Ok(SteelVal::IntV(arg.ilog(*base) as isize)),
4✔
2015
        (SteelVal::NumV(arg), SteelVal::NumV(n)) => Ok(SteelVal::NumV(arg.log(*n))),
8✔
2016
        (SteelVal::NumV(arg), SteelVal::IntV(base)) => Ok(SteelVal::NumV(arg.log(*base as f64))),
4✔
2017
        // TODO: Support BigNum, Rational, and BigRational.
2018
        _ => {
2019
            steelerr!(TypeMismatch => "log expects one or two numbers, found: {} and {}", first, base)
×
2020
        }
2021
    }
2022
}
2023

2024
/// Computes the integer square root of the given non-negative integer.
2025
///
2026
/// (exact-integer-sqrt number) -> (integer? integer?)
2027
///
2028
/// * number : (and/c integer? positive?) - The non-negative integer to compute the square root for.
2029
///
2030
/// # Examples
2031
/// ```scheme
2032
/// > (exact-integer-sqrt 25) ;; => (5 0)
2033
/// > (exact-integer-sqrt 35) ;; => (5 10)
2034
/// ```
2035
#[steel_derive::function(name = "exact-integer-sqrt", constant = true)]
2036
fn exact_integer_sqrt(number: &SteelVal) -> Result<SteelVal> {
40,022✔
2037
    match number {
16✔
2038
        SteelVal::IntV(x) if *x >= 0 => {
47✔
2039
            let (ans, rem) = exact_integer_impl::<isize>(x);
45✔
2040
            (ans.into_steelval()?, rem.into_steelval()?).into_steelval()
75✔
2041
        }
2042
        SteelVal::BigNum(x) if !x.is_negative() => {
160,007✔
2043
            let (ans, rem) = exact_integer_impl::<BigInt>(x.as_ref());
160,004✔
2044
            (ans.into_steelval()?, rem.into_steelval()?).into_steelval()
200,005✔
2045
        }
2046
        _ => {
2047
            steelerr!(TypeMismatch => "exact-integer-sqrt expects a non-negative integer but found {}", number)
12✔
2048
        }
2049
    }
2050
}
2051

2052
fn exact_integer_impl<'a, N>(target: &'a N) -> (N, N)
40,016✔
2053
where
2054
    N: num_integer::Roots + Clone,
2055
    &'a N: std::ops::Mul<&'a N, Output = N>,
2056
    N: std::ops::Sub<N, Output = N>,
2057
{
2058
    let x = target.sqrt();
120,048✔
2059
    let x_sq = x.clone() * x.clone();
160,064✔
2060
    let rem = target.clone() - x_sq;
120,048✔
2061
    (x, rem)
40,016✔
2062
}
2063

2064
/// Performs a bitwise arithmetic shift using the given 2 numbers
2065
///
2066
/// (arithmetic-shift n m) -> integer?
2067
///
2068
/// * n : integer? - The number to shift.
2069
/// * m : integer? - The number by which to shift.
2070
///
2071
/// # Examples
2072
/// ```scheme
2073
/// > (arithmetic-shift 10 1) ;; => 20
2074
/// > (arithmetic-shift 20 1) ;; => 40
2075
/// > (arithmetic-shift 40 -2) ;; => 10
2076
/// ```
2077
#[steel_derive::native(name = "arithmetic-shift", constant = true, arity = "Exact(2)")]
2078
pub fn arithmetic_shift(args: &[SteelVal]) -> Result<SteelVal> {
×
2079
    match (&args[0], &args[1]) {
×
2080
        (SteelVal::IntV(n), SteelVal::IntV(m)) => {
×
2081
            if *m >= 0 {
×
2082
                Ok(SteelVal::IntV(n << m))
×
2083
            } else {
2084
                Ok(SteelVal::IntV(n >> -m))
×
2085
            }
2086
        }
2087
        _ => stop!(TypeMismatch => "arithmetic-shift expected 2 integers"),
×
2088
    }
2089
}
2090

2091
/// Performs a bitwise xor using the given numbers
2092
///
2093
/// (bitwise-xor n ...) -> integer?
2094
///
2095
/// * n : integer?
2096
///
2097
/// # Examples
2098
/// ```scheme
2099
/// > (bitwise-xor 1 5) ;; => 4
2100
/// > (bitwise-xor -32 -1) ;; => 31
2101
/// ```
2102
#[steel_derive::native(name = "bitwise-xor", constant = true, arity = "AtLeast(1)")]
NEW
2103
pub fn bitwise_xor(args: &[SteelVal]) -> Result<SteelVal> {
×
NEW
2104
    let mut accum = if let SteelVal::IntV(i) = &args[0] {
×
NEW
2105
        *i
×
2106
    } else {
NEW
2107
        stop!(TypeMismatch => "bitwise-xor expects an exact integer");
×
2108
    };
2109

NEW
2110
    if args.len() == 1 {
×
NEW
2111
        return Ok(args[0].clone());
×
2112
    }
2113

NEW
2114
    for value in &args[1..] {
×
NEW
2115
        if let SteelVal::IntV(v) = value {
×
NEW
2116
            accum = accum.bitxor(v);
×
2117
        }
2118
    }
2119

NEW
2120
    Ok(SteelVal::IntV(accum))
×
2121
}
2122

2123
/// Performs a bitwise ior using the given numbers
2124
///
2125
/// (bitwise-ior n ...) -> integer?
2126
///
2127
/// * n : integer?
2128
///
2129
/// # Examples
2130
/// ```scheme
2131
/// > (bitwise-ior 1 2) ;; => 3
2132
/// > (bitwise-ior -32 1) ;; => -31
2133
/// ```
2134
#[steel_derive::native(name = "bitwise-ior", constant = true, arity = "AtLeast(1)")]
NEW
2135
pub fn bitwise_ior(args: &[SteelVal]) -> Result<SteelVal> {
×
NEW
2136
    let mut accum = if let SteelVal::IntV(i) = &args[0] {
×
NEW
2137
        *i
×
2138
    } else {
NEW
2139
        stop!(TypeMismatch => "bitwise-ior expects an exact integer");
×
2140
    };
2141

NEW
2142
    if args.len() == 1 {
×
NEW
2143
        return Ok(args[0].clone());
×
2144
    }
2145

NEW
2146
    for value in &args[1..] {
×
NEW
2147
        if let SteelVal::IntV(v) = value {
×
NEW
2148
            accum = accum.bitor(v);
×
2149
        }
2150
    }
2151

NEW
2152
    Ok(SteelVal::IntV(accum))
×
2153
}
2154

2155
/// Performs a bitwise and using the given numbers
2156
///
2157
/// (bitwise-and n ...) -> integer?
2158
///
2159
/// * n : integer?
2160
///
2161
/// # Examples
2162
/// ```scheme
2163
/// > (bitwise-and 1 2) ;; => 0
2164
/// > (bitwise-and -32 -1) ;; => -32
2165
/// ```
2166
#[steel_derive::native(name = "bitwise-and", constant = true, arity = "AtLeast(1)")]
NEW
2167
pub fn bitwise_and(args: &[SteelVal]) -> Result<SteelVal> {
×
NEW
2168
    let mut accum = if let SteelVal::IntV(i) = &args[0] {
×
NEW
2169
        *i
×
2170
    } else {
NEW
2171
        stop!(TypeMismatch => "bitwise-and expects an exact integer");
×
2172
    };
2173

NEW
2174
    if args.len() == 1 {
×
NEW
2175
        return Ok(args[0].clone());
×
2176
    }
2177

NEW
2178
    for value in &args[1..] {
×
NEW
2179
        if let SteelVal::IntV(v) = value {
×
NEW
2180
            accum = accum.bitand(v);
×
2181
        }
2182
    }
2183

NEW
2184
    Ok(SteelVal::IntV(accum))
×
2185
}
2186

2187
/// Performs a bitwise not using the given numbers
2188
///
2189
/// (bitwise-not n ...) -> integer?
2190
///
2191
/// * n : integer?
2192
///
2193
/// # Examples
2194
/// ```scheme
2195
/// > (bitwise-not 5) ;; => -6
2196
/// > (bitwise-not -1) ;; => 0
2197
/// ```
2198
#[steel_derive::native(name = "bitwise-not", constant = true, arity = "Exact(1)")]
NEW
2199
pub fn bitwise_not(args: &[SteelVal]) -> Result<SteelVal> {
×
NEW
2200
    match &args[0] {
×
NEW
2201
        SteelVal::IntV(i) => Ok(SteelVal::IntV(!i)),
×
NEW
2202
        SteelVal::BigNum(i) => Ok(SteelVal::BigNum(Gc::new(!i.as_ref()))),
×
2203
        _ => {
NEW
2204
            stop!(TypeMismatch => "bitwise-not expects an exact integer");
×
2205
        }
2206
    }
2207
}
2208

2209
/// Checks if the given number is even
2210
///
2211
/// (even? n) -> bool?
2212
///
2213
/// * n : number? - The number to check for evenness.
2214
///
2215
/// # Examples
2216
/// ```scheme
2217
/// > (even? 2) ;; => #true
2218
/// > (even? 3) ;; => #false
2219
/// > (even? 4.0) ;; => #true
2220
/// ```
2221
#[steel_derive::function(name = "even?", constant = true)]
2222
pub fn even(arg: &SteelVal) -> Result<SteelVal> {
10,754,358✔
2223
    match arg {
×
2224
        SteelVal::IntV(n) => Ok(SteelVal::BoolV(n & 1 == 0)),
21,508,716✔
2225
        SteelVal::BigNum(n) => Ok(SteelVal::BoolV(n.is_even())),
×
2226
        SteelVal::NumV(n) if n.fract() == 0.0 => (*n as i64).is_even().into_steelval(),
×
2227
        _ => steelerr!(TypeMismatch => "even? requires an integer, found: {:?}", arg),
×
2228
    }
2229
}
2230

2231
/// Checks if the given number is odd
2232
///
2233
/// (odd? n) -> bool?
2234
///
2235
/// * n : number? - The number to check for oddness.
2236
///
2237
/// # Examples
2238
/// ```scheme
2239
/// > (odd? 2) ;; => #false
2240
/// > (odd? 3) ;; => #true
2241
/// > (odd? 5.0) ;; => #true
2242
/// ```
2243
#[steel_derive::function(name = "odd?", constant = true)]
2244
pub fn odd(arg: &SteelVal) -> Result<SteelVal> {
1,449✔
2245
    match arg {
×
2246
        SteelVal::IntV(n) => Ok(SteelVal::BoolV(n & 1 == 1)),
2,898✔
2247
        SteelVal::BigNum(n) => Ok(SteelVal::BoolV(n.is_odd())),
×
2248
        SteelVal::NumV(n) if n.fract() == 0.0 => (*n as i64).is_odd().into_steelval(),
×
2249
        _ => {
2250
            steelerr!(TypeMismatch => "odd? requires an integer, found: {:?}", arg)
×
2251
        }
2252
    }
2253
}
2254

2255
/// Sums all given floats
2256
///
2257
/// (f+ nums) -> number?
2258
///
2259
/// * nums : float? - The floats to sum up.
2260
///
2261
/// # Examples
2262
/// ```scheme
2263
/// > (f+ 5.5) ;; => 5.5
2264
/// > (f+ 1.1 2.2) ;; => 3.3
2265
/// > (f+ 3.3 3.3 3.3) ;; => 9.9
2266
/// ```
2267
#[steel_derive::native(name = "f+", constant = true, arity = "AtLeast(1)")]
2268
pub fn float_add(args: &[SteelVal]) -> Result<SteelVal> {
×
2269
    let mut sum = 0.0;
×
2270

2271
    for arg in args {
×
2272
        if let SteelVal::NumV(n) = arg {
×
2273
            sum += n;
×
2274
        } else {
2275
            stop!(TypeMismatch => "f+ expected a float, found {:?}", arg);
×
2276
        }
2277
    }
2278

2279
    Ok(SteelVal::NumV(sum))
×
2280
}
2281

2282
fn ensure_args_are_numbers(op: &str, args: &[SteelVal]) -> Result<()> {
132,526,221✔
2283
    for arg in args {
666,457,402✔
2284
        if !numberp(arg) {
266,965,592✔
2285
            stop!(TypeMismatch => "{op} expects a number, found: {:?}", arg);
6✔
2286
        }
2287
    }
2288
    Ok(())
132,526,218✔
2289
}
2290

2291
/// Multiplies `x` and `y` without any type checking.
2292
///
2293
/// # Precondition
2294
/// - `x` and `y` must be valid numerical types.
2295
fn multiply_two(x: &SteelVal, y: &SteelVal) -> Result<SteelVal> {
45,704,843✔
2296
    match (x, y) {
91,409,686✔
2297
        (SteelVal::NumV(x), SteelVal::NumV(y)) => (x * y).into_steelval(),
100,945,072✔
2298
        (SteelVal::NumV(x), SteelVal::IntV(y)) | (SteelVal::IntV(y), SteelVal::NumV(x)) => {
242,022✔
2299
            (x * *y as f64).into_steelval()
242,022✔
2300
        }
2301
        (SteelVal::NumV(x), SteelVal::BigNum(y)) | (SteelVal::BigNum(y), SteelVal::NumV(x)) => {
×
2302
            (x * y.to_f64().unwrap()).into_steelval()
×
2303
        }
2304
        (SteelVal::NumV(x), SteelVal::Rational(y)) | (SteelVal::Rational(y), SteelVal::NumV(x)) => {
4✔
2305
            (x * y.to_f64().unwrap()).into_steelval()
8✔
2306
        }
2307
        (SteelVal::NumV(x), SteelVal::BigRational(y))
×
2308
        | (SteelVal::BigRational(y), SteelVal::NumV(x)) => {
×
2309
            (x * y.to_f64().unwrap()).into_steelval()
×
2310
        }
2311
        (SteelVal::IntV(x), SteelVal::IntV(y)) => match x.checked_mul(y) {
43,128,672✔
2312
            Some(res) => res.into_steelval(),
31,331,892✔
2313
            None => {
2314
                let mut res = BigInt::from(*x);
1,014,612✔
2315
                res *= *y;
338,204✔
2316
                res.into_steelval()
676,408✔
2317
            }
2318
        },
2319
        (SteelVal::IntV(x), SteelVal::BigNum(y)) | (SteelVal::BigNum(y), SteelVal::IntV(x)) => {
535,408✔
2320
            (y.as_ref() * x).into_steelval()
803,112✔
2321
        }
2322
        (SteelVal::IntV(x), SteelVal::Rational(y)) | (SteelVal::Rational(y), SteelVal::IntV(x)) => {
14,378,284✔
2323
            match i32::try_from(*x) {
7,189,142✔
2324
                Ok(x) => match y.checked_mul(&Rational32::new(x, 1)) {
28,756,568✔
2325
                    Some(res) => res.into_steelval(),
21,567,426✔
2326
                    None => {
2327
                        let mut res =
×
2328
                            BigRational::new(BigInt::from(*y.numer()), BigInt::from(*y.denom()));
×
2329
                        res *= BigInt::from(x);
×
2330
                        res.into_steelval()
×
2331
                    }
2332
                },
2333
                Err(_) => {
2334
                    let mut res =
×
2335
                        BigRational::new(BigInt::from(*y.numer()), BigInt::from(*y.denom()));
×
2336
                    res *= BigInt::from(*x);
×
2337
                    res.into_steelval()
×
2338
                }
2339
            }
2340
        }
2341
        (SteelVal::IntV(x), SteelVal::BigRational(y))
×
2342
        | (SteelVal::BigRational(y), SteelVal::IntV(x)) => {
×
2343
            let mut res = y.as_ref().clone();
×
2344
            res *= BigInt::from(*x);
×
2345
            res.into_steelval()
×
2346
        }
2347
        (SteelVal::Rational(x), SteelVal::Rational(y)) => match x.checked_mul(y) {
4✔
2348
            Some(res) => res.into_steelval(),
3✔
2349
            None => {
2350
                let mut res = BigRational::new(BigInt::from(*x.numer()), BigInt::from(*x.denom()));
×
2351
                res *= BigRational::new(BigInt::from(*y.numer()), BigInt::from(*y.denom()));
×
2352
                res.into_steelval()
×
2353
            }
2354
        },
2355
        (SteelVal::Rational(x), SteelVal::BigNum(y))
×
2356
        | (SteelVal::BigNum(y), SteelVal::Rational(x)) => {
×
2357
            let mut res = BigRational::new(BigInt::from(*x.numer()), BigInt::from(*x.denom()));
×
2358
            res *= y.as_ref();
×
2359
            res.into_steelval()
×
2360
        }
2361
        (SteelVal::BigRational(x), SteelVal::BigRational(y)) => {
×
2362
            (x.as_ref() * y.as_ref()).into_steelval()
×
2363
        }
2364
        (SteelVal::BigRational(x), SteelVal::BigNum(y))
×
2365
        | (SteelVal::BigNum(y), SteelVal::BigRational(x)) => {
6✔
2366
            (x.as_ref() * y.as_ref()).into_steelval()
12✔
2367
        }
2368
        (SteelVal::BigNum(x), SteelVal::BigNum(y)) => (x.as_ref() * y.as_ref()).into_steelval(),
2,324,400✔
2369
        // Complex numbers.
2370
        (SteelVal::Complex(x), SteelVal::Complex(y)) => multiply_complex(x, y),
8,328,220✔
2371
        (SteelVal::Complex(x), y) | (y, SteelVal::Complex(x)) => {
111,000✔
2372
            let y = SteelComplex::new(y.clone(), SteelVal::IntV(0));
277,500✔
2373
            multiply_complex(x, &y)
166,500✔
2374
        }
2375
        (SteelVal::BigRational(x), SteelVal::Rational(y)) => {
×
2376
            let mut res = BigRational::new(x.numer().clone(), x.denom().clone());
×
2377
            res *= BigRational::new(BigInt::from(*y.numer()), BigInt::from(*y.denom()));
×
2378
            res.into_steelval()
×
2379
        }
2380
        _ => unreachable!(),
2381
    }
2382
}
2383

2384
/// # Precondition
2385
/// All types in `args` must be numerical types.
2386
fn multiply_primitive_impl(args: &[SteelVal]) -> Result<SteelVal> {
31,055,663✔
2387
    match args {
31,055,663✔
2388
        [] => 1.into_steelval(),
31,055,665✔
2389
        [x] => x.clone().into_steelval(),
8✔
2390
        [x, y] => multiply_two(x, y).into_steelval(),
183,753,948✔
2391
        [x, y, zs @ ..] => {
1,290,003✔
2392
            let mut res = multiply_two(x, y)?;
1,720,004✔
2393
            for z in zs {
1,290,003✔
2394
                // TODO: This use case could be optimized to reuse state instead of creating a new
2395
                // object each time.
2396
                res = multiply_two(&res, z)?;
1,720,004✔
2397
            }
2398
            res.into_steelval()
860,002✔
2399
        }
2400
    }
2401
}
2402

2403
#[cold]
2404
fn complex_reciprocal(c: &SteelComplex) -> Result<SteelVal> {
1✔
2405
    let denominator = add_two(&multiply_two(&c.re, &c.re)?, &multiply_two(&c.im, &c.im)?)?;
8✔
2406
    let re = divide_primitive(&[c.re.clone(), denominator.clone()])?;
5✔
2407
    let neg_im = divide_primitive(&[c.re.clone(), denominator])?;
4✔
2408
    SteelComplex::new(re, subtract_primitive(&[neg_im])?).into_steelval()
5✔
2409
}
2410

2411
/// Negate a number.
2412
///
2413
/// # Precondition
2414
/// `value` must be a number.
2415
#[inline(always)]
2416
fn negate(value: &SteelVal) -> Result<SteelVal> {
21,782,822✔
2417
    match value {
21,782,822✔
2418
        SteelVal::NumV(x) => (-x).into_steelval(),
60,026,400✔
2419
        SteelVal::IntV(x) => match x.checked_neg() {
3,483,234✔
2420
            Some(res) => res.into_steelval(),
5,224,848✔
2421
            None => BigInt::from(*x).neg().into_steelval(),
4✔
2422
        },
2423
        SteelVal::Rational(x) => match 0i32.checked_sub(*x.numer()) {
6✔
2424
            Some(n) => Rational32::new(n, *x.denom()).into_steelval(),
10✔
2425
            None => BigRational::new(BigInt::from(*x.numer()), BigInt::from(*x.denom()))
×
2426
                .neg()
2427
                .into_steelval(),
2428
        },
2429
        SteelVal::BigRational(x) => x.as_ref().neg().into_steelval(),
×
2430
        SteelVal::BigNum(x) => x.as_ref().clone().neg().into_steelval(),
162,005✔
2431
        SteelVal::Complex(x) => negate_complex(x),
6✔
2432
        _ => unreachable!(),
2433
    }
2434
}
2435

2436
/// Adds two numbers.
2437
///
2438
/// # Precondition
2439
/// x and y must be valid numbers.
2440
#[inline(always)]
2441
pub fn add_two(x: &SteelVal, y: &SteelVal) -> Result<SteelVal> {
102,543,319✔
2442
    match (x, y) {
205,086,638✔
2443
        // Simple integer case. Probably very common.
2444
        (SteelVal::IntV(x), SteelVal::IntV(y)) => match x.checked_add(y) {
299,061,332✔
2445
            Some(res) => res.into_steelval(),
224,295,996✔
2446
            None => {
×
2447
                let mut res = BigInt::from(*x);
3✔
2448
                res += *y;
1✔
2449
                res.into_steelval()
2✔
2450
            }
2451
        },
2452
        // Cases that return an `f64`.
2453
        (SteelVal::NumV(x), SteelVal::NumV(y)) => (x + y).into_steelval(),
110,967,612✔
2454
        (SteelVal::NumV(x), SteelVal::IntV(y)) | (SteelVal::IntV(y), SteelVal::NumV(x)) => {
1,522✔
2455
            (x + *y as f64).into_steelval()
1,522✔
2456
        }
2457
        (SteelVal::NumV(x), SteelVal::BigNum(y)) | (SteelVal::BigNum(y), SteelVal::NumV(x)) => {
4✔
2458
            (x + y.to_f64().unwrap()).into_steelval()
6✔
2459
        }
2460
        (SteelVal::NumV(x), SteelVal::Rational(y)) | (SteelVal::Rational(y), SteelVal::NumV(x)) => {
4✔
2461
            (x + y.to_f64().unwrap()).into_steelval()
8✔
2462
        }
2463
        (SteelVal::NumV(x), SteelVal::BigRational(y))
×
2464
        | (SteelVal::BigRational(y), SteelVal::NumV(x)) => {
×
2465
            (x + y.to_f64().unwrap()).into_steelval()
×
2466
        }
2467
        // Cases that interact with `Rational`.
2468
        (SteelVal::Rational(x), SteelVal::Rational(y)) => (x + y).into_steelval(),
4✔
2469
        (SteelVal::Rational(x), SteelVal::IntV(y)) | (SteelVal::IntV(y), SteelVal::Rational(x)) => {
8✔
2470
            match i32::try_from(*y) {
4✔
2471
                Ok(y) => match x.checked_add(&Rational32::new(y, 1)) {
16✔
2472
                    Some(res) => res.into_steelval(),
9✔
2473
                    None => {
×
2474
                        let res =
1✔
2475
                            BigRational::new(BigInt::from(*x.numer()), BigInt::from(*x.denom()))
6✔
2476
                                + BigInt::from(y);
1✔
2477
                        res.into_steelval()
2✔
2478
                    }
2479
                },
2480
                Err(_) => {
×
2481
                    let res = BigRational::new(BigInt::from(*x.numer()), BigInt::from(*x.denom()))
×
2482
                        + BigInt::from(*y);
×
2483
                    res.into_steelval()
×
2484
                }
2485
            }
2486
        }
2487
        (SteelVal::Rational(x), SteelVal::BigNum(y))
×
2488
        | (SteelVal::BigNum(y), SteelVal::Rational(x)) => {
2✔
2489
            let res =
1✔
2490
                BigRational::new(BigInt::from(*x.numer()), BigInt::from(*x.denom())) + y.as_ref();
6✔
2491
            res.into_steelval()
2✔
2492
        }
2493
        // Cases that interact with `BigRational`. For the sake of performance, hopefully not too
2494
        // common.
2495
        (SteelVal::BigRational(x), SteelVal::BigRational(y)) => {
2✔
2496
            (x.as_ref() + y.as_ref()).into_steelval()
4✔
2497
        }
2498
        (SteelVal::BigRational(x), SteelVal::Rational(y))
2✔
2499
        | (SteelVal::Rational(y), SteelVal::BigRational(x)) => {
×
2500
            let res =
1✔
2501
                x.as_ref() + BigRational::new(BigInt::from(*y.numer()), BigInt::from(*y.denom()));
7✔
2502
            res.into_steelval()
2✔
2503
        }
2504
        (SteelVal::BigRational(x), SteelVal::IntV(y))
×
2505
        | (SteelVal::IntV(y), SteelVal::BigRational(x)) => {
×
2506
            (x.as_ref() + BigInt::from(*y)).into_steelval()
×
2507
        }
2508
        (SteelVal::BigRational(x), SteelVal::BigNum(y))
×
2509
        | (SteelVal::BigNum(y), SteelVal::BigRational(x)) => {
×
2510
            (x.as_ref() + y.as_ref()).into_steelval()
×
2511
        }
2512
        // Remaining cases that interact with `BigNum`. Probably not too common.
2513
        (SteelVal::BigNum(x), SteelVal::BigNum(y)) => {
64,806✔
2514
            let mut res = x.as_ref().clone();
97,209✔
2515
            res += y.as_ref();
64,806✔
2516
            res.into_steelval()
64,806✔
2517
        }
2518
        (SteelVal::BigNum(x), SteelVal::IntV(y)) | (SteelVal::IntV(y), SteelVal::BigNum(x)) => {
5,808✔
2519
            let mut res = x.as_ref().clone();
8,712✔
2520
            res += *y;
2,904✔
2521
            res.into_steelval()
5,808✔
2522
        }
2523
        // Complex numbers
2524
        (SteelVal::Complex(x), SteelVal::Complex(y)) => add_complex(x, y),
15✔
2525
        (SteelVal::Complex(x), y) | (y, SteelVal::Complex(x)) => {
×
2526
            debug_assert!(realp(y));
×
2527
            add_complex(x, &SteelComplex::new(y.clone(), SteelVal::IntV(0)))
×
2528
        }
2529
        _ => unreachable!(),
2530
    }
2531
}
2532

2533
#[cold]
2534
fn multiply_complex(x: &SteelComplex, y: &SteelComplex) -> Result<SteelVal> {
1,721,144✔
2535
    // TODO: Optimize the implementation if needed.
2536
    let real = add_two(
2537
        &multiply_two(&x.re, &y.re)?,
5,163,432✔
2538
        &negate(&multiply_two(&x.im, &y.im)?)?,
6,884,576✔
2539
    )?;
2540
    let im = add_two(&multiply_two(&x.re, &y.im)?, &multiply_two(&x.im, &y.re)?)?;
13,769,152✔
2541
    SteelComplex::new(real, im).into_steelval()
6,884,576✔
2542
}
2543

2544
#[cold]
2545
fn negate_complex(x: &SteelComplex) -> Result<SteelVal> {
2✔
2546
    // TODO: Optimize the implementation if needed.
2547
    SteelComplex::new(negate(&x.re)?, negate(&x.im)?).into_steelval()
12✔
2548
}
2549

2550
#[cold]
2551
fn add_complex(x: &SteelComplex, y: &SteelComplex) -> Result<SteelVal> {
1,721,893✔
2552
    // TODO: Optimize the implementation if needed.
2553
    SteelComplex::new(add_two(&x.re, &y.re)?, add_two(&x.im, &y.im)?).into_steelval()
13,775,144✔
2554
}
2555

2556
#[cfg(test)]
2557
mod num_op_tests {
2558
    use super::*;
2559
    use crate::{gc::Gc, rvals::SteelVal::*};
2560
    use std::str::FromStr;
2561

2562
    #[test]
2563
    fn division_test() {
2564
        assert_eq!(
2565
            divide_primitive(&[IntV(10), IntV(2)]).unwrap().to_string(),
2566
            IntV(5).to_string()
2567
        );
2568
    }
2569

2570
    #[test]
2571
    fn dvision_by_integer_zero_returns_positive_infinity() {
2572
        // assert_eq!(
2573
        //     divide_primitive(&[IntV(1), IntV(0)]).unwrap().to_string(),
2574
        //     NumV(f64::INFINITY).to_string()
2575
        // )
2576

2577
        assert!(divide_primitive(&[IntV(1), IntV(0)]).is_err())
2578
    }
2579

2580
    #[test]
2581
    fn division_on_single_integer_returns_reciprocal_rational() {
2582
        assert_eq!(
2583
            divide_primitive(&[IntV(10)]).unwrap().to_string(),
2584
            Rational(Rational32::new(1, 10)).to_string()
2585
        );
2586
    }
2587

2588
    #[test]
2589
    fn division_on_single_rational_returns_reciprocal_rational() {
2590
        assert_eq!(
2591
            divide_primitive(&[Rational32::new(2, 5).into_steelval().unwrap()])
2592
                .unwrap()
2593
                .to_string(),
2594
            Rational(Rational32::new(5, 2)).to_string()
2595
        );
2596
    }
2597

2598
    #[test]
2599
    fn division_on_rational_with_numerator_one_returns_integer() {
2600
        assert_eq!(
2601
            divide_primitive(&[Rational32::new(1, 5).into_steelval().unwrap()])
2602
                .unwrap()
2603
                .to_string(),
2604
            IntV(5).to_string()
2605
        );
2606
    }
2607

2608
    #[test]
2609
    fn division_on_bignum_returns_bigrational() {
2610
        assert_eq!(
2611
            divide_primitive(
2612
                &([BigInt::from_str("18446744073709551616")
2613
                    .unwrap()
2614
                    .into_steelval()
2615
                    .unwrap(),])
2616
            )
2617
            .unwrap()
2618
            .to_string(),
2619
            BigRational(Gc::new(num_rational::BigRational::new(
2620
                BigInt::from(1),
2621
                BigInt::from_str("18446744073709551616").unwrap()
2622
            )))
2623
            .to_string()
2624
        );
2625
    }
2626

2627
    #[test]
2628
    fn multiplication_test() {
2629
        let args = [IntV(10), IntV(2)];
2630
        let got = multiply_primitive(&args).unwrap();
2631
        let expected = IntV(20);
2632
        assert_eq!(got, expected);
2633
    }
2634

2635
    #[test]
2636
    fn multiplication_different_types() {
2637
        let args = [IntV(10), NumV(2.0)];
2638
        let got = multiply_primitive(&args).unwrap();
2639
        let expected = NumV(20.0);
2640
        assert_eq!(got.to_string(), expected.to_string());
2641
    }
2642

2643
    #[test]
2644
    fn multiply_multiple_numbers() {
2645
        assert_eq!(
2646
            multiply_primitive(&[IntV(16), NumV(2.0), Rational(Rational32::new(1, 4))])
2647
                .unwrap()
2648
                .to_string(),
2649
            NumV(8.0).to_string(),
2650
        );
2651
    }
2652

2653
    #[test]
2654
    fn adding_exact_with_inexact_returns_inexact() {
2655
        assert_eq!(
2656
            add_primitive(&([IntV(10), NumV(2.0)])).unwrap().to_string(),
2657
            NumV(12.0).to_string()
2658
        );
2659
        assert_eq!(
2660
            add_primitive(
2661
                &([
2662
                    BigInt::from_str("18446744073709551616")
2663
                        .unwrap()
2664
                        .into_steelval()
2665
                        .unwrap(),
2666
                    NumV(18446744073709551616.0),
2667
                ])
2668
            )
2669
            .unwrap()
2670
            .to_string(),
2671
            NumV(18446744073709551616.0 * 2.0).to_string()
2672
        );
2673
        assert_eq!(
2674
            add_primitive(
2675
                &([
2676
                    BigInt::from_str("18446744073709551616")
2677
                        .unwrap()
2678
                        .into_steelval()
2679
                        .unwrap(),
2680
                    NumV(18446744073709551616.0),
2681
                ])
2682
            )
2683
            .unwrap()
2684
            .to_string(),
2685
            NumV(18446744073709551616.0 * 2.0).to_string()
2686
        );
2687
        assert_eq!(
2688
            add_primitive(&([Rational32::new(1, 2).into_steelval().unwrap(), NumV(0.5),]))
2689
                .unwrap()
2690
                .to_string(),
2691
            NumV(1.0).to_string()
2692
        );
2693
    }
2694

2695
    #[test]
2696
    fn subtraction_different_types() {
2697
        let args = [IntV(10), NumV(2.0)];
2698
        let got = subtract_primitive(&args).unwrap();
2699
        let expected = NumV(8.0);
2700
        assert_eq!(got.to_string(), expected.to_string());
2701
    }
2702

2703
    #[test]
2704
    fn test_integer_add() {
2705
        let args = [IntV(10), IntV(2)];
2706
        let got = add_primitive(&args).unwrap();
2707
        let expected = IntV(12);
2708
        assert_eq!(got, expected);
2709
    }
2710

2711
    #[test]
2712
    fn test_integer_sub() {
2713
        let args = [IntV(10), IntV(2)];
2714
        let got = subtract_primitive(&args).unwrap();
2715
        let expected = IntV(8);
2716
        assert_eq!(got, expected);
2717
    }
2718

2719
    #[test]
2720
    fn test_exact_integer_sqrt() {
2721
        assert_eq!(
2722
            exact_integer_sqrt(&0.into()),
2723
            (0.into_steelval().unwrap(), 0.into_steelval().unwrap()).into_steelval()
2724
        );
2725
        assert_eq!(
2726
            exact_integer_sqrt(&1.into()),
2727
            (1.into_steelval().unwrap(), 0.into_steelval().unwrap()).into_steelval()
2728
        );
2729
        assert_eq!(
2730
            exact_integer_sqrt(&2.into()),
2731
            (1.into_steelval().unwrap(), 1.into_steelval().unwrap()).into_steelval()
2732
        );
2733
        assert_eq!(
2734
            exact_integer_sqrt(&2.into()),
2735
            (1.into_steelval().unwrap(), 1.into_steelval().unwrap()).into_steelval()
2736
        );
2737
        assert_eq!(
2738
            exact_integer_sqrt(&3.into()),
2739
            (1.into_steelval().unwrap(), 2.into_steelval().unwrap()).into_steelval()
2740
        );
2741
        assert_eq!(
2742
            exact_integer_sqrt(&4.into()),
2743
            (2.into_steelval().unwrap(), 0.into_steelval().unwrap()).into_steelval()
2744
        );
2745
        assert_eq!(
2746
            exact_integer_sqrt(&5.into()),
2747
            (2.into_steelval().unwrap(), 1.into_steelval().unwrap()).into_steelval()
2748
        );
2749
        assert_eq!(
2750
            exact_integer_sqrt(&6.into()),
2751
            (2.into_steelval().unwrap(), 2.into_steelval().unwrap()).into_steelval()
2752
        );
2753
        assert_eq!(
2754
            exact_integer_sqrt(&7.into()),
2755
            (2.into_steelval().unwrap(), 3.into_steelval().unwrap()).into_steelval()
2756
        );
2757
    }
2758

2759
    #[test]
2760
    fn test_exact_integer_sqrt_fails_on_negative_or_noninteger() {
2761
        assert!(exact_integer_sqrt(&(-7).into()).is_err());
2762
        assert!(exact_integer_sqrt(&Rational32::new(-1, 2).into_steelval().unwrap()).is_err());
2763
        assert!(exact_integer_sqrt(
2764
            &BigInt::from_str("-10000000000000000000000000000000000001")
2765
                .unwrap()
2766
                .into_steelval()
2767
                .unwrap()
2768
        )
2769
        .is_err());
2770
        assert!(exact_integer_sqrt(
2771
            &num_rational::BigRational::new(
2772
                BigInt::from_str("-10000000000000000000000000000000000001").unwrap(),
2773
                BigInt::from_str("2").unwrap()
2774
            )
2775
            .into_steelval()
2776
            .unwrap()
2777
        )
2778
        .is_err());
2779
        assert!(exact_integer_sqrt(&(1.0).into()).is_err());
2780
        assert!(exact_integer_sqrt(
2781
            &SteelComplex::new(1.into(), 1.into())
2782
                .into_steelval()
2783
                .unwrap()
2784
        )
2785
        .is_err());
2786
    }
2787

2788
    #[test]
2789
    fn test_sqrt() {
2790
        assert_eq!(sqrt(&4isize.into()).unwrap(), 2isize.into());
2791
        assert_eq!(
2792
            sqrt(
2793
                &SteelComplex::new(0.into(), 2.into())
2794
                    .into_steelval()
2795
                    .unwrap()
2796
            )
2797
            .unwrap(),
2798
            SteelComplex::new(1.into(), 1.into())
2799
                .into_steelval()
2800
                .unwrap()
2801
        );
2802
        assert_eq!(
2803
            sqrt(
2804
                &SteelComplex::new((-3).into(), (-4).into())
2805
                    .into_steelval()
2806
                    .unwrap()
2807
            )
2808
            .unwrap(),
2809
            SteelComplex::new(1.into(), (-2).into())
2810
                .into_steelval()
2811
                .unwrap()
2812
        );
2813
    }
2814
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc