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