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