Skip to main content

binius_field/arch/x86_64/
m128.rs

1// Copyright 2024-2025 Irreducible Inc.
2// Copyright 2026 The Binius Developers
3
4use std::{
5	arch::x86_64::*,
6	mem,
7	ops::{BitAnd, BitAndAssign, BitOr, BitOrAssign, BitXor, BitXorAssign, Not, Shl, Shr},
8};
9
10use binius_utils::{
11	DeserializeBytes, SerializationError, SerializeBytes,
12	bytes::{Buf, BufMut},
13	serialization::{assert_enough_data_for, assert_enough_space_for},
14};
15use bytemuck::{Pod, Zeroable};
16use rand::{
17	distr::{Distribution, StandardUniform},
18	prelude::*,
19};
20use seq_macro::seq;
21
22use crate::{
23	BinaryField,
24	arch::portable::packed::PackedPrimitiveType,
25	underlier::{
26		Divisible, SmallU, UnderlierType, impl_divisible_bitmask, impl_divisible_self, mapget,
27	},
28};
29
30pub const fn m128i_from_u128(x: u128) -> __m128i {
31	// Static assertion that u128 and __m128i have equal alignment
32	let _: [(); align_of::<u128>()] = [(); align_of::<__m128i>()];
33	unsafe { mem::transmute(x) }
34}
35
36/// 128-bit value that is used for 128-bit SIMD operations
37#[derive(Copy, Clone)]
38#[repr(transparent)]
39pub struct M128(pub(super) __m128i);
40
41impl M128 {
42	#[inline(always)]
43	pub const fn from_u128(val: u128) -> Self {
44		Self(m128i_from_u128(val))
45	}
46}
47
48impl From<__m128i> for M128 {
49	#[inline(always)]
50	fn from(value: __m128i) -> Self {
51		Self(value)
52	}
53}
54
55impl From<u128> for M128 {
56	fn from(value: u128) -> Self {
57		Self(m128i_from_u128(value))
58	}
59}
60
61impl From<u64> for M128 {
62	fn from(value: u64) -> Self {
63		Self::from(value as u128)
64	}
65}
66
67impl From<u32> for M128 {
68	fn from(value: u32) -> Self {
69		Self::from(value as u128)
70	}
71}
72
73impl From<u16> for M128 {
74	fn from(value: u16) -> Self {
75		Self::from(value as u128)
76	}
77}
78
79impl From<u8> for M128 {
80	fn from(value: u8) -> Self {
81		Self::from(value as u128)
82	}
83}
84
85impl<const N: usize> From<SmallU<N>> for M128 {
86	fn from(value: SmallU<N>) -> Self {
87		Self::from(value.val() as u128)
88	}
89}
90
91impl From<M128> for u128 {
92	fn from(value: M128) -> Self {
93		let mut result = 0u128;
94		unsafe {
95			// Safety: u128 is 16-byte aligned
96			assert_eq!(align_of::<u128>(), 16);
97			_mm_store_si128(&raw mut result as *mut __m128i, value.0)
98		};
99		result
100	}
101}
102
103impl From<M128> for __m128i {
104	#[inline(always)]
105	fn from(value: M128) -> Self {
106		value.0
107	}
108}
109
110impl SerializeBytes for M128 {
111	fn serialize(&self, mut write_buf: impl BufMut) -> Result<(), SerializationError> {
112		assert_enough_space_for(&write_buf, std::mem::size_of::<Self>())?;
113
114		let raw_value: u128 = (*self).into();
115
116		write_buf.put_u128_le(raw_value);
117		Ok(())
118	}
119}
120
121impl DeserializeBytes for M128 {
122	fn deserialize(mut read_buf: impl Buf) -> Result<Self, SerializationError>
123	where
124		Self: Sized,
125	{
126		assert_enough_data_for(&read_buf, std::mem::size_of::<Self>())?;
127
128		let raw_value = read_buf.get_u128_le();
129
130		Ok(Self::from(raw_value))
131	}
132}
133
134impl_divisible_bitmask!(M128, 1, 2, 4);
135
136impl Default for M128 {
137	#[inline(always)]
138	fn default() -> Self {
139		Self(unsafe { _mm_setzero_si128() })
140	}
141}
142
143impl BitAnd for M128 {
144	type Output = Self;
145
146	#[inline(always)]
147	fn bitand(self, rhs: Self) -> Self::Output {
148		Self(unsafe { _mm_and_si128(self.0, rhs.0) })
149	}
150}
151
152impl BitAndAssign for M128 {
153	#[inline(always)]
154	fn bitand_assign(&mut self, rhs: Self) {
155		*self = *self & rhs
156	}
157}
158
159impl BitOr for M128 {
160	type Output = Self;
161
162	#[inline(always)]
163	fn bitor(self, rhs: Self) -> Self::Output {
164		Self(unsafe { _mm_or_si128(self.0, rhs.0) })
165	}
166}
167
168impl BitOrAssign for M128 {
169	#[inline(always)]
170	fn bitor_assign(&mut self, rhs: Self) {
171		*self = *self | rhs
172	}
173}
174
175impl BitXor for M128 {
176	type Output = Self;
177
178	#[inline(always)]
179	fn bitxor(self, rhs: Self) -> Self::Output {
180		Self(unsafe { _mm_xor_si128(self.0, rhs.0) })
181	}
182}
183
184impl BitXorAssign for M128 {
185	#[inline(always)]
186	fn bitxor_assign(&mut self, rhs: Self) {
187		*self = *self ^ rhs;
188	}
189}
190
191impl Not for M128 {
192	type Output = Self;
193
194	fn not(self) -> Self::Output {
195		const ONES: M128 = M128::from_u128(u128::MAX);
196
197		self ^ ONES
198	}
199}
200
201/// `std::cmp::max` isn't const, so we need our own implementation
202const fn max_i32(left: i32, right: i32) -> i32 {
203	if left > right { left } else { right }
204}
205
206/// This solution shows 4X better performance.
207/// We have to use macro because parameter `count` in _mm_slli_epi64/_mm_srli_epi64 should be passed
208/// as constant and Rust currently doesn't allow passing expressions (`count - 64`) where variable
209/// is a generic constant parameter. Source: <https://stackoverflow.com/questions/34478328/the-best-way-to-shift-a-m128i/34482688#34482688>
210macro_rules! bitshift_128b {
211	($val:expr, $shift:ident, $byte_shift:ident, $bit_shift_64:ident, $bit_shift_64_opposite:ident, $or:ident) => {
212		unsafe {
213			let carry = $byte_shift($val, 8);
214			seq!(N in 64..128 {
215				if $shift == N {
216					return $bit_shift_64(
217						carry,
218						crate::arch::x86_64::m128::max_i32((N - 64) as i32, 0) as _,
219					).into();
220				}
221			});
222			seq!(N in 0..64 {
223				if $shift == N {
224					let carry = $bit_shift_64_opposite(
225						carry,
226						crate::arch::x86_64::m128::max_i32((64 - N) as i32, 0) as _,
227					);
228
229					let val = $bit_shift_64($val, N);
230					return $or(val, carry).into();
231				}
232			});
233
234			return Default::default()
235		}
236	};
237}
238
239impl Shr<usize> for M128 {
240	type Output = Self;
241
242	#[inline(always)]
243	fn shr(self, rhs: usize) -> Self::Output {
244		// This implementation is effective when `rhs` is known at compile-time.
245		// In our code this is always the case.
246		bitshift_128b!(self.0, rhs, _mm_bsrli_si128, _mm_srli_epi64, _mm_slli_epi64, _mm_or_si128)
247	}
248}
249
250impl Shl<usize> for M128 {
251	type Output = Self;
252
253	#[inline(always)]
254	fn shl(self, rhs: usize) -> Self::Output {
255		// This implementation is effective when `rhs` is known at compile-time.
256		// In our code this is always the case.
257		bitshift_128b!(self.0, rhs, _mm_bslli_si128, _mm_slli_epi64, _mm_srli_epi64, _mm_or_si128);
258	}
259}
260
261impl PartialEq for M128 {
262	fn eq(&self, other: &Self) -> bool {
263		unsafe {
264			let neq = _mm_xor_si128(self.0, other.0);
265			_mm_test_all_zeros(neq, neq) == 1
266		}
267	}
268}
269
270impl Eq for M128 {}
271
272impl PartialOrd for M128 {
273	fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
274		Some(self.cmp(other))
275	}
276}
277
278impl Ord for M128 {
279	fn cmp(&self, other: &Self) -> std::cmp::Ordering {
280		u128::from(*self).cmp(&u128::from(*other))
281	}
282}
283
284impl std::hash::Hash for M128 {
285	fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
286		u128::from(*self).hash(state);
287	}
288}
289
290impl std::fmt::LowerHex for M128 {
291	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
292		std::fmt::LowerHex::fmt(&u128::from(*self), f)
293	}
294}
295
296impl Distribution<M128> for StandardUniform {
297	fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> M128 {
298		M128(rng.random())
299	}
300}
301
302impl std::fmt::Display for M128 {
303	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
304		let data: u128 = (*self).into();
305		write!(f, "{data:02X?}")
306	}
307}
308
309impl std::fmt::Debug for M128 {
310	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
311		write!(f, "M128({self})")
312	}
313}
314
315impl UnderlierType for M128 {
316	const LOG_BITS: usize = 7;
317	const ZERO: Self = { Self::from_u128(0) };
318	const ONE: Self = { Self::from_u128(1) };
319	const ONES: Self = { Self::from_u128(u128::MAX) };
320
321	#[inline(always)]
322	fn interleave(self, other: Self, log_block_len: usize) -> (Self, Self) {
323		unsafe {
324			let (c, d) = interleave_bits(
325				Into::<Self>::into(self).into(),
326				Into::<Self>::into(other).into(),
327				log_block_len,
328			);
329			(Self::from(c), Self::from(d))
330		}
331	}
332}
333
334unsafe impl Zeroable for M128 {}
335
336unsafe impl Pod for M128 {}
337
338unsafe impl Send for M128 {}
339
340unsafe impl Sync for M128 {}
341
342impl<Scalar: BinaryField> From<__m128i> for PackedPrimitiveType<M128, Scalar> {
343	fn from(value: __m128i) -> Self {
344		M128::from(value).into()
345	}
346}
347
348impl<Scalar: BinaryField> From<u128> for PackedPrimitiveType<M128, Scalar> {
349	fn from(value: u128) -> Self {
350		M128::from(value).into()
351	}
352}
353
354impl<Scalar: BinaryField> From<PackedPrimitiveType<M128, Scalar>> for __m128i {
355	fn from(value: PackedPrimitiveType<M128, Scalar>) -> Self {
356		value.to_underlier().into()
357	}
358}
359
360#[inline]
361unsafe fn interleave_bits(a: __m128i, b: __m128i, log_block_len: usize) -> (__m128i, __m128i) {
362	match log_block_len {
363		0 => unsafe {
364			let mask = _mm_set1_epi8(0x55i8);
365			interleave_bits_imm::<1>(a, b, mask)
366		},
367		1 => unsafe {
368			let mask = _mm_set1_epi8(0x33i8);
369			interleave_bits_imm::<2>(a, b, mask)
370		},
371		2 => unsafe {
372			let mask = _mm_set1_epi8(0x0fi8);
373			interleave_bits_imm::<4>(a, b, mask)
374		},
375		3 => unsafe {
376			let shuffle = _mm_set_epi8(15, 13, 11, 9, 7, 5, 3, 1, 14, 12, 10, 8, 6, 4, 2, 0);
377			let a = _mm_shuffle_epi8(a, shuffle);
378			let b = _mm_shuffle_epi8(b, shuffle);
379			let a_prime = _mm_unpacklo_epi8(a, b);
380			let b_prime = _mm_unpackhi_epi8(a, b);
381			(a_prime, b_prime)
382		},
383		4 => unsafe {
384			let shuffle = _mm_set_epi8(15, 14, 11, 10, 7, 6, 3, 2, 13, 12, 9, 8, 5, 4, 1, 0);
385			let a = _mm_shuffle_epi8(a, shuffle);
386			let b = _mm_shuffle_epi8(b, shuffle);
387			let a_prime = _mm_unpacklo_epi16(a, b);
388			let b_prime = _mm_unpackhi_epi16(a, b);
389			(a_prime, b_prime)
390		},
391		5 => unsafe {
392			let shuffle = _mm_set_epi8(15, 14, 13, 12, 7, 6, 5, 4, 11, 10, 9, 8, 3, 2, 1, 0);
393			let a = _mm_shuffle_epi8(a, shuffle);
394			let b = _mm_shuffle_epi8(b, shuffle);
395			let a_prime = _mm_unpacklo_epi32(a, b);
396			let b_prime = _mm_unpackhi_epi32(a, b);
397			(a_prime, b_prime)
398		},
399		6 => unsafe {
400			let a_prime = _mm_unpacklo_epi64(a, b);
401			let b_prime = _mm_unpackhi_epi64(a, b);
402			(a_prime, b_prime)
403		},
404		_ => panic!("unsupported block length"),
405	}
406}
407
408#[inline]
409unsafe fn interleave_bits_imm<const BLOCK_LEN: i32>(
410	a: __m128i,
411	b: __m128i,
412	mask: __m128i,
413) -> (__m128i, __m128i) {
414	unsafe {
415		let t = _mm_and_si128(_mm_xor_si128(_mm_srli_epi64::<BLOCK_LEN>(a), b), mask);
416		let a_prime = _mm_xor_si128(a, _mm_slli_epi64::<BLOCK_LEN>(t));
417		let b_prime = _mm_xor_si128(b, t);
418		(a_prime, b_prime)
419	}
420}
421
422// Divisible implementations using SIMD extract/insert intrinsics
423
424// Reflexive divisibility, needed when M128 is itself a field underlier (a width-1 packed field).
425impl_divisible_self!(M128);
426
427impl Divisible<u128> for M128 {
428	const LOG_N: usize = 0;
429
430	#[inline]
431	fn value_iter(value: Self) -> impl ExactSizeIterator<Item = u128> + Send + Clone {
432		std::iter::once(u128::from(value))
433	}
434
435	#[inline]
436	fn ref_iter(value: &Self) -> impl ExactSizeIterator<Item = u128> + Send + Clone + '_ {
437		std::iter::once(u128::from(*value))
438	}
439
440	#[inline]
441	fn slice_iter(slice: &[Self]) -> impl ExactSizeIterator<Item = u128> + Send + Clone + '_ {
442		slice.iter().map(|&v| u128::from(v))
443	}
444
445	#[inline]
446	unsafe fn get_unchecked(&self, _index: usize) -> u128 {
447		u128::from(*self)
448	}
449
450	#[inline]
451	unsafe fn set_unchecked(&mut self, _index: usize, val: u128) {
452		*self = Self::from(val);
453	}
454
455	#[inline]
456	fn broadcast(val: u128) -> Self {
457		Self::from(val)
458	}
459
460	#[inline]
461	fn from_iter(mut iter: impl Iterator<Item = u128>) -> Self {
462		iter.next().map(Self::from).unwrap_or(Self::ZERO)
463	}
464}
465
466impl Divisible<u64> for M128 {
467	const LOG_N: usize = 1;
468
469	#[inline]
470	fn value_iter(value: Self) -> impl ExactSizeIterator<Item = u64> + Send + Clone {
471		mapget::value_iter(value)
472	}
473
474	#[inline]
475	fn ref_iter(value: &Self) -> impl ExactSizeIterator<Item = u64> + Send + Clone + '_ {
476		mapget::value_iter(*value)
477	}
478
479	#[inline]
480	fn slice_iter(slice: &[Self]) -> impl ExactSizeIterator<Item = u64> + Send + Clone + '_ {
481		mapget::slice_iter(slice)
482	}
483
484	#[inline]
485	unsafe fn get_unchecked(&self, index: usize) -> u64 {
486		unsafe {
487			match index {
488				0 => _mm_extract_epi64(self.0, 0) as u64,
489				1 => _mm_extract_epi64(self.0, 1) as u64,
490				_ => core::hint::unreachable_unchecked(),
491			}
492		}
493	}
494
495	#[inline]
496	unsafe fn set_unchecked(&mut self, index: usize, val: u64) {
497		*self = unsafe {
498			match index {
499				0 => Self(_mm_insert_epi64(self.0, val as i64, 0)),
500				1 => Self(_mm_insert_epi64(self.0, val as i64, 1)),
501				_ => core::hint::unreachable_unchecked(),
502			}
503		};
504	}
505
506	#[inline]
507	fn broadcast(val: u64) -> Self {
508		unsafe { Self(_mm_set1_epi64x(val as i64)) }
509	}
510
511	#[inline]
512	fn from_iter(iter: impl Iterator<Item = u64>) -> Self {
513		let mut result = Self::ZERO;
514		let arr: &mut [u64; 2] = bytemuck::cast_mut(&mut result);
515		for (i, val) in iter.take(2).enumerate() {
516			arr[i] = val;
517		}
518		result
519	}
520}
521
522impl Divisible<u32> for M128 {
523	const LOG_N: usize = 2;
524
525	#[inline]
526	fn value_iter(value: Self) -> impl ExactSizeIterator<Item = u32> + Send + Clone {
527		mapget::value_iter(value)
528	}
529
530	#[inline]
531	fn ref_iter(value: &Self) -> impl ExactSizeIterator<Item = u32> + Send + Clone + '_ {
532		mapget::value_iter(*value)
533	}
534
535	#[inline]
536	fn slice_iter(slice: &[Self]) -> impl ExactSizeIterator<Item = u32> + Send + Clone + '_ {
537		mapget::slice_iter(slice)
538	}
539
540	#[inline]
541	unsafe fn get_unchecked(&self, index: usize) -> u32 {
542		unsafe {
543			match index {
544				0 => _mm_extract_epi32(self.0, 0) as u32,
545				1 => _mm_extract_epi32(self.0, 1) as u32,
546				2 => _mm_extract_epi32(self.0, 2) as u32,
547				3 => _mm_extract_epi32(self.0, 3) as u32,
548				_ => core::hint::unreachable_unchecked(),
549			}
550		}
551	}
552
553	#[inline]
554	unsafe fn set_unchecked(&mut self, index: usize, val: u32) {
555		*self = unsafe {
556			match index {
557				0 => Self(_mm_insert_epi32(self.0, val as i32, 0)),
558				1 => Self(_mm_insert_epi32(self.0, val as i32, 1)),
559				2 => Self(_mm_insert_epi32(self.0, val as i32, 2)),
560				3 => Self(_mm_insert_epi32(self.0, val as i32, 3)),
561				_ => core::hint::unreachable_unchecked(),
562			}
563		};
564	}
565
566	#[inline]
567	fn broadcast(val: u32) -> Self {
568		unsafe { Self(_mm_set1_epi32(val as i32)) }
569	}
570
571	#[inline]
572	fn from_iter(iter: impl Iterator<Item = u32>) -> Self {
573		let mut result = Self::ZERO;
574		let arr: &mut [u32; 4] = bytemuck::cast_mut(&mut result);
575		for (i, val) in iter.take(4).enumerate() {
576			arr[i] = val;
577		}
578		result
579	}
580}
581
582impl Divisible<u16> for M128 {
583	const LOG_N: usize = 3;
584
585	#[inline]
586	fn value_iter(value: Self) -> impl ExactSizeIterator<Item = u16> + Send + Clone {
587		mapget::value_iter(value)
588	}
589
590	#[inline]
591	fn ref_iter(value: &Self) -> impl ExactSizeIterator<Item = u16> + Send + Clone + '_ {
592		mapget::value_iter(*value)
593	}
594
595	#[inline]
596	fn slice_iter(slice: &[Self]) -> impl ExactSizeIterator<Item = u16> + Send + Clone + '_ {
597		mapget::slice_iter(slice)
598	}
599
600	#[inline]
601	unsafe fn get_unchecked(&self, index: usize) -> u16 {
602		unsafe {
603			match index {
604				0 => _mm_extract_epi16(self.0, 0) as u16,
605				1 => _mm_extract_epi16(self.0, 1) as u16,
606				2 => _mm_extract_epi16(self.0, 2) as u16,
607				3 => _mm_extract_epi16(self.0, 3) as u16,
608				4 => _mm_extract_epi16(self.0, 4) as u16,
609				5 => _mm_extract_epi16(self.0, 5) as u16,
610				6 => _mm_extract_epi16(self.0, 6) as u16,
611				7 => _mm_extract_epi16(self.0, 7) as u16,
612				_ => core::hint::unreachable_unchecked(),
613			}
614		}
615	}
616
617	#[inline]
618	unsafe fn set_unchecked(&mut self, index: usize, val: u16) {
619		*self = unsafe {
620			match index {
621				0 => Self(_mm_insert_epi16(self.0, val as i32, 0)),
622				1 => Self(_mm_insert_epi16(self.0, val as i32, 1)),
623				2 => Self(_mm_insert_epi16(self.0, val as i32, 2)),
624				3 => Self(_mm_insert_epi16(self.0, val as i32, 3)),
625				4 => Self(_mm_insert_epi16(self.0, val as i32, 4)),
626				5 => Self(_mm_insert_epi16(self.0, val as i32, 5)),
627				6 => Self(_mm_insert_epi16(self.0, val as i32, 6)),
628				7 => Self(_mm_insert_epi16(self.0, val as i32, 7)),
629				_ => core::hint::unreachable_unchecked(),
630			}
631		};
632	}
633
634	#[inline]
635	fn broadcast(val: u16) -> Self {
636		unsafe { Self(_mm_set1_epi16(val as i16)) }
637	}
638
639	#[inline]
640	fn from_iter(iter: impl Iterator<Item = u16>) -> Self {
641		let mut result = Self::ZERO;
642		let arr: &mut [u16; 8] = bytemuck::cast_mut(&mut result);
643		for (i, val) in iter.take(8).enumerate() {
644			arr[i] = val;
645		}
646		result
647	}
648}
649
650impl Divisible<u8> for M128 {
651	const LOG_N: usize = 4;
652
653	#[inline]
654	fn value_iter(value: Self) -> impl ExactSizeIterator<Item = u8> + Send + Clone {
655		mapget::value_iter(value)
656	}
657
658	#[inline]
659	fn ref_iter(value: &Self) -> impl ExactSizeIterator<Item = u8> + Send + Clone + '_ {
660		mapget::value_iter(*value)
661	}
662
663	#[inline]
664	fn slice_iter(slice: &[Self]) -> impl ExactSizeIterator<Item = u8> + Send + Clone + '_ {
665		mapget::slice_iter(slice)
666	}
667
668	#[inline]
669	unsafe fn get_unchecked(&self, index: usize) -> u8 {
670		unsafe {
671			match index {
672				0 => _mm_extract_epi8(self.0, 0) as u8,
673				1 => _mm_extract_epi8(self.0, 1) as u8,
674				2 => _mm_extract_epi8(self.0, 2) as u8,
675				3 => _mm_extract_epi8(self.0, 3) as u8,
676				4 => _mm_extract_epi8(self.0, 4) as u8,
677				5 => _mm_extract_epi8(self.0, 5) as u8,
678				6 => _mm_extract_epi8(self.0, 6) as u8,
679				7 => _mm_extract_epi8(self.0, 7) as u8,
680				8 => _mm_extract_epi8(self.0, 8) as u8,
681				9 => _mm_extract_epi8(self.0, 9) as u8,
682				10 => _mm_extract_epi8(self.0, 10) as u8,
683				11 => _mm_extract_epi8(self.0, 11) as u8,
684				12 => _mm_extract_epi8(self.0, 12) as u8,
685				13 => _mm_extract_epi8(self.0, 13) as u8,
686				14 => _mm_extract_epi8(self.0, 14) as u8,
687				15 => _mm_extract_epi8(self.0, 15) as u8,
688				_ => core::hint::unreachable_unchecked(),
689			}
690		}
691	}
692
693	#[inline]
694	unsafe fn set_unchecked(&mut self, index: usize, val: u8) {
695		*self = unsafe {
696			match index {
697				0 => Self(_mm_insert_epi8(self.0, val as i32, 0)),
698				1 => Self(_mm_insert_epi8(self.0, val as i32, 1)),
699				2 => Self(_mm_insert_epi8(self.0, val as i32, 2)),
700				3 => Self(_mm_insert_epi8(self.0, val as i32, 3)),
701				4 => Self(_mm_insert_epi8(self.0, val as i32, 4)),
702				5 => Self(_mm_insert_epi8(self.0, val as i32, 5)),
703				6 => Self(_mm_insert_epi8(self.0, val as i32, 6)),
704				7 => Self(_mm_insert_epi8(self.0, val as i32, 7)),
705				8 => Self(_mm_insert_epi8(self.0, val as i32, 8)),
706				9 => Self(_mm_insert_epi8(self.0, val as i32, 9)),
707				10 => Self(_mm_insert_epi8(self.0, val as i32, 10)),
708				11 => Self(_mm_insert_epi8(self.0, val as i32, 11)),
709				12 => Self(_mm_insert_epi8(self.0, val as i32, 12)),
710				13 => Self(_mm_insert_epi8(self.0, val as i32, 13)),
711				14 => Self(_mm_insert_epi8(self.0, val as i32, 14)),
712				15 => Self(_mm_insert_epi8(self.0, val as i32, 15)),
713				_ => core::hint::unreachable_unchecked(),
714			}
715		};
716	}
717
718	#[inline]
719	fn broadcast(val: u8) -> Self {
720		unsafe { Self(_mm_set1_epi8(val as i8)) }
721	}
722
723	#[inline]
724	fn from_iter(iter: impl Iterator<Item = u8>) -> Self {
725		let mut result = Self::ZERO;
726		let arr: &mut [u8; 16] = bytemuck::cast_mut(&mut result);
727		for (i, val) in iter.take(16).enumerate() {
728			arr[i] = val;
729		}
730		result
731	}
732}
733
734#[cfg(test)]
735mod tests {
736	use binius_utils::bytes::BytesMut;
737	use proptest::{arbitrary::any, proptest};
738	use rand::prelude::*;
739
740	use super::*;
741
742	fn check_roundtrip<T>(val: M128)
743	where
744		T: From<M128>,
745		M128: From<T>,
746	{
747		assert_eq!(M128::from(T::from(val)), val);
748	}
749
750	#[test]
751	fn test_constants() {
752		assert_eq!(M128::default(), M128::ZERO);
753		assert_eq!(M128::from(0u128), M128::ZERO);
754		assert_eq!(M128::from(1u128), M128::ONE);
755	}
756
757	fn get(value: M128, log_block_len: usize, index: usize) -> M128 {
758		(value >> (index << log_block_len)) & M128::from(1u128 << log_block_len)
759	}
760
761	proptest! {
762		#[test]
763		fn test_conversion(a in any::<u128>()) {
764			check_roundtrip::<u128>(a.into());
765			check_roundtrip::<__m128i>(a.into());
766		}
767
768		#[test]
769		fn test_binary_bit_operations(a in any::<u128>(), b in any::<u128>()) {
770			assert_eq!(M128::from(a & b), M128::from(a) & M128::from(b));
771			assert_eq!(M128::from(a | b), M128::from(a) | M128::from(b));
772			assert_eq!(M128::from(a ^ b), M128::from(a) ^ M128::from(b));
773		}
774
775		#[test]
776		fn test_negate(a in any::<u128>()) {
777			assert_eq!(M128::from(!a), !M128::from(a))
778		}
779
780		#[test]
781		fn test_shifts(a in any::<u128>(), b in 0..128usize) {
782			assert_eq!(M128::from(a << b), M128::from(a) << b);
783			assert_eq!(M128::from(a >> b), M128::from(a) >> b);
784		}
785
786		#[test]
787		fn test_interleave_bits(a in any::<u128>(), b in any::<u128>(), height in 0usize..7) {
788			let a = M128::from(a);
789			let b = M128::from(b);
790
791			let (c, d) = unsafe {interleave_bits(a.0, b.0, height)};
792			let (c, d) = (M128::from(c), M128::from(d));
793
794			for i in (0..128>>height).step_by(2) {
795				assert_eq!(get(c, height, i), get(a, height, i));
796				assert_eq!(get(c, height, i+1), get(b, height, i));
797				assert_eq!(get(d, height, i), get(a, height, i+1));
798				assert_eq!(get(d, height, i+1), get(b, height, i+1));
799			}
800		}
801	}
802
803	#[test]
804	fn test_fill_with_bit() {
805		assert_eq!(M128::fill_with_bit(1), M128::from(u128::MAX));
806		assert_eq!(M128::fill_with_bit(0), M128::from(0u128));
807	}
808
809	#[test]
810	fn test_eq() {
811		let a = M128::from(0u128);
812		let b = M128::from(42u128);
813		let c = M128::from(u128::MAX);
814
815		assert_eq!(a, a);
816		assert_eq!(b, b);
817		assert_eq!(c, c);
818
819		assert_ne!(a, b);
820		assert_ne!(a, c);
821		assert_ne!(b, c);
822	}
823
824	#[test]
825	fn test_serialize_and_deserialize_m128() {
826		let mut rng = StdRng::from_seed([0; 32]);
827
828		let original_value = M128::from(rng.random::<u128>());
829
830		let mut buf = BytesMut::new();
831		original_value.serialize(&mut buf).unwrap();
832
833		let deserialized_value = M128::deserialize(buf.freeze()).unwrap();
834
835		assert_eq!(original_value, deserialized_value);
836	}
837}