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

mattwparas / steel / 11924200389

20 Nov 2024 12:20AM UTC coverage: 46.725% (-0.2%) from 46.956%
11924200389

Pull #290

github

web-flow
Merge faebbd075 into a6f7286de
Pull Request #290: Fix provides within macros

37 of 263 new or added lines in 7 files covered. (14.07%)

27 existing lines in 11 files now uncovered.

12469 of 26686 relevant lines covered (46.72%)

483326.6 hits per line

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

54.13
/crates/steel-core/src/primitives/numbers.rs
1
use crate::rvals::{IntoSteelVal, Result, SteelComplex, SteelVal};
2
use crate::{steelerr, stop};
3
use num::Zero;
4
use num::{
5
    pow::Pow, BigInt, BigRational, CheckedAdd, CheckedMul, Integer, Rational32, Signed, ToPrimitive,
6
};
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 {
24,649,093✔
23
    matches!(
12✔
24
        value,
24,649,093✔
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 {
9,538✔
UNCOV
65
    matches!(
×
66
        value,
9,538✔
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 {
94✔
116
    match value {
5✔
117
        SteelVal::IntV(_) | SteelVal::BigNum(_) => true,
89✔
118
        SteelVal::NumV(n) if n.fract() == 0.0 => true,
1✔
119
        _ => false,
4✔
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)
4✔
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(),
3✔
242
        SteelVal::Rational(n) => n.is_positive().into_steelval(),
2✔
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(),
7✔
266
        SteelVal::Rational(n) => n.is_negative().into_steelval(),
2✔
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> {
1,426,372✔
287
    ensure_args_are_numbers("-", args)?;
1,426,373✔
288
    match args {
1,426,371✔
289
        [] => steelerr!(TypeMismatch => "- requires at least one argument"),
1,426,371✔
290
        [x] => negate(x),
490,060✔
291
        [x, ys @ ..] => {
936,311✔
292
            let y = negate(&add_primitive(ys)?)?;
1,872,622✔
293
            add_two(x, &y)
936,311✔
294
        }
295
    }
296
}
297

298
/// Adds the given numbers.
299
///
300
/// (+ . nums) -> number?
301
///
302
/// * nums : number? - The numbers to add. Can have any number of arguments including zero.
303
///
304
/// # Examples
305
/// ```scheme
306
/// > (+ 5 3) ;; => 8
307
/// > (+ 10 3 2) ;; => 15
308
/// > (+) ;; => 0
309
/// ```
310
#[steel_derive::native(name = "+", constant = true, arity = "AtLeast(0)")]
311
pub fn add_primitive(args: &[SteelVal]) -> Result<SteelVal> {
10,650,715✔
312
    ensure_args_are_numbers("+", args)?;
10,650,717✔
313
    match args {
10,650,713✔
314
        [] => 0.into_steelval(),
10,650,715✔
315
        [x] => x.clone().into_steelval(),
936,310✔
316
        [x, y] => add_two(x, y),
9,664,375✔
317
        [x, y, zs @ ..] => {
50,026✔
318
            let mut res = add_two(x, y)?;
100,052✔
319
            for z in zs {
170,092✔
320
                res = add_two(&res, z)?;
60,033✔
321
            }
322
            res.into_steelval()
50,026✔
323
        }
324
    }
325
}
326

327
/// Multiplies the given numbers.
328
///
329
/// (* . nums) -> number?
330
///
331
/// * nums : number? - The numbers to multiply. Can have any number of arguments including zero.
332
///
333
/// # Examples
334
/// ```scheme
335
/// > (* 5 3) ;; => 15
336
/// > (* 10 3 2) ;; => 60
337
/// > (*) ;; => 1
338
/// ```
339
#[steel_derive::native(name = "*", constant = true, arity = "AtLeast(0)")]
340
pub fn multiply_primitive(args: &[SteelVal]) -> Result<SteelVal> {
845,480✔
341
    ensure_args_are_numbers("*", args)?;
845,480✔
342
    multiply_primitive_impl(args)
845,480✔
343
}
344

345
/// Returns quotient of dividing numerator by denomintator.
346
///
347
/// (quotient numerator denominator) -> integer?
348
///
349
/// * numerator : integer? - The numerator.
350
/// * denominator : integer? - The denominator.
351
///
352
/// # Examples
353
/// ```scheme
354
/// > (quotient 11 2) ;; => 5
355
/// > (quotient 10 2) ;; => 5
356
/// > (quotient -10 2) ;; => -5
357
/// ```
358
#[steel_derive::native(name = "quotient", constant = true, arity = "Exact(2)")]
359
pub fn quotient(args: &[SteelVal]) -> Result<SteelVal> {
1✔
360
    match &args {
1✔
361
        [l, r] => match (l, r) {
1✔
362
            (SteelVal::IntV(l), SteelVal::IntV(r)) => (l / r).into_steelval(),
1✔
363
            _ => steelerr!(TypeMismatch => "quotient only supports integers"),
×
364
        },
365
        _ => steelerr!(ArityMismatch => "quotient requires 2 arguments"),
×
366
    }
367
}
368

369
/// Returns the remainder of the division of the first number by the second
370
///
371
/// (modulo n m) -> integer?
372
///
373
/// * n : integer?
374
/// * m : integer?
375
///
376
/// # Examples
377
/// ```scheme
378
/// > (modulo 10 3) ;; => 1
379
/// > (modulo -10 3) ;; => 2
380
/// > (modulo 10 -3) ;; => -2
381
/// > (module -10 -3) ;; => -1
382
/// ```
383
#[steel_derive::native(name = "modulo", constant = true, arity = "Exact(2)")]
384
pub fn modulo(args: &[SteelVal]) -> Result<SteelVal> {
16✔
385
    match &args {
16✔
386
        [l, r] => match (l, r) {
16✔
387
            (SteelVal::IntV(l), SteelVal::IntV(r)) => ((l % r + r) % r).into_steelval(),
16✔
388
            _ => steelerr!(TypeMismatch => "modulo only supports integers"),
×
389
        },
390
        _ => steelerr!(ArityMismatch => "modulo requires 2 arguments"),
×
391
    }
392
}
393

394
#[steel_derive::native(name = "remainder", constant = true, arity = "Exact(2)")]
395
pub fn remainder(args: &[SteelVal]) -> Result<SteelVal> {
8✔
396
    match &args {
8✔
397
        [l, r] => match (l, r) {
8✔
398
            (SteelVal::IntV(l), SteelVal::IntV(r)) => (l % r).into_steelval(),
8✔
399
            _ => steelerr!(TypeMismatch => "remainder only supports integers"),
×
400
        },
401
        _ => steelerr!(ArityMismatch => "remainder requires 2 arguments"),
×
402
    }
403
}
404

405
// TODO: Do this for sin, cos, tan, asin, acos, atan
406
#[steel_derive::function(name = "sin", constant = true)]
407
pub fn sin(arg: &SteelVal) -> Result<SteelVal> {
×
408
    match arg {
×
409
        SteelVal::IntV(i) => (*i as f64).sin(),
×
410
        SteelVal::BigNum(i) => i.to_f64().unwrap().sin(),
×
411
        SteelVal::NumV(n) => n.sin(),
×
412
        SteelVal::Rational(r) => (*r.numer() as f32 / *r.denom() as f32).sin() as f64,
×
413
        _ => stop!(TypeMismatch => "sin expects a number, found: {}", arg),
×
414
    }
415
    .into_steelval()
416
}
417

418
#[steel_derive::function(name = "cos", constant = true)]
419
pub fn cos(arg: &SteelVal) -> Result<SteelVal> {
×
420
    match arg {
×
421
        SteelVal::IntV(i) => (*i as f64).cos(),
×
422
        SteelVal::BigNum(i) => i.to_f64().unwrap().cos(),
×
423
        SteelVal::NumV(n) => n.cos(),
×
424
        SteelVal::Rational(r) => (*r.numer() as f32 / *r.denom() as f32).cos() as f64,
×
425
        _ => stop!(TypeMismatch => "cos expects a number, found: {}", arg),
×
426
    }
427
    .into_steelval()
428
}
429

430
#[steel_derive::function(name = "tan", constant = true)]
431
pub fn tan(arg: &SteelVal) -> Result<SteelVal> {
×
432
    match arg {
×
433
        SteelVal::IntV(i) => (*i as f64).tan(),
×
434
        SteelVal::BigNum(i) => i.to_f64().unwrap().tan(),
×
435
        SteelVal::NumV(n) => n.tan(),
×
436
        SteelVal::Rational(r) => (*r.numer() as f32 / *r.denom() as f32).tan() as f64,
×
437
        _ => stop!(TypeMismatch => "tan expects a number, found: {}", arg),
×
438
    }
439
    .into_steelval()
440
}
441

442
#[steel_derive::function(name = "asin", constant = true)]
443
pub fn asin(arg: &SteelVal) -> Result<SteelVal> {
×
444
    match arg {
×
445
        SteelVal::IntV(i) => (*i as f64).asin(),
×
446
        SteelVal::BigNum(i) => i.to_f64().unwrap().asin(),
×
447
        SteelVal::NumV(n) => n.asin(),
×
448
        SteelVal::Rational(r) => (*r.numer() as f32 / *r.denom() as f32).asin() as f64,
×
449
        _ => stop!(TypeMismatch => "asin expects a number, found: {}", arg),
×
450
    }
451
    .into_steelval()
452
}
453

454
#[steel_derive::function(name = "acos", constant = true)]
455
pub fn acos(arg: &SteelVal) -> Result<SteelVal> {
2✔
456
    match arg {
2✔
457
        SteelVal::IntV(i) => (*i as f64).acos(),
2✔
458
        SteelVal::BigNum(i) => i.to_f64().unwrap().acos(),
×
459
        SteelVal::NumV(n) => n.acos(),
×
460
        SteelVal::Rational(r) => (*r.numer() as f32 / *r.denom() as f32).acos() as f64,
×
461
        _ => stop!(TypeMismatch => "acos expects a number, found: {}", arg),
×
462
    }
463
    .into_steelval()
464
}
465

466
#[steel_derive::function(name = "atan", constant = true)]
467
pub fn atan(arg: &SteelVal) -> Result<SteelVal> {
×
468
    match arg {
×
469
        SteelVal::IntV(i) => (*i as f64).atan(),
×
470
        SteelVal::BigNum(i) => i.to_f64().unwrap().atan(),
×
471
        SteelVal::NumV(n) => n.atan(),
×
472
        SteelVal::Rational(r) => (*r.numer() as f32 / *r.denom() as f32).atan() as f64,
×
473
        _ => stop!(TypeMismatch => "atan expects a number, found: {}", arg),
×
474
    }
475
    .into_steelval()
476
}
477

478
/// Divides the given numbers.
479
///
480
/// (/ . nums) -> number?
481
///
482
/// * nums : number? - The numbers to divide. Must have at least one number.
483
///
484
/// # Examples
485
/// ```scheme
486
/// > (/ 10 2) ;; => 5
487
/// > (/ 10 2 2.0) ;; => 2.5
488
/// > (/ 1 3.0) ;; => 0.3333333333333333
489
/// > (/ 1 3) ;; => 1/3
490
/// ```
491
#[steel_derive::native(name = "/", constant = true, arity = "AtLeast(1)")]
492
pub fn divide_primitive(args: &[SteelVal]) -> Result<SteelVal> {
100,034✔
493
    ensure_args_are_numbers("/", args)?;
100,034✔
494
    let recip = |x: &SteelVal| -> Result<SteelVal> {
200,068✔
495
        match x {
100,034✔
496
            SteelVal::IntV(n) => match i32::try_from(*n) {
14✔
497
                Ok(0) => {
498
                    stop!(Generic => "/: division by zero")
1✔
499
                }
500
                Ok(n) => Rational32::new(1, n).into_steelval(),
13✔
501
                Err(_) => BigRational::new(BigInt::from(1), BigInt::from(*n)).into_steelval(),
×
502
            },
503
            SteelVal::NumV(n) => n.recip().into_steelval(),
100,013✔
504
            SteelVal::Rational(r) => r.recip().into_steelval(),
2✔
505
            SteelVal::BigRational(r) => r.recip().into_steelval(),
×
506
            SteelVal::BigNum(n) => BigRational::new(1.into(), n.as_ref().clone()).into_steelval(),
4✔
507
            SteelVal::Complex(c) => complex_reciprocal(c),
1✔
508
            unexpected => {
×
509
                steelerr!(TypeMismatch => "/ expects a number, but found: {:?}", unexpected)
×
510
            }
511
        }
512
    };
513
    match &args {
514
        [] => steelerr!(ArityMismatch => "/ requires at least one argument"),
×
515
        [x] => recip(x),
30,011✔
516
        // TODO: Provide custom implementation to optimize by joining the multiply and recip calls.
517
        [x, y] => multiply_two(x, &recip(y)?),
70,023✔
518
        [x, ys @ ..] => {
1✔
519
            let d = multiply_primitive_impl(ys)?;
2✔
520
            multiply_two(&x, &recip(&d)?)
×
521
        }
522
    }
523
}
524

525
/// Checks if the given value is exact.
526
///
527
/// (exact? val) -> boolean?
528
///
529
/// * val : any - The value to check for exactness.
530
///
531
/// # Examples
532
/// ```scheme
533
/// > (exact? 42) ;; => #t
534
/// > (exact? 3.14) ;; => #f
535
/// > (exact? "hello") ;; => #f
536
/// ```
537
#[steel_derive::function(name = "exact?", constant = true)]
538
pub fn exactp(value: &SteelVal) -> bool {
10✔
539
    match value {
10✔
540
        SteelVal::IntV(_)
541
        | SteelVal::BigNum(_)
542
        | SteelVal::Rational(_)
543
        | SteelVal::BigRational(_) => true,
6✔
544
        SteelVal::Complex(x) => exactp(&x.re) && exactp(&x.im),
3✔
545
        _ => false,
2✔
546
    }
547
}
548

549
#[steel_derive::function(name = "exact", constant = true)]
550
pub fn exact(value: &SteelVal) -> Result<SteelVal> {
2✔
551
    match value {
2✔
552
        SteelVal::IntV(_)
553
        | SteelVal::BigNum(_)
554
        | SteelVal::Rational(_)
555
        | SteelVal::BigRational(_) => Ok(value.clone()),
×
556
        SteelVal::NumV(n) if n.fract() == 0.0 => Ok(SteelVal::IntV(*n as isize)),
2✔
557
        _ => stop!(Generic => "unable to convert to exact number: {}", value),
×
558
    }
559
}
560

561
/// Checks if the given value is inexact.
562
///
563
/// (inexact? val) -> boolean?
564
///
565
/// * val : any - The value to check for inexactness.
566
///
567
/// # Examples
568
/// ```scheme
569
/// > (inexact? 42) ;; => #f
570
/// > (inexact? 3.14) ;; => #t
571
/// ```
572
#[steel_derive::function(name = "inexact?", constant = true)]
573
pub fn inexactp(value: &SteelVal) -> bool {
12✔
574
    match value {
12✔
575
        SteelVal::NumV(_) => true,
4✔
576
        SteelVal::Complex(x) => inexactp(&x.re) || inexactp(&x.im),
3✔
577
        _ => false,
6✔
578
    }
579
}
580

581
fn number_to_float(number: &SteelVal) -> Result<f64> {
×
582
    let res = match number {
×
583
        SteelVal::IntV(i) => *i as f64,
×
584
        SteelVal::Rational(f) => f.to_f64().unwrap(),
×
585
        SteelVal::BigRational(f) => f.to_f64().unwrap(),
×
586
        SteelVal::NumV(n) => *n,
×
587
        SteelVal::BigNum(n) => n.to_f64().unwrap(),
×
588
        _ => stop!(TypeMismatch => "number->float expects a real number, found: {}", number),
×
589
    };
590
    Ok(res)
591
}
592

593
/// Converts an exact number to an inexact number.
594
///
595
/// (exact->inexact num) -> number?
596
///
597
/// * num : number? - The number to convert from exact to inexact.
598
///
599
/// # Examples
600
/// ```scheme
601
/// > (exact->inexact 10) ;; => 10
602
/// > (exact->inexact 1/2) ;; => 0.5
603
/// > (exact->inexact 1+2i) ;; => 1+2i
604
/// ```
605
#[steel_derive::function(name = "exact->inexact", constant = true)]
606
fn exact_to_inexact(number: &SteelVal) -> Result<SteelVal> {
2✔
607
    match number {
2✔
608
        SteelVal::IntV(i) => (*i as f64).into_steelval(),
×
609
        SteelVal::Rational(f) => f.to_f64().unwrap().into_steelval(),
1✔
610
        SteelVal::BigRational(f) => f.to_f64().unwrap().into_steelval(),
×
611
        SteelVal::NumV(n) => n.into_steelval(),
1✔
612
        SteelVal::BigNum(n) => Ok(SteelVal::NumV(n.to_f64().unwrap())),
×
613
        SteelVal::Complex(x) => {
×
614
            SteelComplex::new(exact_to_inexact(&x.re)?, exact_to_inexact(&x.im)?).into_steelval()
×
615
        }
616
        _ => steelerr!(TypeMismatch => "exact->inexact expects a number type, found: {}", number),
×
617
    }
618
}
619

620
/// Converts an inexact number to an exact number.
621
///
622
/// (inexact->exact num) -> number?
623
///
624
/// * num : number? - The number to convert from inexact to exact.
625
///
626
/// # Examples
627
/// ```scheme
628
/// > (inexact->exact 10.0) ;; => 10
629
/// > (inexact->exact 1.5) ;; => 3/2
630
/// > (inexact->exact 1.5+2.5i) ;; => 3/2+5/2i
631
/// ```
632
#[steel_derive::function(name = "inexact->exact", constant = true)]
633
fn inexact_to_exact(number: &SteelVal) -> Result<SteelVal> {
3✔
634
    match number {
3✔
635
        SteelVal::IntV(x) => x.into_steelval(),
1✔
636
        SteelVal::Rational(x) => x.into_steelval(),
1✔
637
        SteelVal::BigRational(x) => SteelVal::BigRational(x.clone()).into_steelval(),
×
638
        SteelVal::NumV(x) => {
1✔
639
            let x_isize = *x as isize;
1✔
640
            if x_isize as f64 == *x {
1✔
641
                return x_isize.into_steelval();
×
642
            }
643
            BigRational::from_float(*x).into_steelval()
1✔
644
        }
645
        SteelVal::BigNum(x) => SteelVal::BigNum(x.clone()).into_steelval(),
×
646
        SteelVal::Complex(x) => {
×
647
            SteelComplex::new(inexact_to_exact(&x.re)?, inexact_to_exact(&x.im)?).into_steelval()
×
648
        }
649
        _ => steelerr!(TypeMismatch => "exact->inexact expects a number type, found: {}", number),
×
650
    }
651
}
652

653
fn finitep_impl(number: &SteelVal) -> Result<bool> {
7✔
654
    match number {
6✔
655
        SteelVal::NumV(x) if x.is_nan() || x.is_infinite() => Ok(false),
14✔
656
        SteelVal::IntV(_)
657
        | SteelVal::NumV(_)
658
        | SteelVal::BigNum(_)
659
        | SteelVal::Rational(_)
660
        | SteelVal::BigRational(_) => Ok(true),
2✔
661
        SteelVal::Complex(x) => Ok(finitep_impl(&x.re)? && finitep_impl(&x.im)?),
×
662
        _ => steelerr!(TypeMismatch => "finite? expects a number, found: {}", number),
×
663
    }
664
}
665

666
/// Returns `#t` if the given number is finite.
667
///
668
/// (finite? number) -> boolean?
669
///
670
/// * number : number? - The number to check for finiteness.
671
///
672
/// # Examples
673
/// ```scheme
674
/// > (finite? 42) ;; => #t
675
/// > (finite? 0.1) ;; => #t
676
/// > (finite? +inf.0) ;; => #f
677
/// > (finite? -inf.0) ;; => #f
678
/// > (finite? +nan.0) ;; => #f
679
/// ```
680
#[steel_derive::function(name = "finite?", constant = true)]
681
fn finitep(number: &SteelVal) -> Result<SteelVal> {
7✔
682
    finitep_impl(number).into_steelval()
7✔
683
}
684

685
fn infinitep_impl(number: &SteelVal) -> Result<bool> {
5✔
686
    match number {
4✔
687
        SteelVal::NumV(x) if x.is_infinite() => Ok(true),
6✔
688
        SteelVal::IntV(_)
689
        | SteelVal::NumV(_)
690
        | SteelVal::BigNum(_)
691
        | SteelVal::Rational(_)
692
        | SteelVal::BigRational(_) => Ok(false),
2✔
693
        SteelVal::Complex(n) => Ok(infinitep_impl(&n.re)? || infinitep_impl(&n.im)?),
×
694
        _ => steelerr!(TypeMismatch => "exact->inexact expects a real number, found: {}", number),
×
695
    }
696
}
697

698
/// Returns `#t` if the given number is infinite.
699
///
700
/// (infinite? number) -> boolean?
701
///
702
/// * number : number? - The number to check for infiniteness.
703
///
704
/// # Examples
705
/// ```scheme
706
/// > (infinite? 42) ;; => #f
707
/// > (infinite? -nan.0) ;; => #f
708
/// > (infinite? +inf.0) ;; => #t
709
/// ```
710
#[steel_derive::function(name = "infinite?", constant = true)]
711
fn infinitep(number: &SteelVal) -> Result<SteelVal> {
5✔
712
    infinitep_impl(number)?.into_steelval()
10✔
713
}
714

715
/// Computes the absolute value of the given number.
716
///
717
/// (abs number) -> number?
718
///
719
/// * number : number? - The number to compute the absolute value of.
720
///
721
/// # Examples
722
/// ```scheme
723
/// > (abs 42) ;; => 42
724
/// > (abs -42) ;; => 42
725
/// > (abs 0) ;; => 0
726
/// ```
727
#[steel_derive::function(name = "abs", constant = true)]
728
fn abs(number: &SteelVal) -> Result<SteelVal> {
17✔
729
    match number {
17✔
730
        SteelVal::IntV(i) => Ok(SteelVal::IntV(i.abs())),
15✔
731
        SteelVal::NumV(n) => Ok(SteelVal::NumV(n.abs())),
1✔
732
        SteelVal::Rational(f) => f.abs().into_steelval(),
1✔
733
        SteelVal::BigRational(f) => f.abs().into_steelval(),
×
734
        SteelVal::BigNum(n) => n.as_ref().abs().into_steelval(),
×
735
        _ => steelerr!(TypeMismatch => "abs expects a real number, found: {}", number),
×
736
    }
737
}
738

739
/// Rounds the given number up to the nearest integer not less than it.
740
///
741
/// (ceiling number) -> integer?
742
///
743
/// * number : number? - The number to round up.
744
///
745
/// # Examples
746
/// ```scheme
747
/// > (ceiling 42) ;; => 42
748
/// > (ceiling 42.1) ;; => 43
749
/// > (ceiling -42.1) ;; => -42
750
/// ```
751
#[steel_derive::function(name = "ceiling", constant = true)]
752
fn ceiling(number: &SteelVal) -> Result<SteelVal> {
6✔
753
    match number {
6✔
754
        n @ SteelVal::IntV(_) | n @ SteelVal::BigNum(_) => Ok(n.clone()),
2✔
755
        SteelVal::NumV(n) => Ok(SteelVal::NumV(n.ceil())),
3✔
756
        SteelVal::Rational(f) => f.ceil().into_steelval(),
2✔
757
        SteelVal::BigRational(f) => f.ceil().into_steelval(),
×
758
        _ => steelerr!(TypeMismatch => "ceiling expects a real number, found: {}", number),
×
759
    }
760
}
761

762
/// Retrieves the denominator of the given rational number.
763
///
764
/// (denominator number) -> integer?
765
///
766
/// * number : number? - The rational number to retrieve the denominator from.
767
///
768
/// # Examples
769
/// ```scheme
770
/// > (denominator 1/2) ;; => 2
771
/// > (denominator 3/4) ;; => 4
772
/// > (denominator 4) ;; => 1
773
/// ```
774
#[steel_derive::function(name = "denominator", constant = true)]
775
fn denominator(number: &SteelVal) -> Result<SteelVal> {
2✔
776
    match number {
2✔
777
        SteelVal::IntV(_) | SteelVal::BigNum(_) => 1.into_steelval(),
1✔
778
        SteelVal::NumV(_) => {
779
            steelerr!(TypeMismatch => "denominator not supported for number {}", number)
×
780
        }
781
        SteelVal::Rational(f) => f.denom().into_steelval(),
1✔
782
        SteelVal::BigRational(f) => f.denom().clone().into_steelval(),
×
783
        _ => steelerr!(TypeMismatch => "ceiling expects a real number, found: {}", number),
×
784
    }
785
}
786

787
// TODO: Add support for BigNum.
788
/// Raises the left operand to the power of the right operand.
789
///
790
/// (expt base exponent) -> number?
791
///
792
/// * base : number? - The base number.
793
/// * exponent : number? - The exponent to raise the base to.
794
///
795
/// # Examples
796
/// ```scheme
797
/// > (expt 2 3) ;; => 8
798
/// > (expt 2.0 0.5) ;; => 1.4142135623730951
799
/// > (expt 9 0.5) ;; => 3
800
/// ```
801
#[steel_derive::function(name = "expt", constant = true)]
802
fn expt(left: &SteelVal, right: &SteelVal) -> Result<SteelVal> {
22✔
803
    match (left, right) {
22✔
804
        (SteelVal::IntV(l), SteelVal::IntV(r)) => match u32::try_from(*r) {
17✔
805
            Ok(r) => l.pow(r).into_steelval(),
17✔
806
            Err(_) => (*l as f64).powf(*r as f64).into_steelval(),
×
807
        },
808
        (SteelVal::IntV(l), SteelVal::NumV(r)) => (*l as f64).powf(*r).into_steelval(),
1✔
809
        (SteelVal::IntV(l), SteelVal::Rational(r)) => {
1✔
810
            (*l as f64).powf(r.to_f64().unwrap()).into_steelval()
1✔
811
        }
812
        (SteelVal::IntV(l), SteelVal::BigNum(r)) => {
×
813
            (*l as f64).powf(r.to_f64().unwrap()).into_steelval()
×
814
        }
815
        (SteelVal::IntV(l), SteelVal::BigRational(r)) => {
×
816
            (*l as f64).powf(r.to_f64().unwrap()).into_steelval()
×
817
        }
818
        (SteelVal::NumV(l), SteelVal::NumV(r)) => Ok(SteelVal::NumV(l.powf(*r))),
1✔
819
        (SteelVal::NumV(l), SteelVal::IntV(r)) => match i32::try_from(*r) {
×
820
            Ok(r) => l.powi(r).into_steelval(),
×
821
            Err(_) => l.powf(*r as f64).into_steelval(),
×
822
        },
823
        (SteelVal::NumV(l), SteelVal::Rational(r)) => l.powf(r.to_f64().unwrap()).into_steelval(),
×
824
        (SteelVal::NumV(l), SteelVal::BigNum(r)) => l.powf(r.to_f64().unwrap()).into_steelval(),
×
825
        (SteelVal::NumV(l), SteelVal::BigRational(r)) => {
×
826
            l.powf(r.to_f64().unwrap()).into_steelval()
×
827
        }
828
        (SteelVal::Rational(l), SteelVal::Rational(r)) => l
×
829
            .to_f64()
830
            .unwrap()
831
            .powf(r.to_f64().unwrap())
×
832
            .into_steelval(),
833
        (SteelVal::Rational(l), SteelVal::NumV(r)) => l.to_f64().unwrap().powf(*r).into_steelval(),
1✔
834
        (SteelVal::Rational(l), SteelVal::IntV(r)) => match i32::try_from(*r) {
1✔
835
            Ok(r) => l.pow(r).into_steelval(),
1✔
836
            Err(_) => {
837
                let base = BigRational::new(BigInt::from(*l.numer()), BigInt::from(*l.denom()));
×
838
                let exp = BigInt::from(*r);
×
839
                base.pow(exp).into_steelval()
×
840
            }
841
        },
842
        (SteelVal::Rational(l), SteelVal::BigNum(r)) => l
×
843
            .to_f64()
844
            .unwrap()
845
            .powf(r.to_f64().unwrap())
×
846
            .into_steelval(),
847
        (SteelVal::Rational(l), SteelVal::BigRational(r)) => l
×
848
            .to_f64()
849
            .unwrap()
850
            .powf(r.to_f64().unwrap())
×
851
            .into_steelval(),
852
        (SteelVal::BigNum(l), SteelVal::BigNum(r)) => match r.as_ref().sign() {
×
853
            num::bigint::Sign::NoSign | num::bigint::Sign::Plus => l
×
854
                .as_ref()
855
                .clone()
856
                .pow(r.as_ref().magnitude())
×
857
                .into_steelval(),
858
            num::bigint::Sign::Minus => Ok(SteelVal::NumV(
×
859
                l.to_f64().unwrap().powf(r.to_f64().unwrap()),
×
860
            )),
861
        },
862
        (SteelVal::BigNum(l), SteelVal::IntV(r)) => match *r {
×
863
            0 => 1.into_steelval(),
×
864
            r if r < 0 => Ok(SteelVal::NumV(
×
865
                l.to_f64().unwrap().powf(r.to_f64().unwrap()),
×
866
            )),
867
            r => l.as_ref().clone().pow(r as usize).into_steelval(),
×
868
        },
869
        (SteelVal::BigNum(l), SteelVal::NumV(r)) => l.to_f64().unwrap().powf(*r).into_steelval(),
×
870
        (SteelVal::BigNum(l), SteelVal::Rational(r)) => l
×
871
            .to_f64()
872
            .unwrap()
873
            .powf(r.to_f64().unwrap())
×
874
            .into_steelval(),
875
        (SteelVal::BigNum(l), SteelVal::BigRational(r)) => l
×
876
            .to_f64()
877
            .unwrap()
878
            .powf(r.to_f64().unwrap())
×
879
            .into_steelval(),
880
        (SteelVal::BigRational(l), SteelVal::Rational(r)) => l
×
881
            .to_f64()
882
            .unwrap()
883
            .powf(r.to_f64().unwrap())
×
884
            .into_steelval(),
885
        (SteelVal::BigRational(l), SteelVal::NumV(r)) => {
×
886
            l.to_f64().unwrap().powf(*r).into_steelval()
×
887
        }
888
        (SteelVal::BigRational(l), SteelVal::IntV(r)) => match i32::try_from(*r) {
×
889
            Ok(r) => l.as_ref().pow(r).into_steelval(),
×
890
            Err(_) => {
891
                let exp = BigInt::from(*r);
×
892
                l.as_ref().clone().pow(exp).into_steelval()
×
893
            }
894
        },
895
        (SteelVal::BigRational(l), SteelVal::BigNum(r)) => l
×
896
            .to_f64()
897
            .unwrap()
898
            .powf(r.to_f64().unwrap())
×
899
            .into_steelval(),
900
        (SteelVal::BigRational(l), SteelVal::BigRational(r)) => l
×
901
            .to_f64()
902
            .unwrap()
903
            .powf(r.to_f64().unwrap())
×
904
            .into_steelval(),
905
        (l, r) => {
×
906
            steelerr!(TypeMismatch => "expt expected two numbers but found {} and {}", l, r)
×
907
        }
908
    }
909
}
910

911
/// Returns Euler’s number raised to the power of z.
912
///
913
/// (exp z) -> number?
914
///
915
/// * z : number? - The number to raise e to the power of.
916
///
917
/// # Examples
918
/// ```scheme
919
/// > (exp 0) ;; => 1
920
/// > (exp 2) ;; => 7.38905609893065
921
/// > (exp 1.5) ;; => 4.4816890703380645
922
/// ```
923
#[steel_derive::function(name = "exp", constant = true)]
924
fn exp(left: &SteelVal) -> Result<SteelVal> {
2✔
925
    match left {
1✔
926
        SteelVal::IntV(0) => Ok(SteelVal::IntV(1)),
1✔
927
        SteelVal::IntV(l) if *l < i32::MAX as isize => {
2✔
928
            Ok(SteelVal::NumV(std::f64::consts::E.powi(*l as i32)))
1✔
929
        }
930
        maybe_number => match number_to_float(maybe_number) {
×
931
            Ok(n) => Ok(SteelVal::NumV(std::f64::consts::E.powf(n))),
×
932
            Err(_) => steelerr!(Generic => "exp expected a real number"),
×
933
        },
934
    }
935
}
936

937
/// Computes the largest integer less than or equal to the given number.
938
///
939
/// (floor number) -> number?
940
///
941
/// * number : number? - The number to compute the floor for.
942
///
943
/// # Examples
944
/// ```scheme
945
/// > (floor 3.14) ;; => 3
946
/// > (floor 4.99) ;; => 4
947
/// > (floor -2.5) ;; => -3
948
/// ```
949
#[steel_derive::function(name = "floor", constant = true)]
950
fn floor(number: &SteelVal) -> Result<SteelVal> {
8✔
951
    match number {
8✔
952
        SteelVal::NumV(x) => Ok(SteelVal::NumV(x.floor())),
3✔
953
        SteelVal::IntV(x) => x.into_steelval(),
3✔
954
        SteelVal::Rational(x) => x.floor().into_steelval(),
2✔
955
        SteelVal::BigNum(x) => Ok(SteelVal::BigNum(x.clone())),
×
956
        SteelVal::BigRational(x) => x.floor().into_steelval(),
×
957
        _ => steelerr!(Generic => "floor expected a real number"),
×
958
    }
959
}
960

961
/// Retrieves the numerator of the given rational number.
962
///
963
/// (numerator number) -> number?
964
///
965
/// * number : number? - The rational number to retrieve the numerator from.
966
///
967
/// # Examples
968
/// ```scheme
969
/// > (numerator 3/4) ;; => 3
970
/// > (numerator 5/2) ;; => 5
971
/// > (numerator -2) ;; => -2
972
/// ```
973
#[steel_derive::function(name = "numerator", constant = true)]
974
fn numerator(number: &SteelVal) -> Result<SteelVal> {
2✔
975
    match number {
2✔
976
        SteelVal::IntV(x) => x.into_steelval(),
1✔
977
        SteelVal::Rational(x) => (*x.numer() as isize).into_steelval(),
1✔
978
        SteelVal::BigNum(x) => Ok(SteelVal::BigNum(x.clone())),
×
979
        SteelVal::BigRational(x) => (x.numer().clone()).into_steelval(),
×
980
        _ => steelerr!(Generic => "numerator expects an integer or rational number"),
×
981
    }
982
}
983

984
/// Rounds the given number to the nearest integer.
985
///
986
/// (round number) -> number?
987
///
988
/// * number : number? - The number to round.
989
///
990
/// # Examples
991
/// ```scheme
992
/// > (round 3.14) ;; => 3
993
/// > (round 4.6) ;; => 5
994
/// > (round -2.5) ;; => -3
995
/// ```
996
#[steel_derive::function(name = "round", constant = true)]
997
fn round(number: &SteelVal) -> Result<SteelVal> {
8✔
998
    match number {
8✔
999
        SteelVal::IntV(i) => i.into_steelval(),
1✔
1000
        SteelVal::NumV(n) => n.round().into_steelval(),
4✔
1001
        SteelVal::Rational(f) => f.round().into_steelval(),
2✔
1002
        SteelVal::BigRational(f) => f.round().into_steelval(),
×
1003
        SteelVal::BigNum(n) => Ok(SteelVal::BigNum(n.clone())),
1✔
1004
        _ => steelerr!(TypeMismatch => "round expects a real number, found: {}", number),
×
1005
    }
1006
}
1007

1008
/// Computes the square of the given number.
1009
///
1010
/// (square number) -> number?
1011
///
1012
/// * number : number? - The number to square.
1013
///
1014
/// # Examples
1015
/// ```scheme
1016
/// > (square 5) ;; => 25
1017
/// > (square -3) ;; => 9
1018
/// > (square 2.5) ;; => 6.25
1019
/// ```
1020
#[steel_derive::function(name = "square", constant = true)]
1021
fn square(number: &SteelVal) -> Result<SteelVal> {
16✔
1022
    if !numberp(number) {
16✔
1023
        stop!(TypeMismatch => "square expects a number, found: {:?}", number)
×
1024
    }
1025
    multiply_two(&number, &number)
16✔
1026
}
1027

1028
/// Computes the square root of the given number.
1029
///
1030
/// (sqrt number) -> number?
1031
///
1032
/// * number : number? - The number to compute the square root for.
1033
///
1034
/// # Examples
1035
/// ```scheme
1036
/// > (sqrt 4) ;; => 2
1037
/// > (sqrt 2) ;; => 1.4142135623730951
1038
/// > (sqrt -1) ;; => 0+1i
1039
/// ```
1040
#[steel_derive::function(name = "sqrt", constant = true)]
1041
fn sqrt(number: &SteelVal) -> Result<SteelVal> {
21✔
1042
    match number {
21✔
1043
        SteelVal::NumV(x) => {
1✔
1044
            if x.is_negative() {
1✔
1045
                let imag = x.neg().sqrt();
×
1046
                SteelComplex::new(0.0.into_steelval()?, imag.into_steelval()?).into_steelval()
×
1047
            } else {
1048
                x.sqrt().into_steelval()
1✔
1049
            }
1050
        }
1051
        SteelVal::IntV(x) => {
15✔
1052
            if x.is_negative() {
15✔
1053
                let sqrt = (*x as f64).abs().sqrt();
×
1054
                if sqrt as isize as f64 == sqrt {
×
1055
                    SteelComplex::new(0.into_steelval()?, (sqrt as isize).into_steelval()?)
×
1056
                        .into_steelval()
1057
                } else {
1058
                    SteelComplex::new(0.into_steelval()?, sqrt.into_steelval()?).into_steelval()
×
1059
                }
1060
            } else {
1061
                let sqrt = (*x as f64).sqrt();
15✔
1062
                if sqrt as isize as f64 == sqrt {
15✔
1063
                    (sqrt as isize).into_steelval()
15✔
1064
                } else {
1065
                    sqrt.into_steelval()
×
1066
                }
1067
            }
1068
        }
1069
        SteelVal::Rational(x) => {
1✔
1070
            let n = x.numer().abs();
1✔
1071
            let d = *x.denom();
1✔
1072
            let n_sqrt = (n as f64).sqrt();
1✔
1073
            let d_sqrt = (d as f64).sqrt();
1✔
1074
            let sqrt = if n_sqrt as i32 as f64 == n_sqrt && d_sqrt as i32 as f64 == d_sqrt {
3✔
1075
                Rational32::new(n_sqrt as i32, d_sqrt as i32).into_steelval()?
1✔
1076
            } else {
1077
                (n_sqrt / d_sqrt).into_steelval()?
×
1078
            };
1079
            if x.is_negative() {
1080
                let re = if exactp(&sqrt) {
×
1081
                    0.into_steelval()?
×
1082
                } else {
1083
                    0.0.into_steelval()?
×
1084
                };
1085
                SteelComplex::new(re, sqrt).into_steelval()
1086
            } else {
1087
                Ok(sqrt)
1✔
1088
            }
1089
        }
1090
        SteelVal::BigNum(n) => {
×
1091
            let sqrt = n.as_ref().to_f64().unwrap().sqrt();
×
1092
            if n.as_ref().is_negative() {
×
1093
                SteelComplex::new(0.0.into_steelval()?, sqrt.into_steelval()?).into_steelval()
×
1094
            } else {
1095
                sqrt.into_steelval()
×
1096
            }
1097
        }
1098
        SteelVal::BigRational(n) => {
×
1099
            let sqrt = n.as_ref().to_f64().unwrap().sqrt();
×
1100
            if n.as_ref().is_negative() {
×
1101
                SteelComplex::new(0.0.into_steelval()?, sqrt.into_steelval()?).into_steelval()
×
1102
            } else {
1103
                sqrt.into_steelval()
×
1104
            }
1105
        }
1106
        SteelVal::Complex(n) => {
4✔
1107
            let z_mag = magnitude(number)?;
8✔
1108
            let half = Rational32::new(1, 2).into_steelval()?;
4✔
1109
            let re = sqrt(&multiply_two(&add_two(&z_mag, &n.re)?, &half)?)?;
8✔
1110
            let im = sqrt(&multiply_two(&add_two(&z_mag, &negate(&n.re)?)?, &half)?)?;
12✔
1111
            if negativep(&n.im)? == SteelVal::BoolV(true) {
8✔
1112
                SteelComplex::new(re, negate(&im)?).into_steelval()
4✔
1113
            } else {
1114
                SteelComplex::new(re, im).into_steelval()
2✔
1115
            }
1116
        }
1117
        _ => steelerr!(TypeMismatch => "sqrt expected a number"),
×
1118
    }
1119
}
1120

1121
/// Computes the magnitude of the given number.
1122
///
1123
/// (magnitude number) -> number?
1124
///
1125
/// * number : number? - The number to compute the magnitude for.
1126
///
1127
/// # Examples
1128
/// ```scheme
1129
/// > (magnitude 3+4i) ;; => 5
1130
/// > (magnitude 5) ;; => 5
1131
/// > (magnitude -5) ;; => 5
1132
/// ```
1133
#[steel_derive::function(name = "magnitude", constant = true)]
1134
fn magnitude(number: &SteelVal) -> Result<SteelVal> {
8✔
1135
    match number {
8✔
1136
        SteelVal::NumV(x) => x.abs().into_steelval(),
1✔
1137
        SteelVal::IntV(x) => x.abs().into_steelval(),
1✔
1138
        SteelVal::Rational(x) => x.abs().into_steelval(),
1✔
1139
        SteelVal::BigNum(x) => x.as_ref().abs().into_steelval(),
×
1140
        SteelVal::BigRational(x) => x.as_ref().abs().into_steelval(),
×
1141
        SteelVal::Complex(x) => {
5✔
1142
            let c_squared = add_two(&square(&x.re)?, &square(&x.im)?)?;
15✔
1143
            sqrt(&c_squared)
5✔
1144
        }
1145
        _ => steelerr!(TypeMismatch => "magnitude expects a number, found {number}"),
×
1146
    }
1147
}
1148

1149
/// Computes the natural logarithm of the given number.
1150
///
1151
/// (log number [base]) -> number?
1152
///
1153
/// * number : number? - The number to compute the logarithm for.
1154
/// * base : number? - The base of the logarithm. If not provided, defaults to Euler's number (e).
1155
///
1156
/// # Examples
1157
/// ```scheme
1158
/// > (log 10) ;; => 2.302585092994046
1159
/// > (log 100 10) ;; => 2
1160
/// > (log 27 3) ;; => 3
1161
/// ```
1162
#[steel_derive::native(name = "log", arity = "AtLeast(1)")]
1163
fn log(args: &[SteelVal]) -> Result<SteelVal> {
6✔
1164
    if args.len() > 2 {
6✔
1165
        stop!(ArityMismatch => "log expects one or two arguments, found: {}", args.len());
×
1166
    }
1167

1168
    let first = &args[0];
6✔
1169
    let base = args
6✔
1170
        .get(1)
1171
        .cloned()
1172
        .unwrap_or(SteelVal::NumV(std::f64::consts::E));
6✔
1173

1174
    match (first, &base) {
6✔
1175
        (SteelVal::IntV(1), _) => Ok(SteelVal::IntV(0)),
1✔
1176
        (SteelVal::IntV(_) | SteelVal::NumV(_), SteelVal::IntV(1)) => {
1177
            steelerr!(Generic => "log: divide by zero with args: {} and {}", first, base)
×
1178
        }
1179
        (SteelVal::IntV(arg), SteelVal::NumV(n)) => Ok(SteelVal::NumV((*arg as f64).log(*n))),
1✔
1180
        (SteelVal::IntV(arg), SteelVal::IntV(base)) => Ok(SteelVal::IntV(arg.ilog(*base) as isize)),
1✔
1181
        (SteelVal::NumV(arg), SteelVal::NumV(n)) => Ok(SteelVal::NumV(arg.log(*n))),
2✔
1182
        (SteelVal::NumV(arg), SteelVal::IntV(base)) => Ok(SteelVal::NumV(arg.log(*base as f64))),
1✔
1183
        // TODO: Support BigNum, Rational, and BigRational.
1184
        _ => {
1185
            steelerr!(TypeMismatch => "log expects one or two numbers, found: {} and {}", first, base)
×
1186
        }
1187
    }
1188
}
1189

1190
/// Computes the integer square root of the given non-negative integer.
1191
///
1192
/// (exact-integer-sqrt number) -> (integer? integer?)
1193
///
1194
/// * number : (and/c integer? positive?) - The non-negative integer to compute the square root for.
1195
///
1196
/// # Examples
1197
/// ```scheme
1198
/// > (exact-integer-sqrt 25) ;; => (5 0)
1199
/// > (exact-integer-sqrt 35) ;; => (5 10)
1200
/// ```
1201
#[steel_derive::function(name = "exact-integer-sqrt", constant = true)]
1202
fn exact_integer_sqrt(number: &SteelVal) -> Result<SteelVal> {
22✔
1203
    match number {
16✔
1204
        SteelVal::IntV(x) if *x >= 0 => {
31✔
1205
            let (ans, rem) = exact_integer_impl(x);
15✔
1206
            (ans.into_steelval()?, rem.into_steelval()?).into_steelval()
45✔
1207
        }
1208
        SteelVal::BigNum(x) if !x.is_negative() => {
3✔
1209
            let (ans, rem) = exact_integer_impl(x.as_ref());
1✔
1210
            (ans.into_steelval()?, rem.into_steelval()?).into_steelval()
3✔
1211
        }
1212
        _ => {
1213
            steelerr!(TypeMismatch => "exact-integer-sqrt expects a non-negative integer but found {number}")
6✔
1214
        }
1215
    }
1216
}
1217

1218
fn exact_integer_impl<'a, N>(target: &'a N) -> (N, N)
16✔
1219
where
1220
    N: num::integer::Roots + Clone,
1221
    &'a N: std::ops::Mul<&'a N, Output = N>,
1222
    N: std::ops::Sub<N, Output = N>,
1223
{
1224
    let x = target.sqrt();
16✔
1225
    let x_sq = x.clone() * x.clone();
16✔
1226
    let rem = target.clone() - x_sq;
16✔
1227
    (x, rem)
16✔
1228
}
1229

1230
fn ensure_args_are_numbers(op: &str, args: &[SteelVal]) -> Result<()> {
13,022,601✔
1231
    for arg in args {
62,320,300✔
1232
        if !numberp(arg) {
1233
            stop!(TypeMismatch => "{op} expects a number, found: {:?}", arg)
3✔
1234
        }
1235
    }
1236
    Ok(())
13,022,598✔
1237
}
1238

1239
/// Multiplies `x` and `y` without any type checking.
1240
///
1241
/// # Precondition
1242
/// - `x` and `y` must be valid numerical types.
1243
fn multiply_two(x: &SteelVal, y: &SteelVal) -> Result<SteelVal> {
915,542✔
1244
    match (x, y) {
915,542✔
1245
        (SteelVal::NumV(x), SteelVal::NumV(y)) => (x * y).into_steelval(),
890,093✔
1246
        (SteelVal::NumV(x), SteelVal::IntV(y)) | (SteelVal::IntV(y), SteelVal::NumV(x)) => {
5✔
1247
            (x * *y as f64).into_steelval()
5✔
1248
        }
1249
        (SteelVal::NumV(x), SteelVal::BigNum(y)) | (SteelVal::BigNum(y), SteelVal::NumV(x)) => {
×
1250
            (x * y.to_f64().unwrap()).into_steelval()
×
1251
        }
1252
        (SteelVal::NumV(x), SteelVal::Rational(y)) | (SteelVal::Rational(y), SteelVal::NumV(x)) => {
2✔
1253
            (x * y.to_f64().unwrap()).into_steelval()
2✔
1254
        }
1255
        (SteelVal::NumV(x), SteelVal::BigRational(y))
×
1256
        | (SteelVal::BigRational(y), SteelVal::NumV(x)) => {
×
1257
            (x * y.to_f64().unwrap()).into_steelval()
×
1258
        }
1259
        (SteelVal::IntV(x), SteelVal::IntV(y)) => match x.checked_mul(y) {
25,415✔
1260
            Some(res) => res.into_steelval(),
25,414✔
1261
            None => {
1262
                let mut res = BigInt::from(*x);
1✔
1263
                res *= *y;
1✔
1264
                res.into_steelval()
1✔
1265
            }
1266
        },
1267
        (SteelVal::IntV(x), SteelVal::BigNum(y)) | (SteelVal::BigNum(y), SteelVal::IntV(x)) => {
1✔
1268
            (y.as_ref() * x).into_steelval()
1✔
1269
        }
1270
        (SteelVal::IntV(x), SteelVal::Rational(y)) | (SteelVal::Rational(y), SteelVal::IntV(x)) => {
18✔
1271
            match i32::try_from(*x) {
18✔
1272
                Ok(x) => match y.checked_mul(&Rational32::new(x, 1)) {
18✔
1273
                    Some(res) => res.into_steelval(),
18✔
1274
                    None => {
1275
                        let mut res =
×
1276
                            BigRational::new(BigInt::from(*y.numer()), BigInt::from(*y.denom()));
×
1277
                        res *= BigInt::from(x);
×
1278
                        res.into_steelval()
×
1279
                    }
1280
                },
1281
                Err(_) => {
1282
                    let mut res =
×
1283
                        BigRational::new(BigInt::from(*y.numer()), BigInt::from(*y.denom()));
×
1284
                    res *= BigInt::from(*x);
×
1285
                    res.into_steelval()
×
1286
                }
1287
            }
1288
        }
1289
        (SteelVal::IntV(x), SteelVal::BigRational(y))
×
1290
        | (SteelVal::BigRational(y), SteelVal::IntV(x)) => {
×
1291
            let mut res = y.as_ref().clone();
×
1292
            res *= BigInt::from(*x);
×
1293
            res.into_steelval()
×
1294
        }
1295
        (SteelVal::Rational(x), SteelVal::Rational(y)) => match x.checked_mul(y) {
1✔
1296
            Some(res) => res.into_steelval(),
1✔
1297
            None => {
1298
                let mut res = BigRational::new(BigInt::from(*x.numer()), BigInt::from(*x.denom()));
×
1299
                res *= BigRational::new(BigInt::from(*y.numer()), BigInt::from(*y.denom()));
×
1300
                res.into_steelval()
×
1301
            }
1302
        },
1303
        (SteelVal::Rational(x), SteelVal::BigNum(y))
×
1304
        | (SteelVal::BigNum(y), SteelVal::Rational(x)) => {
×
1305
            let mut res = BigRational::new(BigInt::from(*x.numer()), BigInt::from(*x.denom()));
×
1306
            res *= y.as_ref();
×
1307
            res.into_steelval()
×
1308
        }
1309
        (SteelVal::BigRational(x), SteelVal::BigRational(y)) => {
×
1310
            (x.as_ref() * y.as_ref()).into_steelval()
×
1311
        }
1312
        (SteelVal::BigRational(x), SteelVal::BigNum(y))
×
1313
        | (SteelVal::BigNum(y), SteelVal::BigRational(x)) => {
3✔
1314
            (x.as_ref() * y.as_ref()).into_steelval()
3✔
1315
        }
1316
        (SteelVal::BigNum(x), SteelVal::BigNum(y)) => (x.as_ref() * y.as_ref()).into_steelval(),
×
1317
        // Complex numbers.
1318
        (SteelVal::Complex(x), SteelVal::Complex(y)) => multiply_complex(x, y),
4✔
1319
        (SteelVal::Complex(x), y) | (y, SteelVal::Complex(x)) => {
×
1320
            let y = SteelComplex::new(y.clone(), SteelVal::IntV(0));
×
1321
            multiply_complex(x, &y)
×
1322
        }
1323
        (SteelVal::BigRational(x), SteelVal::Rational(y)) => {
×
1324
            let mut res = BigRational::new(
1325
                BigInt::from(x.numer().clone()),
×
1326
                BigInt::from(x.denom().clone()),
×
1327
            );
1328
            res *= BigRational::new(BigInt::from(*y.numer()), BigInt::from(*y.denom()));
×
1329
            res.into_steelval()
×
1330
        }
1331
        _ => unreachable!(),
1332
    }
1333
}
1334

1335
/// # Precondition
1336
/// All types in `args` must be numerical types.
1337
fn multiply_primitive_impl(args: &[SteelVal]) -> Result<SteelVal> {
845,481✔
1338
    match args {
845,481✔
1339
        [] => 1.into_steelval(),
845,483✔
1340
        [x] => x.clone().into_steelval(),
2✔
1341
        [x, y] => multiply_two(x, y).into_steelval(),
845,476✔
1342
        [x, y, zs @ ..] => {
1✔
1343
            let mut res = multiply_two(x, y)?;
2✔
1344
            for z in zs {
3✔
1345
                // TODO: This use case could be optimized to reuse state instead of creating a new
1346
                // object each time.
1347
                res = multiply_two(&res, &z)?;
1✔
1348
            }
1349
            res.into_steelval()
1✔
1350
        }
1351
    }
1352
}
1353

1354
#[cold]
1355
fn complex_reciprocal(c: &SteelComplex) -> Result<SteelVal> {
1✔
1356
    let denominator = add_two(&multiply_two(&c.re, &c.re)?, &multiply_two(&c.im, &c.im)?)?;
3✔
1357
    let re = divide_primitive(&[c.re.clone(), denominator.clone()])?;
2✔
1358
    let neg_im = divide_primitive(&[c.re.clone(), denominator])?;
1✔
1359
    SteelComplex::new(re, subtract_primitive(&[neg_im])?).into_steelval()
1✔
1360
}
1361

1362
/// Negate a number.
1363
///
1364
/// # Precondition
1365
/// `value` must be a number.
1366
fn negate(value: &SteelVal) -> Result<SteelVal> {
1,426,385✔
1367
    match value {
1,426,385✔
1368
        SteelVal::NumV(x) => (-x).into_steelval(),
1,170,122✔
1369
        SteelVal::IntV(x) => match x.checked_neg() {
256,259✔
1370
            Some(res) => res.into_steelval(),
256,258✔
1371
            None => BigInt::from(*x).neg().into_steelval(),
1✔
1372
        },
1373
        SteelVal::Rational(x) => match 0i32.checked_sub(*x.numer()) {
2✔
1374
            Some(n) => Rational32::new(n, *x.denom()).into_steelval(),
2✔
1375
            None => BigRational::new(BigInt::from(*x.numer()), BigInt::from(*x.denom()))
×
1376
                .neg()
1377
                .into_steelval(),
1378
        },
1379
        SteelVal::BigRational(x) => x.as_ref().neg().into_steelval(),
×
1380
        SteelVal::BigNum(x) => x.as_ref().clone().neg().into_steelval(),
×
1381
        SteelVal::Complex(x) => negate_complex(x),
2✔
1382
        _ => unreachable!(),
1383
    }
1384
}
1385

1386
/// Adds two numbers.
1387
///
1388
/// # Precondition
1389
/// x and y must be valid numbers.
1390
pub fn add_two(x: &SteelVal, y: &SteelVal) -> Result<SteelVal> {
10,921,523✔
1391
    match (x, y) {
10,921,523✔
1392
        // Simple integer case. Probably very common.
1393
        (SteelVal::IntV(x), SteelVal::IntV(y)) => match x.checked_add(y) {
10,121,416✔
1394
            Some(res) => res.into_steelval(),
10,121,415✔
1395
            None => {
1396
                let mut res = BigInt::from(*x);
1✔
1397
                res += *y;
1✔
1398
                res.into_steelval()
1✔
1399
            }
1400
        },
1401
        // Cases that return an `f64`.
1402
        (SteelVal::NumV(x), SteelVal::NumV(y)) => (x + y).into_steelval(),
800,081✔
1403
        (SteelVal::NumV(x), SteelVal::IntV(y)) | (SteelVal::IntV(y), SteelVal::NumV(x)) => {
11✔
1404
            (x + *y as f64).into_steelval()
11✔
1405
        }
1406
        (SteelVal::NumV(x), SteelVal::BigNum(y)) | (SteelVal::BigNum(y), SteelVal::NumV(x)) => {
2✔
1407
            (x + y.to_f64().unwrap()).into_steelval()
2✔
1408
        }
1409
        (SteelVal::NumV(x), SteelVal::Rational(y)) | (SteelVal::Rational(y), SteelVal::NumV(x)) => {
2✔
1410
            (x + y.to_f64().unwrap()).into_steelval()
2✔
1411
        }
1412
        (SteelVal::NumV(x), SteelVal::BigRational(y))
×
1413
        | (SteelVal::BigRational(y), SteelVal::NumV(x)) => {
×
1414
            (x + y.to_f64().unwrap()).into_steelval()
×
1415
        }
1416
        // Cases that interact with `Rational`.
1417
        (SteelVal::Rational(x), SteelVal::Rational(y)) => (x + y).into_steelval(),
1✔
1418
        (SteelVal::Rational(x), SteelVal::IntV(y)) | (SteelVal::IntV(y), SteelVal::Rational(x)) => {
2✔
1419
            match i32::try_from(*y) {
2✔
1420
                Ok(y) => match x.checked_add(&Rational32::new(y, 1)) {
2✔
1421
                    Some(res) => res.into_steelval(),
2✔
1422
                    None => {
1423
                        let res =
×
1424
                            BigRational::new(BigInt::from(*x.numer()), BigInt::from(*x.denom()))
×
1425
                                * BigInt::from(y);
×
1426
                        res.into_steelval()
×
1427
                    }
1428
                },
1429
                Err(_) => {
1430
                    let res = BigRational::new(BigInt::from(*x.numer()), BigInt::from(*x.denom()))
×
1431
                        * BigInt::from(*y);
×
1432
                    res.into_steelval()
×
1433
                }
1434
            }
1435
        }
1436
        (SteelVal::Rational(x), SteelVal::BigNum(y))
×
1437
        | (SteelVal::BigNum(y), SteelVal::Rational(x)) => {
×
1438
            let res =
×
1439
                BigRational::new(BigInt::from(*x.numer()), BigInt::from(*x.denom())) * y.as_ref();
×
1440
            res.into_steelval()
×
1441
        }
1442
        // Cases that interact with `BigRational`. For the sake of performance, hopefully not too
1443
        // common.
1444
        (SteelVal::BigRational(x), SteelVal::BigRational(y)) => {
1✔
1445
            (x.as_ref() + y.as_ref()).into_steelval()
1✔
1446
        }
1447
        (SteelVal::BigRational(x), SteelVal::IntV(y))
×
1448
        | (SteelVal::IntV(y), SteelVal::BigRational(x)) => {
×
1449
            (x.as_ref() + BigInt::from(*y)).into_steelval()
×
1450
        }
1451
        (SteelVal::BigRational(x), SteelVal::BigNum(y))
×
1452
        | (SteelVal::BigNum(y), SteelVal::BigRational(x)) => {
×
1453
            (x.as_ref() * y.as_ref()).into_steelval()
×
1454
        }
1455
        // Remaining cases that interact with `BigNum`. Probably not too common.
1456
        (SteelVal::BigNum(x), SteelVal::BigNum(y)) => {
3✔
1457
            let mut res = x.as_ref().clone();
3✔
1458
            res += y.as_ref();
3✔
1459
            res.into_steelval()
3✔
1460
        }
1461
        (SteelVal::BigNum(x), SteelVal::IntV(y)) | (SteelVal::IntV(y), SteelVal::BigNum(x)) => {
1✔
1462
            let mut res = x.as_ref().clone();
1✔
1463
            res += *y;
1✔
1464
            res.into_steelval()
1✔
1465
        }
1466
        // Complex numbers
1467
        (SteelVal::Complex(x), SteelVal::Complex(y)) => add_complex(x, y),
3✔
1468
        (SteelVal::Complex(x), y) | (y, SteelVal::Complex(x)) => {
×
1469
            debug_assert!(realp(y));
×
1470
            add_complex(x, &SteelComplex::new(y.clone(), SteelVal::IntV(0)))
×
1471
        }
1472
        _ => unreachable!(),
1473
    }
1474
}
1475

1476
#[cold]
1477
fn multiply_complex(x: &SteelComplex, y: &SteelComplex) -> Result<SteelVal> {
4✔
1478
    // TODO: Optimize the implementation if needed.
1479
    let real = add_two(
1480
        &multiply_two(&x.re, &y.re)?,
4✔
1481
        &negate(&multiply_two(&x.im, &y.im)?)?,
4✔
1482
    )?;
1483
    let im = add_two(&multiply_two(&x.re, &y.im)?, &multiply_two(&x.im, &y.re)?)?;
12✔
1484
    SteelComplex::new(real, im).into_steelval()
4✔
1485
}
1486

1487
#[cold]
1488
fn negate_complex(x: &SteelComplex) -> Result<SteelVal> {
2✔
1489
    // TODO: Optimize the implementation if needed.
1490
    SteelComplex::new(negate(&x.re)?, negate(&x.im)?).into_steelval()
6✔
1491
}
1492

1493
#[cold]
1494
fn add_complex(x: &SteelComplex, y: &SteelComplex) -> Result<SteelVal> {
3✔
1495
    // TODO: Optimize the implementation if needed.
1496
    SteelComplex::new(add_two(&x.re, &y.re)?, add_two(&x.im, &y.im)?).into_steelval()
9✔
1497
}
1498

1499
pub struct NumOperations {}
1500
impl NumOperations {
1501
    pub fn arithmetic_shift() -> SteelVal {
1✔
1502
        SteelVal::FuncV(|args: &[SteelVal]| -> Result<SteelVal> {
1✔
1503
            if args.len() != 2 {
×
1504
                stop!(ArityMismatch => "arithmetic-shift takes 2 arguments")
×
1505
            }
1506
            let n = args[0].clone();
×
1507
            let m = args[1].clone();
×
1508

1509
            match (n, m) {
×
1510
                (SteelVal::IntV(n), SteelVal::IntV(m)) => {
×
1511
                    if m >= 0 {
×
1512
                        Ok(SteelVal::IntV(n << m))
×
1513
                    } else {
1514
                        Ok(SteelVal::IntV(n >> -m))
×
1515
                    }
1516
                }
1517
                _ => steelerr!(TypeMismatch => "arithmetic-shift expected 2 integers"),
×
1518
            }
1519
        })
1520
    }
1521

1522
    pub fn even() -> SteelVal {
1✔
1523
        SteelVal::FuncV(|args: &[SteelVal]| -> Result<SteelVal> {
308✔
1524
            if args.len() != 1 {
307✔
1525
                stop!(ArityMismatch => "even? takes one argument")
×
1526
            }
1527

1528
            match &args[0] {
307✔
1529
                SteelVal::IntV(n) => Ok(SteelVal::BoolV(n & 1 == 0)),
307✔
1530
                SteelVal::BigNum(n) => Ok(SteelVal::BoolV(n.is_even())),
×
1531
                SteelVal::NumV(n) if n.fract() == 0.0 => (*n as i64).is_even().into_steelval(),
×
1532
                _ => {
1533
                    steelerr!(TypeMismatch => format!("even? requires an integer, found: {:?}", &args[0]))
×
1534
                }
1535
            }
1536
        })
1537
    }
1538

1539
    pub fn odd() -> SteelVal {
1✔
1540
        SteelVal::FuncV(|args: &[SteelVal]| -> Result<SteelVal> {
1,495✔
1541
            if args.len() != 1 {
1,494✔
1542
                stop!(ArityMismatch => "odd? takes one argument")
×
1543
            }
1544

1545
            match &args[0] {
1,494✔
1546
                SteelVal::IntV(n) => Ok(SteelVal::BoolV(n & 1 == 1)),
1,494✔
1547
                SteelVal::BigNum(n) => Ok(SteelVal::BoolV(n.is_odd())),
×
1548
                SteelVal::NumV(n) if n.fract() == 0.0 => (*n as i64).is_odd().into_steelval(),
×
1549
                _ => {
1550
                    steelerr!(TypeMismatch => format!("odd? requires an integer, found: {:?}", &args[0]))
×
1551
                }
1552
            }
1553
        })
1554
    }
1555

1556
    pub fn float_add() -> SteelVal {
1✔
1557
        SteelVal::FuncV(|args: &[SteelVal]| -> Result<SteelVal> {
1✔
1558
            if args.is_empty() {
×
1559
                stop!(ArityMismatch => "+ requires at least one argument")
×
1560
            }
1561

1562
            let mut sum = 0.0;
×
1563

1564
            for arg in args {
×
1565
                if let SteelVal::NumV(n) = arg {
×
1566
                    sum += n;
×
1567
                } else {
1568
                    stop!(TypeMismatch => "+ expected a number, found {:?}", arg);
×
1569
                }
1570
            }
1571

1572
            Ok(SteelVal::NumV(sum))
×
1573
        })
1574
    }
1575
}
1576

1577
#[cfg(test)]
1578
mod num_op_tests {
1579
    use super::*;
1580
    use crate::{gc::Gc, rvals::SteelVal::*};
1581
    use std::str::FromStr;
1582

1583
    #[test]
1584
    fn division_test() {
1585
        assert_eq!(
1586
            divide_primitive(&[IntV(10), IntV(2)]).unwrap().to_string(),
1587
            IntV(5).to_string()
1588
        );
1589
    }
1590

1591
    #[test]
1592
    fn dvision_by_integer_zero_returns_positive_infinity() {
1593
        // assert_eq!(
1594
        //     divide_primitive(&[IntV(1), IntV(0)]).unwrap().to_string(),
1595
        //     NumV(f64::INFINITY).to_string()
1596
        // )
1597

1598
        assert!(divide_primitive(&[IntV(1), IntV(0)]).is_err())
1599
    }
1600

1601
    #[test]
1602
    fn division_on_single_integer_returns_reciprocal_rational() {
1603
        assert_eq!(
1604
            divide_primitive(&[IntV(10)]).unwrap().to_string(),
1605
            Rational(Rational32::new(1, 10)).to_string()
1606
        );
1607
    }
1608

1609
    #[test]
1610
    fn division_on_single_rational_returns_reciprocal_rational() {
1611
        assert_eq!(
1612
            divide_primitive(&[Rational32::new(2, 5).into_steelval().unwrap()])
1613
                .unwrap()
1614
                .to_string(),
1615
            Rational(Rational32::new(5, 2)).to_string()
1616
        );
1617
    }
1618

1619
    #[test]
1620
    fn division_on_rational_with_numerator_one_returns_integer() {
1621
        assert_eq!(
1622
            divide_primitive(&[Rational32::new(1, 5).into_steelval().unwrap()])
1623
                .unwrap()
1624
                .to_string(),
1625
            IntV(5).to_string()
1626
        );
1627
    }
1628

1629
    #[test]
1630
    fn division_on_bignum_returns_bigrational() {
1631
        assert_eq!(
1632
            divide_primitive(
1633
                &([BigInt::from_str("18446744073709551616")
1634
                    .unwrap()
1635
                    .into_steelval()
1636
                    .unwrap(),])
1637
            )
1638
            .unwrap()
1639
            .to_string(),
1640
            BigRational(Gc::new(num::BigRational::new(
1641
                BigInt::from(1),
1642
                BigInt::from_str("18446744073709551616").unwrap()
1643
            )))
1644
            .to_string()
1645
        );
1646
    }
1647

1648
    #[test]
1649
    fn multiplication_test() {
1650
        let args = [IntV(10), IntV(2)];
1651
        let got = multiply_primitive(&args).unwrap();
1652
        let expected = IntV(20);
1653
        assert_eq!(got, expected);
1654
    }
1655

1656
    #[test]
1657
    fn multiplication_different_types() {
1658
        let args = [IntV(10), NumV(2.0)];
1659
        let got = multiply_primitive(&args).unwrap();
1660
        let expected = NumV(20.0);
1661
        assert_eq!(got.to_string(), expected.to_string());
1662
    }
1663

1664
    #[test]
1665
    fn multiply_multiple_numbers() {
1666
        assert_eq!(
1667
            multiply_primitive(&[IntV(16), NumV(2.0), Rational(Rational32::new(1, 4))])
1668
                .unwrap()
1669
                .to_string(),
1670
            NumV(8.0).to_string(),
1671
        );
1672
    }
1673

1674
    #[test]
1675
    fn adding_exact_with_inexact_returns_inexact() {
1676
        assert_eq!(
1677
            add_primitive(&([IntV(10), NumV(2.0)])).unwrap().to_string(),
1678
            NumV(12.0).to_string()
1679
        );
1680
        assert_eq!(
1681
            add_primitive(
1682
                &([
1683
                    BigInt::from_str("18446744073709551616")
1684
                        .unwrap()
1685
                        .into_steelval()
1686
                        .unwrap(),
1687
                    NumV(18446744073709551616.0),
1688
                ])
1689
            )
1690
            .unwrap()
1691
            .to_string(),
1692
            NumV(18446744073709551616.0 * 2.0).to_string()
1693
        );
1694
        assert_eq!(
1695
            add_primitive(
1696
                &([
1697
                    BigInt::from_str("18446744073709551616")
1698
                        .unwrap()
1699
                        .into_steelval()
1700
                        .unwrap(),
1701
                    NumV(18446744073709551616.0),
1702
                ])
1703
            )
1704
            .unwrap()
1705
            .to_string(),
1706
            NumV(18446744073709551616.0 * 2.0).to_string()
1707
        );
1708
        assert_eq!(
1709
            add_primitive(&([Rational32::new(1, 2).into_steelval().unwrap(), NumV(0.5),]))
1710
                .unwrap()
1711
                .to_string(),
1712
            NumV(1.0).to_string()
1713
        );
1714
    }
1715

1716
    #[test]
1717
    fn subtraction_different_types() {
1718
        let args = [IntV(10), NumV(2.0)];
1719
        let got = subtract_primitive(&args).unwrap();
1720
        let expected = NumV(8.0);
1721
        assert_eq!(got.to_string(), expected.to_string());
1722
    }
1723

1724
    #[test]
1725
    fn test_integer_add() {
1726
        let args = [IntV(10), IntV(2)];
1727
        let got = add_primitive(&args).unwrap();
1728
        let expected = IntV(12);
1729
        assert_eq!(got, expected);
1730
    }
1731

1732
    #[test]
1733
    fn test_integer_sub() {
1734
        let args = [IntV(10), IntV(2)];
1735
        let got = subtract_primitive(&args).unwrap();
1736
        let expected = IntV(8);
1737
        assert_eq!(got, expected);
1738
    }
1739

1740
    #[test]
1741
    fn test_exact_integer_sqrt() {
1742
        assert_eq!(
1743
            exact_integer_sqrt(&0.into()),
1744
            (0.into_steelval().unwrap(), 0.into_steelval().unwrap()).into_steelval()
1745
        );
1746
        assert_eq!(
1747
            exact_integer_sqrt(&1.into()),
1748
            (1.into_steelval().unwrap(), 0.into_steelval().unwrap()).into_steelval()
1749
        );
1750
        assert_eq!(
1751
            exact_integer_sqrt(&2.into()),
1752
            (1.into_steelval().unwrap(), 1.into_steelval().unwrap()).into_steelval()
1753
        );
1754
        assert_eq!(
1755
            exact_integer_sqrt(&2.into()),
1756
            (1.into_steelval().unwrap(), 1.into_steelval().unwrap()).into_steelval()
1757
        );
1758
        assert_eq!(
1759
            exact_integer_sqrt(&3.into()),
1760
            (1.into_steelval().unwrap(), 2.into_steelval().unwrap()).into_steelval()
1761
        );
1762
        assert_eq!(
1763
            exact_integer_sqrt(&4.into()),
1764
            (2.into_steelval().unwrap(), 0.into_steelval().unwrap()).into_steelval()
1765
        );
1766
        assert_eq!(
1767
            exact_integer_sqrt(&5.into()),
1768
            (2.into_steelval().unwrap(), 1.into_steelval().unwrap()).into_steelval()
1769
        );
1770
        assert_eq!(
1771
            exact_integer_sqrt(&6.into()),
1772
            (2.into_steelval().unwrap(), 2.into_steelval().unwrap()).into_steelval()
1773
        );
1774
        assert_eq!(
1775
            exact_integer_sqrt(&7.into()),
1776
            (2.into_steelval().unwrap(), 3.into_steelval().unwrap()).into_steelval()
1777
        );
1778
    }
1779

1780
    #[test]
1781
    fn test_exact_integer_sqrt_fails_on_negative_or_noninteger() {
1782
        assert!(exact_integer_sqrt(&(-7).into()).is_err());
1783
        assert!(exact_integer_sqrt(&Rational32::new(-1, 2).into_steelval().unwrap()).is_err());
1784
        assert!(exact_integer_sqrt(
1785
            &BigInt::from_str("-10000000000000000000000000000000000001")
1786
                .unwrap()
1787
                .into_steelval()
1788
                .unwrap()
1789
        )
1790
        .is_err());
1791
        assert!(exact_integer_sqrt(
1792
            &num::BigRational::new(
1793
                BigInt::from_str("-10000000000000000000000000000000000001").unwrap(),
1794
                BigInt::from_str("2").unwrap()
1795
            )
1796
            .into_steelval()
1797
            .unwrap()
1798
        )
1799
        .is_err());
1800
        assert!(exact_integer_sqrt(&(1.0).into()).is_err());
1801
        assert!(exact_integer_sqrt(
1802
            &SteelComplex::new(1.into(), 1.into())
1803
                .into_steelval()
1804
                .unwrap()
1805
        )
1806
        .is_err());
1807
    }
1808

1809
    #[test]
1810
    fn test_sqrt() {
1811
        assert_eq!(sqrt(&4isize.into()).unwrap(), 2isize.into());
1812
        assert_eq!(
1813
            sqrt(
1814
                &SteelComplex::new(0.into(), 2.into())
1815
                    .into_steelval()
1816
                    .unwrap()
1817
            )
1818
            .unwrap(),
1819
            SteelComplex::new(1.into(), 1.into())
1820
                .into_steelval()
1821
                .unwrap()
1822
        );
1823
        assert_eq!(
1824
            sqrt(
1825
                &SteelComplex::new((-3).into(), (-4).into())
1826
                    .into_steelval()
1827
                    .unwrap()
1828
            )
1829
            .unwrap(),
1830
            SteelComplex::new(1.into(), (-2).into())
1831
                .into_steelval()
1832
                .unwrap()
1833
        );
1834
    }
1835
}
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