uni_core/primitives/
trunc_div.rs1use crate::compat::format;
4use crate::interpreter::Interpreter;
5use crate::primitives::numeric_promotion::promote_pair;
6use crate::value::{RuntimeError, Value};
7use num_traits::Zero;
8
9#[cfg(target_os = "none")]
13use num_traits::Float;
14
15pub fn trunc_div_builtin(interp: &mut Interpreter) -> Result<(), RuntimeError> {
19 let b = interp.pop()?;
20 let a = interp.pop()?;
21
22 let is_zero = match &b {
24 Value::Int32(i) => *i == 0,
25 Value::Integer(i) => i.is_zero(),
26 Value::Rational(r) => r.is_zero(),
27 Value::Number(n) => *n == 0.0,
28 _ => false,
29 };
30 if is_zero {
31 return Err(RuntimeError::DivisionByZero);
32 }
33
34 let (pa, pb) = promote_pair(&a, &b);
36
37 let result = match (&pa, &pb) {
38 (Value::Int32(i1), Value::Int32(i2)) => {
39 Value::Int32(i1 / i2)
41 }
42 (Value::Integer(i1), Value::Integer(i2)) => {
43 Value::Integer(i1 / i2)
45 }
46 (Value::Rational(r1), Value::Rational(r2)) => {
47 let division = r1 / r2;
49 let trunc_val = division.trunc();
50 Value::Rational(trunc_val).demote()
51 }
52 (Value::Number(n1), Value::Number(n2)) => Value::Number(n1 / n2).trunc(),
53 _ => {
54 return Err(RuntimeError::TypeError(format!(
55 "Cannot truncating divide {:?} and {:?}",
56 a, b
57 )))
58 }
59 };
60
61 interp.push(result);
62 Ok(())
63}
64
65trait Truncate {
67 fn trunc(self) -> Self;
68}
69
70impl Truncate for Value {
71 fn trunc(self) -> Self {
72 match self {
73 Value::Number(n) => Value::Number(n.trunc()),
74 other => other,
75 }
76 }
77}
78
79#[cfg(test)]
80mod tests {
81 use super::*;
82 use crate::value::Value;
83 use num_bigint::BigInt;
84 use num_rational::BigRational;
85
86 fn setup_interpreter() -> Interpreter {
87 Interpreter::new()
88 }
89
90 #[test]
91 fn test_trunc_div_builtin_floats() {
92 let mut interp = setup_interpreter();
93
94 interp.push(Value::Number(7.0));
96 interp.push(Value::Number(2.0));
97 trunc_div_builtin(&mut interp).unwrap();
98
99 let result = interp.pop().unwrap();
100 assert!(matches!(result, Value::Number(n) if n == 3.0));
101
102 interp.push(Value::Number(10.0));
104 interp.push(Value::Number(2.0));
105 trunc_div_builtin(&mut interp).unwrap();
106
107 let result = interp.pop().unwrap();
108 assert!(matches!(result, Value::Number(n) if n == 5.0));
109
110 interp.push(Value::Number(-7.0));
112 interp.push(Value::Number(2.0));
113 trunc_div_builtin(&mut interp).unwrap();
114
115 let result = interp.pop().unwrap();
116 assert!(matches!(result, Value::Number(n) if n == -3.0));
117
118 interp.push(Value::Number(7.0));
120 interp.push(Value::Number(-2.0));
121 trunc_div_builtin(&mut interp).unwrap();
122
123 let result = interp.pop().unwrap();
124 assert!(matches!(result, Value::Number(n) if n == -3.0));
125
126 interp.push(Value::Number(-7.0));
128 interp.push(Value::Number(-2.0));
129 trunc_div_builtin(&mut interp).unwrap();
130
131 let result = interp.pop().unwrap();
132 assert!(matches!(result, Value::Number(n) if n == 3.0));
133 }
134
135 #[test]
136 fn test_trunc_div_builtin_integers() {
137 let mut interp = setup_interpreter();
138
139 interp.push(Value::Integer(BigInt::from(7)));
141 interp.push(Value::Integer(BigInt::from(2)));
142 trunc_div_builtin(&mut interp).unwrap();
143
144 let result = interp.pop().unwrap();
145 assert!(matches!(result, Value::Integer(ref i) if *i == BigInt::from(3)));
146
147 interp.push(Value::Integer(BigInt::from(10)));
149 interp.push(Value::Integer(BigInt::from(2)));
150 trunc_div_builtin(&mut interp).unwrap();
151
152 let result = interp.pop().unwrap();
153 assert!(matches!(result, Value::Integer(ref i) if *i == BigInt::from(5)));
154
155 interp.push(Value::Integer(BigInt::from(-7)));
157 interp.push(Value::Integer(BigInt::from(2)));
158 trunc_div_builtin(&mut interp).unwrap();
159
160 let result = interp.pop().unwrap();
161 assert!(matches!(result, Value::Integer(ref i) if *i == BigInt::from(-3)));
162
163 interp.push(Value::Integer(BigInt::from(7)));
165 interp.push(Value::Integer(BigInt::from(-2)));
166 trunc_div_builtin(&mut interp).unwrap();
167
168 let result = interp.pop().unwrap();
169 assert!(matches!(result, Value::Integer(ref i) if *i == BigInt::from(-3)));
170
171 interp.push(Value::Integer(BigInt::from(-7)));
173 interp.push(Value::Integer(BigInt::from(-2)));
174 trunc_div_builtin(&mut interp).unwrap();
175
176 let result = interp.pop().unwrap();
177 assert!(matches!(result, Value::Integer(ref i) if *i == BigInt::from(3)));
178 }
179
180 #[test]
181 fn test_trunc_div_builtin_rationals() {
182 let mut interp = setup_interpreter();
183
184 interp.push(Value::Rational(BigRational::new(
186 BigInt::from(7),
187 BigInt::from(2),
188 )));
189 interp.push(Value::Rational(BigRational::new(
190 BigInt::from(3),
191 BigInt::from(2),
192 )));
193 trunc_div_builtin(&mut interp).unwrap();
194
195 let result = interp.pop().unwrap();
196 assert!(matches!(result, Value::Int32(2)));
198
199 interp.push(Value::Rational(BigRational::new(
201 BigInt::from(-7),
202 BigInt::from(2),
203 )));
204 interp.push(Value::Rational(BigRational::new(
205 BigInt::from(3),
206 BigInt::from(2),
207 )));
208 trunc_div_builtin(&mut interp).unwrap();
209
210 let result = interp.pop().unwrap();
211 assert!(matches!(result, Value::Int32(-2)));
213 }
214
215 #[test]
216 fn test_trunc_div_builtin_mixed_types() {
217 let mut interp = setup_interpreter();
218
219 interp.push(Value::Integer(BigInt::from(7)));
221 interp.push(Value::Number(2.0));
222 trunc_div_builtin(&mut interp).unwrap();
223
224 let result = interp.pop().unwrap();
225 assert!(matches!(result, Value::Number(n) if n == 3.0));
226
227 interp.push(Value::Rational(BigRational::new(
229 BigInt::from(7),
230 BigInt::from(2),
231 )));
232 interp.push(Value::Integer(BigInt::from(2)));
233 trunc_div_builtin(&mut interp).unwrap();
234
235 let result = interp.pop().unwrap();
236 assert!(matches!(result, Value::Int32(1)));
238 }
239
240 #[test]
241 fn test_trunc_div_vs_floor_div() {
242 let mut interp = setup_interpreter();
243
244 interp.push(Value::Integer(BigInt::from(-7)));
250 interp.push(Value::Integer(BigInt::from(2)));
251 trunc_div_builtin(&mut interp).unwrap();
252
253 let result = interp.pop().unwrap();
254 assert!(matches!(result, Value::Integer(ref i) if *i == BigInt::from(-3)));
255
256 interp.push(Value::Integer(BigInt::from(7)));
258 interp.push(Value::Integer(BigInt::from(2)));
259 trunc_div_builtin(&mut interp).unwrap();
260
261 let result = interp.pop().unwrap();
262 assert!(matches!(result, Value::Integer(ref i) if *i == BigInt::from(3)));
263 }
264
265 #[test]
266 fn test_trunc_div_builtin_division_by_zero() {
267 let mut interp = setup_interpreter();
268
269 interp.push(Value::Number(5.0));
271 interp.push(Value::Number(0.0));
272 let result = trunc_div_builtin(&mut interp);
273 assert!(matches!(result, Err(RuntimeError::DivisionByZero)));
274
275 interp.push(Value::Integer(BigInt::from(5)));
277 interp.push(Value::Integer(BigInt::from(0)));
278 let result = trunc_div_builtin(&mut interp);
279 assert!(matches!(result, Err(RuntimeError::DivisionByZero)));
280 }
281
282 #[test]
283 fn test_trunc_div_builtin_stack_underflow() {
284 let mut interp = setup_interpreter();
285
286 let result = trunc_div_builtin(&mut interp);
288 assert!(matches!(result, Err(RuntimeError::StackUnderflow)));
289
290 interp.push(Value::Number(5.0));
292 let result = trunc_div_builtin(&mut interp);
293 assert!(matches!(result, Err(RuntimeError::StackUnderflow)));
294 }
295
296 #[test]
297 fn test_trunc_div_builtin_type_error() {
298 let mut interp = setup_interpreter();
299
300 interp.push(Value::Number(5.0));
302 interp.push(Value::Null);
303 let result = trunc_div_builtin(&mut interp);
304 assert!(matches!(result, Err(RuntimeError::TypeError(_))));
305 }
306
307 #[test]
308 fn test_trunc_div_large_numbers() {
309 let mut interp = setup_interpreter();
310
311 interp.push(Value::Integer(BigInt::from(1000000)));
313 interp.push(Value::Integer(BigInt::from(3)));
314 trunc_div_builtin(&mut interp).unwrap();
315
316 let result = interp.pop().unwrap();
317 assert!(matches!(result, Value::Integer(ref i) if *i == BigInt::from(333333)));
318
319 interp.push(Value::Integer(BigInt::from(-1000000)));
321 interp.push(Value::Integer(BigInt::from(3)));
322 trunc_div_builtin(&mut interp).unwrap();
323
324 let result = interp.pop().unwrap();
325 assert!(matches!(result, Value::Integer(ref i) if *i == BigInt::from(-333333)));
326 }
327}