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

mattwparas / steel / 17772690385

16 Sep 2025 04:36PM UTC coverage: 43.331% (+0.04%) from 43.289%
17772690385

Pull #519

github

web-flow
Merge 98d4fd22c into 3c10433b9
Pull Request #519: fix a bunch more clippy lints

56 of 123 new or added lines in 30 files covered. (45.53%)

8 existing lines in 3 files now uncovered.

12416 of 28654 relevant lines covered (43.33%)

2985398.75 hits per line

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

55.62
/crates/steel-core/src/primitives/numbers.rs
1
use crate::rvals::{IntoSteelVal, Result, SteelComplex, SteelVal};
2
use crate::{steelerr, stop, throw};
3
use num_bigint::BigInt;
4
use num_integer::Integer;
5
use num_rational::{BigRational, Rational32};
6
use num_traits::{pow::Pow, CheckedAdd, CheckedMul, Signed, ToPrimitive, Zero};
7
use std::ops::Neg;
8

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

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

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

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

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

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

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

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

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

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

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

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

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

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

314
/// Adds the given numbers.
315
///
316
/// (+ . nums) -> number?
317
///
318
/// * nums : number? - The numbers to add. Can have any number of arguments including zero.
319
///
320
/// # Examples
321
/// ```scheme
322
/// > (+ 5 3) ;; => 8
323
/// > (+ 10 3 2) ;; => 15
324
/// > (+) ;; => 0
325
/// ```
326
#[steel_derive::native(name = "+", constant = true, arity = "AtLeast(0)")]
327
pub fn add_primitive(args: &[SteelVal]) -> Result<SteelVal> {
59,690,164✔
328
    ensure_args_are_numbers("+", args)?;
179,070,494✔
329
    match args {
59,690,162✔
330
        [] => 0.into_steelval(),
2✔
331
        [x] => x.clone().into_steelval(),
12✔
332
        [x, y] => add_two(x, y),
292,981,845✔
333
        [x, y, zs @ ..] => {
1,093,788✔
334
            let mut res = add_two(x, y)?;
1,093,788✔
335
            for z in zs {
4,365,166✔
336
                res = add_two(&res, z)?;
6,542,756✔
337
            }
338
            res.into_steelval()
1,093,788✔
339
        }
340
    }
341
}
342

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

361
#[steel_derive::function(name = "truncate", constant = true)]
362
pub fn truncate(arg: &SteelVal) -> Result<SteelVal> {
30,000✔
363
    match arg {
30,000✔
364
        SteelVal::NumV(n) => n.trunc().into_steelval(),
30,000✔
365
        SteelVal::IntV(i) => Ok(SteelVal::IntV(*i)),
×
366
        // SteelVal::Rational(ratio) => ratio.trunc(),
367
        SteelVal::BigNum(gc) => Ok(SteelVal::BigNum(gc.clone())),
×
368
        // SteelVal::BigRational(gc) => gc.trunc(),
369
        _ => stop!(TypeMismatch => "truncate expects a real number, found: {}", arg),
×
370
    }
371
}
372

373
/// Returns quotient of dividing numerator by denomintator.
374
///
375
/// (quotient numerator denominator) -> integer?
376
///
377
/// * numerator : integer? - The numerator.
378
/// * denominator : integer? - The denominator.
379
///
380
/// # Examples
381
/// ```scheme
382
/// > (quotient 11 2) ;; => 5
383
/// > (quotient 10 2) ;; => 5
384
/// > (quotient -10 2) ;; => -5
385
/// ```
386
#[steel_derive::native(name = "quotient", constant = true, arity = "Exact(2)")]
387
pub fn quotient(args: &[SteelVal]) -> Result<SteelVal> {
6,106,442✔
388
    match (&args[0], &args[1]) {
6,106,442✔
389
        (SteelVal::IntV(l), SteelVal::IntV(r)) => (l / r).into_steelval(),
5,848,742✔
390
        (SteelVal::BigNum(l), SteelVal::IntV(r)) => (l.as_ref() / r).into_steelval(),
953,000✔
391
        (SteelVal::IntV(l), SteelVal::BigNum(r)) => (l / r.as_ref()).into_steelval(),
9,500✔
392
        (SteelVal::BigNum(l), SteelVal::BigNum(r)) => (l.as_ref() / r.as_ref()).into_steelval(),
391,200✔
393
        _ => steelerr!(TypeMismatch => "quotient only supports integers"),
×
394
    }
395
}
396

397
/// Returns the euclidean remainder of the division of the first number by the second
398
/// This differs from the remainder operator when using negative numbers.
399
///
400
/// (modulo n m) -> integer?
401
///
402
/// * n : integer?
403
/// * m : integer?
404
///
405
/// # Examples
406
/// ```scheme
407
/// > (modulo 10 3) ;; => 1
408
/// > (modulo -10 3) ;; => 2
409
/// > (modulo 10 -3) ;; => -2
410
/// > (module -10 -3) ;; => -1
411
/// ```
412
#[steel_derive::native(name = "modulo", constant = true, arity = "Exact(2)")]
413
pub fn modulo(args: &[SteelVal]) -> Result<SteelVal> {
16✔
414
    match (&args[0], &args[1]) {
16✔
415
        (SteelVal::IntV(l), SteelVal::IntV(r)) => ((l % r + r) % r).into_steelval(),
16✔
416
        _ => steelerr!(TypeMismatch => "modulo only supports integers"),
×
417
    }
418
}
419

420
/// Returns the arithmetic remainder of the division of the first number by the second.
421
/// This differs from the modulo operator when using negative numbers.
422
///
423
/// (remainder n m) -> integer?
424
///
425
/// * n : integer?
426
/// * m : integer?
427
///
428
/// # Examples
429
/// ```scheme
430
/// > (remainder 10 3) ;; => 1
431
/// > (remainder -10 3) ;; => -1
432
/// > (remainder 10 -3) ;; => 1
433
/// > (remainder -10 -3) ;; => -1
434
/// ```
435
#[steel_derive::native(name = "remainder", constant = true, arity = "Exact(2)")]
436
pub fn remainder(args: &[SteelVal]) -> Result<SteelVal> {
9,920,588✔
437
    match (&args[0], &args[1]) {
9,920,588✔
438
        (SteelVal::IntV(l), SteelVal::IntV(r)) => (l % r).into_steelval(),
9,920,588✔
439
        _ => steelerr!(TypeMismatch => "remainder only supports integers"),
×
440
    }
441
}
442

443
/// Returns the sine value of the input angle, measured in radians.
444
///
445
/// (sin n) -> number?
446
///
447
/// * n : number? - The input angle, in radians.
448
///
449
/// # Examples
450
/// ```scheme
451
/// > (sin 0) ;; => 0
452
/// > (sin 1) ;; => 0.8414709848078965
453
/// > (sin 2.0) ;; => 0.9092974268256817
454
/// > (sin 3.14) ;; => 0.0015926529164868282
455
/// ```
456
#[steel_derive::function(name = "sin", constant = true)]
457
pub fn sin(arg: &SteelVal) -> Result<SteelVal> {
40,484✔
458
    match arg {
40,484✔
459
        SteelVal::IntV(i) => (*i as f64).sin(),
×
460
        SteelVal::BigNum(i) => i.to_f64().unwrap().sin(),
×
461
        SteelVal::NumV(n) => n.sin(),
40,484✔
462
        SteelVal::Rational(r) => (*r.numer() as f32 / *r.denom() as f32).sin() as f64,
×
463
        _ => stop!(TypeMismatch => "sin expects a number, found: {}", arg),
×
464
    }
465
    .into_steelval()
466
}
467

468
/// Returns the cosine value of the input angle, measured in radians.
469
///
470
/// (cos n) -> number?
471
///
472
/// * n : number? - The input angle, in radians.
473
///
474
/// # Examples
475
/// ```scheme
476
/// > (cos 0) ;; => 1
477
/// > (cos 1) ;; => 0.5403023058681398
478
/// > (cos 2.0) ;; => -0.4161468365471424
479
/// > (cos 3.14) ;; => -0.9999987317275395
480
/// ```
481
#[steel_derive::function(name = "cos", constant = true)]
482
pub fn cos(arg: &SteelVal) -> Result<SteelVal> {
40,184✔
483
    match arg {
40,184✔
484
        SteelVal::IntV(i) => (*i as f64).cos(),
×
485
        SteelVal::BigNum(i) => i.to_f64().unwrap().cos(),
×
486
        SteelVal::NumV(n) => n.cos(),
40,184✔
487
        SteelVal::Rational(r) => (*r.numer() as f32 / *r.denom() as f32).cos() as f64,
×
488
        _ => stop!(TypeMismatch => "cos expects a number, found: {}", arg),
×
489
    }
490
    .into_steelval()
491
}
492

493
/// Returns the tangent value of the input angle, measured in radians.
494
///
495
/// (tan n) -> number?
496
///
497
/// * n : number? - The input angle, in radians.
498
///
499
/// # Examples
500
/// ```scheme
501
/// > (tan 0) ;; => 0
502
/// > (tan 1) ;; => 1.557407724654902
503
/// > (tan 2.0) ;; => -2.185039863261519
504
/// > (tan 3.14) ;; => -0.0015926549364072232
505
/// ```
506
#[steel_derive::function(name = "tan", constant = true)]
507
pub fn tan(arg: &SteelVal) -> Result<SteelVal> {
×
508
    match arg {
×
509
        SteelVal::IntV(i) => (*i as f64).tan(),
×
510
        SteelVal::BigNum(i) => i.to_f64().unwrap().tan(),
×
511
        SteelVal::NumV(n) => n.tan(),
×
512
        SteelVal::Rational(r) => (*r.numer() as f32 / *r.denom() as f32).tan() as f64,
×
513
        _ => stop!(TypeMismatch => "tan expects a number, found: {}", arg),
×
514
    }
515
    .into_steelval()
516
}
517

518
/// Returns the arcsine, or inverse sine, of a value; output is in radians.
519
///
520
/// (asin n) -> number?
521
///
522
/// * n : number? - The input value is the sine of the angle you want and must be from -1 to 1.
523
///
524
/// # Examples
525
/// ```scheme
526
/// > (asin -1) ;; => -1.5707963267948966
527
/// > (asin 0) ;; => 0
528
/// > (asin 0.5) ;; => 0.5235987755982988
529
/// > (asin 2) ;; => +nan.0
530
/// ```
531
#[steel_derive::function(name = "asin", constant = true)]
532
pub fn asin(arg: &SteelVal) -> Result<SteelVal> {
×
533
    match arg {
×
534
        SteelVal::IntV(i) => (*i as f64).asin(),
×
535
        SteelVal::BigNum(i) => i.to_f64().unwrap().asin(),
×
536
        SteelVal::NumV(n) => n.asin(),
×
537
        SteelVal::Rational(r) => (*r.numer() as f32 / *r.denom() as f32).asin() as f64,
×
538
        _ => stop!(TypeMismatch => "asin expects a number, found: {}", arg),
×
539
    }
540
    .into_steelval()
541
}
542

543
/// Returns the arccosine, or inverse cosine, of a value; output is in radians.
544
///
545
/// (acos n) -> number?
546
///
547
/// * n : number? - The input value is the cosine of the angle you want and must be from -1 to 1.
548
///
549
/// # Examples
550
/// ```scheme
551
/// > (acos -1) ;; => 3.141592653589793
552
/// > (acos 0) ;; => 1.5707963267948966
553
/// > (acos 0.5) ;; => 1.0471975511965976
554
/// > (acos 2) ;; => +nan.0
555
/// ```
556
#[steel_derive::function(name = "acos", constant = true)]
557
pub fn acos(arg: &SteelVal) -> Result<SteelVal> {
2✔
558
    match arg {
2✔
559
        SteelVal::IntV(i) => (*i as f64).acos(),
6✔
560
        SteelVal::BigNum(i) => i.to_f64().unwrap().acos(),
×
561
        SteelVal::NumV(n) => n.acos(),
×
562
        SteelVal::Rational(r) => (*r.numer() as f32 / *r.denom() as f32).acos() as f64,
×
563
        _ => stop!(TypeMismatch => "acos expects a number, found: {}", arg),
×
564
    }
565
    .into_steelval()
566
}
567

568
/// Returns the arctangent, or inverse tangent, of a value; output is in radians.
569
///
570
/// (atan n) -> number?
571
///
572
/// * n : number? - The input value is the tangent of the angle you want.
573
///
574
/// # Examples
575
/// ```scheme
576
/// > (atan -1) ;; => -0.7853981633974483
577
/// > (atan 0) ;; => 0
578
/// > (atan 0.5) ;; => 0.46364760900080615
579
/// > (atan 2) ;; => 1.1071487177940906
580
/// ```
581
#[steel_derive::function(name = "atan", constant = true)]
582
pub fn atan(arg: &SteelVal) -> Result<SteelVal> {
40,184✔
583
    match arg {
40,184✔
584
        SteelVal::IntV(i) => (*i as f64).atan(),
×
585
        SteelVal::BigNum(i) => i.to_f64().unwrap().atan(),
×
586
        SteelVal::NumV(n) => n.atan(),
40,184✔
587
        SteelVal::Rational(r) => (*r.numer() as f32 / *r.denom() as f32).atan() as f64,
×
588
        _ => stop!(TypeMismatch => "atan expects a number, found: {}", arg),
×
589
    }
590
    .into_steelval()
591
}
592

593
/// Divides the given numbers.
594
///
595
/// (/ . nums) -> number?
596
///
597
/// * nums : number? - The numbers to divide. Must have at least one number.
598
///
599
/// # Examples
600
/// ```scheme
601
/// > (/ 10 2) ;; => 5
602
/// > (/ 10 2 2.0) ;; => 2.5
603
/// > (/ 1 3.0) ;; => 0.3333333333333333
604
/// > (/ 1 3) ;; => 1/3
605
/// ```
606
#[steel_derive::native(name = "/", constant = true, arity = "AtLeast(1)")]
607
pub fn divide_primitive(args: &[SteelVal]) -> Result<SteelVal> {
110,405✔
608
    ensure_args_are_numbers("/", args)?;
110,405✔
609
    let recip = |x: &SteelVal| -> Result<SteelVal> {
220,810✔
610
        match x {
110,405✔
611
            SteelVal::IntV(n) => match i32::try_from(*n) {
100✔
612
                Ok(0) => {
613
                    stop!(Generic => "/: division by zero")
1✔
614
                }
615
                Ok(n) => Rational32::new(1, n).into_steelval(),
196✔
616
                Err(_) => BigRational::new(BigInt::from(1), BigInt::from(*n)).into_steelval(),
×
617
            },
618
            SteelVal::NumV(n) => n.recip().into_steelval(),
110,348✔
619
            SteelVal::Rational(r) => r.recip().into_steelval(),
8✔
620
            SteelVal::BigRational(r) => r.recip().into_steelval(),
×
621
            SteelVal::BigNum(n) => BigRational::new(1.into(), n.as_ref().clone()).into_steelval(),
24✔
622
            SteelVal::Complex(c) => complex_reciprocal(c),
3✔
623
            unexpected => {
×
624
                steelerr!(TypeMismatch => "/ expects a number, but found: {:?}", unexpected)
×
625
            }
626
        }
627
    };
628
    match &args {
629
        [] => steelerr!(ArityMismatch => "/ requires at least one argument"),
×
630
        [x] => recip(x),
6,022✔
631
        // TODO: Provide custom implementation to optimize by joining the multiply and recip calls.
632
        [x, y] => multiply_two(x, &recip(y)?),
536,965✔
633
        [x, ys @ ..] => {
1✔
634
            let d = multiply_primitive_impl(ys)?;
1✔
635
            multiply_two(x, &recip(&d)?)
1✔
636
        }
637
    }
638
}
639

640
/// Checks if the given value is exact.
641
///
642
/// (exact? val) -> boolean?
643
///
644
/// * val : any - The value to check for exactness.
645
///
646
/// # Examples
647
/// ```scheme
648
/// > (exact? 42) ;; => #t
649
/// > (exact? 3.14) ;; => #f
650
/// > (exact? "hello") ;; => #f
651
/// ```
652
#[steel_derive::function(name = "exact?", constant = true)]
653
pub fn exactp(value: &SteelVal) -> bool {
10✔
654
    match value {
10✔
655
        SteelVal::IntV(_)
656
        | SteelVal::BigNum(_)
657
        | SteelVal::Rational(_)
658
        | SteelVal::BigRational(_) => true,
6✔
659
        SteelVal::Complex(x) => exactp(&x.re) && exactp(&x.im),
8✔
660
        _ => false,
2✔
661
    }
662
}
663

664
/// Checks if the given value is inexact.
665
///
666
/// (inexact? val) -> boolean?
667
///
668
/// * val : any - The value to check for inexactness.
669
///
670
/// # Examples
671
/// ```scheme
672
/// > (inexact? 42) ;; => #f
673
/// > (inexact? 3.14) ;; => #t
674
/// ```
675
#[steel_derive::function(name = "inexact?", constant = true)]
676
pub fn inexactp(value: &SteelVal) -> bool {
12✔
677
    match value {
12✔
678
        SteelVal::NumV(_) => true,
4✔
679
        SteelVal::Complex(x) => inexactp(&x.re) || inexactp(&x.im),
7✔
680
        _ => false,
6✔
681
    }
682
}
683

684
fn number_to_float(number: &SteelVal) -> Result<f64> {
×
685
    let res = match number {
×
686
        SteelVal::IntV(i) => *i as f64,
×
687
        SteelVal::Rational(f) => f.to_f64().unwrap(),
×
688
        SteelVal::BigRational(f) => f.to_f64().unwrap(),
×
689
        SteelVal::NumV(n) => *n,
×
690
        SteelVal::BigNum(n) => n.to_f64().unwrap(),
×
691
        _ => stop!(TypeMismatch => "number->float expects a real number, found: {}", number),
×
692
    };
693
    Ok(res)
694
}
695

696
/// Converts a number to an inexact number.
697
///
698
/// (inexact num) -> number?
699
///
700
/// * num : number? - The number to convert from exact to inexact.
701
///
702
/// # Examples
703
/// ```scheme
704
/// > (inexact 10) ;; => 10
705
/// > (inexact 1/2) ;; => 0.5
706
/// > (inexact 1+2i) ;; => 1+2i
707
/// ```
708
#[steel_derive::function(name = "inexact", constant = true)]
709
fn inexact(number: &SteelVal) -> Result<SteelVal> {
122,670✔
710
    match number {
122,670✔
711
        SteelVal::IntV(i) => (*i as f64).into_steelval(),
367,950✔
712
        SteelVal::Rational(f) => f.to_f64().unwrap().into_steelval(),
95✔
713
        SteelVal::BigRational(f) => f.to_f64().unwrap().into_steelval(),
×
714
        SteelVal::NumV(n) => n.into_steelval(),
1✔
715
        SteelVal::BigNum(n) => Ok(SteelVal::NumV(n.to_f64().unwrap())),
×
716
        SteelVal::Complex(x) => SteelComplex::new(inexact(&x.re)?, inexact(&x.im)?).into_steelval(),
×
717
        _ => steelerr!(TypeMismatch => "exact->inexact expects a number type, found: {}", number),
×
718
    }
719
}
720

721
/// Converts an exact number to an inexact number.
722
///
723
/// (exact->inexact num) -> number?
724
///
725
/// * num : number? - The number to convert from exact to inexact.
726
///
727
/// # Examples
728
/// ```scheme
729
/// > (exact->inexact 10) ;; => 10
730
/// > (exact->inexact 1/2) ;; => 0.5
731
/// > (exact->inexact 1+2i) ;; => 1+2i
732
/// ```
733
#[steel_derive::function(name = "exact->inexact", constant = true)]
734
fn exact_to_inexact(number: &SteelVal) -> Result<SteelVal> {
20✔
735
    inexact(number)
40✔
736
}
737

738
/// Converts a number to an exact number.
739
///
740
/// (exact num) -> number?
741
///
742
/// * num : number? - The value to convert to exact.
743
///
744
/// # Examples
745
/// ```scheme
746
/// > (exact 10.0) ;; => 10
747
/// > (exact 1.5) ;; => 3/2
748
/// > (exact 1.5+2.5i) ;; => 3/2+5/2i
749
/// ```
750
#[steel_derive::function(name = "exact", constant = true)]
751
pub fn exact(number: &SteelVal) -> Result<SteelVal> {
20,005✔
752
    match number {
20,005✔
753
        SteelVal::IntV(_)
754
        | SteelVal::Rational(_)
755
        | SteelVal::BigRational(_)
756
        | SteelVal::BigNum(_) => Ok(number.clone()),
2✔
757
        SteelVal::NumV(x) => {
20,003✔
758
            if x.fract() == 0. {
759
                (*x as isize).into_steelval()
40,004✔
760
            } else {
761
                BigRational::from_float(*x)
1✔
762
                    .ok_or_else(throw!(ConversionError => "no exact representation for {}", x))?
×
763
                    .into_steelval()
764
            }
765
        }
766
        SteelVal::Complex(x) => SteelComplex::new(exact(&x.re)?, exact(&x.im)?).into_steelval(),
×
767
        _ => steelerr!(TypeMismatch => "exact->inexact expects a number type, found: {}", number),
×
768
    }
769
}
770

771
/// Converts an inexact number to an exact number.
772
///
773
/// (inexact->exact num) -> number?
774
///
775
/// * num : number? - The number to convert from inexact to exact.
776
///
777
/// # Examples
778
/// ```scheme
779
/// > (inexact->exact 10.0) ;; => 10
780
/// > (inexact->exact 1.5) ;; => 3/2
781
/// > (inexact->exact 1.5+2.5i) ;; => 3/2+5/2i
782
/// ```
783
#[steel_derive::function(name = "inexact->exact", constant = true)]
784
fn inexact_to_exact(number: &SteelVal) -> Result<SteelVal> {
3✔
785
    exact(number)
6✔
786
}
787

788
fn finitep_impl(number: &SteelVal) -> Result<bool> {
7✔
789
    match number {
5✔
790
        SteelVal::NumV(x) if x.is_nan() || x.is_infinite() => Ok(false),
22✔
791
        SteelVal::IntV(_)
792
        | SteelVal::NumV(_)
793
        | SteelVal::BigNum(_)
794
        | SteelVal::Rational(_)
795
        | SteelVal::BigRational(_) => Ok(true),
3✔
796
        SteelVal::Complex(x) => Ok(finitep_impl(&x.re)? && finitep_impl(&x.im)?),
×
797
        _ => steelerr!(TypeMismatch => "finite? expects a number, found: {}", number),
×
798
    }
799
}
800

801
/// Returns `#t` if the given number is finite.
802
///
803
/// (finite? number) -> boolean?
804
///
805
/// * number : number? - The number to check for finiteness.
806
///
807
/// # Examples
808
/// ```scheme
809
/// > (finite? 42) ;; => #t
810
/// > (finite? 0.1) ;; => #t
811
/// > (finite? +inf.0) ;; => #f
812
/// > (finite? -inf.0) ;; => #f
813
/// > (finite? +nan.0) ;; => #f
814
/// ```
815
#[steel_derive::function(name = "finite?", constant = true)]
816
fn finitep(number: &SteelVal) -> Result<SteelVal> {
7✔
817
    finitep_impl(number).into_steelval()
21✔
818
}
819

820
fn infinitep_impl(number: &SteelVal) -> Result<bool> {
5✔
821
    match number {
3✔
822
        SteelVal::NumV(x) if x.is_infinite() => Ok(true),
13✔
823
        SteelVal::IntV(_)
824
        | SteelVal::NumV(_)
825
        | SteelVal::BigNum(_)
826
        | SteelVal::Rational(_)
827
        | SteelVal::BigRational(_) => Ok(false),
3✔
828
        SteelVal::Complex(n) => Ok(infinitep_impl(&n.re)? || infinitep_impl(&n.im)?),
×
829
        _ => steelerr!(TypeMismatch => "exact->inexact expects a real number, found: {}", number),
×
830
    }
831
}
832

833
/// Returns `#t` if the given number is infinite.
834
///
835
/// (infinite? number) -> boolean?
836
///
837
/// * number : number? - The number to check for infiniteness.
838
///
839
/// # Examples
840
/// ```scheme
841
/// > (infinite? 42) ;; => #f
842
/// > (infinite? -nan.0) ;; => #f
843
/// > (infinite? +inf.0) ;; => #t
844
/// ```
845
#[steel_derive::function(name = "infinite?", constant = true)]
846
fn infinitep(number: &SteelVal) -> Result<SteelVal> {
5✔
847
    infinitep_impl(number)?.into_steelval()
15✔
848
}
849

850
/// Computes the absolute value of the given number.
851
///
852
/// (abs number) -> number?
853
///
854
/// * number : number? - The number to compute the absolute value of.
855
///
856
/// # Examples
857
/// ```scheme
858
/// > (abs 42) ;; => 42
859
/// > (abs -42) ;; => 42
860
/// > (abs 0) ;; => 0
861
/// ```
862
#[steel_derive::function(name = "abs", constant = true)]
863
fn abs(number: &SteelVal) -> Result<SteelVal> {
17✔
864
    match number {
17✔
865
        SteelVal::IntV(i) => Ok(SteelVal::IntV(i.abs())),
30✔
866
        SteelVal::NumV(n) => Ok(SteelVal::NumV(n.abs())),
1✔
867
        SteelVal::Rational(f) => f.abs().into_steelval(),
4✔
868
        SteelVal::BigRational(f) => f.abs().into_steelval(),
×
869
        SteelVal::BigNum(n) => n.as_ref().abs().into_steelval(),
×
870
        _ => steelerr!(TypeMismatch => "abs expects a real number, found: {}", number),
×
871
    }
872
}
873

874
/// Rounds the given number up to the nearest integer not less than it.
875
///
876
/// (ceiling number) -> integer?
877
///
878
/// * number : number? - The number to round up.
879
///
880
/// # Examples
881
/// ```scheme
882
/// > (ceiling 42) ;; => 42
883
/// > (ceiling 42.1) ;; => 43
884
/// > (ceiling -42.1) ;; => -42
885
/// ```
886
#[steel_derive::function(name = "ceiling", constant = true)]
887
fn ceiling(number: &SteelVal) -> Result<SteelVal> {
6✔
888
    match number {
6✔
889
        n @ SteelVal::IntV(_) | n @ SteelVal::BigNum(_) => Ok(n.clone()),
2✔
890
        SteelVal::NumV(n) => Ok(SteelVal::NumV(n.ceil())),
3✔
891
        SteelVal::Rational(f) => f.ceil().into_steelval(),
8✔
892
        SteelVal::BigRational(f) => f.ceil().into_steelval(),
×
893
        _ => steelerr!(TypeMismatch => "ceiling expects a real number, found: {}", number),
×
894
    }
895
}
896

897
/// Retrieves the denominator of the given rational number.
898
///
899
/// (denominator number) -> integer?
900
///
901
/// * number : number? - The rational number to retrieve the denominator from.
902
///
903
/// # Examples
904
/// ```scheme
905
/// > (denominator 1/2) ;; => 2
906
/// > (denominator 3/4) ;; => 4
907
/// > (denominator 4) ;; => 1
908
/// ```
909
#[steel_derive::function(name = "denominator", constant = true)]
910
fn denominator(number: &SteelVal) -> Result<SteelVal> {
2✔
911
    match number {
2✔
912
        SteelVal::IntV(_) | SteelVal::BigNum(_) => 1.into_steelval(),
1✔
913
        SteelVal::NumV(_) => {
914
            steelerr!(TypeMismatch => "denominator not supported for number {}", number)
×
915
        }
916
        SteelVal::Rational(f) => f.denom().into_steelval(),
3✔
917
        SteelVal::BigRational(f) => f.denom().clone().into_steelval(),
×
918
        _ => steelerr!(TypeMismatch => "ceiling expects a real number, found: {}", number),
×
919
    }
920
}
921

922
// TODO: Add support for BigNum.
923
/// Raises the left operand to the power of the right operand.
924
///
925
/// (expt base exponent) -> number?
926
///
927
/// * base : number? - The base number.
928
/// * exponent : number? - The exponent to raise the base to.
929
///
930
/// # Examples
931
/// ```scheme
932
/// > (expt 2 3) ;; => 8
933
/// > (expt 2.0 0.5) ;; => 1.4142135623730951
934
/// > (expt 9 0.5) ;; => 3
935
/// ```
936
#[steel_derive::function(name = "expt", constant = true)]
937
fn expt(left: &SteelVal, right: &SteelVal) -> Result<SteelVal> {
433,023✔
938
    match (left, right) {
866,046✔
939
        (SteelVal::IntV(l), SteelVal::IntV(r)) if *r >= 0 => {
1,732,072✔
940
            match u32::try_from(*r).ok().and_then(|r| l.checked_pow(r)) {
3,031,126✔
941
                Some(val) => val.into_steelval(),
1,260,054✔
942
                None => BigInt::from(*l).pow(*r as usize).into_steelval(),
13,000✔
943
            }
944
        }
945
        // r is negative here
946
        (SteelVal::IntV(l), SteelVal::IntV(r)) => {
×
947
            if l.is_zero() {
948
                stop!(Generic => "expt: 0 cannot be raised to a negative power");
×
949
            }
950

951
            let r = r.unsigned_abs();
952
            // a^-b = 1/(a^b)
953
            match (u32::try_from(r).ok())
954
                .and_then(|r| l.checked_pow(r))
×
955
                .and_then(|l| i32::try_from(l).ok())
×
956
            {
957
                Some(val) => Rational32::new_raw(1, val).into_steelval(),
×
958
                None => {
959
                    BigRational::new_raw(BigInt::from(1), BigInt::from(*l).pow(r)).into_steelval()
×
960
                }
961
            }
962
        }
963
        (SteelVal::IntV(l), SteelVal::NumV(r)) => (*l as f64).powf(*r).into_steelval(),
6✔
964
        (SteelVal::IntV(l), SteelVal::Rational(r)) => {
2✔
965
            (*l as f64).powf(r.to_f64().unwrap()).into_steelval()
6✔
966
        }
967
        (SteelVal::IntV(l), SteelVal::BigNum(r)) => {
×
968
            if l.is_zero() {
×
969
                stop!(Generic => "expt: 0 cannot be raised to a negative power");
×
970
            }
971

972
            let expt = BigInt::from(*l).pow(r.magnitude());
973
            match r.sign() {
974
                num_bigint::Sign::Plus | num_bigint::Sign::NoSign => expt.into_steelval(),
×
975
                num_bigint::Sign::Minus => {
976
                    BigRational::new_raw(BigInt::from(1), expt).into_steelval()
×
977
                }
978
            }
979
        }
980
        (SteelVal::IntV(l), SteelVal::BigRational(r)) => {
×
981
            (*l as f64).powf(r.to_f64().unwrap()).into_steelval()
×
982
        }
983
        (SteelVal::NumV(l), SteelVal::NumV(r)) => Ok(SteelVal::NumV(l.powf(*r))),
1✔
984
        (SteelVal::NumV(l), SteelVal::IntV(r)) => match i32::try_from(*r) {
×
985
            Ok(r) => l.powi(r).into_steelval(),
×
986
            Err(_) => l.powf(*r as f64).into_steelval(),
×
987
        },
988
        (SteelVal::NumV(l), SteelVal::Rational(r)) => l.powf(r.to_f64().unwrap()).into_steelval(),
×
989
        (SteelVal::NumV(l), SteelVal::BigNum(r)) => l.powf(r.to_f64().unwrap()).into_steelval(),
×
990
        (SteelVal::NumV(l), SteelVal::BigRational(r)) => {
×
991
            l.powf(r.to_f64().unwrap()).into_steelval()
×
992
        }
993
        (SteelVal::Rational(l), SteelVal::Rational(r)) => l
×
994
            .to_f64()
995
            .unwrap()
996
            .powf(r.to_f64().unwrap())
×
997
            .into_steelval(),
998
        (SteelVal::Rational(l), SteelVal::NumV(r)) => l.to_f64().unwrap().powf(*r).into_steelval(),
8✔
999
        (SteelVal::Rational(l), SteelVal::IntV(r)) => match i32::try_from(*r) {
3✔
1000
            Ok(r) => l.pow(r).into_steelval(),
1✔
1001
            Err(_) => {
1002
                let base = BigRational::new(BigInt::from(*l.numer()), BigInt::from(*l.denom()));
×
1003
                let exp = BigInt::from(*r);
×
1004
                base.pow(exp).into_steelval()
×
1005
            }
1006
        },
1007
        (SteelVal::Rational(l), SteelVal::BigNum(r)) => l
×
1008
            .to_f64()
1009
            .unwrap()
1010
            .powf(r.to_f64().unwrap())
×
1011
            .into_steelval(),
1012
        (SteelVal::Rational(l), SteelVal::BigRational(r)) => l
×
1013
            .to_f64()
1014
            .unwrap()
1015
            .powf(r.to_f64().unwrap())
×
1016
            .into_steelval(),
1017
        (SteelVal::BigNum(l), SteelVal::BigNum(r)) => {
×
1018
            let expt = l.as_ref().clone().pow(r.magnitude());
×
1019
            match r.sign() {
×
1020
                num_bigint::Sign::NoSign | num_bigint::Sign::Plus => expt.into_steelval(),
×
1021
                num_bigint::Sign::Minus => {
1022
                    BigRational::new_raw(BigInt::from(1), expt).into_steelval()
×
1023
                }
1024
            }
1025
        }
1026
        (SteelVal::BigNum(l), SteelVal::IntV(r)) => match *r {
×
1027
            0 => 1.into_steelval(),
×
1028
            r if r < 0 => {
×
1029
                BigRational::new_raw(BigInt::from(1), l.as_ref().clone().pow(r.unsigned_abs()))
×
1030
                    .into_steelval()
1031
            }
1032
            r => l.as_ref().clone().pow(r as usize).into_steelval(),
×
1033
        },
1034
        (SteelVal::BigNum(l), SteelVal::NumV(r)) => l.to_f64().unwrap().powf(*r).into_steelval(),
×
1035
        (SteelVal::BigNum(l), SteelVal::Rational(r)) => l
×
1036
            .to_f64()
1037
            .unwrap()
1038
            .powf(r.to_f64().unwrap())
×
1039
            .into_steelval(),
1040
        (SteelVal::BigNum(l), SteelVal::BigRational(r)) => l
×
1041
            .to_f64()
1042
            .unwrap()
1043
            .powf(r.to_f64().unwrap())
×
1044
            .into_steelval(),
1045
        (SteelVal::BigRational(l), SteelVal::Rational(r)) => l
×
1046
            .to_f64()
1047
            .unwrap()
1048
            .powf(r.to_f64().unwrap())
×
1049
            .into_steelval(),
1050
        (SteelVal::BigRational(l), SteelVal::NumV(r)) => {
×
1051
            l.to_f64().unwrap().powf(*r).into_steelval()
×
1052
        }
1053
        (SteelVal::BigRational(l), SteelVal::IntV(r)) => match i32::try_from(*r) {
×
1054
            Ok(r) => l.as_ref().pow(r).into_steelval(),
×
1055
            Err(_) => {
1056
                let exp = BigInt::from(*r);
×
1057
                l.as_ref().clone().pow(exp).into_steelval()
×
1058
            }
1059
        },
1060
        (SteelVal::BigRational(l), SteelVal::BigNum(r)) => l
×
1061
            .to_f64()
1062
            .unwrap()
1063
            .powf(r.to_f64().unwrap())
×
1064
            .into_steelval(),
1065
        (SteelVal::BigRational(l), SteelVal::BigRational(r)) => l
×
1066
            .to_f64()
1067
            .unwrap()
1068
            .powf(r.to_f64().unwrap())
×
1069
            .into_steelval(),
1070
        (l, r) => {
×
1071
            steelerr!(TypeMismatch => "expt expected two numbers but found {} and {}", l, r)
×
1072
        }
1073
    }
1074
}
1075

1076
/// Returns Euler’s number raised to the power of z.
1077
///
1078
/// (exp z) -> number?
1079
///
1080
/// * z : number? - The number to raise e to the power of.
1081
///
1082
/// # Examples
1083
/// ```scheme
1084
/// > (exp 0) ;; => 1
1085
/// > (exp 2) ;; => 7.38905609893065
1086
/// > (exp 1.5) ;; => 4.4816890703380645
1087
/// ```
1088
#[steel_derive::function(name = "exp", constant = true)]
1089
fn exp(left: &SteelVal) -> Result<SteelVal> {
2✔
1090
    match left {
1✔
1091
        SteelVal::IntV(0) => Ok(SteelVal::IntV(1)),
1✔
1092
        SteelVal::IntV(l) if *l < i32::MAX as isize => {
3✔
1093
            Ok(SteelVal::NumV(std::f64::consts::E.powi(*l as i32)))
1✔
1094
        }
1095
        maybe_number => match number_to_float(maybe_number) {
×
1096
            Ok(n) => Ok(SteelVal::NumV(std::f64::consts::E.powf(n))),
×
1097
            Err(_) => steelerr!(Generic => "exp expected a real number"),
×
1098
        },
1099
    }
1100
}
1101

1102
/// Computes the largest integer less than or equal to the given number.
1103
///
1104
/// (floor number) -> number?
1105
///
1106
/// * number : number? - The number to compute the floor for.
1107
///
1108
/// # Examples
1109
/// ```scheme
1110
/// > (floor 3.14) ;; => 3
1111
/// > (floor 4.99) ;; => 4
1112
/// > (floor -2.5) ;; => -3
1113
/// ```
1114
#[steel_derive::function(name = "floor", constant = true)]
1115
fn floor(number: &SteelVal) -> Result<SteelVal> {
10,008✔
1116
    match number {
10,008✔
1117
        SteelVal::NumV(x) => Ok(SteelVal::NumV(x.floor())),
10,003✔
1118
        SteelVal::IntV(x) => x.into_steelval(),
9✔
1119
        SteelVal::Rational(x) => x.floor().into_steelval(),
8✔
1120
        SteelVal::BigNum(x) => Ok(SteelVal::BigNum(x.clone())),
×
1121
        SteelVal::BigRational(x) => x.floor().into_steelval(),
×
1122
        _ => steelerr!(Generic => "floor expected a real number"),
×
1123
    }
1124
}
1125

1126
/// Retrieves the numerator of the given rational number.
1127
///
1128
/// (numerator number) -> number?
1129
///
1130
/// * number : number? - The rational number to retrieve the numerator from.
1131
///
1132
/// # Examples
1133
/// ```scheme
1134
/// > (numerator 3/4) ;; => 3
1135
/// > (numerator 5/2) ;; => 5
1136
/// > (numerator -2) ;; => -2
1137
/// ```
1138
#[steel_derive::function(name = "numerator", constant = true)]
1139
fn numerator(number: &SteelVal) -> Result<SteelVal> {
2✔
1140
    match number {
2✔
1141
        SteelVal::IntV(x) => x.into_steelval(),
1✔
1142
        SteelVal::Rational(x) => (*x.numer() as isize).into_steelval(),
3✔
1143
        SteelVal::BigNum(x) => Ok(SteelVal::BigNum(x.clone())),
×
1144
        SteelVal::BigRational(x) => (x.numer().clone()).into_steelval(),
×
1145
        _ => steelerr!(Generic => "numerator expects an integer or rational number"),
×
1146
    }
1147
}
1148

1149
/// Rounds the given number to the nearest integer.
1150
///
1151
/// (round number) -> number?
1152
///
1153
/// * number : number? - The number to round.
1154
///
1155
/// # Examples
1156
/// ```scheme
1157
/// > (round 3.14) ;; => 3
1158
/// > (round 4.6) ;; => 5
1159
/// > (round -2.5) ;; => -3
1160
/// ```
1161
#[steel_derive::function(name = "round", constant = true)]
1162
fn round(number: &SteelVal) -> Result<SteelVal> {
26✔
1163
    match number {
26✔
1164
        SteelVal::IntV(i) => i.into_steelval(),
57✔
1165
        SteelVal::NumV(n) => n.round().into_steelval(),
4✔
1166
        SteelVal::Rational(f) => f.round().into_steelval(),
8✔
1167
        SteelVal::BigRational(f) => f.round().into_steelval(),
×
1168
        SteelVal::BigNum(n) => Ok(SteelVal::BigNum(n.clone())),
2✔
1169
        _ => steelerr!(TypeMismatch => "round expects a real number, found: {}", number),
×
1170
    }
1171
}
1172

1173
/// Computes the square of the given number.
1174
///
1175
/// (square number) -> number?
1176
///
1177
/// * number : number? - The number to square.
1178
///
1179
/// # Examples
1180
/// ```scheme
1181
/// > (square 5) ;; => 25
1182
/// > (square -3) ;; => 9
1183
/// > (square 2.5) ;; => 6.25
1184
/// ```
1185
#[steel_derive::function(name = "square", constant = true)]
1186
fn square(number: &SteelVal) -> Result<SteelVal> {
38,116✔
1187
    if !numberp(number) {
38,116✔
1188
        stop!(TypeMismatch => "square expects a number, found: {:?}", number)
×
1189
    }
1190
    multiply_two(number, number)
114,348✔
1191
}
1192

1193
/// Computes the square root of the given number.
1194
///
1195
/// (sqrt number) -> number?
1196
///
1197
/// * number : number? - The number to compute the square root for.
1198
///
1199
/// # Examples
1200
/// ```scheme
1201
/// > (sqrt 4) ;; => 2
1202
/// > (sqrt 2) ;; => 1.4142135623730951
1203
/// > (sqrt -1) ;; => 0+1i
1204
/// ```
1205
#[steel_derive::function(name = "sqrt", constant = true)]
1206
fn sqrt(number: &SteelVal) -> Result<SteelVal> {
69,621✔
1207
    match number {
69,621✔
1208
        SteelVal::NumV(x) => {
69,601✔
1209
            if x.is_negative() {
1210
                let imag = x.neg().sqrt();
×
1211
                SteelComplex::new(0.0.into_steelval()?, imag.into_steelval()?).into_steelval()
×
1212
            } else {
1213
                x.sqrt().into_steelval()
69,601✔
1214
            }
1215
        }
1216
        SteelVal::IntV(x) => {
15✔
1217
            if x.is_negative() {
30✔
1218
                let sqrt = (*x as f64).abs().sqrt();
×
1219
                if sqrt as isize as f64 == sqrt {
×
1220
                    SteelComplex::new(0.into_steelval()?, (sqrt as isize).into_steelval()?)
×
1221
                        .into_steelval()
1222
                } else {
1223
                    SteelComplex::new(0.into_steelval()?, sqrt.into_steelval()?).into_steelval()
×
1224
                }
1225
            } else {
1226
                let sqrt = (*x as f64).sqrt();
15✔
1227
                if sqrt as isize as f64 == sqrt {
1228
                    (sqrt as isize).into_steelval()
30✔
1229
                } else {
1230
                    sqrt.into_steelval()
×
1231
                }
1232
            }
1233
        }
1234
        SteelVal::Rational(x) => {
1✔
1235
            let n = x.numer().abs();
3✔
1236
            let d = *x.denom();
2✔
1237
            let n_sqrt = (n as f64).sqrt();
3✔
1238
            let d_sqrt = (d as f64).sqrt();
3✔
1239
            let sqrt = if n_sqrt as i32 as f64 == n_sqrt && d_sqrt as i32 as f64 == d_sqrt {
3✔
1240
                Rational32::new(n_sqrt as i32, d_sqrt as i32).into_steelval()?
4✔
1241
            } else {
1242
                (n_sqrt / d_sqrt).into_steelval()?
×
1243
            };
1244
            if x.is_negative() {
1245
                let re = if exactp(&sqrt) {
×
1246
                    0.into_steelval()?
×
1247
                } else {
1248
                    0.0.into_steelval()?
×
1249
                };
1250
                SteelComplex::new(re, sqrt).into_steelval()
1251
            } else {
1252
                Ok(sqrt)
1✔
1253
            }
1254
        }
1255
        SteelVal::BigNum(n) => {
×
1256
            let sqrt = n.as_ref().to_f64().unwrap().sqrt();
×
1257
            if n.as_ref().is_negative() {
×
1258
                SteelComplex::new(0.0.into_steelval()?, sqrt.into_steelval()?).into_steelval()
×
1259
            } else {
1260
                sqrt.into_steelval()
×
1261
            }
1262
        }
1263
        SteelVal::BigRational(n) => {
×
1264
            let sqrt = n.as_ref().to_f64().unwrap().sqrt();
×
1265
            if n.as_ref().is_negative() {
×
1266
                SteelComplex::new(0.0.into_steelval()?, sqrt.into_steelval()?).into_steelval()
×
1267
            } else {
1268
                sqrt.into_steelval()
×
1269
            }
1270
        }
1271
        SteelVal::Complex(n) => {
4✔
1272
            let z_mag = magnitude(number)?;
12✔
1273
            let half = Rational32::new(1, 2).into_steelval()?;
4✔
1274
            let re = sqrt(&multiply_two(&add_two(&z_mag, &n.re)?, &half)?)?;
12✔
1275
            let im = sqrt(&multiply_two(&add_two(&z_mag, &negate(&n.re)?)?, &half)?)?;
16✔
1276
            if negativep(&n.im)? == SteelVal::BoolV(true) {
4✔
1277
                SteelComplex::new(re, negate(&im)?).into_steelval()
8✔
1278
            } else {
1279
                SteelComplex::new(re, im).into_steelval()
2✔
1280
            }
1281
        }
1282
        _ => steelerr!(TypeMismatch => "sqrt expected a number"),
×
1283
    }
1284
}
1285

1286
/// Create a complex number with `re` as the real part and `im` as the imaginary part.
1287
///
1288
/// (make-rectangular re im) -> number?
1289
///
1290
/// - re : real?
1291
/// - im : real?
1292
#[steel_derive::function(name = "make-rectangular", constant = true)]
1293
pub fn make_rectangular(re: &SteelVal, im: &SteelVal) -> Result<SteelVal> {
56,250✔
1294
    ensure_arg_is_real("make-rectangular", re)?;
168,750✔
1295
    ensure_arg_is_real("make-rectangular", im)?;
56,250✔
1296

1297
    SteelComplex {
1298
        re: re.clone(),
56,250✔
1299
        im: im.clone(),
56,250✔
1300
    }
1301
    .into_steelval()
1302
}
1303

1304
/// Make a complex number out of a magnitude `r` and an angle `θ`, so that the result is `r * (cos θ + i sin θ)`
1305
///
1306
/// (make-polar r θ) -> number?
1307
///
1308
/// - r : real?
1309
/// - theta : real?
1310
#[steel_derive::function(name = "make-polar", constant = true)]
1311
pub fn make_polar(r: &SteelVal, theta: &SteelVal) -> Result<SteelVal> {
×
1312
    ensure_arg_is_real("make-polar", r)?;
×
1313
    ensure_arg_is_real("make-polar", theta)?;
×
1314

NEW
1315
    let re = multiply_primitive(&[r.clone(), cos(theta)?])?;
×
NEW
1316
    let im = multiply_primitive(&[r.clone(), sin(theta)?])?;
×
1317
    SteelComplex { re, im }.into_steelval()
1318
}
1319

1320
fn ensure_arg_is_real(op: &str, arg: &SteelVal) -> Result<()> {
112,500✔
1321
    if !realp(arg) {
112,500✔
1322
        stop!(TypeMismatch => "{op} expects a real number, found: {:?}", arg);
×
1323
    } else {
1324
        Ok(())
112,500✔
1325
    }
1326
}
1327

1328
/// Returns the real part of a number
1329
///
1330
/// (real-part number) -> number?
1331
///
1332
/// # Examples
1333
/// ```scheme
1334
/// > (real-part 3+4i) ;; => 3
1335
/// > (real-part 42) ;; => 42
1336
/// ```
1337
#[steel_derive::function(name = "real-part", constant = true)]
1338
pub fn real_part(value: &SteelVal) -> Result<SteelVal> {
1,703,532✔
1339
    match value {
1,703,532✔
1340
        val @ SteelVal::IntV(_)
1✔
1341
        | val @ SteelVal::BigNum(_)
×
1342
        | val @ SteelVal::Rational(_)
×
1343
        | val @ SteelVal::BigRational(_)
×
1344
        | val @ SteelVal::NumV(_) => Ok(val.clone()),
1✔
1345
        SteelVal::Complex(complex) => Ok(complex.re.clone()),
3,407,062✔
1346
        _ => steelerr!(TypeMismatch => "real-part expected number"),
×
1347
    }
1348
}
1349

1350
/// Returns the imaginary part of a number
1351
///
1352
/// (imag-part number) -> number?
1353
///
1354
/// # Examples
1355
/// ```scheme
1356
/// > (imag-part 3+4i) ;; => 4
1357
/// > (imag-part 42) ;; => 0
1358
/// ```
1359
#[steel_derive::function(name = "imag-part", constant = true)]
1360
pub fn imag_part(value: &SteelVal) -> Result<SteelVal> {
1,703,532✔
1361
    match value {
1,703,532✔
1362
        SteelVal::IntV(_)
1363
        | SteelVal::BigNum(_)
1364
        | SteelVal::Rational(_)
1365
        | SteelVal::BigRational(_)
1366
        | SteelVal::NumV(_) => Ok(SteelVal::IntV(0)),
1✔
1367
        SteelVal::Complex(complex) => Ok(complex.im.clone()),
3,407,062✔
1368
        _ => steelerr!(TypeMismatch => "imag-part expected number"),
×
1369
    }
1370
}
1371

1372
/// Computes the magnitude of the given number.
1373
///
1374
/// (magnitude number) -> number?
1375
///
1376
/// * number : number? - The number to compute the magnitude for.
1377
///
1378
/// # Examples
1379
/// ```scheme
1380
/// > (magnitude 3+4i) ;; => 5
1381
/// > (magnitude 5) ;; => 5
1382
/// > (magnitude -5) ;; => 5
1383
/// ```
1384
#[steel_derive::function(name = "magnitude", constant = true)]
1385
fn magnitude(number: &SteelVal) -> Result<SteelVal> {
8✔
1386
    match number {
8✔
1387
        SteelVal::NumV(x) => x.abs().into_steelval(),
1✔
1388
        SteelVal::IntV(x) => x.abs().into_steelval(),
4✔
1389
        SteelVal::Rational(x) => x.abs().into_steelval(),
4✔
1390
        SteelVal::BigNum(x) => x.as_ref().abs().into_steelval(),
×
1391
        SteelVal::BigRational(x) => x.as_ref().abs().into_steelval(),
×
1392
        SteelVal::Complex(x) => {
5✔
1393
            let c_squared = add_two(&square(&x.re)?, &square(&x.im)?)?;
25✔
1394
            sqrt(&c_squared)
1395
        }
1396
        _ => steelerr!(TypeMismatch => "magnitude expects a number, found {number}"),
×
1397
    }
1398
}
1399

1400
/// Computes the angle `θ` of a complex number `z` where `z = r * (cos θ + i sin θ)` and `r` is the magnitude.
1401
///
1402
/// (angle number) -> number?
1403
///
1404
/// - number : number?
1405
#[steel_derive::function(name = "angle", constant = true)]
1406
pub fn angle(number: &SteelVal) -> Result<SteelVal> {
×
1407
    let (re, im) = match number {
×
1408
        re @ SteelVal::NumV(_)
×
1409
        | re @ SteelVal::IntV(_)
×
1410
        | re @ SteelVal::Rational(_)
×
1411
        | re @ SteelVal::BigNum(_) => (re, &SteelVal::IntV(0)),
×
1412
        SteelVal::Complex(complex) => (&complex.re, &complex.im),
×
1413
        _ => stop!(TypeMismatch => "angle expects a number, found {number}"),
×
1414
    };
1415

1416
    atan2(im, re)
1417
}
1418

1419
/// Computes the quadratic arctan of `y` and `x`
1420
fn atan2(y: &SteelVal, x: &SteelVal) -> Result<SteelVal> {
×
1421
    let as_f64 = |arg: &_| match arg {
×
1422
        SteelVal::NumV(arg) => Ok(*arg),
×
1423
        SteelVal::IntV(arg) => Ok(*arg as f64),
×
1424
        SteelVal::Rational(arg) => Ok(*arg.numer() as f64 / *arg.denom() as f64),
×
1425
        SteelVal::BigNum(arg) => Ok(arg.to_f64().unwrap()),
×
1426
        _ => steelerr!(TypeMismatch => "atan2 expects a number, found {arg}"),
×
1427
    };
1428

1429
    let y = as_f64(y)?;
×
1430
    let x = as_f64(x)?;
×
1431
    if y == 0. && x == 0. {
×
1432
        // as this is currently only used for `angle`, make the error
1433
        // message a little better by saying `angle` instead of `atan2`
1434
        stop!(Generic => "angle: undefined for zero");
×
1435
    }
1436

1437
    f64::atan2(y, x).into_steelval()
1438
}
1439

1440
/// Computes the natural logarithm of the given number.
1441
///
1442
/// (log number [base]) -> number?
1443
///
1444
/// * number : number? - The number to compute the logarithm for.
1445
/// * base : number? - The base of the logarithm. If not provided, defaults to Euler's number (e).
1446
///
1447
/// # Examples
1448
/// ```scheme
1449
/// > (log 10) ;; => 2.302585092994046
1450
/// > (log 100 10) ;; => 2
1451
/// > (log 27 3) ;; => 3
1452
/// ```
1453
#[steel_derive::native(name = "log", arity = "Range(1,2)")]
1454
fn log(args: &[SteelVal]) -> Result<SteelVal> {
6✔
1455
    let first = &args[0];
6✔
1456
    let base = args
1457
        .get(1)
1458
        .cloned()
1459
        .unwrap_or(SteelVal::NumV(std::f64::consts::E));
1460

1461
    match (first, &base) {
1462
        (SteelVal::IntV(1), _) => Ok(SteelVal::IntV(0)),
1✔
1463
        (SteelVal::IntV(_) | SteelVal::NumV(_), SteelVal::IntV(1)) => {
1464
            steelerr!(Generic => "log: divide by zero with args: {} and {}", first, base)
×
1465
        }
1466
        (SteelVal::IntV(arg), SteelVal::NumV(n)) => Ok(SteelVal::NumV((*arg as f64).log(*n))),
4✔
1467
        (SteelVal::IntV(arg), SteelVal::IntV(base)) => Ok(SteelVal::IntV(arg.ilog(*base) as isize)),
4✔
1468
        (SteelVal::NumV(arg), SteelVal::NumV(n)) => Ok(SteelVal::NumV(arg.log(*n))),
2✔
1469
        (SteelVal::NumV(arg), SteelVal::IntV(base)) => Ok(SteelVal::NumV(arg.log(*base as f64))),
4✔
1470
        // TODO: Support BigNum, Rational, and BigRational.
1471
        _ => {
1472
            steelerr!(TypeMismatch => "log expects one or two numbers, found: {} and {}", first, base)
×
1473
        }
1474
    }
1475
}
1476

1477
/// Computes the integer square root of the given non-negative integer.
1478
///
1479
/// (exact-integer-sqrt number) -> (integer? integer?)
1480
///
1481
/// * number : (and/c integer? positive?) - The non-negative integer to compute the square root for.
1482
///
1483
/// # Examples
1484
/// ```scheme
1485
/// > (exact-integer-sqrt 25) ;; => (5 0)
1486
/// > (exact-integer-sqrt 35) ;; => (5 10)
1487
/// ```
1488
#[steel_derive::function(name = "exact-integer-sqrt", constant = true)]
1489
fn exact_integer_sqrt(number: &SteelVal) -> Result<SteelVal> {
40,022✔
1490
    match number {
16✔
1491
        SteelVal::IntV(x) if *x >= 0 => {
47✔
1492
            let (ans, rem) = exact_integer_impl::<isize>(x);
45✔
1493
            (ans.into_steelval()?, rem.into_steelval()?).into_steelval()
75✔
1494
        }
1495
        SteelVal::BigNum(x) if !x.is_negative() => {
160,007✔
1496
            let (ans, rem) = exact_integer_impl::<BigInt>(x.as_ref());
1497
            (ans.into_steelval()?, rem.into_steelval()?).into_steelval()
80,002✔
1498
        }
1499
        _ => {
1500
            steelerr!(TypeMismatch => "exact-integer-sqrt expects a non-negative integer but found {number}")
6✔
1501
        }
1502
    }
1503
}
1504

1505
fn exact_integer_impl<'a, N>(target: &'a N) -> (N, N)
40,016✔
1506
where
1507
    N: num_integer::Roots + Clone,
1508
    &'a N: std::ops::Mul<&'a N, Output = N>,
1509
    N: std::ops::Sub<N, Output = N>,
1510
{
1511
    let x = target.sqrt();
120,048✔
1512
    let x_sq = x.clone() * x.clone();
160,064✔
1513
    let rem = target.clone() - x_sq;
120,048✔
1514
    (x, rem)
40,016✔
1515
}
1516

1517
/// Performs a bitwise arithmetic shift using the given 2 numbers
1518
///
1519
/// (arithmetic-shift n m) -> integer?
1520
///
1521
/// * n : integer? - The number to shift.
1522
/// * m : integer? - The number by which to shift.
1523
///
1524
/// # Examples
1525
/// ```scheme
1526
/// > (arithmetic-shift 10 1) ;; => 20
1527
/// > (arithmetic-shift 20 1) ;; => 40
1528
/// > (arithmetic-shift 40 -2) ;; => 10
1529
/// ```
1530
#[steel_derive::native(name = "arithmetic-shift", constant = true, arity = "Exact(2)")]
1531
pub fn arithmetic_shift(args: &[SteelVal]) -> Result<SteelVal> {
×
1532
    match (&args[0], &args[1]) {
×
1533
        (SteelVal::IntV(n), SteelVal::IntV(m)) => {
×
1534
            if *m >= 0 {
1535
                Ok(SteelVal::IntV(n << m))
×
1536
            } else {
1537
                Ok(SteelVal::IntV(n >> -m))
×
1538
            }
1539
        }
1540
        _ => stop!(TypeMismatch => "arithmetic-shift expected 2 integers"),
×
1541
    }
1542
}
1543

1544
/// Checks if the given number is even
1545
///
1546
/// (even? n) -> bool?
1547
///
1548
/// * n : number? - The number to check for evenness.
1549
///
1550
/// # Examples
1551
/// ```scheme
1552
/// > (even? 2) ;; => #true
1553
/// > (even? 3) ;; => #false
1554
/// > (even? 4.0) ;; => #true
1555
/// ```
1556
#[steel_derive::function(name = "even?", constant = true)]
1557
pub fn even(arg: &SteelVal) -> Result<SteelVal> {
307✔
1558
    match arg {
×
1559
        SteelVal::IntV(n) => Ok(SteelVal::BoolV(n & 1 == 0)),
614✔
1560
        SteelVal::BigNum(n) => Ok(SteelVal::BoolV(n.is_even())),
×
1561
        SteelVal::NumV(n) if n.fract() == 0.0 => (*n as i64).is_even().into_steelval(),
×
1562
        _ => steelerr!(TypeMismatch => "even? requires an integer, found: {:?}", arg),
×
1563
    }
1564
}
1565

1566
/// Checks if the given number is odd
1567
///
1568
/// (odd? n) -> bool?
1569
///
1570
/// * n : number? - The number to check for oddness.
1571
///
1572
/// # Examples
1573
/// ```scheme
1574
/// > (odd? 2) ;; => #false
1575
/// > (odd? 3) ;; => #true
1576
/// > (odd? 5.0) ;; => #true
1577
/// ```
1578
#[steel_derive::function(name = "odd?", constant = true)]
1579
pub fn odd(arg: &SteelVal) -> Result<SteelVal> {
1,364✔
1580
    match arg {
×
1581
        SteelVal::IntV(n) => Ok(SteelVal::BoolV(n & 1 == 1)),
2,728✔
1582
        SteelVal::BigNum(n) => Ok(SteelVal::BoolV(n.is_odd())),
×
1583
        SteelVal::NumV(n) if n.fract() == 0.0 => (*n as i64).is_odd().into_steelval(),
×
1584
        _ => {
1585
            steelerr!(TypeMismatch => "odd? requires an integer, found: {:?}", arg)
×
1586
        }
1587
    }
1588
}
1589

1590
/// Sums all given floats
1591
///
1592
/// (f+ nums) -> number?
1593
///
1594
/// * nums : float? - The floats to sum up.
1595
///
1596
/// # Examples
1597
/// ```scheme
1598
/// > (f+ 5.5) ;; => 5.5
1599
/// > (f+ 1.1 2.2) ;; => 3.3
1600
/// > (f+ 3.3 3.3 3.3) ;; => 9.9
1601
/// ```
1602
#[steel_derive::native(name = "f+", constant = true, arity = "AtLeast(1)")]
1603
pub fn float_add(args: &[SteelVal]) -> Result<SteelVal> {
×
1604
    let mut sum = 0.0;
×
1605

1606
    for arg in args {
×
1607
        if let SteelVal::NumV(n) = arg {
×
1608
            sum += n;
1609
        } else {
1610
            stop!(TypeMismatch => "f+ expected a float, found {:?}", arg);
×
1611
        }
1612
    }
1613

1614
    Ok(SteelVal::NumV(sum))
×
1615
}
1616

1617
fn ensure_args_are_numbers(op: &str, args: &[SteelVal]) -> Result<()> {
107,352,878✔
1618
    for arg in args {
540,590,691✔
1619
        if !numberp(arg) {
216,618,908✔
1620
            stop!(TypeMismatch => "{op} expects a number, found: {:?}", arg);
3✔
1621
        }
1622
    }
1623
    Ok(())
107,352,875✔
1624
}
1625

1626
/// Multiplies `x` and `y` without any type checking.
1627
///
1628
/// # Precondition
1629
/// - `x` and `y` must be valid numerical types.
1630
fn multiply_two(x: &SteelVal, y: &SteelVal) -> Result<SteelVal> {
34,950,733✔
1631
    match (x, y) {
69,901,466✔
1632
        (SteelVal::NumV(x), SteelVal::NumV(y)) => (x * y).into_steelval(),
25,236,250✔
1633
        (SteelVal::NumV(x), SteelVal::IntV(y)) | (SteelVal::IntV(y), SteelVal::NumV(x)) => {
242,010✔
1634
            (x * *y as f64).into_steelval()
121,005✔
1635
        }
1636
        (SteelVal::NumV(x), SteelVal::BigNum(y)) | (SteelVal::BigNum(y), SteelVal::NumV(x)) => {
×
1637
            (x * y.to_f64().unwrap()).into_steelval()
×
1638
        }
1639
        (SteelVal::NumV(x), SteelVal::Rational(y)) | (SteelVal::Rational(y), SteelVal::NumV(x)) => {
4✔
1640
            (x * y.to_f64().unwrap()).into_steelval()
2✔
1641
        }
1642
        (SteelVal::NumV(x), SteelVal::BigRational(y))
×
1643
        | (SteelVal::BigRational(y), SteelVal::NumV(x)) => {
×
1644
            (x * y.to_f64().unwrap()).into_steelval()
×
1645
        }
1646
        (SteelVal::IntV(x), SteelVal::IntV(y)) => match x.checked_mul(y) {
28,868,692✔
1647
            Some(res) => res.into_steelval(),
20,636,916✔
1648
            None => {
1649
                let mut res = BigInt::from(*x);
338,201✔
1650
                res *= *y;
1651
                res.into_steelval()
1652
            }
1653
        },
1654
        (SteelVal::IntV(x), SteelVal::BigNum(y)) | (SteelVal::BigNum(y), SteelVal::IntV(x)) => {
535,402✔
1655
            (y.as_ref() * x).into_steelval()
267,701✔
1656
        }
1657
        (SteelVal::IntV(x), SteelVal::Rational(y)) | (SteelVal::Rational(y), SteelVal::IntV(x)) => {
108✔
1658
            match i32::try_from(*x) {
54✔
1659
                Ok(x) => match y.checked_mul(&Rational32::new(x, 1)) {
54✔
1660
                    Some(res) => res.into_steelval(),
162✔
1661
                    None => {
1662
                        let mut res =
×
1663
                            BigRational::new(BigInt::from(*y.numer()), BigInt::from(*y.denom()));
1664
                        res *= BigInt::from(x);
1665
                        res.into_steelval()
1666
                    }
1667
                },
1668
                Err(_) => {
1669
                    let mut res =
×
1670
                        BigRational::new(BigInt::from(*y.numer()), BigInt::from(*y.denom()));
×
1671
                    res *= BigInt::from(*x);
×
1672
                    res.into_steelval()
×
1673
                }
1674
            }
1675
        }
1676
        (SteelVal::IntV(x), SteelVal::BigRational(y))
×
1677
        | (SteelVal::BigRational(y), SteelVal::IntV(x)) => {
×
1678
            let mut res = y.as_ref().clone();
×
1679
            res *= BigInt::from(*x);
1680
            res.into_steelval()
1681
        }
1682
        (SteelVal::Rational(x), SteelVal::Rational(y)) => match x.checked_mul(y) {
4✔
1683
            Some(res) => res.into_steelval(),
3✔
1684
            None => {
1685
                let mut res = BigRational::new(BigInt::from(*x.numer()), BigInt::from(*x.denom()));
×
1686
                res *= BigRational::new(BigInt::from(*y.numer()), BigInt::from(*y.denom()));
1687
                res.into_steelval()
1688
            }
1689
        },
1690
        (SteelVal::Rational(x), SteelVal::BigNum(y))
×
1691
        | (SteelVal::BigNum(y), SteelVal::Rational(x)) => {
×
1692
            let mut res = BigRational::new(BigInt::from(*x.numer()), BigInt::from(*x.denom()));
×
1693
            res *= y.as_ref();
1694
            res.into_steelval()
1695
        }
1696
        (SteelVal::BigRational(x), SteelVal::BigRational(y)) => {
×
1697
            (x.as_ref() * y.as_ref()).into_steelval()
×
1698
        }
1699
        (SteelVal::BigRational(x), SteelVal::BigNum(y))
×
1700
        | (SteelVal::BigNum(y), SteelVal::BigRational(x)) => {
6✔
1701
            (x.as_ref() * y.as_ref()).into_steelval()
3✔
1702
        }
1703
        (SteelVal::BigNum(x), SteelVal::BigNum(y)) => (x.as_ref() * y.as_ref()).into_steelval(),
2,324,400✔
1704
        // Complex numbers.
1705
        (SteelVal::Complex(x), SteelVal::Complex(y)) => multiply_complex(x, y),
1,665,644✔
1706
        (SteelVal::Complex(x), y) | (y, SteelVal::Complex(x)) => {
111,000✔
1707
            let y = SteelComplex::new(y.clone(), SteelVal::IntV(0));
55,500✔
1708
            multiply_complex(x, &y)
1709
        }
1710
        (SteelVal::BigRational(x), SteelVal::Rational(y)) => {
×
1711
            let mut res = BigRational::new(x.numer().clone(), x.denom().clone());
×
1712
            res *= BigRational::new(BigInt::from(*y.numer()), BigInt::from(*y.denom()));
×
1713
            res.into_steelval()
×
1714
        }
1715
        _ => unreachable!(),
1716
    }
1717
}
1718

1719
/// # Precondition
1720
/// All types in `args` must be numerical types.
1721
fn multiply_primitive_impl(args: &[SteelVal]) -> Result<SteelVal> {
27,490,641✔
1722
    match args {
27,490,641✔
1723
        [] => 1.into_steelval(),
27,490,643✔
1724
        [x] => x.clone().into_steelval(),
8✔
1725
        [x, y] => multiply_two(x, y).into_steelval(),
162,363,816✔
1726
        [x, y, zs @ ..] => {
430,001✔
1727
            let mut res = multiply_two(x, y)?;
430,001✔
1728
            for z in zs {
1,290,003✔
1729
                // TODO: This use case could be optimized to reuse state instead of creating a new
1730
                // object each time.
1731
                res = multiply_two(&res, z)?;
1,720,004✔
1732
            }
1733
            res.into_steelval()
430,001✔
1734
        }
1735
    }
1736
}
1737

1738
#[cold]
1739
fn complex_reciprocal(c: &SteelComplex) -> Result<SteelVal> {
1✔
1740
    let denominator = add_two(&multiply_two(&c.re, &c.re)?, &multiply_two(&c.im, &c.im)?)?;
6✔
1741
    let re = divide_primitive(&[c.re.clone(), denominator.clone()])?;
1✔
1742
    let neg_im = divide_primitive(&[c.re.clone(), denominator])?;
1✔
1743
    SteelComplex::new(re, subtract_primitive(&[neg_im])?).into_steelval()
1✔
1744
}
1745

1746
/// Negate a number.
1747
///
1748
/// # Precondition
1749
/// `value` must be a number.
1750
#[inline(always)]
1751
fn negate(value: &SteelVal) -> Result<SteelVal> {
21,782,822✔
1752
    match value {
21,782,822✔
1753
        SteelVal::NumV(x) => (-x).into_steelval(),
60,026,400✔
1754
        SteelVal::IntV(x) => match x.checked_neg() {
3,483,236✔
1755
            Some(res) => res.into_steelval(),
5,224,851✔
1756
            None => BigInt::from(*x).neg().into_steelval(),
1✔
1757
        },
1758
        SteelVal::Rational(x) => match 0i32.checked_sub(*x.numer()) {
6✔
1759
            Some(n) => Rational32::new(n, *x.denom()).into_steelval(),
10✔
1760
            None => BigRational::new(BigInt::from(*x.numer()), BigInt::from(*x.denom()))
×
1761
                .neg()
1762
                .into_steelval(),
1763
        },
1764
        SteelVal::BigRational(x) => x.as_ref().neg().into_steelval(),
×
1765
        SteelVal::BigNum(x) => x.as_ref().clone().neg().into_steelval(),
162,000✔
1766
        SteelVal::Complex(x) => negate_complex(x),
6✔
1767
        _ => unreachable!(),
1768
    }
1769
}
1770

1771
/// Adds two numbers.
1772
///
1773
/// # Precondition
1774
/// x and y must be valid numbers.
1775
#[inline(always)]
1776
pub fn add_two(x: &SteelVal, y: &SteelVal) -> Result<SteelVal> {
38,781,310✔
1777
    match (x, y) {
77,562,620✔
1778
        // Simple integer case. Probably very common.
1779
        (SteelVal::IntV(x), SteelVal::IntV(y)) => match x.checked_add(y) {
66,302,648✔
1780
            Some(res) => res.into_steelval(),
49,726,983✔
1781
            None => {
×
1782
                let mut res = BigInt::from(*x);
1✔
1783
                res += *y;
×
1784
                res.into_steelval()
×
1785
            }
1786
        },
1787
        // Cases that return an `f64`.
1788
        (SteelVal::NumV(x), SteelVal::NumV(y)) => (x + y).into_steelval(),
20,197,433✔
1789
        (SteelVal::NumV(x), SteelVal::IntV(y)) | (SteelVal::IntV(y), SteelVal::NumV(x)) => {
20,018✔
1790
            (x + *y as f64).into_steelval()
10,009✔
1791
        }
1792
        (SteelVal::NumV(x), SteelVal::BigNum(y)) | (SteelVal::BigNum(y), SteelVal::NumV(x)) => {
4✔
1793
            (x + y.to_f64().unwrap()).into_steelval()
2✔
1794
        }
1795
        (SteelVal::NumV(x), SteelVal::Rational(y)) | (SteelVal::Rational(y), SteelVal::NumV(x)) => {
4✔
1796
            (x + y.to_f64().unwrap()).into_steelval()
2✔
1797
        }
1798
        (SteelVal::NumV(x), SteelVal::BigRational(y))
×
1799
        | (SteelVal::BigRational(y), SteelVal::NumV(x)) => {
×
1800
            (x + y.to_f64().unwrap()).into_steelval()
×
1801
        }
1802
        // Cases that interact with `Rational`.
1803
        (SteelVal::Rational(x), SteelVal::Rational(y)) => (x + y).into_steelval(),
4✔
1804
        (SteelVal::Rational(x), SteelVal::IntV(y)) | (SteelVal::IntV(y), SteelVal::Rational(x)) => {
2✔
1805
            match i32::try_from(*y) {
1✔
1806
                Ok(y) => match x.checked_add(&Rational32::new(y, 1)) {
1✔
1807
                    Some(res) => res.into_steelval(),
×
1808
                    None => {
×
1809
                        let res =
1✔
1810
                            BigRational::new(BigInt::from(*x.numer()), BigInt::from(*x.denom()))
×
1811
                                + BigInt::from(y);
×
1812
                        res.into_steelval()
×
1813
                    }
1814
                },
1815
                Err(_) => {
×
1816
                    let res = BigRational::new(BigInt::from(*x.numer()), BigInt::from(*x.denom()))
×
1817
                        + BigInt::from(*y);
×
1818
                    res.into_steelval()
×
1819
                }
1820
            }
1821
        }
1822
        (SteelVal::Rational(x), SteelVal::BigNum(y))
×
1823
        | (SteelVal::BigNum(y), SteelVal::Rational(x)) => {
2✔
1824
            let res =
1✔
1825
                BigRational::new(BigInt::from(*x.numer()), BigInt::from(*x.denom())) + y.as_ref();
×
1826
            res.into_steelval()
×
1827
        }
1828
        // Cases that interact with `BigRational`. For the sake of performance, hopefully not too
1829
        // common.
1830
        (SteelVal::BigRational(x), SteelVal::BigRational(y)) => {
2✔
1831
            (x.as_ref() + y.as_ref()).into_steelval()
4✔
1832
        }
1833
        (SteelVal::BigRational(x), SteelVal::Rational(y))
2✔
1834
        | (SteelVal::Rational(y), SteelVal::BigRational(x)) => {
×
1835
            let res =
1✔
1836
                x.as_ref() + BigRational::new(BigInt::from(*y.numer()), BigInt::from(*y.denom()));
×
1837
            res.into_steelval()
×
1838
        }
1839
        (SteelVal::BigRational(x), SteelVal::IntV(y))
×
1840
        | (SteelVal::IntV(y), SteelVal::BigRational(x)) => {
×
1841
            (x.as_ref() + BigInt::from(*y)).into_steelval()
×
1842
        }
1843
        (SteelVal::BigRational(x), SteelVal::BigNum(y))
×
1844
        | (SteelVal::BigNum(y), SteelVal::BigRational(x)) => {
×
1845
            (x.as_ref() + y.as_ref()).into_steelval()
×
1846
        }
1847
        // Remaining cases that interact with `BigNum`. Probably not too common.
1848
        (SteelVal::BigNum(x), SteelVal::BigNum(y)) => {
477,206✔
1849
            let mut res = x.as_ref().clone();
715,809✔
1850
            res += y.as_ref();
477,206✔
1851
            res.into_steelval()
477,206✔
1852
        }
1853
        (SteelVal::BigNum(x), SteelVal::IntV(y)) | (SteelVal::IntV(y), SteelVal::BigNum(x)) => {
75,402✔
1854
            let mut res = x.as_ref().clone();
37,701✔
1855
            res += *y;
×
1856
            res.into_steelval()
×
1857
        }
1858
        // Complex numbers
1859
        (SteelVal::Complex(x), SteelVal::Complex(y)) => add_complex(x, y),
1,721,143✔
1860
        (SteelVal::Complex(x), y) | (y, SteelVal::Complex(x)) => {
1,500✔
1861
            debug_assert!(realp(y));
750✔
1862
            add_complex(x, &SteelComplex::new(y.clone(), SteelVal::IntV(0)))
3,750✔
1863
        }
1864
        _ => unreachable!(),
1865
    }
1866
}
1867

1868
#[cold]
1869
fn multiply_complex(x: &SteelComplex, y: &SteelComplex) -> Result<SteelVal> {
1,721,144✔
1870
    // TODO: Optimize the implementation if needed.
1871
    let real = add_two(
1872
        &multiply_two(&x.re, &y.re)?,
5,163,432✔
1873
        &negate(&multiply_two(&x.im, &y.im)?)?,
3,442,288✔
1874
    )?;
1875
    let im = add_two(&multiply_two(&x.re, &y.im)?, &multiply_two(&x.im, &y.re)?)?;
5,163,432✔
1876
    SteelComplex::new(real, im).into_steelval()
1877
}
1878

1879
#[cold]
1880
fn negate_complex(x: &SteelComplex) -> Result<SteelVal> {
2✔
1881
    // TODO: Optimize the implementation if needed.
1882
    SteelComplex::new(negate(&x.re)?, negate(&x.im)?).into_steelval()
8✔
1883
}
1884

1885
#[cold]
1886
fn add_complex(x: &SteelComplex, y: &SteelComplex) -> Result<SteelVal> {
1,721,893✔
1887
    // TODO: Optimize the implementation if needed.
1888
    SteelComplex::new(add_two(&x.re, &y.re)?, add_two(&x.im, &y.im)?).into_steelval()
8,609,465✔
1889
}
1890

1891
#[cfg(test)]
1892
mod num_op_tests {
1893
    use super::*;
1894
    use crate::{gc::Gc, rvals::SteelVal::*};
1895
    use std::str::FromStr;
1896

1897
    #[test]
1898
    fn division_test() {
1899
        assert_eq!(
1900
            divide_primitive(&[IntV(10), IntV(2)]).unwrap().to_string(),
1901
            IntV(5).to_string()
1902
        );
1903
    }
1904

1905
    #[test]
1906
    fn dvision_by_integer_zero_returns_positive_infinity() {
1907
        // assert_eq!(
1908
        //     divide_primitive(&[IntV(1), IntV(0)]).unwrap().to_string(),
1909
        //     NumV(f64::INFINITY).to_string()
1910
        // )
1911

1912
        assert!(divide_primitive(&[IntV(1), IntV(0)]).is_err())
1913
    }
1914

1915
    #[test]
1916
    fn division_on_single_integer_returns_reciprocal_rational() {
1917
        assert_eq!(
1918
            divide_primitive(&[IntV(10)]).unwrap().to_string(),
1919
            Rational(Rational32::new(1, 10)).to_string()
1920
        );
1921
    }
1922

1923
    #[test]
1924
    fn division_on_single_rational_returns_reciprocal_rational() {
1925
        assert_eq!(
1926
            divide_primitive(&[Rational32::new(2, 5).into_steelval().unwrap()])
1927
                .unwrap()
1928
                .to_string(),
1929
            Rational(Rational32::new(5, 2)).to_string()
1930
        );
1931
    }
1932

1933
    #[test]
1934
    fn division_on_rational_with_numerator_one_returns_integer() {
1935
        assert_eq!(
1936
            divide_primitive(&[Rational32::new(1, 5).into_steelval().unwrap()])
1937
                .unwrap()
1938
                .to_string(),
1939
            IntV(5).to_string()
1940
        );
1941
    }
1942

1943
    #[test]
1944
    fn division_on_bignum_returns_bigrational() {
1945
        assert_eq!(
1946
            divide_primitive(
1947
                &([BigInt::from_str("18446744073709551616")
1948
                    .unwrap()
1949
                    .into_steelval()
1950
                    .unwrap(),])
1951
            )
1952
            .unwrap()
1953
            .to_string(),
1954
            BigRational(Gc::new(num_rational::BigRational::new(
1955
                BigInt::from(1),
1956
                BigInt::from_str("18446744073709551616").unwrap()
1957
            )))
1958
            .to_string()
1959
        );
1960
    }
1961

1962
    #[test]
1963
    fn multiplication_test() {
1964
        let args = [IntV(10), IntV(2)];
1965
        let got = multiply_primitive(&args).unwrap();
1966
        let expected = IntV(20);
1967
        assert_eq!(got, expected);
1968
    }
1969

1970
    #[test]
1971
    fn multiplication_different_types() {
1972
        let args = [IntV(10), NumV(2.0)];
1973
        let got = multiply_primitive(&args).unwrap();
1974
        let expected = NumV(20.0);
1975
        assert_eq!(got.to_string(), expected.to_string());
1976
    }
1977

1978
    #[test]
1979
    fn multiply_multiple_numbers() {
1980
        assert_eq!(
1981
            multiply_primitive(&[IntV(16), NumV(2.0), Rational(Rational32::new(1, 4))])
1982
                .unwrap()
1983
                .to_string(),
1984
            NumV(8.0).to_string(),
1985
        );
1986
    }
1987

1988
    #[test]
1989
    fn adding_exact_with_inexact_returns_inexact() {
1990
        assert_eq!(
1991
            add_primitive(&([IntV(10), NumV(2.0)])).unwrap().to_string(),
1992
            NumV(12.0).to_string()
1993
        );
1994
        assert_eq!(
1995
            add_primitive(
1996
                &([
1997
                    BigInt::from_str("18446744073709551616")
1998
                        .unwrap()
1999
                        .into_steelval()
2000
                        .unwrap(),
2001
                    NumV(18446744073709551616.0),
2002
                ])
2003
            )
2004
            .unwrap()
2005
            .to_string(),
2006
            NumV(18446744073709551616.0 * 2.0).to_string()
2007
        );
2008
        assert_eq!(
2009
            add_primitive(
2010
                &([
2011
                    BigInt::from_str("18446744073709551616")
2012
                        .unwrap()
2013
                        .into_steelval()
2014
                        .unwrap(),
2015
                    NumV(18446744073709551616.0),
2016
                ])
2017
            )
2018
            .unwrap()
2019
            .to_string(),
2020
            NumV(18446744073709551616.0 * 2.0).to_string()
2021
        );
2022
        assert_eq!(
2023
            add_primitive(&([Rational32::new(1, 2).into_steelval().unwrap(), NumV(0.5),]))
2024
                .unwrap()
2025
                .to_string(),
2026
            NumV(1.0).to_string()
2027
        );
2028
    }
2029

2030
    #[test]
2031
    fn subtraction_different_types() {
2032
        let args = [IntV(10), NumV(2.0)];
2033
        let got = subtract_primitive(&args).unwrap();
2034
        let expected = NumV(8.0);
2035
        assert_eq!(got.to_string(), expected.to_string());
2036
    }
2037

2038
    #[test]
2039
    fn test_integer_add() {
2040
        let args = [IntV(10), IntV(2)];
2041
        let got = add_primitive(&args).unwrap();
2042
        let expected = IntV(12);
2043
        assert_eq!(got, expected);
2044
    }
2045

2046
    #[test]
2047
    fn test_integer_sub() {
2048
        let args = [IntV(10), IntV(2)];
2049
        let got = subtract_primitive(&args).unwrap();
2050
        let expected = IntV(8);
2051
        assert_eq!(got, expected);
2052
    }
2053

2054
    #[test]
2055
    fn test_exact_integer_sqrt() {
2056
        assert_eq!(
2057
            exact_integer_sqrt(&0.into()),
2058
            (0.into_steelval().unwrap(), 0.into_steelval().unwrap()).into_steelval()
2059
        );
2060
        assert_eq!(
2061
            exact_integer_sqrt(&1.into()),
2062
            (1.into_steelval().unwrap(), 0.into_steelval().unwrap()).into_steelval()
2063
        );
2064
        assert_eq!(
2065
            exact_integer_sqrt(&2.into()),
2066
            (1.into_steelval().unwrap(), 1.into_steelval().unwrap()).into_steelval()
2067
        );
2068
        assert_eq!(
2069
            exact_integer_sqrt(&2.into()),
2070
            (1.into_steelval().unwrap(), 1.into_steelval().unwrap()).into_steelval()
2071
        );
2072
        assert_eq!(
2073
            exact_integer_sqrt(&3.into()),
2074
            (1.into_steelval().unwrap(), 2.into_steelval().unwrap()).into_steelval()
2075
        );
2076
        assert_eq!(
2077
            exact_integer_sqrt(&4.into()),
2078
            (2.into_steelval().unwrap(), 0.into_steelval().unwrap()).into_steelval()
2079
        );
2080
        assert_eq!(
2081
            exact_integer_sqrt(&5.into()),
2082
            (2.into_steelval().unwrap(), 1.into_steelval().unwrap()).into_steelval()
2083
        );
2084
        assert_eq!(
2085
            exact_integer_sqrt(&6.into()),
2086
            (2.into_steelval().unwrap(), 2.into_steelval().unwrap()).into_steelval()
2087
        );
2088
        assert_eq!(
2089
            exact_integer_sqrt(&7.into()),
2090
            (2.into_steelval().unwrap(), 3.into_steelval().unwrap()).into_steelval()
2091
        );
2092
    }
2093

2094
    #[test]
2095
    fn test_exact_integer_sqrt_fails_on_negative_or_noninteger() {
2096
        assert!(exact_integer_sqrt(&(-7).into()).is_err());
2097
        assert!(exact_integer_sqrt(&Rational32::new(-1, 2).into_steelval().unwrap()).is_err());
2098
        assert!(exact_integer_sqrt(
2099
            &BigInt::from_str("-10000000000000000000000000000000000001")
2100
                .unwrap()
2101
                .into_steelval()
2102
                .unwrap()
2103
        )
2104
        .is_err());
2105
        assert!(exact_integer_sqrt(
2106
            &num_rational::BigRational::new(
2107
                BigInt::from_str("-10000000000000000000000000000000000001").unwrap(),
2108
                BigInt::from_str("2").unwrap()
2109
            )
2110
            .into_steelval()
2111
            .unwrap()
2112
        )
2113
        .is_err());
2114
        assert!(exact_integer_sqrt(&(1.0).into()).is_err());
2115
        assert!(exact_integer_sqrt(
2116
            &SteelComplex::new(1.into(), 1.into())
2117
                .into_steelval()
2118
                .unwrap()
2119
        )
2120
        .is_err());
2121
    }
2122

2123
    #[test]
2124
    fn test_sqrt() {
2125
        assert_eq!(sqrt(&4isize.into()).unwrap(), 2isize.into());
2126
        assert_eq!(
2127
            sqrt(
2128
                &SteelComplex::new(0.into(), 2.into())
2129
                    .into_steelval()
2130
                    .unwrap()
2131
            )
2132
            .unwrap(),
2133
            SteelComplex::new(1.into(), 1.into())
2134
                .into_steelval()
2135
                .unwrap()
2136
        );
2137
        assert_eq!(
2138
            sqrt(
2139
                &SteelComplex::new((-3).into(), (-4).into())
2140
                    .into_steelval()
2141
                    .unwrap()
2142
            )
2143
            .unwrap(),
2144
            SteelComplex::new(1.into(), (-2).into())
2145
                .into_steelval()
2146
                .unwrap()
2147
        );
2148
    }
2149
}
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