uni_core/primitives/
shr.rs

1use crate::compat::ToString;
2use crate::interpreter::Interpreter;
3use crate::value::{RuntimeError, Value};
4
5pub fn shr_builtin(interp: &mut Interpreter) -> Result<(), RuntimeError> {
6    let shift_amount = interp.pop_number()?;
7    let value = interp.pop_number()?;
8
9    // Convert to integers for shift operations
10    let value_int = value as i64;
11    let shift_int = shift_amount as u32;
12
13    // Check for reasonable shift amounts
14    if shift_int > 63 {
15        return Err(RuntimeError::DomainError(
16            "shift amount too large".to_string(),
17        ));
18    }
19
20    // Use arithmetic right shift (preserves sign bit)
21    let result = value_int >> shift_int;
22    interp.push(Value::Number(result as f64));
23    Ok(())
24}
25
26#[cfg(test)]
27mod tests {
28    use super::*;
29    use crate::value::Value;
30
31    fn setup_interpreter() -> Interpreter {
32        Interpreter::new()
33    }
34
35    #[test]
36    fn test_shr_basic() {
37        let mut interp = setup_interpreter();
38        interp.push(Value::Number(8.0));
39        interp.push(Value::Number(3.0)); // Shift right by 3
40
41        shr_builtin(&mut interp).unwrap();
42
43        let result = interp.pop().unwrap();
44        assert!(matches!(result, Value::Number(n) if n == 1.0)); // 8 >> 3 = 1
45    }
46
47    #[test]
48    fn test_shr_zero_shift() {
49        let mut interp = setup_interpreter();
50        interp.push(Value::Number(42.0));
51        interp.push(Value::Number(0.0)); // Shift right by 0
52
53        shr_builtin(&mut interp).unwrap();
54
55        let result = interp.pop().unwrap();
56        assert!(matches!(result, Value::Number(n) if n == 42.0)); // No change
57    }
58
59    #[test]
60    fn test_shr_powers_of_two() {
61        let mut interp = setup_interpreter();
62
63        let test_cases = [
64            (16.0, 1.0, 8.0),  // 16 >> 1 = 8
65            (32.0, 2.0, 8.0),  // 32 >> 2 = 8
66            (64.0, 4.0, 4.0),  // 64 >> 4 = 4
67            (256.0, 8.0, 1.0), // 256 >> 8 = 1
68        ];
69
70        for (value, shift, expected) in test_cases {
71            interp.push(Value::Number(value));
72            interp.push(Value::Number(shift));
73            shr_builtin(&mut interp).unwrap();
74            let result = interp.pop().unwrap();
75            assert!(
76                matches!(result, Value::Number(n) if n == expected),
77                "{} >> {} should be {}, got {:?}",
78                value,
79                shift,
80                expected,
81                result
82            );
83        }
84    }
85
86    #[test]
87    fn test_shr_multiple_bits() {
88        let mut interp = setup_interpreter();
89
90        let test_cases = [
91            (12.0, 2.0, 3.0), // 1100 >> 2 = 11 = 3
92            (10.0, 1.0, 5.0), // 1010 >> 1 = 101 = 5
93            (56.0, 3.0, 7.0), // 111000 >> 3 = 111 = 7
94        ];
95
96        for (value, shift, expected) in test_cases {
97            interp.push(Value::Number(value));
98            interp.push(Value::Number(shift));
99            shr_builtin(&mut interp).unwrap();
100            let result = interp.pop().unwrap();
101            assert!(
102                matches!(result, Value::Number(n) if n == expected),
103                "{} >> {} should be {}, got {:?}",
104                value,
105                shift,
106                expected,
107                result
108            );
109        }
110    }
111
112    #[test]
113    fn test_shr_zero_value() {
114        let mut interp = setup_interpreter();
115        interp.push(Value::Number(0.0));
116        interp.push(Value::Number(5.0)); // Shift any amount
117
118        shr_builtin(&mut interp).unwrap();
119
120        let result = interp.pop().unwrap();
121        assert!(matches!(result, Value::Number(n) if n == 0.0)); // 0 shifted is still 0
122    }
123
124    #[test]
125    fn test_shr_negative_numbers_arithmetic() {
126        let mut interp = setup_interpreter();
127
128        // Arithmetic right shift preserves sign (fills with sign bit)
129        let test_cases = [
130            (-8.0, 1.0, -4.0), // -8 >> 1 = -4
131            (-4.0, 2.0, -1.0), // -4 >> 2 = -1
132            (-1.0, 1.0, -1.0), // -1 >> 1 = -1 (sign extension)
133            (-2.0, 1.0, -1.0), // -2 >> 1 = -1
134        ];
135
136        for (value, shift, expected) in test_cases {
137            interp.push(Value::Number(value));
138            interp.push(Value::Number(shift));
139            shr_builtin(&mut interp).unwrap();
140            let result = interp.pop().unwrap();
141            assert!(
142                matches!(result, Value::Number(n) if n == expected),
143                "{} >> {} should be {}, got {:?}",
144                value,
145                shift,
146                expected,
147                result
148            );
149        }
150    }
151
152    #[test]
153    fn test_shr_large_shifts() {
154        let mut interp = setup_interpreter();
155
156        let test_cases = [
157            (1024.0, 10.0, 1.0),    // 1024 >> 10 = 1
158            (1048576.0, 20.0, 1.0), // 2^20 >> 20 = 1
159        ];
160
161        for (value, shift, expected) in test_cases {
162            interp.push(Value::Number(value));
163            interp.push(Value::Number(shift));
164            shr_builtin(&mut interp).unwrap();
165            let result = interp.pop().unwrap();
166            assert!(
167                matches!(result, Value::Number(n) if n == expected),
168                "{} >> {} should be {}, got {:?}",
169                value,
170                shift,
171                expected,
172                result
173            );
174        }
175    }
176
177    #[test]
178    fn test_shr_fractional_truncation() {
179        let mut interp = setup_interpreter();
180
181        // Both value and shift should be truncated
182        interp.push(Value::Number(15.7));
183        interp.push(Value::Number(2.9)); // Should truncate to 2
184
185        shr_builtin(&mut interp).unwrap();
186
187        let result = interp.pop().unwrap();
188        assert!(matches!(result, Value::Number(n) if n == 3.0)); // 15 >> 2 = 3
189    }
190
191    #[test]
192    fn test_shr_division_equivalence_positive() {
193        let mut interp = setup_interpreter();
194
195        // Right shift by n is roughly equivalent to division by 2^n for positive numbers
196        let test_cases = [
197            (20.0, 2.0), // 20 >> 2 = 5 (20 / 4 = 5)
198            (48.0, 4.0), // 48 >> 4 = 3 (48 / 16 = 3)
199        ];
200
201        for (value, shift) in test_cases {
202            interp.push(Value::Number(value));
203            interp.push(Value::Number(shift));
204            shr_builtin(&mut interp).unwrap();
205            let shift_result = interp.pop().unwrap();
206
207            let expected = (value / (2.0_f64.powf(shift))).floor();
208
209            assert!(
210                matches!(shift_result, Value::Number(n) if n == expected),
211                "{} >> {} should equal floor({} / 2^{}) = {}, got {:?}",
212                value,
213                shift,
214                value,
215                shift,
216                expected,
217                shift_result
218            );
219        }
220    }
221
222    #[test]
223    fn test_shr_sign_extension() {
224        let mut interp = setup_interpreter();
225
226        // Test that negative numbers maintain their sign with large shifts
227        interp.push(Value::Number(-100.0));
228        interp.push(Value::Number(50.0)); // Very large shift
229
230        shr_builtin(&mut interp).unwrap();
231
232        let result = interp.pop().unwrap();
233        assert!(matches!(result, Value::Number(n) if n == -1.0)); // Should be -1 due to sign extension
234    }
235
236    #[test]
237    fn test_shr_excessive_shift_error() {
238        let mut interp = setup_interpreter();
239        interp.push(Value::Number(8.0));
240        interp.push(Value::Number(100.0)); // Shift amount too large
241
242        let result = shr_builtin(&mut interp);
243        assert!(matches!(result, Err(RuntimeError::DomainError(_))));
244    }
245
246    #[test]
247    fn test_shr_boundary_shift() {
248        let mut interp = setup_interpreter();
249        interp.push(Value::Number(1024.0));
250        interp.push(Value::Number(63.0)); // Maximum allowed shift
251
252        let result = shr_builtin(&mut interp);
253        // Should succeed
254        assert!(result.is_ok());
255    }
256
257    #[test]
258    fn test_shr_bit_pattern() {
259        let mut interp = setup_interpreter();
260
261        // Test specific bit pattern
262        interp.push(Value::Number(340.0)); // 101010100 in binary
263        interp.push(Value::Number(2.0)); // Shift right by 2
264
265        shr_builtin(&mut interp).unwrap();
266
267        let result = interp.pop().unwrap();
268        assert!(matches!(result, Value::Number(n) if n == 85.0)); // 1010101 in binary
269    }
270
271    #[test]
272    fn test_shr_odd_numbers() {
273        let mut interp = setup_interpreter();
274
275        // Test that odd numbers are handled correctly (truncation behavior)
276        let test_cases = [
277            (15.0, 1.0, 7.0), // 15 >> 1 = 7 (not 7.5)
278            (7.0, 1.0, 3.0),  // 7 >> 1 = 3 (not 3.5)
279            (5.0, 2.0, 1.0),  // 5 >> 2 = 1 (not 1.25)
280        ];
281
282        for (value, shift, expected) in test_cases {
283            interp.push(Value::Number(value));
284            interp.push(Value::Number(shift));
285            shr_builtin(&mut interp).unwrap();
286            let result = interp.pop().unwrap();
287            assert!(
288                matches!(result, Value::Number(n) if n == expected),
289                "{} >> {} should be {}, got {:?}",
290                value,
291                shift,
292                expected,
293                result
294            );
295        }
296    }
297
298    #[test]
299    fn test_shr_reversibility_with_shl() {
300        // Test that (x << n) >> n = x for values that don't overflow
301        let value = 42.0;
302        let shift = 3.0;
303
304        // Simulate left shift then right shift
305        let value_int = value as i64;
306        let shifted_left = value_int << (shift as u32);
307        let shifted_back = shifted_left >> (shift as u32);
308
309        assert_eq!(
310            shifted_back, value_int,
311            "({} << {}) >> {} should equal {}",
312            value, shift, shift, value
313        );
314    }
315
316    #[test]
317    fn test_shr_stack_underflow() {
318        let mut interp = setup_interpreter();
319
320        let result = shr_builtin(&mut interp);
321        assert!(matches!(result, Err(RuntimeError::StackUnderflow)));
322
323        interp.push(Value::Number(8.0));
324        let result = shr_builtin(&mut interp);
325        assert!(matches!(result, Err(RuntimeError::StackUnderflow)));
326    }
327
328    #[test]
329    fn test_shr_type_error() {
330        let mut interp = setup_interpreter();
331        interp.push(Value::String("hello".into()));
332        interp.push(Value::Number(2.0));
333
334        let result = shr_builtin(&mut interp);
335        assert!(matches!(result, Err(RuntimeError::TypeError(_))));
336    }
337}