builtins/
math.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
use crate::SloshVm;
use bridge_macros::sl_sh_fn;
use slvm::{VMError, VMResult, Value};

/// Usage: (abs arg)
///
/// Returns absolute value of arg.
///
/// Section: math
///
/// Example:
/// (test::assert-equal 2 (abs 2))
/// (test::assert-equal 144 (abs -144))
/// (test::assert-equal 4.53 (abs -4.53))
/// (test::assert-equal 36028797018963967 (abs -36028797018963967))
#[sl_sh_fn(fn_name = "abs")]
pub fn abs(v: Value) -> VMResult<Value> {
    match v {
        Value::Float(f56) => {
            // Convert to f64, abs, convert back to f56
            Ok(Value::Float(f64::from(f56).abs().into()))
        }
        Value::Int(i56_bytes) => {
            // Convert to i64, abs, convert back to i56
            let i64 = slvm::from_i56(&i56_bytes);
            Ok(slvm::to_i56(i64.abs()))
        }
        Value::Byte(_b) => {
            // Byte is unsigned so just return the input
            Ok(v)
        }
        _ => Err(VMError::new_vm("abs: not a number".to_string())),
    }
}

/// Usage: (% int int)
///
/// Remainder from dividing (arg 1) by (arg 2).
/// Note: Remainder and Modulo are two similar mathematical operations,
/// called `rem` and `rem_euclid` in Rust.
/// This function uses `rem` which is the same as the `%` operator in C.
/// With `rem`, the sign of the result is the same as the dividend (arg 1).
/// With `rem_euclid`, the sign of the result is always positive.
///
/// Section: math
///
/// Example:
/// (test::assert-equal 0 (% 50 10))
/// (test::assert-equal 5 (% 55 10))
/// (test::assert-equal 1 (% 1 2))
/// (test::assert-equal -1 (% -10 3))
/// (test::assert-equal  1 (% 10 -3))
/// (test::assert-equal -1 (% -10 -3))
///
/// (test::assert-error (%))
/// (test::assert-error (% 1))
/// (test::assert-error (% 1 2 3))
/// (test::assert-error (% 1 2.0))
#[sl_sh_fn(fn_name = "%")]
pub fn rem_as_percent(dividend: i64, divisor: i64) -> VMResult<i64> {
    dividend.checked_rem(divisor).ok_or_else(|| {
        VMError::new_vm(format!(
            "rem: division by zero or overflow: {} % {}",
            dividend, divisor
        ))
    })
}

/// Usage: (rem int int)
///
/// Remainder from dividing (arg 1) by (arg 2).
/// Note: Remainder and Modulo are two similar mathematical operations,
/// called `rem` and `rem_euclid` in Rust.
/// This function uses `rem` which is the same as the `%` operator in C.
/// With `rem`, the sign of the result is the same as the dividend (arg 1).
/// With `rem_euclid`, the sign of the result is always positive.
///
/// Section: math
///
/// Example:
/// (test::assert-equal 0 (rem 50 10))
/// (test::assert-equal 5 (rem 55 10))
/// (test::assert-equal 1 (rem 1 2))
/// (test::assert-equal -1 (rem -10 3))
/// (test::assert-equal  1 (rem 10 -3))
/// (test::assert-equal -1 (rem -10 -3))
///
/// (test::assert-error (rem))
/// (test::assert-error (rem 1))
/// (test::assert-error (rem 1 2 3))
/// (test::assert-error (rem 1 2.0))
#[sl_sh_fn(fn_name = "rem")]
pub fn rem_as_rem(dividend: i64, divisor: i64) -> VMResult<i64> {
    rem_as_percent(dividend, divisor)
}

/// Usage: (rem_euclid int int)
///
/// Least Non-negative number that can be added to a multiple of the divisor (arg 2) to get the dividend (arg 1).
/// The result should always be 0 <= result < divisor (arg 2).
/// Note: Remainder and Modulo are two similar mathematical operations,
/// called `rem` and `rem_euclid` in Rust.
/// With `rem`, the sign of the result is the same as the dividend (arg 1).
/// With `rem_euclid`, the sign of the result is always positive.
///
/// Section: math
///
/// Example:
/// (test::assert-equal 0 (rem_euclid 50 10))
/// (test::assert-equal 5 (rem_euclid 55 10))
/// (test::assert-equal 1 (rem_euclid 1 2))
/// (test::assert-equal 2 (rem_euclid -10 3))
/// (test::assert-equal 1 (rem_euclid 10 -3))
/// (test::assert-equal 2 (rem_euclid -10 -3))
///
/// (test::assert-error (rem_euclid))
/// (test::assert-error (rem_euclid 1))
/// (test::assert-error (rem_euclid 1 2 3))
/// (test::assert-error (rem_euclid 1 2.0))
#[sl_sh_fn(fn_name = "rem_euclid")]
pub fn rem_euclid(dividend: i64, divisor: i64) -> VMResult<i64> {
    dividend.checked_rem_euclid(divisor).ok_or_else(|| {
        VMError::new_vm(format!(
            "rem_euclid: division by zero or overflow: {} % {}",
            dividend, divisor
        ))
    })
}

pub fn add_math_builtins(env: &mut SloshVm) {
    intern_abs(env);
    intern_rem_as_rem(env);
    intern_rem_as_percent(env);
    intern_rem_euclid(env);
}