nanvm_lib/vm/bigint/
shr.rs1use core::ops::Shr;
2
3use crate::{
4 common::sized_index::SizedIndex,
5 sign::Sign,
6 vm::{Any, BigInt, IVm},
7};
8
9use super::{ShiftAmount, add_one};
10
11fn shifted_to_extreme<A: IVm>(sign: Sign) -> BigInt<A> {
19 match sign {
20 Sign::Positive => BigInt::default(),
21 Sign::Negative => BigInt::unchecked_new(Sign::Negative, [1u64]),
22 }
23}
24
25impl<A: IVm> Shr for BigInt<A> {
36 type Output = Result<Self, Any<A>>;
37
38 fn shr(self, rhs: Self) -> Self::Output {
39 if rhs.sign() == Sign::Negative {
41 return self << -rhs;
42 }
43
44 let sign = self.sign();
45 let (word_shift, bit_shift) = match self.shift_amount(&rhs) {
46 ShiftAmount::Noop => return Ok(self),
47 ShiftAmount::TooWide => return Ok(shifted_to_extreme(sign)),
50 ShiftAmount::Words(word_shift, bit_shift) => (word_shift, bit_shift),
51 };
52 let n_len = self.length();
53 if word_shift >= n_len as u64 {
54 return Ok(shifted_to_extreme(sign));
55 }
56 let word_shift = word_shift as u32;
57
58 let sticky = (0..word_shift).any(|i| self[i] != 0)
61 || (bit_shift > 0 && self[word_shift] & ((1u64 << bit_shift) - 1) != 0);
62
63 let mut value: Vec<u64> = (word_shift..n_len).map(|i| self[i]).collect();
64 if bit_shift > 0 {
65 let mut carry = 0u64;
66 for digit in value.iter_mut().rev() {
67 let new_carry = *digit << (64 - bit_shift);
68 *digit = (*digit >> bit_shift) | carry;
69 carry = new_carry;
70 }
71 }
72
73 if sign == Sign::Negative && sticky {
74 add_one(&mut value);
77 }
78
79 Ok(Self::normalize_new(sign, value))
80 }
81}
82
83#[cfg(test)]
84mod tests {
85 use crate::vm::bigint::test_util::{T, neg, pos};
86
87 #[test]
88 fn zero_shr_zero() {
89 let a: T = 0u64.into();
90 let b: T = 0u64.into();
91 assert_eq!((a >> b).unwrap(), T::default());
92 }
93
94 #[test]
95 fn value_shr_zero() {
96 let a: T = 42u64.into();
97 let b: T = 0u64.into();
98 assert_eq!((a >> b).unwrap(), 42u64.into());
99 }
100
101 #[test]
102 fn zero_shr_value() {
103 let a: T = 0u64.into();
104 let b: T = 5u64.into();
105 assert_eq!((a >> b).unwrap(), T::default());
106 }
107
108 #[test]
109 fn shr_by_one() {
110 let a: T = 100u64.into();
111 let b: T = 1u64.into();
112 assert_eq!((a >> b).unwrap(), 50u64.into());
113 }
114
115 #[test]
116 fn shr_odd_number() {
117 let a: T = 101u64.into();
120 let b: T = 1u64.into();
121 assert_eq!((a >> b).unwrap(), 50u64.into());
122 }
123
124 #[test]
125 fn shr_full_word() {
126 let a = pos(vec![0, 1]);
128 let b: T = 64u64.into();
129 assert_eq!((a >> b).unwrap(), 1u64.into());
130 }
131
132 #[test]
133 fn shr_more_than_word() {
134 let a = pos(vec![0, 4]);
136 let b: T = 65u64.into();
137 assert_eq!((a >> b).unwrap(), 2u64.into());
138 }
139
140 #[test]
141 fn shr_shift_exceeds_length() {
142 let a: T = 1u64.into();
143 let b: T = 64u64.into();
144 assert_eq!((a >> b).unwrap(), T::default());
145 }
146
147 #[test]
148 fn shr_shift_exceeds_length_large() {
149 let a: T = 1u64.into();
150 let b: T = 128u64.into();
151 assert_eq!((a >> b).unwrap(), T::default());
152 }
153
154 #[test]
155 fn shr_multi_word_shift_amount() {
156 let a: T = u64::MAX.into();
159 let b = pos(vec![0, 1]);
160 assert_eq!((a >> b).unwrap(), T::default());
161 }
162
163 #[test]
164 fn shr_multi_word_shift_amount_negative_floors_to_minus_one() {
165 let a: T = (-1i64).into();
168 let b = pos(vec![0, 1]);
169 assert_eq!((a >> b).unwrap(), neg(vec![1]));
170 }
171
172 #[test]
173 fn shr_preserves_positive_sign() {
174 let a = pos(vec![200]);
175 let b: T = 1u64.into();
176 let result = (a >> b).unwrap();
177 assert_eq!(result, 100u64.into());
178 }
179
180 #[test]
181 fn shr_preserves_negative_sign() {
182 let a = neg(vec![200]);
184 let b: T = 1u64.into();
185 assert_eq!((a >> b).unwrap(), neg(vec![100]));
186 }
187
188 #[test]
189 fn shr_cross_word_boundary() {
190 let a = pos(vec![3, 1]);
194 let b: T = 1u64.into();
195 assert_eq!((a >> b).unwrap(), pos(vec![0x8000_0000_0000_0001]));
196 }
197
198 #[test]
199 fn shr_normalizes_result() {
200 let a = pos(vec![0, 1]);
202 let b: T = 64u64.into();
203 let result = (a >> b).unwrap();
204 assert_eq!(result, 1u64.into());
205 }
206
207 #[test]
208 fn shr_large_bit_shift() {
209 let a: T = 0xFFu64.into();
211 let b: T = 4u64.into();
212 assert_eq!((a >> b).unwrap(), 0xFu64.into());
213 }
214
215 #[test]
216 fn shr_63_bits() {
217 let a: T = (1u64 << 63).into();
218 let b: T = 63u64.into();
219 assert_eq!((a >> b).unwrap(), 1u64.into());
220 }
221
222 #[test]
223 fn shr_three_words_by_two_words() {
224 let a = pos(vec![0, 0, 7]);
226 let b: T = 128u64.into();
227 assert_eq!((a >> b).unwrap(), 7u64.into());
228 }
229
230 #[test]
231 fn shr_three_words_by_word_plus_bits() {
232 let a = pos(vec![0, 0, 0x80]);
234 let b: T = 65u64.into();
235 assert_eq!((a >> b).unwrap(), pos(vec![0, 0x40]));
236 }
237
238 #[test]
239 fn shr_by_u64_max() {
240 let a = pos(vec![u64::MAX, u64::MAX, u64::MAX]);
241 let b: T = u64::MAX.into();
242 assert_eq!((a >> b).unwrap(), T::default());
243 }
244
245 #[test]
246 fn shr_four_words_by_192() {
247 let a = pos(vec![0, 0, 0, 5]);
249 let b: T = 192u64.into();
250 assert_eq!((a >> b).unwrap(), 5u64.into());
251 }
252
253 #[test]
254 fn shr_negative_floors_instead_of_truncating_to_zero() {
255 let a = neg(vec![1]);
259 let b: T = 64u64.into();
260 assert_eq!((a >> b).unwrap(), neg(vec![1]));
261 }
262
263 #[test]
264 fn shr_multi_word_carry_propagation() {
265 let a = pos(vec![0, 0, 3]);
271 let b: T = 1u64.into();
272 let expected = pos(vec![0, 0x8000_0000_0000_0000, 1]);
273 assert_eq!((a >> b).unwrap(), expected);
274 }
275
276 #[test]
277 fn shr_exact_word_boundary_no_remainder() {
278 let a = pos(vec![42, 99]);
280 let b: T = 64u64.into();
281 assert_eq!((a >> b).unwrap(), 99u64.into());
282 }
283
284 #[test]
285 fn shr_large_single_word_shift_no_truncation() {
286 let a = pos(vec![u64::MAX, u64::MAX]);
289 let b: T = 274_877_906_944u64.into();
290 assert_eq!((a >> b).unwrap(), T::default());
291 }
292
293 #[test]
294 fn shr_negative_rounds_up_on_bit_shift_remainder() {
295 let a = neg(vec![1]);
299 let b: T = 1u64.into();
300 assert_eq!((a >> b).unwrap(), neg(vec![1]));
301 }
302
303 #[test]
304 fn shr_negative_word_shift_with_remainder_rounds_up() {
305 let a = neg(vec![5, 7]);
309 let b: T = 64u64.into();
310 assert_eq!((a >> b).unwrap(), neg(vec![8]));
311 }
312
313 #[test]
314 fn shr_negative_by_negative_shift_is_left_shift() {
315 let a = neg(vec![5]);
317 let b: T = (-3i64).into();
318 assert_eq!((a >> b).unwrap(), neg(vec![40]));
319 }
320
321 #[test]
322 fn shr_by_very_negative_shift_delegates_to_too_large_shl() {
323 let a: T = 1u64.into();
327 let b = neg(vec![0, 1]);
328 assert!((a >> b).is_err());
329 }
330}