Skip to main content

binius_field/underlier/
small_uint.rs

1// Copyright 2024-2025 Irreducible Inc.
2// Copyright 2026 The Binius Developers
3
4use std::{
5	fmt::{Debug, Display, LowerHex},
6	hash::{Hash, Hasher},
7	mem::size_of,
8	ops::{Not, Shl, Shr},
9};
10
11use binius_utils::{
12	FixedSizeSerializeBytes, SerializationError, SerializeBytes,
13	bytes::{Buf, BufMut},
14	checked_arithmetics::checked_log_2,
15	serialization::DeserializeBytes,
16};
17use bytemuck::{NoUninit, Zeroable};
18use derive_more::{BitAnd, BitAndAssign, BitOr, BitOrAssign, BitXor, BitXorAssign};
19use rand::{
20	distr::{Distribution, StandardUniform},
21	prelude::*,
22};
23
24use super::Underlier;
25use crate::{
26	arch::{interleave_mask_even, interleave_with_mask},
27	divisible::{Divisible, impl_divisible_self, mapget},
28};
29
30/// Unsigned type with a size strictly less than 8 bits.
31#[derive(
32	Default,
33	Zeroable,
34	Clone,
35	Copy,
36	PartialEq,
37	Eq,
38	PartialOrd,
39	Ord,
40	BitAnd,
41	BitAndAssign,
42	BitOr,
43	BitOrAssign,
44	BitXor,
45	BitXorAssign,
46)]
47#[repr(transparent)]
48pub struct SmallU<const N: usize>(u8);
49
50impl<const N: usize> SmallU<N> {
51	const _CHECK_SIZE: () = {
52		assert!(N < 8);
53	};
54
55	/// All bits set to one.
56	pub const ONES: Self = Self((1u8 << N) - 1);
57
58	#[inline(always)]
59	pub const fn new(val: u8) -> Self {
60		Self(val & Self::ONES.0)
61	}
62
63	#[inline(always)]
64	pub const fn new_unchecked(val: u8) -> Self {
65		Self(val)
66	}
67
68	#[inline(always)]
69	pub const fn val(&self) -> u8 {
70		self.0
71	}
72}
73
74impl<const N: usize> Debug for SmallU<N> {
75	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
76		Debug::fmt(&self.val(), f)
77	}
78}
79
80impl<const N: usize> Display for SmallU<N> {
81	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
82		Display::fmt(&self.val(), f)
83	}
84}
85
86impl<const N: usize> LowerHex for SmallU<N> {
87	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
88		LowerHex::fmt(&self.0, f)
89	}
90}
91impl<const N: usize> Hash for SmallU<N> {
92	#[inline]
93	fn hash<H: Hasher>(&self, state: &mut H) {
94		self.val().hash(state);
95	}
96}
97
98impl<const N: usize> Distribution<SmallU<N>> for StandardUniform {
99	fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> SmallU<N> {
100		SmallU(rng.random_range(0..1u8 << N))
101	}
102}
103
104impl<const N: usize> Shr<usize> for SmallU<N> {
105	type Output = Self;
106
107	#[inline(always)]
108	fn shr(self, rhs: usize) -> Self::Output {
109		Self(self.val() >> rhs)
110	}
111}
112
113impl<const N: usize> Shl<usize> for SmallU<N> {
114	type Output = Self;
115
116	#[inline(always)]
117	fn shl(self, rhs: usize) -> Self::Output {
118		Self(self.val() << rhs) & Self::ONES
119	}
120}
121
122impl<const N: usize> Not for SmallU<N> {
123	type Output = Self;
124
125	fn not(self) -> Self::Output {
126		self ^ Self::ONES
127	}
128}
129
130unsafe impl<const N: usize> NoUninit for SmallU<N> {}
131
132impl Underlier for U1 {
133	const LOG_BITS: usize = checked_log_2(1);
134
135	const ZERO: Self = Self(0);
136	const ONE: Self = Self(1);
137	const ONES: Self = Self(1);
138
139	fn interleave(self, _other: Self, _log_block_len: usize) -> (Self, Self) {
140		panic!("interleave not supported for U1")
141	}
142}
143
144impl Underlier for U2 {
145	const LOG_BITS: usize = checked_log_2(2);
146
147	const ZERO: Self = Self(0);
148	const ONE: Self = Self(1);
149	const ONES: Self = Self(0b11);
150
151	fn interleave(self, other: Self, log_block_len: usize) -> (Self, Self) {
152		const MASKS: &[U2] = &[U2::new(interleave_mask_even!(u8, 0))];
153		interleave_with_mask(self, other, log_block_len, MASKS)
154	}
155}
156
157impl Underlier for U4 {
158	const LOG_BITS: usize = checked_log_2(4);
159
160	const ZERO: Self = Self(0);
161	const ONE: Self = Self(1);
162	const ONES: Self = Self(0b1111);
163
164	fn interleave(self, other: Self, log_block_len: usize) -> (Self, Self) {
165		const MASKS: &[U4] = &[
166			U4::new(interleave_mask_even!(u8, 0)),
167			U4::new(interleave_mask_even!(u8, 1)),
168		];
169		interleave_with_mask(self, other, log_block_len, MASKS)
170	}
171}
172
173impl<const N: usize> From<SmallU<N>> for u8 {
174	#[inline(always)]
175	fn from(value: SmallU<N>) -> Self {
176		value.val()
177	}
178}
179
180impl<const N: usize> From<SmallU<N>> for u16 {
181	#[inline(always)]
182	fn from(value: SmallU<N>) -> Self {
183		u8::from(value) as _
184	}
185}
186
187impl<const N: usize> From<SmallU<N>> for u32 {
188	#[inline(always)]
189	fn from(value: SmallU<N>) -> Self {
190		u8::from(value) as _
191	}
192}
193
194impl<const N: usize> From<SmallU<N>> for u64 {
195	#[inline(always)]
196	fn from(value: SmallU<N>) -> Self {
197		u8::from(value) as _
198	}
199}
200
201impl<const N: usize> From<SmallU<N>> for usize {
202	#[inline(always)]
203	fn from(value: SmallU<N>) -> Self {
204		u8::from(value) as _
205	}
206}
207
208impl<const N: usize> From<SmallU<N>> for u128 {
209	#[inline(always)]
210	fn from(value: SmallU<N>) -> Self {
211		u8::from(value) as _
212	}
213}
214
215impl From<SmallU<1>> for SmallU<2> {
216	#[inline(always)]
217	fn from(value: SmallU<1>) -> Self {
218		Self(value.val())
219	}
220}
221
222impl From<SmallU<1>> for SmallU<4> {
223	#[inline(always)]
224	fn from(value: SmallU<1>) -> Self {
225		Self(value.val())
226	}
227}
228
229impl From<SmallU<2>> for SmallU<4> {
230	#[inline(always)]
231	fn from(value: SmallU<2>) -> Self {
232		Self(value.val())
233	}
234}
235
236pub type U1 = SmallU<1>;
237pub type U2 = SmallU<2>;
238pub type U4 = SmallU<4>;
239
240impl From<bool> for U1 {
241	fn from(value: bool) -> Self {
242		Self::new_unchecked(value as u8)
243	}
244}
245
246impl From<U1> for bool {
247	fn from(value: U1) -> Self {
248		value == U1::ONE
249	}
250}
251
252impl<const N: usize> SerializeBytes for SmallU<N> {
253	fn serialize(&self, write_buf: impl BufMut) -> Result<(), SerializationError> {
254		self.val().serialize(write_buf)
255	}
256}
257
258impl<const N: usize> DeserializeBytes for SmallU<N> {
259	fn deserialize(read_buf: impl Buf) -> Result<Self, SerializationError>
260	where
261		Self: Sized,
262	{
263		Ok(Self::new(DeserializeBytes::deserialize(read_buf)?))
264	}
265}
266
267impl<const N: usize> FixedSizeSerializeBytes for SmallU<N> {
268	const BYTE_SIZE: usize = 1;
269}
270
271/// Helper functions for Divisible implementations using bitmask operations on sub-byte elements.
272///
273/// These functions work on any type that implements `Divisible<u8>` by extracting
274/// and modifying sub-byte elements through the byte interface.
275pub mod bitmask {
276	use super::{Divisible, SmallU};
277
278	/// Get a sub-byte element at index (LSB-first ordering) without bounds checking.
279	///
280	/// # Safety
281	///
282	/// The caller must ensure that `index < Big::N` (over `SmallU<BITS>` elements).
283	#[inline]
284	pub unsafe fn get<Big, const BITS: usize>(value: &Big, index: usize) -> SmallU<BITS>
285	where
286		Big: Divisible<u8>,
287	{
288		let elems_per_byte = 8 / BITS;
289		let byte_index = index / elems_per_byte;
290		let sub_index = index % elems_per_byte;
291		// Safety: `index < Big::N` over `SmallU<BITS>` implies `byte_index < Big::N` over `u8`.
292		let byte = unsafe { Divisible::<u8>::get_unchecked(value, byte_index) };
293		let shift = sub_index * BITS;
294		SmallU::<BITS>::new(byte >> shift)
295	}
296
297	/// Set a sub-byte element at index (LSB-first ordering), returning modified value, without
298	/// bounds checking.
299	///
300	/// # Safety
301	///
302	/// The caller must ensure that `index < Big::N` (over `SmallU<BITS>` elements).
303	#[inline]
304	pub unsafe fn set<Big, const BITS: usize>(
305		mut value: Big,
306		index: usize,
307		val: SmallU<BITS>,
308	) -> Big
309	where
310		Big: Divisible<u8>,
311	{
312		let elems_per_byte = 8 / BITS;
313		let byte_index = index / elems_per_byte;
314		let sub_index = index % elems_per_byte;
315		// Safety: `index < Big::N` over `SmallU<BITS>` implies `byte_index < Big::N` over `u8`.
316		let byte = unsafe { Divisible::<u8>::get_unchecked(&value, byte_index) };
317		let shift = sub_index * BITS;
318		let mask = (1u8 << BITS) - 1;
319		let new_byte = (byte & !(mask << shift)) | (val.val() << shift);
320		// Safety: `byte_index < Big::N` over `u8`, as above.
321		unsafe { Divisible::<u8>::set_unchecked(&mut value, byte_index, new_byte) };
322		value
323	}
324}
325
326/// Iterator for dividing an underlier into sub-byte elements (ie. [`SmallU`]).
327///
328/// This iterator wraps a byte iterator and extracts sub-byte elements from each byte.
329/// Generic over the byte iterator type `I`.
330#[derive(Clone)]
331pub struct SmallUDivisIter<I, const N: usize> {
332	byte_iter: I,
333	current_byte: Option<u8>,
334	sub_idx: usize,
335}
336
337impl<I: Iterator<Item = u8>, const N: usize> SmallUDivisIter<I, N> {
338	const ELEMS_PER_BYTE: usize = 8 / N;
339
340	pub fn new(mut byte_iter: I) -> Self {
341		let current_byte = byte_iter.next();
342		Self {
343			byte_iter,
344			current_byte,
345			sub_idx: 0,
346		}
347	}
348}
349
350impl<I: ExactSizeIterator<Item = u8>, const N: usize> Iterator for SmallUDivisIter<I, N> {
351	type Item = SmallU<N>;
352
353	#[inline]
354	fn next(&mut self) -> Option<Self::Item> {
355		let byte = self.current_byte?;
356		let shift = self.sub_idx * N;
357		let result = SmallU::<N>::new(byte >> shift);
358
359		self.sub_idx += 1;
360		if self.sub_idx >= Self::ELEMS_PER_BYTE {
361			self.sub_idx = 0;
362			self.current_byte = self.byte_iter.next();
363		}
364
365		Some(result)
366	}
367
368	#[inline]
369	fn size_hint(&self) -> (usize, Option<usize>) {
370		let remaining_in_current = if self.current_byte.is_some() {
371			Self::ELEMS_PER_BYTE - self.sub_idx
372		} else {
373			0
374		};
375		let remaining_bytes = self.byte_iter.len();
376		let total = remaining_in_current + remaining_bytes * Self::ELEMS_PER_BYTE;
377		(total, Some(total))
378	}
379}
380
381impl<I: ExactSizeIterator<Item = u8>, const N: usize> ExactSizeIterator for SmallUDivisIter<I, N> {}
382
383/// Implements `Divisible` trait for SmallU types using bitmask operations.
384///
385/// This macro generates `Divisible<SmallU<BITS>>` implementations for a big type
386/// by wrapping byte iteration with bitmasking to extract sub-byte elements.
387macro_rules! impl_divisible_bitmask {
388	// Special case for u8: operates directly on the byte without needing Divisible::<u8>
389	(u8, $($bits:expr),+) => {
390		$(
391			impl $crate::divisible::Divisible<$crate::underlier::SmallU<$bits>> for u8 {
392				const LOG_N: usize = (8usize / $bits).ilog2() as usize;
393
394				#[inline]
395				fn value_iter(value: Self) -> impl ExactSizeIterator<Item = $crate::underlier::SmallU<$bits>> + Send + Clone {
396					$crate::underlier::SmallUDivisIter::new(std::iter::once(value))
397				}
398
399				#[inline]
400				fn ref_iter(value: &Self) -> impl ExactSizeIterator<Item = $crate::underlier::SmallU<$bits>> + Send + Clone + '_ {
401					$crate::underlier::SmallUDivisIter::new(std::iter::once(*value))
402				}
403
404				#[inline]
405				fn slice_iter(slice: &[Self]) -> impl ExactSizeIterator<Item = $crate::underlier::SmallU<$bits>> + Send + Clone + '_ {
406					$crate::underlier::SmallUDivisIter::new(slice.iter().copied())
407				}
408
409				#[inline]
410				unsafe fn get_unchecked(&self, index: usize) -> $crate::underlier::SmallU<$bits> {
411					let shift = index * $bits;
412					$crate::underlier::SmallU::<$bits>::new(*self >> shift)
413				}
414
415				#[inline]
416				unsafe fn set_unchecked(&mut self, index: usize, val: $crate::underlier::SmallU<$bits>) {
417					let shift = index * $bits;
418					let mask = (1u8 << $bits) - 1;
419					*self = (*self & !(mask << shift)) | (val.val() << shift);
420				}
421
422				#[inline]
423				fn broadcast(val: $crate::underlier::SmallU<$bits>) -> Self {
424					if $bits == 1 {
425						// For 1-bit values: 0 -> 0x00, 1 -> 0xFF
426						val.val().wrapping_neg()
427					} else {
428						let mut result = val.val();
429						// Self-replicate to fill the byte
430						let mut current_bits = $bits;
431						while current_bits < 8 {
432							result |= result << current_bits;
433							current_bits *= 2;
434						}
435						result
436					}
437				}
438
439				#[inline]
440				fn from_iter(iter: impl Iterator<Item = $crate::underlier::SmallU<$bits>>) -> Self {
441					const N: usize = 8 / $bits;
442					let mut result: Self = 0;
443					for (i, val) in iter.take(N).enumerate() {
444						$crate::divisible::Divisible::<$crate::underlier::SmallU<$bits>>::set(&mut result, i, val);
445					}
446					result
447				}
448			}
449		)+
450	};
451
452	// General case for types larger than u8: wraps byte iteration
453	($big:ty, $($bits:expr),+) => {
454		$(
455			impl $crate::divisible::Divisible<$crate::underlier::SmallU<$bits>> for $big {
456				const LOG_N: usize = (8 * size_of::<$big>() / $bits).ilog2() as usize;
457
458				#[inline]
459				fn value_iter(value: Self) -> impl ExactSizeIterator<Item = $crate::underlier::SmallU<$bits>> + Send + Clone {
460					$crate::underlier::SmallUDivisIter::new(
461						$crate::divisible::Divisible::<u8>::value_iter(value)
462					)
463				}
464
465				#[inline]
466				fn ref_iter(value: &Self) -> impl ExactSizeIterator<Item = $crate::underlier::SmallU<$bits>> + Send + Clone + '_ {
467					$crate::underlier::SmallUDivisIter::new(
468						$crate::divisible::Divisible::<u8>::ref_iter(value)
469					)
470				}
471
472				#[inline]
473				fn slice_iter(slice: &[Self]) -> impl ExactSizeIterator<Item = $crate::underlier::SmallU<$bits>> + Send + Clone + '_ {
474					$crate::underlier::SmallUDivisIter::new(
475						$crate::divisible::Divisible::<u8>::slice_iter(slice)
476					)
477				}
478
479				#[inline]
480				unsafe fn get_unchecked(&self, index: usize) -> $crate::underlier::SmallU<$bits> {
481					// Safety: the caller guarantees `index < Self::N`.
482					unsafe { $crate::underlier::bitmask::get::<Self, $bits>(self, index) }
483				}
484
485				#[inline]
486				unsafe fn set_unchecked(&mut self, index: usize, val: $crate::underlier::SmallU<$bits>) {
487					// Safety: the caller guarantees `index < Self::N`.
488					*self = unsafe { $crate::underlier::bitmask::set::<Self, $bits>(*self, index, val) };
489				}
490
491				#[inline]
492				fn broadcast(val: $crate::underlier::SmallU<$bits>) -> Self {
493					// First splat to u8, then splat the byte to fill Self
494					let byte = $crate::divisible::Divisible::<$crate::underlier::SmallU<$bits>>::broadcast(val);
495					$crate::divisible::Divisible::<u8>::broadcast(byte)
496				}
497
498				#[inline]
499				fn from_iter(iter: impl Iterator<Item = $crate::underlier::SmallU<$bits>>) -> Self {
500					const N: usize = 8 * size_of::<$big>() / $bits;
501					let mut result: Self = bytemuck::Zeroable::zeroed();
502					for (i, val) in iter.take(N).enumerate() {
503						$crate::divisible::Divisible::<$crate::underlier::SmallU<$bits>>::set(&mut result, i, val);
504					}
505					result
506				}
507			}
508		)+
509	};
510}
511
512#[allow(unused)]
513pub(crate) use impl_divisible_bitmask;
514
515// Implement Divisible using bitmask for SmallU types
516impl_divisible_bitmask!(u8, 1, 2, 4);
517impl_divisible_bitmask!(u16, 1, 2, 4);
518impl_divisible_bitmask!(u32, 1, 2, 4);
519impl_divisible_bitmask!(u64, 1, 2, 4);
520impl_divisible_bitmask!(u128, 1, 2, 4);
521
522impl_divisible_self!(SmallU<1>, SmallU<2>, SmallU<4>);
523
524// Divisible for SmallU types that subdivide into smaller SmallU types
525impl Divisible<SmallU<1>> for SmallU<2> {
526	const LOG_N: usize = 1;
527
528	#[inline]
529	fn value_iter(value: Self) -> impl ExactSizeIterator<Item = SmallU<1>> + Send + Clone {
530		mapget::value_iter(value)
531	}
532
533	#[inline]
534	fn ref_iter(value: &Self) -> impl ExactSizeIterator<Item = SmallU<1>> + Send + Clone + '_ {
535		mapget::value_iter(*value)
536	}
537
538	#[inline]
539	fn slice_iter(slice: &[Self]) -> impl ExactSizeIterator<Item = SmallU<1>> + Send + Clone + '_ {
540		mapget::slice_iter(slice)
541	}
542
543	#[inline]
544	unsafe fn get_unchecked(&self, index: usize) -> SmallU<1> {
545		SmallU::<1>::new(self.val() >> index)
546	}
547
548	#[inline]
549	unsafe fn set_unchecked(&mut self, index: usize, val: SmallU<1>) {
550		let mask = 1u8 << index;
551		*self = SmallU::<2>::new((self.val() & !mask) | (val.val() << index));
552	}
553
554	#[inline]
555	fn broadcast(val: SmallU<1>) -> Self {
556		// 0b0 -> 0b00, 0b1 -> 0b11
557		let v = val.val();
558		SmallU::<2>::new(v | (v << 1))
559	}
560
561	#[inline]
562	fn from_iter(iter: impl Iterator<Item = SmallU<1>>) -> Self {
563		iter.chain(std::iter::repeat(SmallU::<1>::new(0)))
564			.take(2)
565			.enumerate()
566			.fold(SmallU::<2>::new(0), |mut acc, (i, val)| {
567				acc.set(i, val);
568				acc
569			})
570	}
571}
572
573impl Divisible<SmallU<1>> for SmallU<4> {
574	const LOG_N: usize = 2;
575
576	#[inline]
577	fn value_iter(value: Self) -> impl ExactSizeIterator<Item = SmallU<1>> + Send + Clone {
578		mapget::value_iter(value)
579	}
580
581	#[inline]
582	fn ref_iter(value: &Self) -> impl ExactSizeIterator<Item = SmallU<1>> + Send + Clone + '_ {
583		mapget::value_iter(*value)
584	}
585
586	#[inline]
587	fn slice_iter(slice: &[Self]) -> impl ExactSizeIterator<Item = SmallU<1>> + Send + Clone + '_ {
588		mapget::slice_iter(slice)
589	}
590
591	#[inline]
592	unsafe fn get_unchecked(&self, index: usize) -> SmallU<1> {
593		SmallU::<1>::new(self.val() >> index)
594	}
595
596	#[inline]
597	unsafe fn set_unchecked(&mut self, index: usize, val: SmallU<1>) {
598		let mask = 1u8 << index;
599		*self = SmallU::<4>::new((self.val() & !mask) | (val.val() << index));
600	}
601
602	#[inline]
603	fn broadcast(val: SmallU<1>) -> Self {
604		// 0b0 -> 0b0000, 0b1 -> 0b1111
605		let mut v = val.val();
606		v |= v << 1;
607		v |= v << 2;
608		SmallU::<4>::new(v)
609	}
610
611	#[inline]
612	fn from_iter(iter: impl Iterator<Item = SmallU<1>>) -> Self {
613		iter.chain(std::iter::repeat(SmallU::<1>::new(0)))
614			.take(4)
615			.enumerate()
616			.fold(SmallU::<4>::new(0), |mut acc, (i, val)| {
617				acc.set(i, val);
618				acc
619			})
620	}
621}
622
623impl Divisible<SmallU<2>> for SmallU<4> {
624	const LOG_N: usize = 1;
625
626	#[inline]
627	fn value_iter(value: Self) -> impl ExactSizeIterator<Item = SmallU<2>> + Send + Clone {
628		mapget::value_iter(value)
629	}
630
631	#[inline]
632	fn ref_iter(value: &Self) -> impl ExactSizeIterator<Item = SmallU<2>> + Send + Clone + '_ {
633		mapget::value_iter(*value)
634	}
635
636	#[inline]
637	fn slice_iter(slice: &[Self]) -> impl ExactSizeIterator<Item = SmallU<2>> + Send + Clone + '_ {
638		mapget::slice_iter(slice)
639	}
640
641	#[inline]
642	unsafe fn get_unchecked(&self, index: usize) -> SmallU<2> {
643		SmallU::<2>::new(self.val() >> (index * 2))
644	}
645
646	#[inline]
647	unsafe fn set_unchecked(&mut self, index: usize, val: SmallU<2>) {
648		let shift = index * 2;
649		let mask = 0b11u8 << shift;
650		*self = SmallU::<4>::new((self.val() & !mask) | (val.val() << shift));
651	}
652
653	#[inline]
654	fn broadcast(val: SmallU<2>) -> Self {
655		// 0bXX -> 0bXXXX
656		let v = val.val();
657		SmallU::<4>::new(v | (v << 2))
658	}
659
660	#[inline]
661	fn from_iter(iter: impl Iterator<Item = SmallU<2>>) -> Self {
662		iter.chain(std::iter::repeat(SmallU::<2>::new(0)))
663			.take(2)
664			.enumerate()
665			.fold(SmallU::<4>::new(0), |mut acc, (i, val)| {
666				acc.set(i, val);
667				acc
668			})
669	}
670}
671
672#[cfg(test)]
673impl<const N: usize> proptest::arbitrary::Arbitrary for SmallU<N> {
674	type Parameters = ();
675	type Strategy = proptest::strategy::BoxedStrategy<Self>;
676
677	fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy {
678		use proptest::strategy::Strategy;
679
680		(0u8..(1u8 << N)).prop_map(Self::new_unchecked).boxed()
681	}
682}
683
684#[cfg(test)]
685mod tests {
686	use proptest::{arbitrary::any, proptest};
687
688	use super::*;
689
690	#[test]
691	fn test_divisible_u8_u4() {
692		let val: u8 = 0x34;
693
694		// Test get - LSB first: nibbles
695		assert_eq!(Divisible::<U4>::get(&val, 0), U4::new(0x4));
696		assert_eq!(Divisible::<U4>::get(&val, 1), U4::new(0x3));
697
698		// Test set
699		let mut modified = val;
700		Divisible::<U4>::set(&mut modified, 0, U4::new(0xF));
701		assert_eq!(modified, 0x3F);
702		let mut modified = val;
703		Divisible::<U4>::set(&mut modified, 1, U4::new(0xA));
704		assert_eq!(modified, 0xA4);
705
706		// Test ref_iter
707		let parts: Vec<U4> = Divisible::<U4>::ref_iter(&val).collect();
708		assert_eq!(parts.len(), 2);
709		assert_eq!(parts[0], U4::new(0x4));
710		assert_eq!(parts[1], U4::new(0x3));
711
712		// Test value_iter
713		let parts: Vec<U4> = Divisible::<U4>::value_iter(val).collect();
714		assert_eq!(parts.len(), 2);
715		assert_eq!(parts[0], U4::new(0x4));
716		assert_eq!(parts[1], U4::new(0x3));
717
718		// Test slice_iter
719		let vals = [0x34u8, 0x56u8];
720		let parts: Vec<U4> = Divisible::<U4>::slice_iter(&vals).collect();
721		assert_eq!(parts.len(), 4);
722		assert_eq!(parts[0], U4::new(0x4));
723		assert_eq!(parts[1], U4::new(0x3));
724		assert_eq!(parts[2], U4::new(0x6));
725		assert_eq!(parts[3], U4::new(0x5));
726	}
727
728	#[test]
729	fn test_divisible_u16_u4() {
730		let val: u16 = 0x1234;
731
732		// Test get - LSB first: nibbles
733		assert_eq!(Divisible::<U4>::get(&val, 0), U4::new(0x4));
734		assert_eq!(Divisible::<U4>::get(&val, 1), U4::new(0x3));
735		assert_eq!(Divisible::<U4>::get(&val, 2), U4::new(0x2));
736		assert_eq!(Divisible::<U4>::get(&val, 3), U4::new(0x1));
737
738		// Test set
739		let mut modified = val;
740		Divisible::<U4>::set(&mut modified, 1, U4::new(0xF));
741		assert_eq!(modified, 0x12F4);
742
743		// Test ref_iter
744		let parts: Vec<U4> = Divisible::<U4>::ref_iter(&val).collect();
745		assert_eq!(parts.len(), 4);
746		assert_eq!(parts[0], U4::new(0x4));
747		assert_eq!(parts[3], U4::new(0x1));
748	}
749
750	#[test]
751	fn test_divisible_u16_u2() {
752		// 0b1011_0010_1101_0011 = 0xB2D3
753		let val: u16 = 0b1011001011010011;
754
755		// Test get - LSB first: 2-bit chunks
756		assert_eq!(Divisible::<U2>::get(&val, 0), U2::new(0b11)); // bits 0-1
757		assert_eq!(Divisible::<U2>::get(&val, 1), U2::new(0b00)); // bits 2-3
758		assert_eq!(Divisible::<U2>::get(&val, 7), U2::new(0b10)); // bits 14-15
759
760		// Test ref_iter
761		let parts: Vec<U2> = Divisible::<U2>::ref_iter(&val).collect();
762		assert_eq!(parts.len(), 8);
763		assert_eq!(parts[0], U2::new(0b11));
764		assert_eq!(parts[7], U2::new(0b10));
765	}
766
767	#[test]
768	fn test_divisible_u16_u1() {
769		// 0b1010_1100_0011_0101 = 0xAC35
770		let val: u16 = 0b1010110000110101;
771
772		// Test get - LSB first: individual bits
773		assert_eq!(Divisible::<U1>::get(&val, 0), U1::new(1)); // bit 0
774		assert_eq!(Divisible::<U1>::get(&val, 1), U1::new(0)); // bit 1
775		assert_eq!(Divisible::<U1>::get(&val, 15), U1::new(1)); // bit 15
776
777		// Test set
778		let mut modified = val;
779		Divisible::<U1>::set(&mut modified, 0, U1::new(0));
780		assert_eq!(modified, 0b1010110000110100);
781
782		// Test ref_iter
783		let parts: Vec<U1> = Divisible::<U1>::ref_iter(&val).collect();
784		assert_eq!(parts.len(), 16);
785		assert_eq!(parts[0], U1::new(1));
786		assert_eq!(parts[15], U1::new(1));
787	}
788
789	#[test]
790	fn test_divisible_u64_u4() {
791		let val: u64 = 0x123456789ABCDEF0;
792
793		// Test get - LSB first: nibbles
794		assert_eq!(Divisible::<U4>::get(&val, 0), U4::new(0x0));
795		assert_eq!(Divisible::<U4>::get(&val, 1), U4::new(0xF));
796		assert_eq!(Divisible::<U4>::get(&val, 15), U4::new(0x1));
797
798		// Iterating a u64 as nibbles yields 64 / 4 = 16 parts.
799		assert_eq!(Divisible::<U4>::ref_iter(&val).count(), 16);
800	}
801
802	#[test]
803	fn test_broadcast_u8_u4() {
804		let result: u8 = Divisible::<U4>::broadcast(U4::new(0x5));
805		assert_eq!(result, 0x55);
806	}
807
808	#[test]
809	fn test_broadcast_u16_u4() {
810		let result: u16 = Divisible::<U4>::broadcast(U4::new(0xA));
811		assert_eq!(result, 0xAAAA);
812	}
813
814	#[test]
815	fn test_broadcast_u8_u2() {
816		let result: u8 = Divisible::<U2>::broadcast(U2::new(0b11));
817		assert_eq!(result, 0xFF);
818		let result: u8 = Divisible::<U2>::broadcast(U2::new(0b01));
819		assert_eq!(result, 0x55);
820	}
821
822	#[test]
823	fn test_broadcast_u8_u1() {
824		let result: u8 = Divisible::<U1>::broadcast(U1::new(0));
825		assert_eq!(result, 0x00);
826		let result: u8 = Divisible::<U1>::broadcast(U1::new(1));
827		assert_eq!(result, 0xFF);
828	}
829
830	#[test]
831	fn test_broadcast_smallu2_from_smallu1() {
832		let result: SmallU<2> = Divisible::<SmallU<1>>::broadcast(SmallU::<1>::new(0));
833		assert_eq!(result.val(), 0b00);
834		let result: SmallU<2> = Divisible::<SmallU<1>>::broadcast(SmallU::<1>::new(1));
835		assert_eq!(result.val(), 0b11);
836	}
837
838	#[test]
839	fn test_broadcast_smallu4_from_smallu1() {
840		let result: SmallU<4> = Divisible::<SmallU<1>>::broadcast(SmallU::<1>::new(0));
841		assert_eq!(result.val(), 0b0000);
842		let result: SmallU<4> = Divisible::<SmallU<1>>::broadcast(SmallU::<1>::new(1));
843		assert_eq!(result.val(), 0b1111);
844	}
845
846	#[test]
847	fn test_broadcast_smallu4_from_smallu2() {
848		let result: SmallU<4> = Divisible::<SmallU<2>>::broadcast(SmallU::<2>::new(0b10));
849		assert_eq!(result.val(), 0b1010);
850	}
851
852	#[test]
853	fn test_from_iter_smallu() {
854		let result: u8 = Divisible::<U4>::from_iter([U4::new(0xA), U4::new(0xB)].into_iter());
855		assert_eq!(result, 0xBA);
856	}
857
858	#[test]
859	fn test_divisible_u32_smallu() {
860		let val = 0xab12cd34u32;
861
862		assert_eq!(Divisible::<U1>::get(&val, 0), U1::new(0));
863		assert_eq!(Divisible::<U1>::get(&val, 1), U1::new(0));
864		assert_eq!(Divisible::<U1>::get(&val, 2), U1::new(1));
865		assert_eq!(Divisible::<U1>::get(&val, 31), U1::new(1));
866
867		assert_eq!(Divisible::<U2>::get(&val, 0), U2::new(0));
868		assert_eq!(Divisible::<U2>::get(&val, 1), U2::new(1));
869		assert_eq!(Divisible::<U2>::get(&val, 2), U2::new(3));
870		assert_eq!(Divisible::<U2>::get(&val, 15), U2::new(2));
871
872		assert_eq!(Divisible::<U4>::get(&val, 0), U4::new(4));
873		assert_eq!(Divisible::<U4>::get(&val, 1), U4::new(3));
874		assert_eq!(Divisible::<U4>::get(&val, 2), U4::new(13));
875		assert_eq!(Divisible::<U4>::get(&val, 7), U4::new(10));
876	}
877
878	proptest! {
879		#[test]
880		fn test_set_get_u32_u1(mut val in any::<u32>(), i in 0usize..32, elem in any::<U1>()) {
881			Divisible::<U1>::set(&mut val, i, elem);
882			assert_eq!(Divisible::<U1>::get(&val, i), elem);
883		}
884
885		#[test]
886		fn test_set_get_u32_u2(mut val in any::<u32>(), i in 0usize..16, elem in any::<U2>()) {
887			Divisible::<U2>::set(&mut val, i, elem);
888			assert_eq!(Divisible::<U2>::get(&val, i), elem);
889		}
890
891		#[test]
892		fn test_set_get_u32_u4(mut val in any::<u32>(), i in 0usize..8, elem in any::<U4>()) {
893			Divisible::<U4>::set(&mut val, i, elem);
894			assert_eq!(Divisible::<U4>::get(&val, i), elem);
895		}
896	}
897}