Skip to main content

hekate_math/
hardware.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
18//! The flat basis: `HardwareField` (φ and flat arithmetic), the
19//! `Flat<F>` wrapper that keeps the bases apart, and `FlatPromote`.
20
21use crate::packable::PackedFlat;
22use crate::{PackableField, TowerField};
23use core::fmt::{self, Debug, Formatter};
24use core::ops::{Add, AddAssign, Mul, MulAssign, Sub, SubAssign};
25use zeroize::Zeroize;
26
27/// A [`TowerField`] with a flat basis: the isomorphism
28/// φ and the flat arithmetic behind [`Flat`] operators.
29pub trait HardwareField: TowerField + PackableField {
30    /// Maps `self` to the flat basis: φ(self).
31    fn to_hardware(self) -> Flat<Self>;
32
33    /// Maps `value` back to the tower basis: φ⁻¹(value).
34    fn from_hardware(value: Flat<Self>) -> Self;
35
36    /// Adds two flat elements (XOR).
37    fn add_hardware(lhs: Flat<Self>, rhs: Flat<Self>) -> Flat<Self>;
38
39    /// Adds two packed flat vectors lane by lane (XOR).
40    fn add_hardware_packed(lhs: PackedFlat<Self>, rhs: PackedFlat<Self>) -> PackedFlat<Self>;
41
42    /// Multiplies two flat elements.
43    fn mul_hardware(lhs: Flat<Self>, rhs: Flat<Self>) -> Flat<Self>;
44
45    /// Multiplies two packed flat vectors lane by lane.
46    fn mul_hardware_packed(lhs: PackedFlat<Self>, rhs: PackedFlat<Self>) -> PackedFlat<Self>;
47
48    /// Multiplies every lane of `lhs` by the flat scalar `rhs`.
49    fn mul_hardware_scalar_packed(lhs: PackedFlat<Self>, rhs: Flat<Self>) -> PackedFlat<Self>;
50
51    /// Returns bit `bit_idx` of φ⁻¹(value) without
52    /// a full conversion; constant time in `value`.
53    ///
54    /// # Panics
55    /// If `bit_idx >= Self::BITS`.
56    fn tower_bit_from_hardware(value: Flat<Self>, bit_idx: usize) -> u8;
57}
58
59/// An element of `F` in the flat basis; `+`, `-` and `*` compute in that basis.
60#[derive(Copy, Clone, Default, PartialEq, Eq, Zeroize)]
61#[repr(transparent)]
62pub struct Flat<F>(F);
63
64impl<F> Flat<F> {
65    /// Wraps `raw` as flat-basis bits; does not convert.
66    #[inline(always)]
67    pub fn from_raw(raw: F) -> Self {
68        Self(raw)
69    }
70
71    /// Returns the flat-basis bits; does not convert.
72    #[inline(always)]
73    pub fn into_raw(self) -> F {
74        self.0
75    }
76
77    /// Borrows the flat-basis bits; does not convert.
78    #[inline(always)]
79    pub fn as_raw(&self) -> &F {
80        &self.0
81    }
82}
83
84impl<F: Debug> Debug for Flat<F> {
85    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
86        f.debug_tuple("Flat").field(&self.0).finish()
87    }
88}
89
90impl<F: HardwareField> Flat<F> {
91    /// Maps `self` back to the tower basis: φ⁻¹(self).
92    #[inline(always)]
93    pub fn to_tower(self) -> F {
94        F::from_hardware(self)
95    }
96
97    /// Returns bit `bit_idx` of `self` in the tower basis.
98    ///
99    /// # Panics
100    /// If `bit_idx >= F::BITS`.
101    #[inline(always)]
102    pub fn tower_bit(self, bit_idx: usize) -> u8 {
103        F::tower_bit_from_hardware(self, bit_idx)
104    }
105}
106
107impl<F: HardwareField> Add for Flat<F> {
108    type Output = Self;
109
110    #[inline(always)]
111    fn add(self, rhs: Self) -> Self::Output {
112        F::add_hardware(self, rhs)
113    }
114}
115
116impl<F: HardwareField> AddAssign for Flat<F> {
117    #[inline(always)]
118    fn add_assign(&mut self, rhs: Self) {
119        *self = *self + rhs;
120    }
121}
122
123impl<F: HardwareField> Sub for Flat<F> {
124    type Output = Self;
125
126    #[inline(always)]
127    fn sub(self, rhs: Self) -> Self::Output {
128        F::add_hardware(self, rhs)
129    }
130}
131
132impl<F: HardwareField> SubAssign for Flat<F> {
133    #[inline(always)]
134    fn sub_assign(&mut self, rhs: Self) {
135        *self = *self - rhs;
136    }
137}
138
139impl<F: HardwareField> Mul for Flat<F> {
140    type Output = Self;
141
142    #[inline(always)]
143    fn mul(self, rhs: Self) -> Self::Output {
144        F::mul_hardware(self, rhs)
145    }
146}
147
148impl<F: HardwareField> MulAssign for Flat<F> {
149    #[inline(always)]
150    fn mul_assign(&mut self, rhs: Self) {
151        *self = *self * rhs;
152    }
153}
154
155/// Embedding of a flat subfield `FromF` into the flat basis of `Self`:
156/// `promote_flat(x.to_hardware())` equals `Self::from(x).to_hardware()`.
157pub trait FlatPromote<FromF>: HardwareField
158where
159    FromF: HardwareField,
160{
161    /// Maps a flat `FromF` element into the flat basis of `Self`.
162    fn promote_flat(val: Flat<FromF>) -> Flat<Self>;
163
164    /// Promotes `input[i]` into `output[i]` for every `i` below
165    /// the shorter length; the rest of `output` is left as it is.
166    fn promote_flat_batch(input: &[Flat<FromF>], output: &mut [Flat<Self>]) {
167        for (o, v) in output.iter_mut().zip(input.iter()) {
168            *o = Self::promote_flat(*v);
169        }
170    }
171}
172
173impl<F: HardwareField> FlatPromote<F> for F {
174    #[inline(always)]
175    fn promote_flat(val: Flat<F>) -> Flat<Self> {
176        val
177    }
178}