Skip to main content

hekate_math/
packable.rs

1// SPDX-License-Identifier: Apache-2.0
2// This file is part of the hekate-math project.
3// Copyright (C) 2026 Andrei Kochergin <andrei@oumuamua.dev>
4// Copyright (C) 2026 Oumuamua Labs <info@oumuamua.dev>. All rights reserved.
5//
6// Licensed under the Apache License, Version 2.0 (the "License");
7// you may not use this file except in compliance with the License.
8// You may obtain a copy of the License at
9//
10//     http://www.apache.org/licenses/LICENSE-2.0
11//
12// Unless required by applicable law or agreed to in writing, software
13// distributed under the License is distributed on an "AS IS" BASIS,
14// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15// See the License for the specific language governing permissions and
16// limitations under the License.
17
18use crate::{Flat, HardwareField};
19use core::fmt;
20use core::fmt::{Debug, Formatter};
21use core::ops::{Add, AddAssign, Mul, MulAssign, Sub, SubAssign};
22
23/// A trait linking a Field element
24/// to its SIMD packed representation.
25pub trait PackableField: Sized + Copy + Clone + Default {
26    /// The packed vector type (e.g., PackedBlock128).
27    type Packed: Add<Output = Self::Packed>
28        + Sub<Output = Self::Packed>
29        + Mul<Output = Self::Packed>
30        + Mul<Self, Output = Self::Packed>
31        + AddAssign
32        + SubAssign
33        + MulAssign
34        + Copy
35        + Clone
36        + Default
37        + Send;
38
39    /// How many elements fit in one packed vector.
40    const WIDTH: usize;
41
42    /// Pack a slice of scalars into a vector.
43    /// Panics if slice len < WIDTH.
44    fn pack(chunk: &[Self]) -> Self::Packed;
45
46    /// Unpack vector back to scalars.
47    fn unpack(packed: Self::Packed, output: &mut [Self]);
48}
49
50impl<F: HardwareField> PackableField for Flat<F> {
51    type Packed = PackedFlat<F>;
52
53    const WIDTH: usize = F::WIDTH;
54
55    #[inline(always)]
56    fn pack(chunk: &[Self]) -> Self::Packed {
57        PackedFlat::from_raw(F::pack(flat_slice_as_raw(chunk)))
58    }
59
60    #[inline(always)]
61    fn unpack(packed: Self::Packed, output: &mut [Self]) {
62        F::unpack(packed.into_raw(), flat_slice_as_raw_mut(output));
63    }
64}
65
66/// A packed SIMD register storing
67/// hardware / flat-basis field elements.
68#[repr(transparent)]
69pub struct PackedFlat<F: PackableField>(<F as PackableField>::Packed);
70
71impl<F> PackedFlat<F>
72where
73    F: PackableField,
74{
75    #[inline(always)]
76    pub fn from_raw(raw: F::Packed) -> Self {
77        Self(raw)
78    }
79
80    #[inline(always)]
81    pub fn into_raw(self) -> F::Packed {
82        self.0
83    }
84
85    #[inline(always)]
86    pub fn as_raw(&self) -> &F::Packed {
87        &self.0
88    }
89}
90
91impl<F> Copy for PackedFlat<F>
92where
93    F: PackableField,
94    F::Packed: Copy,
95{
96}
97
98impl<F> Clone for PackedFlat<F>
99where
100    F: PackableField,
101    F::Packed: Copy,
102{
103    #[inline(always)]
104    fn clone(&self) -> Self {
105        *self
106    }
107}
108
109impl<F> Default for PackedFlat<F>
110where
111    F: PackableField,
112    F::Packed: Default,
113{
114    #[inline(always)]
115    fn default() -> Self {
116        Self(F::Packed::default())
117    }
118}
119
120impl<F> PartialEq for PackedFlat<F>
121where
122    F: PackableField,
123    F::Packed: PartialEq,
124{
125    #[inline(always)]
126    fn eq(&self, other: &Self) -> bool {
127        self.0 == other.0
128    }
129}
130
131impl<F> Eq for PackedFlat<F>
132where
133    F: PackableField,
134    F::Packed: Eq,
135{
136}
137
138impl<F> Debug for PackedFlat<F>
139where
140    F: PackableField,
141    F::Packed: Debug,
142{
143    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
144        f.debug_tuple("PackedFlat").field(&self.0).finish()
145    }
146}
147
148impl<F: HardwareField> Add for PackedFlat<F> {
149    type Output = Self;
150
151    #[inline(always)]
152    fn add(self, rhs: Self) -> Self::Output {
153        F::add_hardware_packed(self, rhs)
154    }
155}
156
157impl<F: HardwareField> AddAssign for PackedFlat<F> {
158    #[inline(always)]
159    fn add_assign(&mut self, rhs: Self) {
160        *self = *self + rhs;
161    }
162}
163
164impl<F: HardwareField> Sub for PackedFlat<F> {
165    type Output = Self;
166
167    #[inline(always)]
168    fn sub(self, rhs: Self) -> Self::Output {
169        F::add_hardware_packed(self, rhs)
170    }
171}
172
173impl<F: HardwareField> SubAssign for PackedFlat<F> {
174    #[inline(always)]
175    fn sub_assign(&mut self, rhs: Self) {
176        *self = *self - rhs;
177    }
178}
179
180impl<F: HardwareField> Mul for PackedFlat<F> {
181    type Output = Self;
182
183    #[inline(always)]
184    fn mul(self, rhs: Self) -> Self::Output {
185        F::mul_hardware_packed(self, rhs)
186    }
187}
188
189impl<F: HardwareField> MulAssign for PackedFlat<F> {
190    #[inline(always)]
191    fn mul_assign(&mut self, rhs: Self) {
192        *self = *self * rhs;
193    }
194}
195
196impl<F: HardwareField> Mul<Flat<F>> for PackedFlat<F> {
197    type Output = Self;
198
199    #[inline(always)]
200    fn mul(self, rhs: Flat<F>) -> Self::Output {
201        F::mul_hardware_scalar_packed(self, rhs)
202    }
203}
204
205#[inline(always)]
206fn flat_slice_as_raw<F>(slice: &[Flat<F>]) -> &[F] {
207    unsafe { core::slice::from_raw_parts(slice.as_ptr().cast::<F>(), slice.len()) }
208}
209
210#[inline(always)]
211fn flat_slice_as_raw_mut<F>(slice: &mut [Flat<F>]) -> &mut [F] {
212    unsafe { core::slice::from_raw_parts_mut(slice.as_mut_ptr().cast::<F>(), slice.len()) }
213}