1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
//! Bit-granularity left shift with zero-fill.
use super::U320;
impl U320 {
/// Shifts the value left by `n` bits, filling the vacated low bits
/// with zeros. Bits shifted past the 320th position are discarded.
///
/// For shifts of 320 or more, the result is zero. For a shift of 0,
/// the value is returned unchanged. Handles both intra-limb shifts
/// (within a single 64-bit word) and inter-limb carries where bits
/// cross limb boundaries.
///
/// # Examples
///
/// ```
/// use cnfy_uint::u320::U320;
///
/// let a = U320::from_be_limbs([0, 0, 0, 0, 1]);
/// assert_eq!(a.shl_bits(3), U320::from_be_limbs([0, 0, 0, 0, 8]));
///
/// let b = U320::from_be_limbs([0, 0, 0, 0, 1]);
/// assert_eq!(b.shl_bits(256), U320::from_be_limbs([1, 0, 0, 0, 0]));
/// ```
#[inline]
pub const fn shl_bits(&self, n: u32) -> U320 {
if n >= 320 {
return U320([0, 0, 0, 0, 0]);
}
if n == 0 {
return *self;
}
let limb_shift = (n / 64) as usize;
let bit_shift = n % 64;
// Shift whole limbs (LE: shifting left moves data toward higher indices)
let mut shifted = [0u64; 5];
let mut i = limb_shift;
while i < 5 {
shifted[i] = self.0[i - limb_shift];
i += 1;
}
if bit_shift == 0 {
return U320(shifted);
}
// Shift bits within limbs, carrying from lower to higher indices
let mut result = [0u64; 5];
result[0] = shifted[0] << bit_shift;
let mut j = 1;
while j < 5 {
result[j] = (shifted[j] << bit_shift) | (shifted[j - 1] >> (64 - bit_shift));
j += 1;
}
U320(result)
}
}
#[cfg(test)]
mod ai_tests {
use super::*;
/// Shift by 0 is identity.
#[test]
fn identity() {
let a = U320::from_be_limbs([0x1234, 0x5678, 0x9ABC, 0xDEF0, 0x1111]);
assert_eq!(a.shl_bits(0), a);
}
/// Shift by 320 or more produces zero.
#[test]
fn full_shift() {
assert_eq!(U320::MAX.shl_bits(320), U320::ZERO);
assert_eq!(U320::MAX.shl_bits(400), U320::ZERO);
}
/// Shift left by 1 doubles the value.
#[test]
fn shift_one() {
assert_eq!(
U320::from_be_limbs([0, 0, 0, 0, 4]).shl_bits(1),
U320::from_be_limbs([0, 0, 0, 0, 8]),
);
}
/// Shift by exactly 64 moves one limb.
#[test]
fn one_limb() {
let a = U320::from_be_limbs([0, 0, 0, 0, 1]);
assert_eq!(a.shl_bits(64), U320::from_be_limbs([0, 0, 0, 1, 0]));
}
/// Shift by 256 moves from LSB limb to MSB.
#[test]
fn four_limbs() {
let a = U320::from_be_limbs([0, 0, 0, 0, 0x42]);
assert_eq!(a.shl_bits(256), U320::from_be_limbs([0x42, 0, 0, 0, 0]));
}
/// Cross-limb bit carry works.
#[test]
fn cross_limb_carry() {
let a = U320::from_be_limbs([0, 0, 0, 0, 1 << 63]);
assert_eq!(a.shl_bits(1), U320::from_be_limbs([0, 0, 0, 1, 0]));
}
/// Combined limb and bit shift.
#[test]
fn combined_shift() {
let a = U320::from_be_limbs([0, 0, 0, 0, 0x10]);
assert_eq!(a.shl_bits(68), U320::from_be_limbs([0, 0, 0, 0x100, 0]));
}
/// Shifting zero always produces zero.
#[test]
fn shift_zero() {
assert_eq!(U320::ZERO.shl_bits(42), U320::ZERO);
}
/// Overflow bits are discarded.
#[test]
fn overflow_discarded() {
let a = U320::from_be_limbs([1 << 63, 0, 0, 0, 0]);
assert_eq!(a.shl_bits(1), U320::ZERO);
}
}