1use crate::Uint;
2
3use core::ops::{
4 BitAnd, BitAndAssign, BitOr, BitOrAssign, BitXor, BitXorAssign, Not, Shl, ShlAssign, Shr,
5 ShrAssign,
6};
7
8impl<const BITS: usize, const LIMBS: usize> Uint<BITS, LIMBS> {
9 #[must_use]
10 pub const fn bit(&self, index: usize) -> bool {
11 if index >= BITS {
12 return false;
13 }
14 let (limbs, bits) = (index / 64, index % 64);
15 self.limbs[limbs] & (1 << bits) != 0
16 }
17
18 pub fn set_bit(&mut self, index: usize, value: bool) {
19 if index >= BITS {
20 return;
21 }
22 let (limbs, bits) = (index / 64, index % 64);
23 if value {
24 self.limbs[limbs] |= 1 << bits;
25 } else {
26 self.limbs[limbs] &= !(1 << bits);
27 }
28 }
29
30 #[must_use]
34 pub fn reverse_bits(mut self) -> Self {
35 self.limbs.reverse();
36 for limb in &mut self.limbs {
37 *limb = limb.reverse_bits();
38 }
39 if BITS % 64 != 0 {
40 self >>= 64 - BITS % 64;
41 }
42 self
43 }
44
45 #[must_use]
48 pub fn leading_zeros(&self) -> usize {
49 self.as_limbs()
50 .iter()
51 .rev()
52 .position(|&limb| limb != 0)
53 .map_or(BITS, |n| {
54 let fixed = Self::MASK.leading_zeros() as usize;
55 let skipped = n * 64;
56 let top = self.as_limbs()[LIMBS - n - 1].leading_zeros() as usize;
57 skipped + top - fixed
58 })
59 }
60
61 #[must_use]
64 pub fn leading_ones(&self) -> usize {
65 self.as_limbs()
66 .iter()
67 .rev()
68 .position(|&limb| limb != u64::MAX)
69 .map_or(BITS, |n| {
70 let fixed = Self::MASK.leading_zeros() as usize;
71 let skipped = n * 64;
72 let top = self.as_limbs()[LIMBS - n - 1].leading_ones() as usize;
73 skipped + top - fixed
74 })
75 }
76
77 #[must_use]
80 pub fn trailing_zeros(&self) -> usize {
81 self.as_limbs()
82 .iter()
83 .position(|&limb| limb != 0)
84 .map_or(BITS, |n| {
85 n * 64 + self.as_limbs()[n].trailing_zeros() as usize
86 })
87 }
88
89 #[must_use]
92 pub fn trailing_ones(&self) -> usize {
93 self.as_limbs()
94 .iter()
95 .position(|&limb| limb != u64::MAX)
96 .map_or(BITS, |n| {
97 n * 64 + self.as_limbs()[n].trailing_ones() as usize
98 })
99 }
100
101 #[must_use]
103 pub fn count_ones(&self) -> usize {
104 self.as_limbs()
105 .iter()
106 .map(|limb| limb.count_ones() as usize)
107 .sum()
108 }
109
110 #[must_use]
112 pub fn count_zeros(&self) -> usize {
113 BITS - self.count_ones()
114 }
115
116 #[must_use]
118 pub fn bit_len(&self) -> usize {
119 BITS - self.leading_zeros()
120 }
121
122 #[must_use]
124 pub fn byte_len(&self) -> usize {
125 (self.bit_len() + 7) / 8
126 }
127
128 #[must_use]
140 pub fn most_significant_bits(&self) -> (u64, usize) {
141 let first_set_limb = self
142 .as_limbs()
143 .iter()
144 .rposition(|&limb| limb != 0)
145 .unwrap_or(0);
146 if first_set_limb == 0 {
147 (self.as_limbs().first().copied().unwrap_or(0), 0)
148 } else {
149 let hi = self.as_limbs()[first_set_limb];
150 let lo = self.as_limbs()[first_set_limb - 1];
151 let leading_zeros = hi.leading_zeros();
152 let bits = if leading_zeros > 0 {
153 (hi << leading_zeros) | (lo >> (64 - leading_zeros))
154 } else {
155 hi
156 };
157 let exponent = first_set_limb * 64 - leading_zeros as usize;
158 (bits, exponent)
159 }
160 }
161
162 #[allow(clippy::inline_always)]
171 #[inline(always)]
172 #[must_use]
173 pub fn checked_shl(self, rhs: usize) -> Option<Self> {
174 match self.overflowing_shl(rhs) {
175 (value, false) => Some(value),
176 _ => None,
177 }
178 }
179
180 #[allow(clippy::inline_always)]
186 #[inline(always)]
187 #[must_use]
188 pub fn saturating_shl(self, rhs: usize) -> Self {
189 match self.overflowing_shl(rhs) {
190 (value, false) => value,
191 _ => Self::MAX,
192 }
193 }
194
195 #[allow(clippy::doc_markdown)]
196 #[must_use]
205 pub fn overflowing_shl(mut self, rhs: usize) -> (Self, bool) {
206 let (limbs, bits) = (rhs / 64, rhs % 64);
207 if limbs >= LIMBS {
208 return (Self::ZERO, self != Self::ZERO);
209 }
210 if bits == 0 {
211 let mut overflow = false;
213 for i in (LIMBS - limbs)..LIMBS {
214 overflow |= self.limbs[i] != 0;
215 }
216 if self.limbs[LIMBS - limbs - 1] > Self::MASK {
217 overflow = true;
218 }
219
220 for i in (limbs..LIMBS).rev() {
222 self.limbs[i] = self.limbs[i - limbs];
223 }
224 for i in 0..limbs {
225 self.limbs[i] = 0;
226 }
227 self.limbs[LIMBS - 1] &= Self::MASK;
228 return (self, overflow);
229 }
230
231 let mut overflow = false;
233 for i in (LIMBS - limbs)..LIMBS {
234 overflow |= self.limbs[i] != 0;
235 }
236 if self.limbs[LIMBS - limbs - 1] >> (64 - bits) != 0 {
237 overflow = true;
238 }
239 if self.limbs[LIMBS - limbs - 1] << bits > Self::MASK {
240 overflow = true;
241 }
242
243 for i in (limbs + 1..LIMBS).rev() {
245 self.limbs[i] = self.limbs[i - limbs] << bits;
246 self.limbs[i] |= self.limbs[i - limbs - 1] >> (64 - bits);
247 }
248 self.limbs[limbs] = self.limbs[0] << bits;
249 for i in 0..limbs {
250 self.limbs[i] = 0;
251 }
252 self.limbs[LIMBS - 1] &= Self::MASK;
253 (self, overflow)
254 }
255
256 #[allow(clippy::doc_markdown)]
257 #[allow(clippy::inline_always)]
264 #[inline(always)]
265 #[must_use]
266 pub fn wrapping_shl(self, rhs: usize) -> Self {
267 self.overflowing_shl(rhs).0
268 }
269
270 #[allow(clippy::inline_always)]
282 #[inline(always)]
283 #[must_use]
284 pub fn checked_shr(self, rhs: usize) -> Option<Self> {
285 match self.overflowing_shr(rhs) {
286 (value, false) => Some(value),
287 _ => None,
288 }
289 }
290
291 #[must_use]
304 pub fn overflowing_shr(mut self, rhs: usize) -> (Self, bool) {
305 let (limbs, bits) = (rhs / 64, rhs % 64);
306 if limbs >= LIMBS {
307 return (Self::ZERO, self != Self::ZERO);
308 }
309 if bits == 0 {
310 let mut overflow = false;
312 for i in 0..limbs {
313 overflow |= self.limbs[i] != 0;
314 }
315
316 for i in 0..(LIMBS - limbs) {
318 self.limbs[i] = self.limbs[i + limbs];
319 }
320 for i in (LIMBS - limbs)..LIMBS {
321 self.limbs[i] = 0;
322 }
323 return (self, overflow);
324 }
325
326 let mut overflow = false;
328 for i in 0..limbs {
329 overflow |= self.limbs[i] != 0;
330 }
331 overflow |= self.limbs[limbs] >> bits != 0;
332
333 for i in 0..(LIMBS - limbs - 1) {
335 self.limbs[i] = self.limbs[i + limbs] >> bits;
336 self.limbs[i] |= self.limbs[i + limbs + 1] << (64 - bits);
337 }
338 self.limbs[LIMBS - limbs - 1] = self.limbs[LIMBS - 1] >> bits;
339 for i in (LIMBS - limbs)..LIMBS {
340 self.limbs[i] = 0;
341 }
342 (self, overflow)
343 }
344
345 #[allow(clippy::inline_always)]
355 #[inline(always)]
356 #[must_use]
357 pub fn wrapping_shr(self, rhs: usize) -> Self {
358 self.overflowing_shr(rhs).0
359 }
360
361 #[must_use]
364 #[allow(clippy::missing_const_for_fn)] pub fn rotate_left(self, rhs: usize) -> Self {
366 if BITS == 0 {
367 return Self::ZERO;
368 }
369 let rhs = rhs % BITS;
370 self << rhs | self >> (BITS - rhs)
371 }
372
373 #[allow(clippy::inline_always)]
374 #[inline(always)]
375 #[must_use]
376 pub fn rotate_right(self, rhs: usize) -> Self {
377 if BITS == 0 {
378 return Self::ZERO;
379 }
380 let rhs = rhs % BITS;
381 self.rotate_left(BITS - rhs)
382 }
383}
384
385impl<const BITS: usize, const LIMBS: usize> Not for Uint<BITS, LIMBS> {
386 type Output = Self;
387
388 fn not(mut self) -> Self::Output {
389 if BITS == 0 {
390 return Self::ZERO;
391 }
392 for limb in self.limbs.iter_mut() {
393 *limb = u64::not(*limb);
394 }
395 self.limbs[LIMBS - 1] &= Self::MASK;
396 self
397 }
398}
399
400impl<const BITS: usize, const LIMBS: usize> Not for &Uint<BITS, LIMBS> {
401 type Output = Uint<BITS, LIMBS>;
402
403 fn not(self) -> Self::Output {
404 (*self).not()
405 }
406}
407
408macro_rules! impl_bit_op {
409 ($trait:ident, $fn:ident, $trait_assign:ident, $fn_assign:ident) => {
410 impl<const BITS: usize, const LIMBS: usize> $trait_assign<Uint<BITS, LIMBS>>
411 for Uint<BITS, LIMBS>
412 {
413 #[allow(clippy::inline_always)]
414 #[inline(always)]
415 fn $fn_assign(&mut self, rhs: Uint<BITS, LIMBS>) {
416 self.$fn_assign(&rhs);
417 }
418 }
419 impl<const BITS: usize, const LIMBS: usize> $trait_assign<&Uint<BITS, LIMBS>>
420 for Uint<BITS, LIMBS>
421 {
422 fn $fn_assign(&mut self, rhs: &Uint<BITS, LIMBS>) {
423 for (limb, rhs) in self.limbs.iter_mut().zip(rhs.limbs) {
424 u64::$fn_assign(limb, rhs);
425 }
426 }
427 }
428 impl<const BITS: usize, const LIMBS: usize> $trait<Uint<BITS, LIMBS>>
429 for Uint<BITS, LIMBS>
430 {
431 type Output = Uint<BITS, LIMBS>;
432
433 #[allow(clippy::inline_always)]
434 #[inline(always)]
435 fn $fn(mut self, rhs: Uint<BITS, LIMBS>) -> Self::Output {
436 self.$fn_assign(rhs);
437 self
438 }
439 }
440 impl<const BITS: usize, const LIMBS: usize> $trait<&Uint<BITS, LIMBS>>
441 for Uint<BITS, LIMBS>
442 {
443 type Output = Uint<BITS, LIMBS>;
444
445 #[allow(clippy::inline_always)]
446 #[inline(always)]
447 fn $fn(mut self, rhs: &Uint<BITS, LIMBS>) -> Self::Output {
448 self.$fn_assign(rhs);
449 self
450 }
451 }
452 impl<const BITS: usize, const LIMBS: usize> $trait<Uint<BITS, LIMBS>>
453 for &Uint<BITS, LIMBS>
454 {
455 type Output = Uint<BITS, LIMBS>;
456
457 #[allow(clippy::inline_always)]
458 #[inline(always)]
459 fn $fn(self, mut rhs: Uint<BITS, LIMBS>) -> Self::Output {
460 rhs.$fn_assign(self);
461 rhs
462 }
463 }
464 impl<const BITS: usize, const LIMBS: usize> $trait<&Uint<BITS, LIMBS>>
465 for &Uint<BITS, LIMBS>
466 {
467 type Output = Uint<BITS, LIMBS>;
468
469 #[allow(clippy::inline_always)]
470 #[inline(always)]
471 fn $fn(self, rhs: &Uint<BITS, LIMBS>) -> Self::Output {
472 self.clone().$fn(rhs)
473 }
474 }
475 };
476}
477
478impl_bit_op!(BitOr, bitor, BitOrAssign, bitor_assign);
479impl_bit_op!(BitAnd, bitand, BitAndAssign, bitand_assign);
480impl_bit_op!(BitXor, bitxor, BitXorAssign, bitxor_assign);
481
482impl<const BITS: usize, const LIMBS: usize> ShlAssign<usize> for Uint<BITS, LIMBS> {
483 #[allow(clippy::inline_always)]
484 #[inline(always)]
485 fn shl_assign(&mut self, rhs: usize) {
486 *self = self.wrapping_shl(rhs);
487 }
488}
489
490impl<const BITS: usize, const LIMBS: usize> ShlAssign<&usize> for Uint<BITS, LIMBS> {
491 #[allow(clippy::inline_always)]
492 #[inline(always)]
493 fn shl_assign(&mut self, rhs: &usize) {
494 *self = self.wrapping_shl(*rhs);
495 }
496}
497
498impl<const BITS: usize, const LIMBS: usize> Shl<usize> for Uint<BITS, LIMBS> {
499 type Output = Self;
500
501 #[allow(clippy::inline_always)]
502 #[inline(always)]
503 fn shl(self, rhs: usize) -> Self {
504 self.wrapping_shl(rhs)
505 }
506}
507
508impl<const BITS: usize, const LIMBS: usize> Shl<usize> for &Uint<BITS, LIMBS> {
509 type Output = Uint<BITS, LIMBS>;
510
511 #[allow(clippy::inline_always)]
512 #[inline(always)]
513 fn shl(self, rhs: usize) -> Self::Output {
514 self.wrapping_shl(rhs)
515 }
516}
517
518impl<const BITS: usize, const LIMBS: usize> Shl<&usize> for Uint<BITS, LIMBS> {
519 type Output = Self;
520
521 #[allow(clippy::inline_always)]
522 #[inline(always)]
523 fn shl(self, rhs: &usize) -> Self {
524 self.wrapping_shl(*rhs)
525 }
526}
527
528impl<const BITS: usize, const LIMBS: usize> Shl<&usize> for &Uint<BITS, LIMBS> {
529 type Output = Uint<BITS, LIMBS>;
530
531 #[allow(clippy::inline_always)]
532 #[inline(always)]
533 fn shl(self, rhs: &usize) -> Self::Output {
534 self.wrapping_shl(*rhs)
535 }
536}
537
538impl<const BITS: usize, const LIMBS: usize> ShrAssign<usize> for Uint<BITS, LIMBS> {
539 #[allow(clippy::inline_always)]
540 #[inline(always)]
541 fn shr_assign(&mut self, rhs: usize) {
542 *self = self.wrapping_shr(rhs);
543 }
544}
545
546impl<const BITS: usize, const LIMBS: usize> ShrAssign<&usize> for Uint<BITS, LIMBS> {
547 #[allow(clippy::inline_always)]
548 #[inline(always)]
549 fn shr_assign(&mut self, rhs: &usize) {
550 *self = self.wrapping_shr(*rhs);
551 }
552}
553
554impl<const BITS: usize, const LIMBS: usize> Shr<usize> for Uint<BITS, LIMBS> {
555 type Output = Self;
556
557 #[allow(clippy::inline_always)]
558 #[inline(always)]
559 fn shr(self, rhs: usize) -> Self {
560 self.wrapping_shr(rhs)
561 }
562}
563
564impl<const BITS: usize, const LIMBS: usize> Shr<usize> for &Uint<BITS, LIMBS> {
565 type Output = Uint<BITS, LIMBS>;
566
567 #[allow(clippy::inline_always)]
568 #[inline(always)]
569 fn shr(self, rhs: usize) -> Self::Output {
570 self.wrapping_shr(rhs)
571 }
572}
573
574impl<const BITS: usize, const LIMBS: usize> Shr<&usize> for Uint<BITS, LIMBS> {
575 type Output = Self;
576
577 #[allow(clippy::inline_always)]
578 #[inline(always)]
579 fn shr(self, rhs: &usize) -> Self {
580 self.wrapping_shr(*rhs)
581 }
582}
583
584impl<const BITS: usize, const LIMBS: usize> Shr<&usize> for &Uint<BITS, LIMBS> {
585 type Output = Uint<BITS, LIMBS>;
586
587 #[allow(clippy::inline_always)]
588 #[inline(always)]
589 fn shr(self, rhs: &usize) -> Self::Output {
590 self.wrapping_shr(*rhs)
591 }
592}
593
594#[cfg(test)]
595mod tests {
596 use super::*;
597 use crate::{aliases::U128, const_for, nlimbs};
598 use proptest::proptest;
599
600 #[test]
601 fn test_leading_zeros() {
602 assert_eq!(Uint::<0, 0>::ZERO.leading_zeros(), 0);
603 const_for!(BITS in NON_ZERO {
604 const LIMBS: usize = nlimbs(BITS);
605 type U = Uint::<BITS, LIMBS>;
606 assert_eq!(U::ZERO.leading_zeros(), BITS);
607 assert_eq!(U::MAX.leading_zeros(), 0);
608 assert_eq!(U::from(1).leading_zeros(), BITS - 1);
609 proptest!(|(value: U)| {
610 let zeros = value.leading_zeros();
611 assert!(zeros <= BITS);
612 assert!(zeros < BITS || value == U::ZERO);
613 if zeros < BITS {
614 let (left, overflow) = value.overflowing_shl(zeros);
615 assert!(!overflow);
616 assert!(left.leading_zeros() == 0 || value == U::ZERO);
617 assert!(left.bit(BITS - 1));
618 assert_eq!(value >> (BITS - zeros), Uint::ZERO);
619 }
620 });
621 });
622 proptest!(|(value: u128)| {
623 let uint = U128::from(value);
624 assert_eq!(uint.leading_zeros(), value.leading_zeros() as usize);
625 });
626 }
627
628 #[test]
629 fn test_most_significant_bits() {
630 const_for!(BITS in NON_ZERO {
631 const LIMBS: usize = nlimbs(BITS);
632 type U = Uint::<BITS, LIMBS>;
633 proptest!(|(value: u64)| {
634 let value = if U::LIMBS <= 1 { value & U::MASK } else { value };
635 assert_eq!(U::from(value).most_significant_bits(), (value, 0));
636 });
637 });
638 proptest!(|(mut limbs: [u64; 2])| {
639 if limbs[1] == 0 {
640 limbs[1] = 1;
641 }
642 let (bits, exponent) = U128::from_limbs(limbs).most_significant_bits();
643 assert!(bits >= 1_u64 << 63);
644 assert_eq!(exponent, 64 - limbs[1].leading_zeros() as usize);
645 });
646 }
647
648 #[test]
649 fn test_checked_shl() {
650 assert_eq!(
651 Uint::<65, 2>::from_limbs([0x0010_0000_0000_0000, 0]).checked_shl(1),
652 Some(Uint::<65, 2>::from_limbs([0x0020_0000_0000_0000, 0]))
653 );
654 assert_eq!(
655 Uint::<127, 2>::from_limbs([0x0010_0000_0000_0000, 0]).checked_shl(64),
656 Some(Uint::<127, 2>::from_limbs([0, 0x0010_0000_0000_0000]))
657 );
658 }
659
660 #[test]
661 #[allow(clippy::cast_lossless)]
662 #[allow(clippy::cast_possible_truncation)]
663 fn test_small() {
664 const_for!(BITS in [1, 2, 8, 16, 32, 63, 64] {
665 type U = Uint::<BITS, 1>;
666 proptest!(|(a: U, b: U)| {
667 assert_eq!(a | b, U::from_limbs([a.limbs[0] | b.limbs[0]]));
668 assert_eq!(a & b, U::from_limbs([a.limbs[0] & b.limbs[0]]));
669 assert_eq!(a ^ b, U::from_limbs([a.limbs[0] ^ b.limbs[0]]));
670 });
671 proptest!(|(a: U, s in 0..BITS)| {
672 assert_eq!(a << s, U::from_limbs([a.limbs[0] << s & U::MASK]));
673 assert_eq!(a >> s, U::from_limbs([a.limbs[0] >> s]));
674 });
675 });
676 proptest!(|(a: Uint::<32, 1>, s in 0_usize..=34)| {
677 assert_eq!(a.reverse_bits(), Uint::from((a.limbs[0] as u32).reverse_bits() as u64));
678 assert_eq!(a.rotate_left(s), Uint::from((a.limbs[0] as u32).rotate_left(s as u32) as u64));
679 assert_eq!(a.rotate_right(s), Uint::from((a.limbs[0] as u32).rotate_right(s as u32) as u64));
680 });
681 proptest!(|(a: Uint::<64, 1>, s in 0_usize..=66)| {
682 assert_eq!(a.reverse_bits(), Uint::from(a.limbs[0].reverse_bits()));
683 assert_eq!(a.rotate_left(s), Uint::from(a.limbs[0].rotate_left(s as u32)));
684 assert_eq!(a.rotate_right(s), Uint::from(a.limbs[0].rotate_right(s as u32)));
685 });
686 }
687
688 #[test]
689 fn test_shift_reverse() {
690 const_for!(BITS in SIZES {
691 const LIMBS: usize = nlimbs(BITS);
692 type U = Uint::<BITS, LIMBS>;
693 proptest!(|(value: U, shift in 0..=BITS + 2)| {
694 let left = (value << shift).reverse_bits();
695 let right = value.reverse_bits() >> shift;
696 assert_eq!(left, right);
697 });
698 });
699 }
700
701 #[test]
702 fn test_rotate() {
703 const_for!(BITS in SIZES {
704 const LIMBS: usize = nlimbs(BITS);
705 type U = Uint::<BITS, LIMBS>;
706 proptest!(|(value: U, shift in 0..=BITS + 2)| {
707 let rotated = value.rotate_left(shift).rotate_right(shift);
708 assert_eq!(value, rotated);
709 });
710 });
711 }
712}