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 <zeek@tuta.com>
4// Copyright (C) 2026 Oumuamua Labs. 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
38    /// How many elements fit in one packed vector.
39    const WIDTH: usize;
40
41    /// Pack a slice of scalars into a vector.
42    /// Panics if slice len < WIDTH.
43    fn pack(chunk: &[Self]) -> Self::Packed;
44
45    /// Unpack vector back to scalars.
46    fn unpack(packed: Self::Packed, output: &mut [Self]);
47}
48
49impl<F: HardwareField> PackableField for Flat<F> {
50    type Packed = PackedFlat<F>;
51
52    const WIDTH: usize = F::WIDTH;
53
54    #[inline(always)]
55    fn pack(chunk: &[Self]) -> Self::Packed {
56        PackedFlat::from_raw(F::pack(flat_slice_as_raw(chunk)))
57    }
58
59    #[inline(always)]
60    fn unpack(packed: Self::Packed, output: &mut [Self]) {
61        F::unpack(packed.into_raw(), flat_slice_as_raw_mut(output));
62    }
63}
64
65/// A packed SIMD register storing
66/// hardware / flat-basis field elements.
67#[repr(transparent)]
68pub struct PackedFlat<F: PackableField>(<F as PackableField>::Packed);
69
70impl<F> PackedFlat<F>
71where
72    F: PackableField,
73{
74    #[inline(always)]
75    pub fn from_raw(raw: F::Packed) -> Self {
76        Self(raw)
77    }
78
79    #[inline(always)]
80    pub fn into_raw(self) -> F::Packed {
81        self.0
82    }
83
84    #[inline(always)]
85    pub fn as_raw(&self) -> &F::Packed {
86        &self.0
87    }
88}
89
90impl<F> Copy for PackedFlat<F>
91where
92    F: PackableField,
93    F::Packed: Copy,
94{
95}
96
97impl<F> Clone for PackedFlat<F>
98where
99    F: PackableField,
100    F::Packed: Copy,
101{
102    #[inline(always)]
103    fn clone(&self) -> Self {
104        *self
105    }
106}
107
108impl<F> Default for PackedFlat<F>
109where
110    F: PackableField,
111    F::Packed: Default,
112{
113    #[inline(always)]
114    fn default() -> Self {
115        Self(F::Packed::default())
116    }
117}
118
119impl<F> PartialEq for PackedFlat<F>
120where
121    F: PackableField,
122    F::Packed: PartialEq,
123{
124    #[inline(always)]
125    fn eq(&self, other: &Self) -> bool {
126        self.0 == other.0
127    }
128}
129
130impl<F> Eq for PackedFlat<F>
131where
132    F: PackableField,
133    F::Packed: Eq,
134{
135}
136
137impl<F> Debug for PackedFlat<F>
138where
139    F: PackableField,
140    F::Packed: Debug,
141{
142    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
143        f.debug_tuple("PackedFlat").field(&self.0).finish()
144    }
145}
146
147impl<F: HardwareField> Add for PackedFlat<F> {
148    type Output = Self;
149
150    #[inline(always)]
151    fn add(self, rhs: Self) -> Self::Output {
152        F::add_hardware_packed(self, rhs)
153    }
154}
155
156impl<F: HardwareField> AddAssign for PackedFlat<F> {
157    #[inline(always)]
158    fn add_assign(&mut self, rhs: Self) {
159        *self = *self + rhs;
160    }
161}
162
163impl<F: HardwareField> Sub for PackedFlat<F> {
164    type Output = Self;
165
166    #[inline(always)]
167    fn sub(self, rhs: Self) -> Self::Output {
168        F::add_hardware_packed(self, rhs)
169    }
170}
171
172impl<F: HardwareField> SubAssign for PackedFlat<F> {
173    #[inline(always)]
174    fn sub_assign(&mut self, rhs: Self) {
175        *self = *self - rhs;
176    }
177}
178
179impl<F: HardwareField> Mul for PackedFlat<F> {
180    type Output = Self;
181
182    #[inline(always)]
183    fn mul(self, rhs: Self) -> Self::Output {
184        F::mul_hardware_packed(self, rhs)
185    }
186}
187
188impl<F: HardwareField> MulAssign for PackedFlat<F> {
189    #[inline(always)]
190    fn mul_assign(&mut self, rhs: Self) {
191        *self = *self * rhs;
192    }
193}
194
195impl<F: HardwareField> Mul<Flat<F>> for PackedFlat<F> {
196    type Output = Self;
197
198    #[inline(always)]
199    fn mul(self, rhs: Flat<F>) -> Self::Output {
200        F::mul_hardware_scalar_packed(self, rhs)
201    }
202}
203
204#[inline(always)]
205fn flat_slice_as_raw<F>(slice: &[Flat<F>]) -> &[F] {
206    unsafe { core::slice::from_raw_parts(slice.as_ptr().cast::<F>(), slice.len()) }
207}
208
209#[inline(always)]
210fn flat_slice_as_raw_mut<F>(slice: &mut [Flat<F>]) -> &mut [F] {
211    unsafe { core::slice::from_raw_parts_mut(slice.as_mut_ptr().cast::<F>(), slice.len()) }
212}