Skip to main content

binius_field/underlier/
underlier_type.rs

1// Copyright 2024-2025 Irreducible Inc.
2
3use std::{
4	fmt::Debug,
5	ops::{BitAnd, BitAndAssign, BitOr, BitOrAssign, BitXor, BitXorAssign, Not, Shl, Shr},
6};
7
8use bytemuck::{NoUninit, TransparentWrapper, Zeroable};
9
10use super::{U1, underlier_with_bit_ops::spread_fallback};
11use crate::{Divisible, Random};
12
13/// Primitive integer underlying a binary field or packed binary field implementation.
14/// Note that this type is not guaranteed to be POD, U1, U2 and U4 have some unused bits.
15pub trait UnderlierType:
16	Debug
17	+ Default
18	+ Eq
19	+ Ord
20	+ Copy
21	+ Random
22	+ NoUninit
23	+ Zeroable
24	+ Sized
25	+ Send
26	+ Sync
27	+ 'static
28	+ BitAnd<Self, Output = Self>
29	+ BitAndAssign<Self>
30	+ BitOr<Self, Output = Self>
31	+ BitOrAssign<Self>
32	+ BitXor<Self, Output = Self>
33	+ BitXorAssign<Self>
34	+ Shr<usize, Output = Self>
35	+ Shl<usize, Output = Self>
36	+ Not<Output = Self>
37	+ Divisible<U1>
38{
39	/// Number of bits in value
40	const LOG_BITS: usize;
41	/// Number of bits used to represent a value.
42	/// This may not be equal to the number of bits in a type instance.
43	const BITS: usize = 1 << Self::LOG_BITS;
44
45	const ZERO: Self;
46	const ONE: Self;
47	const ONES: Self;
48
49	/// Fill value with the given bit
50	/// `val` must be 0 or 1.
51	fn fill_with_bit(val: u8) -> Self {
52		Self::broadcast_subvalue(U1::new(val))
53	}
54
55	/// Interleave with the given bit size
56	fn interleave(self, other: Self, log_block_len: usize) -> (Self, Self);
57
58	/// Transpose with the given bit size
59	fn transpose(mut self, mut other: Self, log_block_len: usize) -> (Self, Self) {
60		assert!(log_block_len < Self::LOG_BITS);
61
62		for log_block_len in (log_block_len..Self::LOG_BITS).rev() {
63			(self, other) = self.interleave(other, log_block_len);
64		}
65
66		(self, other)
67	}
68
69	#[inline]
70	fn from_fn<T>(f: impl FnMut(usize) -> T) -> Self
71	where
72		T: UnderlierType,
73		Self: Divisible<T>,
74	{
75		Self::from_iter((0..<Self as Divisible<T>>::N).map(f))
76	}
77
78	/// Broadcast subvalue to fill `Self`.
79	/// `Self::BITS/T::BITS` is supposed to be a power of 2.
80	#[inline]
81	fn broadcast_subvalue<T>(value: T) -> Self
82	where
83		T: UnderlierType,
84		Self: Divisible<T>,
85	{
86		Divisible::<T>::broadcast(value)
87	}
88
89	/// Spread takes a block of sub_elements of `T` type within the current value and
90	/// repeats them to the full underlier width.
91	///
92	/// # Safety
93	/// `log_block_len + T::LOG_BITS` must be less than or equal to `Self::LOG_BITS`.
94	/// `block_idx` must be less than `1 << (Self::LOG_BITS - log_block_len)`.
95	#[inline]
96	unsafe fn spread<T>(self, log_block_len: usize, block_idx: usize) -> Self
97	where
98		T: UnderlierType,
99		Self: Divisible<T>,
100	{
101		unsafe { spread_fallback::<Self, T>(self, log_block_len, block_idx) }
102	}
103}
104
105/// A type that is transparently backed by an underlier.
106///
107/// This trait is needed to make it possible getting the underlier type from already defined type.
108/// Bidirectional `From` trait implementations are not enough, because they do not allow getting
109/// underlier type in a generic code.
110///
111/// # Safety
112/// `WithUnderlier` can be implemented for a type only if it's representation is a transparent
113/// `Underlier`'s representation. That's allows us casting references of type and it's underlier in
114/// both directions.
115pub unsafe trait WithUnderlier:
116	TransparentWrapper<Self::Underlier> + Sized + Zeroable + Copy + Send + Sync + 'static
117{
118	/// Underlier primitive type
119	type Underlier: UnderlierType;
120
121	/// Convert value to underlier.
122	#[inline]
123	fn to_underlier(self) -> Self::Underlier {
124		Self::peel(self)
125	}
126
127	#[inline]
128	fn to_underlier_ref(&self) -> &Self::Underlier {
129		Self::peel_ref(self)
130	}
131
132	#[inline]
133	fn to_underlier_ref_mut(&mut self) -> &mut Self::Underlier {
134		Self::peel_mut(self)
135	}
136
137	#[inline]
138	fn to_underliers_ref(val: &[Self]) -> &[Self::Underlier] {
139		Self::peel_slice(val)
140	}
141
142	#[inline]
143	fn to_underliers_ref_mut(val: &mut [Self]) -> &mut [Self::Underlier] {
144		Self::peel_slice_mut(val)
145	}
146
147	#[inline]
148	fn to_underliers_arr<const N: usize>(val: [Self; N]) -> [Self::Underlier; N] {
149		val.map(Self::to_underlier)
150	}
151
152	#[inline]
153	fn to_underliers_arr_ref<const N: usize>(val: &[Self; N]) -> &[Self::Underlier; N] {
154		Self::to_underliers_ref(val)
155			.try_into()
156			.expect("array size is valid")
157	}
158
159	#[inline]
160	fn to_underliers_arr_ref_mut<const N: usize>(val: &mut [Self; N]) -> &mut [Self::Underlier; N] {
161		Self::to_underliers_ref_mut(val)
162			.try_into()
163			.expect("array size is valid")
164	}
165
166	#[inline]
167	fn from_underlier(val: Self::Underlier) -> Self {
168		Self::wrap(val)
169	}
170
171	#[inline]
172	fn from_underlier_ref(val: &Self::Underlier) -> &Self {
173		Self::wrap_ref(val)
174	}
175
176	#[inline]
177	fn from_underlier_ref_mut(val: &mut Self::Underlier) -> &mut Self {
178		Self::wrap_mut(val)
179	}
180
181	#[inline]
182	fn from_underliers_ref(val: &[Self::Underlier]) -> &[Self] {
183		Self::wrap_slice(val)
184	}
185
186	#[inline]
187	fn from_underliers_ref_mut(val: &mut [Self::Underlier]) -> &mut [Self] {
188		Self::wrap_slice_mut(val)
189	}
190
191	#[inline]
192	fn from_underliers_arr<const N: usize>(val: [Self::Underlier; N]) -> [Self; N] {
193		val.map(Self::from_underlier)
194	}
195
196	#[inline]
197	fn from_underliers_arr_ref<const N: usize>(val: &[Self::Underlier; N]) -> &[Self; N] {
198		Self::from_underliers_ref(val)
199			.try_into()
200			.expect("array size is valid")
201	}
202
203	#[inline]
204	fn from_underliers_arr_ref_mut<const N: usize>(
205		val: &mut [Self::Underlier; N],
206	) -> &mut [Self; N] {
207		Self::from_underliers_ref_mut(val)
208			.try_into()
209			.expect("array size is valid")
210	}
211
212	#[inline]
213	fn mutate_underlier(self, f: impl FnOnce(Self::Underlier) -> Self::Underlier) -> Self {
214		Self::from_underlier(f(self.to_underlier()))
215	}
216}
217
218/// A trait that represents potentially lossy numeric cast.
219/// Is a drop-in replacement of `as _` in a generic code.
220pub trait NumCast<From> {
221	fn num_cast_from(val: From) -> Self;
222}
223
224impl<U: UnderlierType> NumCast<U> for U {
225	#[inline(always)]
226	fn num_cast_from(val: U) -> Self {
227		val
228	}
229}