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
9pub fn trunc_div_builtin(interp: &mut Interpreter) -> Result<(), RuntimeError> {
13 let b = interp.pop()?;
14 let a = interp.pop()?;
15
16 let is_zero = match &b {
18 Value::Int32(i) => *i == 0,
19 Value::Integer(i) => i.is_zero(),
20 Value::Rational(r) => r.is_zero(),
21 Value::Number(n) => *n == 0.0,
22 _ => false,
23 };
24 if is_zero {
25 return Err(RuntimeError::DivisionByZero);
26 }
27
28 let (pa, pb) = promote_pair(&a, &b);
30
31 let result = match (&pa, &pb) {
32 (Value::Int32(i1), Value::Int32(i2)) => {
33 Value::Int32(i1 / i2)
35 }
36 (Value::Integer(i1), Value::Integer(i2)) => {
37 Value::Integer(i1 / i2)
39 }
40 (Value::Rational(r1), Value::Rational(r2)) => {
41 let division = r1 / r2;
43 let trunc_val = division.trunc();
44 Value::Rational(trunc_val).demote()
45 }
46 (Value::Number(n1), Value::Number(n2)) => Value::Number(n1 / n2).trunc(),
47 _ => {
48 return Err(RuntimeError::TypeError(format!(
49 "Cannot truncating divide {:?} and {:?}",
50 a, b
51 )))
52 }
53 };
54
55 interp.push(result);
56 Ok(())
57}
58
59trait Truncate {
61 fn trunc(self) -> Self;
62}
63
64impl Truncate for Value {
65 fn trunc(self) -> Self {
66 match self {
67 Value::Number(n) => Value::Number(n.trunc()),
68 other => other,
69 }
70 }
71}
72
73#[cfg(test)]
74mod tests {
75 use super::*;
76 use crate::value::Value;
77 use num_bigint::BigInt;
78 use num_rational::BigRational;
79
80 fn setup_interpreter() -> Interpreter {
81 Interpreter::new()
82 }
83
84 #[test]
85 fn test_trunc_div_builtin_floats() {
86 let mut interp = setup_interpreter();
87
88 interp.push(Value::Number(7.0));
90 interp.push(Value::Number(2.0));
91 trunc_div_builtin(&mut interp).unwrap();
92
93 let result = interp.pop().unwrap();
94 assert!(matches!(result, Value::Number(n) if n == 3.0));
95
96 interp.push(Value::Number(10.0));
98 interp.push(Value::Number(2.0));
99 trunc_div_builtin(&mut interp).unwrap();
100
101 let result = interp.pop().unwrap();
102 assert!(matches!(result, Value::Number(n) if n == 5.0));
103
104 interp.push(Value::Number(-7.0));
106 interp.push(Value::Number(2.0));
107 trunc_div_builtin(&mut interp).unwrap();
108
109 let result = interp.pop().unwrap();
110 assert!(matches!(result, Value::Number(n) if n == -3.0));
111
112 interp.push(Value::Number(7.0));
114 interp.push(Value::Number(-2.0));
115 trunc_div_builtin(&mut interp).unwrap();
116
117 let result = interp.pop().unwrap();
118 assert!(matches!(result, Value::Number(n) if n == -3.0));
119
120 interp.push(Value::Number(-7.0));
122 interp.push(Value::Number(-2.0));
123 trunc_div_builtin(&mut interp).unwrap();
124
125 let result = interp.pop().unwrap();
126 assert!(matches!(result, Value::Number(n) if n == 3.0));
127 }
128
129 #[test]
130 fn test_trunc_div_builtin_integers() {
131 let mut interp = setup_interpreter();
132
133 interp.push(Value::Integer(BigInt::from(7)));
135 interp.push(Value::Integer(BigInt::from(2)));
136 trunc_div_builtin(&mut interp).unwrap();
137
138 let result = interp.pop().unwrap();
139 assert!(matches!(result, Value::Integer(ref i) if *i == BigInt::from(3)));
140
141 interp.push(Value::Integer(BigInt::from(10)));
143 interp.push(Value::Integer(BigInt::from(2)));
144 trunc_div_builtin(&mut interp).unwrap();
145
146 let result = interp.pop().unwrap();
147 assert!(matches!(result, Value::Integer(ref i) if *i == BigInt::from(5)));
148
149 interp.push(Value::Integer(BigInt::from(-7)));
151 interp.push(Value::Integer(BigInt::from(2)));
152 trunc_div_builtin(&mut interp).unwrap();
153
154 let result = interp.pop().unwrap();
155 assert!(matches!(result, Value::Integer(ref i) if *i == BigInt::from(-3)));
156
157 interp.push(Value::Integer(BigInt::from(7)));
159 interp.push(Value::Integer(BigInt::from(-2)));
160 trunc_div_builtin(&mut interp).unwrap();
161
162 let result = interp.pop().unwrap();
163 assert!(matches!(result, Value::Integer(ref i) if *i == BigInt::from(-3)));
164
165 interp.push(Value::Integer(BigInt::from(-7)));
167 interp.push(Value::Integer(BigInt::from(-2)));
168 trunc_div_builtin(&mut interp).unwrap();
169
170 let result = interp.pop().unwrap();
171 assert!(matches!(result, Value::Integer(ref i) if *i == BigInt::from(3)));
172 }
173
174 #[test]
175 fn test_trunc_div_builtin_rationals() {
176 let mut interp = setup_interpreter();
177
178 interp.push(Value::Rational(BigRational::new(
180 BigInt::from(7),
181 BigInt::from(2),
182 )));
183 interp.push(Value::Rational(BigRational::new(
184 BigInt::from(3),
185 BigInt::from(2),
186 )));
187 trunc_div_builtin(&mut interp).unwrap();
188
189 let result = interp.pop().unwrap();
190 assert!(matches!(result, Value::Int32(2)));
192
193 interp.push(Value::Rational(BigRational::new(
195 BigInt::from(-7),
196 BigInt::from(2),
197 )));
198 interp.push(Value::Rational(BigRational::new(
199 BigInt::from(3),
200 BigInt::from(2),
201 )));
202 trunc_div_builtin(&mut interp).unwrap();
203
204 let result = interp.pop().unwrap();
205 assert!(matches!(result, Value::Int32(-2)));
207 }
208
209 #[test]
210 fn test_trunc_div_builtin_mixed_types() {
211 let mut interp = setup_interpreter();
212
213 interp.push(Value::Integer(BigInt::from(7)));
215 interp.push(Value::Number(2.0));
216 trunc_div_builtin(&mut interp).unwrap();
217
218 let result = interp.pop().unwrap();
219 assert!(matches!(result, Value::Number(n) if n == 3.0));
220
221 interp.push(Value::Rational(BigRational::new(
223 BigInt::from(7),
224 BigInt::from(2),
225 )));
226 interp.push(Value::Integer(BigInt::from(2)));
227 trunc_div_builtin(&mut interp).unwrap();
228
229 let result = interp.pop().unwrap();
230 assert!(matches!(result, Value::Int32(1)));
232 }
233
234 #[test]
235 fn test_trunc_div_vs_floor_div() {
236 let mut interp = setup_interpreter();
237
238 interp.push(Value::Integer(BigInt::from(-7)));
244 interp.push(Value::Integer(BigInt::from(2)));
245 trunc_div_builtin(&mut interp).unwrap();
246
247 let result = interp.pop().unwrap();
248 assert!(matches!(result, Value::Integer(ref i) if *i == BigInt::from(-3)));
249
250 interp.push(Value::Integer(BigInt::from(7)));
252 interp.push(Value::Integer(BigInt::from(2)));
253 trunc_div_builtin(&mut interp).unwrap();
254
255 let result = interp.pop().unwrap();
256 assert!(matches!(result, Value::Integer(ref i) if *i == BigInt::from(3)));
257 }
258
259 #[test]
260 fn test_trunc_div_builtin_division_by_zero() {
261 let mut interp = setup_interpreter();
262
263 interp.push(Value::Number(5.0));
265 interp.push(Value::Number(0.0));
266 let result = trunc_div_builtin(&mut interp);
267 assert!(matches!(result, Err(RuntimeError::DivisionByZero)));
268
269 interp.push(Value::Integer(BigInt::from(5)));
271 interp.push(Value::Integer(BigInt::from(0)));
272 let result = trunc_div_builtin(&mut interp);
273 assert!(matches!(result, Err(RuntimeError::DivisionByZero)));
274 }
275
276 #[test]
277 fn test_trunc_div_builtin_stack_underflow() {
278 let mut interp = setup_interpreter();
279
280 let result = trunc_div_builtin(&mut interp);
282 assert!(matches!(result, Err(RuntimeError::StackUnderflow)));
283
284 interp.push(Value::Number(5.0));
286 let result = trunc_div_builtin(&mut interp);
287 assert!(matches!(result, Err(RuntimeError::StackUnderflow)));
288 }
289
290 #[test]
291 fn test_trunc_div_builtin_type_error() {
292 let mut interp = setup_interpreter();
293
294 interp.push(Value::Number(5.0));
296 interp.push(Value::Null);
297 let result = trunc_div_builtin(&mut interp);
298 assert!(matches!(result, Err(RuntimeError::TypeError(_))));
299 }
300
301 #[test]
302 fn test_trunc_div_large_numbers() {
303 let mut interp = setup_interpreter();
304
305 interp.push(Value::Integer(BigInt::from(1000000)));
307 interp.push(Value::Integer(BigInt::from(3)));
308 trunc_div_builtin(&mut interp).unwrap();
309
310 let result = interp.pop().unwrap();
311 assert!(matches!(result, Value::Integer(ref i) if *i == BigInt::from(333333)));
312
313 interp.push(Value::Integer(BigInt::from(-1000000)));
315 interp.push(Value::Integer(BigInt::from(3)));
316 trunc_div_builtin(&mut interp).unwrap();
317
318 let result = interp.pop().unwrap();
319 assert!(matches!(result, Value::Integer(ref i) if *i == BigInt::from(-333333)));
320 }
321}