Skip to content

Features: Orion implementation related peripheral building blocks #142

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
Nov 12, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
301 changes: 301 additions & 0 deletions arith/gf2/src/gf2x64.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,301 @@
use std::ops::{Add, AddAssign, Mul, MulAssign, Neg, Sub, SubAssign};

use arith::{Field, FieldSerde, FieldSerdeResult, SimdField};

use super::GF2;

#[derive(Debug, Clone, Copy, Default, PartialEq)]
pub struct GF2x64 {
pub v: u64,
}

impl FieldSerde for GF2x64 {
const SERIALIZED_SIZE: usize = 8;

#[inline(always)]
fn serialize_into<W: std::io::Write>(&self, mut writer: W) -> FieldSerdeResult<()> {
writer.write_all(self.v.to_le_bytes().as_ref())?;
Ok(())
}

#[inline(always)]
fn deserialize_from<R: std::io::Read>(mut reader: R) -> FieldSerdeResult<Self> {
let mut u = [0u8; Self::SERIALIZED_SIZE];
reader.read_exact(&mut u)?;
Ok(GF2x64 {
v: u64::from_le_bytes(u),
})
}

#[inline]
fn try_deserialize_from_ecc_format<R: std::io::Read>(_reader: R) -> FieldSerdeResult<Self> {
unimplemented!("We don't have serialization in ecc for gf2x64")
}
}

impl Field for GF2x64 {
const NAME: &'static str = "Galois Field 2 SIMD 64";

const SIZE: usize = 8;

const FIELD_SIZE: usize = 1;

const ZERO: Self = GF2x64 { v: 0 };

const ONE: Self = GF2x64 { v: !0u64 };

const INV_2: Self = GF2x64 { v: 0 }; // NOTE: should not be used

#[inline(always)]
fn zero() -> Self {
GF2x64::ZERO
}

#[inline(always)]
fn one() -> Self {
GF2x64::ONE
}

#[inline(always)]
fn is_zero(&self) -> bool {
self.v == 0
}

#[inline(always)]
fn random_unsafe(mut rng: impl rand::RngCore) -> Self {
GF2x64 { v: rng.next_u64() }
}

#[inline(always)]
fn random_bool(mut rng: impl rand::RngCore) -> Self {
GF2x64 { v: rng.next_u64() }
}

#[inline(always)]
fn exp(&self, exponent: u128) -> Self {
if exponent % 2 == 0 {
Self::one()
} else {
*self
}
}

#[inline(always)]
fn inv(&self) -> Option<Self> {
unimplemented!()
}

#[inline(always)]
fn as_u32_unchecked(&self) -> u32 {
self.v as u32
}

#[inline(always)]
fn from_uniform_bytes(bytes: &[u8; 32]) -> Self {
let mut buf = [0u8; 8];
buf[..].copy_from_slice(&bytes[..8]);
GF2x64 {
v: u64::from_le_bytes(buf),
}
}

#[inline(always)]
fn mul_by_5(&self) -> Self {
*self
}

#[inline(always)]
fn mul_by_6(&self) -> Self {
Self::ZERO
}
}

impl Mul<&GF2x64> for GF2x64 {
type Output = GF2x64;

#[inline(always)]
#[allow(clippy::suspicious_arithmetic_impl)]
fn mul(self, rhs: &GF2x64) -> Self::Output {
GF2x64 { v: self.v & rhs.v }
}
}

impl Mul<GF2x64> for GF2x64 {
type Output = GF2x64;

#[inline(always)]
#[allow(clippy::suspicious_arithmetic_impl)]
fn mul(self, rhs: GF2x64) -> GF2x64 {
GF2x64 { v: self.v & rhs.v }
}
}

impl MulAssign<&GF2x64> for GF2x64 {
#[inline(always)]
#[allow(clippy::suspicious_op_assign_impl)]
fn mul_assign(&mut self, rhs: &GF2x64) {
self.v &= rhs.v;
}
}

impl MulAssign<GF2x64> for GF2x64 {
#[inline(always)]
#[allow(clippy::suspicious_op_assign_impl)]
fn mul_assign(&mut self, rhs: GF2x64) {
self.v &= rhs.v;
}
}

impl Sub for GF2x64 {
type Output = GF2x64;

#[inline(always)]
#[allow(clippy::suspicious_arithmetic_impl)]
fn sub(self, rhs: GF2x64) -> GF2x64 {
GF2x64 { v: self.v ^ rhs.v }
}
}

impl SubAssign for GF2x64 {
#[inline(always)]
#[allow(clippy::suspicious_op_assign_impl)]
fn sub_assign(&mut self, rhs: GF2x64) {
self.v ^= rhs.v;
}
}

impl Add for GF2x64 {
type Output = GF2x64;

#[inline(always)]
#[allow(clippy::suspicious_arithmetic_impl)]
fn add(self, rhs: GF2x64) -> GF2x64 {
GF2x64 { v: self.v ^ rhs.v }
}
}

impl AddAssign for GF2x64 {
#[inline(always)]
#[allow(clippy::suspicious_op_assign_impl)]
fn add_assign(&mut self, rhs: GF2x64) {
self.v ^= rhs.v;
}
}

impl Add<&GF2x64> for GF2x64 {
type Output = GF2x64;

#[inline(always)]
#[allow(clippy::suspicious_arithmetic_impl)]
fn add(self, rhs: &GF2x64) -> GF2x64 {
GF2x64 { v: self.v ^ rhs.v }
}
}

impl Sub<&GF2x64> for GF2x64 {
type Output = GF2x64;

#[inline(always)]
#[allow(clippy::suspicious_arithmetic_impl)]
fn sub(self, rhs: &GF2x64) -> GF2x64 {
GF2x64 { v: self.v ^ rhs.v }
}
}

impl<T: std::borrow::Borrow<GF2x64>> std::iter::Sum<T> for GF2x64 {
fn sum<I: Iterator<Item = T>>(iter: I) -> Self {
iter.fold(Self::zero(), |acc, item| acc + item.borrow())
}
}

impl<T: std::borrow::Borrow<GF2x64>> std::iter::Product<T> for GF2x64 {
fn product<I: Iterator<Item = T>>(iter: I) -> Self {
iter.fold(Self::one(), |acc, item| acc * item.borrow())
}
}

impl Neg for GF2x64 {
type Output = GF2x64;

#[inline(always)]
#[allow(clippy::suspicious_arithmetic_impl)]
fn neg(self) -> GF2x64 {
GF2x64 { v: self.v }
}
}

impl AddAssign<&GF2x64> for GF2x64 {
#[inline(always)]
#[allow(clippy::suspicious_op_assign_impl)]
fn add_assign(&mut self, rhs: &GF2x64) {
self.v ^= rhs.v;
}
}

impl SubAssign<&GF2x64> for GF2x64 {
#[inline(always)]
#[allow(clippy::suspicious_op_assign_impl)]
fn sub_assign(&mut self, rhs: &GF2x64) {
self.v ^= rhs.v;
}
}

impl From<u32> for GF2x64 {
#[inline(always)]
fn from(v: u32) -> Self {
assert!(v < 2);
if v == 0 {
GF2x64 { v: 0 }
} else {
GF2x64 { v: !0u64 }
}
}
}

impl From<GF2> for GF2x64 {
#[inline(always)]
fn from(v: GF2) -> Self {
assert!(v.v < 2);
if v.v == 0 {
GF2x64 { v: 0 }
} else {
GF2x64 { v: !0u64 }
}
}
}

impl SimdField for GF2x64 {
#[inline(always)]
fn scale(&self, challenge: &Self::Scalar) -> Self {
if challenge.v == 0 {
Self::zero()
} else {
*self
}
}

#[inline(always)]
fn pack(base_vec: &[Self::Scalar]) -> Self {
assert!(base_vec.len() == Self::PACK_SIZE);
let mut ret = 0u64;
for (i, scalar) in base_vec.iter().enumerate() {
ret |= (scalar.v as u64) << (Self::PACK_SIZE - 1 - i);
}
Self { v: ret }
}

#[inline(always)]
fn unpack(&self) -> Vec<Self::Scalar> {
let mut ret = vec![];
for i in 0..Self::PACK_SIZE {
ret.push(Self::Scalar {
v: ((self.v >> (Self::PACK_SIZE - 1 - i)) & 1u64) as u8,
});
}
ret
}

type Scalar = crate::GF2;

const PACK_SIZE: usize = 64;
}
13 changes: 5 additions & 8 deletions arith/gf2/src/gf2x8.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ impl FieldSerde for GF2x8 {
impl Field for GF2x8 {
// still will pack 8 bits into a u8

const NAME: &'static str = "Galios Field 2 SIMD";
const NAME: &'static str = "Galios Field 2 SIMD 8";

const SIZE: usize = 1;

Expand Down Expand Up @@ -278,14 +278,9 @@ impl SimdField for GF2x8 {
}
}

#[inline(always)]
fn pack_size() -> usize {
8
}

#[inline(always)]
fn pack(base_vec: &[Self::Scalar]) -> Self {
assert!(base_vec.len() == Self::pack_size());
assert!(base_vec.len() == Self::PACK_SIZE);
let mut ret = 0u8;
for (i, scalar) in base_vec.iter().enumerate() {
ret |= scalar.v << (7 - i);
Expand All @@ -296,7 +291,7 @@ impl SimdField for GF2x8 {
#[inline(always)]
fn unpack(&self) -> Vec<Self::Scalar> {
let mut ret = vec![];
for i in 0..Self::pack_size() {
for i in 0..Self::PACK_SIZE {
ret.push(Self::Scalar {
v: (self.v >> (7 - i)) & 1u8,
});
Expand All @@ -305,4 +300,6 @@ impl SimdField for GF2x8 {
}

type Scalar = crate::GF2;

const PACK_SIZE: usize = 8;
}
3 changes: 3 additions & 0 deletions arith/gf2/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,8 @@ pub use gf2::GF2;
mod gf2x8;
pub use gf2x8::GF2x8;

mod gf2x64;
pub use gf2x64::GF2x64;

#[cfg(test)]
mod tests;
22 changes: 16 additions & 6 deletions arith/gf2/src/tests.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
use ark_std::test_rng;
use std::io::Cursor;

use arith::{random_field_tests, random_inversion_tests, random_simd_field_tests, FieldSerde};
use arith::{
random_field_tests, random_inversion_tests, random_simd_field_tests, Field, FieldSerde,
};

use crate::{GF2x8, GF2};
use crate::{GF2x64, GF2x8, GF2};

#[test]
fn test_field() {
Expand All @@ -17,16 +19,24 @@ fn test_field() {
fn test_simd_field() {
random_field_tests::<GF2x8>("Vectorized GF2".to_string());
random_simd_field_tests::<GF2x8>("Vectorized GF2".to_string());

random_field_tests::<GF2x64>("Vectorized GF2 len 64".to_string());
random_simd_field_tests::<GF2x64>("Vectorized GF2 len 64".to_string());
}

#[test]
fn test_custom_serde_vectorize_gf2() {
let a = GF2x8::from(0);
fn custom_serde_vectorize_gf2<F: Field + FieldSerde>() {
let a = F::from(0);
let mut buffer = vec![];
assert!(a.serialize_into(&mut buffer).is_ok());
let mut cursor = Cursor::new(buffer);
let b = GF2x8::deserialize_from(&mut cursor);
let b = F::deserialize_from(&mut cursor);
assert!(b.is_ok());
let b = b.unwrap();
assert_eq!(a, b);
}

#[test]
fn test_custom_serde_vectorize_gf2() {
custom_serde_vectorize_gf2::<GF2x8>();
custom_serde_vectorize_gf2::<GF2x64>()
}
Loading
Loading