use std::alloc::{Allocator, Global};
use std::cell::OnceCell;
use std::fmt::{Debug, Formatter};
use std::marker::PhantomData;
use std::mem::replace;
use feanor_serde::dependent_tuple::DeserializeSeedDependentTuple;
use feanor_serde::newtype_struct::{DeserializeSeedNewtypeStruct, *};
use feanor_serde::seq::*;
use serde::de::DeserializeSeed;
use serde::{Deserialize, Deserializer, Serialize, Serializer};
use super::{FreeAlgebra, FreeAlgebraStore};
use crate::algorithms::convolution::*;
use crate::algorithms::extension_ops::create_multiplication_matrix;
use crate::algorithms::linsolve::LinSolveRing;
use crate::delegate::DelegateRing;
use crate::divisibility::*;
use crate::homomorphism::*;
use crate::integer::{IntegerRingStore, *};
use crate::iters::{MultiProduct, multi_cartesian_product};
use crate::matrix::OwnedMatrix;
use crate::primitive_int::StaticRing;
use crate::ring::*;
use crate::rings::finite::*;
use crate::rings::poly::PolyRingStore;
use crate::rings::poly::dense_poly::DensePolyRing;
use crate::seq::sparse::SparseMapVector;
use crate::seq::*;
use crate::serialization::*;
use crate::specialization::*;
use crate::{
impl_field_wrap_unwrap_homs, impl_field_wrap_unwrap_isos, impl_localpir_wrap_unwrap_homs,
impl_localpir_wrap_unwrap_isos,
};
pub struct FreeAlgebraImplBase<R, V, A = Global, C = KaratsubaAlgorithm>
where
R: RingStore,
V: VectorView<El<R>>,
A: Allocator + Clone,
C: ConvolutionAlgorithm<R::Type>,
{
base_ring: R,
x_pow_rank: V,
element_allocator: A,
log2_padded_len: usize,
rank: usize,
gen_name: &'static str,
convolution: C,
}
pub type FreeAlgebraImpl<R, V, A = Global, C = KaratsubaAlgorithm> = RingValue<FreeAlgebraImplBase<R, V, A, C>>;
impl<R, V> FreeAlgebraImpl<R, V>
where
R: RingStore,
V: VectorView<El<R>>,
{
pub fn new(base_ring: R, rank: usize, x_pow_rank: V) -> Self {
Self::new_with_convolution(base_ring, rank, x_pow_rank, "θ", Global, STANDARD_CONVOLUTION)
}
}
impl<R, V, A, C> Clone for FreeAlgebraImplBase<R, V, A, C>
where
R: RingStore + Clone,
V: VectorView<El<R>> + Clone,
A: Allocator + Clone,
C: ConvolutionAlgorithm<R::Type> + Clone,
{
fn clone(&self) -> Self {
Self {
base_ring: self.base_ring.clone(),
x_pow_rank: self.x_pow_rank.clone(),
element_allocator: self.element_allocator.clone(),
log2_padded_len: self.log2_padded_len,
rank: self.rank,
convolution: self.convolution.clone(),
gen_name: self.gen_name,
}
}
}
impl<R, V, A, C> Copy for FreeAlgebraImplBase<R, V, A, C>
where
R: RingStore + Copy,
V: VectorView<El<R>> + Copy,
A: Allocator + Copy,
C: ConvolutionAlgorithm<R::Type> + Copy,
El<R>: Copy,
{
}
pub struct FreeAlgebraImplEl<R, A = Global>
where
R: RingStore,
A: Allocator,
{
values: Box<[El<R>], A>,
}
impl<R, A> Debug for FreeAlgebraImplEl<R, A>
where
R: RingStore,
A: Allocator,
El<R>: Debug,
{
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { write!(f, "{:?}", &self.values) }
}
impl<R, V, A, C> FreeAlgebraImpl<R, V, A, C>
where
R: RingStore,
V: VectorView<El<R>>,
A: Allocator + Clone,
C: ConvolutionAlgorithm<R::Type>,
{
#[stability::unstable(feature = "enable")]
pub fn new_with_convolution(
base_ring: R,
rank: usize,
x_pow_rank: V,
gen_name: &'static str,
element_allocator: A,
convolution: C,
) -> Self {
assert!(rank >= 1);
assert!(x_pow_rank.len() <= rank);
let log2_padded_len = StaticRing::<i64>::RING
.abs_log2_ceil(&rank.try_into().unwrap())
.unwrap();
RingValue::from(FreeAlgebraImplBase {
base_ring,
gen_name,
x_pow_rank,
element_allocator,
rank,
log2_padded_len,
convolution,
})
}
}
impl<R, V, A, C> FreeAlgebraImplBase<R, V, A, C>
where
R: RingStore,
V: VectorView<El<R>>,
A: Allocator + Clone,
C: ConvolutionAlgorithm<R::Type>,
{
#[stability::unstable(feature = "enable")]
pub fn set_convolution<C_new: ConvolutionAlgorithm<R::Type>>(
self,
new_convolution: C_new,
) -> FreeAlgebraImpl<R, V, A, C_new> {
RingValue::from(FreeAlgebraImplBase {
base_ring: self.base_ring,
x_pow_rank: self.x_pow_rank,
element_allocator: self.element_allocator,
log2_padded_len: self.log2_padded_len,
rank: self.rank,
convolution: new_convolution,
gen_name: self.gen_name,
})
}
#[stability::unstable(feature = "enable")]
pub fn allocator(&self) -> &A { &self.element_allocator }
#[stability::unstable(feature = "enable")]
pub fn gen_name(&self) -> &'static str { self.gen_name }
#[stability::unstable(feature = "enable")]
pub fn x_pow_rank(&self) -> &V { &self.x_pow_rank }
#[stability::unstable(feature = "enable")]
pub fn convolution(&self) -> &C { &self.convolution }
fn reduce_mod_poly(&self, data: &mut [El<R>]) {
struct ReduceModulo<'a, R>
where
R: RingStore,
{
rank: usize,
base_ring: &'a R,
data: &'a mut [El<R>],
}
impl<'a, R> SparseVectorViewOperation<El<R>> for ReduceModulo<'a, R>
where
R: RingStore,
{
type Output<'b>
= ()
where
Self: 'b;
fn execute<'b, V: 'b + VectorViewSparse<El<R>>>(self, vector: V) {
for i in (self.rank..(2 * self.rank)).rev() {
for (j, c) in vector.nontrivial_entries() {
let add = self.base_ring.mul_ref(c, &self.data[i]);
self.base_ring.add_assign(&mut self.data[i - self.rank + j], add);
}
}
}
}
let was_sparse = self.x_pow_rank.specialize_sparse(ReduceModulo {
rank: self.rank,
base_ring: self.base_ring(),
data,
});
if was_sparse.is_none() {
for i in (self.rank()..(2 * self.rank())).rev() {
for j in 0..self.x_pow_rank.len() {
let add = self.base_ring.mul_ref(self.x_pow_rank.at(j), &data[i]);
self.base_ring.add_assign(&mut data[i - self.rank() + j], add);
}
}
}
}
}
impl<R, V, A, C> PartialEq for FreeAlgebraImplBase<R, V, A, C>
where
R: RingStore,
V: VectorView<El<R>>,
A: Allocator + Clone,
C: ConvolutionAlgorithm<R::Type>,
{
fn eq(&self, other: &Self) -> bool {
self.base_ring().get_ring() == other.base_ring().get_ring()
&& self.rank() == other.rank()
&& (0..self.rank()).all(|i| {
(i >= self.x_pow_rank.len() && i >= other.x_pow_rank.len())
|| (i >= self.x_pow_rank.len() && self.base_ring().is_zero(other.x_pow_rank.at(i)))
|| (i >= other.x_pow_rank.len() && self.base_ring().is_zero(self.x_pow_rank.at(i)))
|| self.base_ring().eq_el(self.x_pow_rank.at(i), other.x_pow_rank.at(i))
})
}
}
impl<R, V, A, C> RingBase for FreeAlgebraImplBase<R, V, A, C>
where
R: RingStore,
V: VectorView<El<R>>,
A: Allocator + Clone,
C: ConvolutionAlgorithm<R::Type>,
{
type Element = FreeAlgebraImplEl<R, A>;
fn clone_el(&self, val: &Self::Element) -> Self::Element {
debug_assert_eq!(1 << self.log2_padded_len, val.values.len());
let mut result_values = Vec::with_capacity_in(val.values.len(), self.element_allocator.clone());
result_values.extend(val.values.iter().map(|x| self.base_ring().clone_el(x)));
return FreeAlgebraImplEl {
values: result_values.into_boxed_slice(),
};
}
fn add_assign_ref(&self, lhs: &mut Self::Element, rhs: &Self::Element) {
debug_assert_eq!(1 << self.log2_padded_len, lhs.values.len());
debug_assert_eq!(1 << self.log2_padded_len, rhs.values.len());
for i in 0..self.rank() {
self.base_ring().add_assign_ref(&mut lhs.values[i], &rhs.values[i]);
}
}
fn add_assign(&self, lhs: &mut Self::Element, rhs: Self::Element) {
debug_assert_eq!(1 << self.log2_padded_len, lhs.values.len());
debug_assert_eq!(1 << self.log2_padded_len, rhs.values.len());
for (i, x) in (0..self.rank()).zip(rhs.values.iter()) {
self.base_ring().add_assign_ref(&mut lhs.values[i], x);
}
}
fn sub_assign_ref(&self, lhs: &mut Self::Element, rhs: &Self::Element) {
debug_assert_eq!(1 << self.log2_padded_len, lhs.values.len());
debug_assert_eq!(1 << self.log2_padded_len, rhs.values.len());
for i in 0..self.rank() {
self.base_ring().sub_assign_ref(&mut lhs.values[i], &rhs.values[i]);
}
}
fn negate_inplace(&self, lhs: &mut Self::Element) {
debug_assert_eq!(1 << self.log2_padded_len, lhs.values.len());
for i in 0..self.rank() {
self.base_ring().negate_inplace(&mut lhs.values[i]);
}
}
fn mul_assign(&self, lhs: &mut Self::Element, rhs: Self::Element) {
debug_assert_eq!(1 << self.log2_padded_len, lhs.values.len());
debug_assert_eq!(1 << self.log2_padded_len, rhs.values.len());
self.mul_assign_ref(lhs, &rhs)
}
fn mul_assign_ref(&self, lhs: &mut Self::Element, rhs: &Self::Element) { *lhs = self.fma(lhs, rhs, self.zero()); }
fn fma(&self, lhs: &Self::Element, rhs: &Self::Element, summand: Self::Element) -> Self::Element {
debug_assert_eq!(1 << self.log2_padded_len, lhs.values.len());
debug_assert_eq!(1 << self.log2_padded_len, rhs.values.len());
debug_assert_eq!(1 << self.log2_padded_len, summand.values.len());
let mut tmp = summand.values.into_vec();
tmp.resize_with(2 << self.log2_padded_len, || self.base_ring.zero());
self.convolution
.compute_convolution(&lhs.values[..], &rhs.values[..], &mut tmp[..], self.base_ring());
self.reduce_mod_poly(&mut tmp);
tmp.truncate(1 << self.log2_padded_len);
for i in self.rank()..(1 << self.log2_padded_len) {
tmp[i] = self.base_ring().zero();
}
return FreeAlgebraImplEl {
values: tmp.into_boxed_slice(),
};
}
fn from_int(&self, value: i32) -> Self::Element { self.from(self.base_ring().int_hom().map(value)) }
fn eq_el(&self, lhs: &Self::Element, rhs: &Self::Element) -> bool {
debug_assert_eq!(1 << self.log2_padded_len, lhs.values.len());
debug_assert_eq!(1 << self.log2_padded_len, rhs.values.len());
(0..self.rank()).all(|i| self.base_ring().eq_el(&lhs.values[i], &rhs.values[i]))
}
fn is_zero(&self, value: &Self::Element) -> bool {
debug_assert_eq!(1 << self.log2_padded_len, value.values.len());
(0..self.rank()).all(|i| self.base_ring.is_zero(&value.values[i]))
}
fn is_one(&self, value: &Self::Element) -> bool {
debug_assert_eq!(1 << self.log2_padded_len, value.values.len());
self.base_ring().is_one(&value.values[0]) && (1..self.rank()).all(|i| self.base_ring.is_zero(&value.values[i]))
}
fn is_neg_one(&self, value: &Self::Element) -> bool {
debug_assert_eq!(1 << self.log2_padded_len, value.values.len());
self.base_ring().is_neg_one(&value.values[0])
&& (1..self.rank()).all(|i| self.base_ring.is_zero(&value.values[i]))
}
fn is_commutative(&self) -> bool { self.base_ring().is_commutative() }
fn is_noetherian(&self) -> bool { self.base_ring().is_noetherian() }
fn is_approximate(&self) -> bool { self.base_ring().get_ring().is_approximate() }
fn dbg<'a>(&self, value: &Self::Element, out: &mut std::fmt::Formatter<'a>) -> std::fmt::Result {
debug_assert_eq!(1 << self.log2_padded_len, value.values.len());
self.dbg_within(value, out, EnvBindingStrength::Weakest)
}
fn dbg_within<'a>(
&self,
value: &Self::Element,
out: &mut std::fmt::Formatter<'a>,
env: EnvBindingStrength,
) -> std::fmt::Result {
debug_assert_eq!(1 << self.log2_padded_len, value.values.len());
let poly_ring = DensePolyRing::new(self.base_ring(), self.gen_name);
poly_ring.get_ring().dbg_within(
&RingRef::new(self).poly_repr(&poly_ring, value, &self.base_ring().identity()),
out,
env,
)
}
fn mul_assign_int(&self, lhs: &mut Self::Element, rhs: i32) {
debug_assert_eq!(1 << self.log2_padded_len, lhs.values.len());
for i in 0..self.rank() {
self.base_ring().int_hom().mul_assign_map(&mut lhs.values[i], rhs);
}
}
fn fma_int(&self, lhs: &Self::Element, rhs: i32, summand: Self::Element) -> Self::Element {
debug_assert_eq!(1 << self.log2_padded_len, lhs.values.len());
debug_assert_eq!(1 << self.log2_padded_len, summand.values.len());
let mut result = Vec::new_in(self.allocator().clone());
result.extend(
summand
.values
.into_iter()
.enumerate()
.map(|(i, x)| self.base_ring().int_hom().fma_map(&lhs.values[i], &rhs, x)),
);
return FreeAlgebraImplEl {
values: result.into_boxed_slice(),
};
}
fn characteristic<I: IntegerRingStore + Copy>(&self, ZZ: I) -> Option<El<I>>
where
I::Type: IntegerRing,
{
self.base_ring().characteristic(ZZ)
}
fn square(&self, value: &mut Self::Element) {
let mut tmp = Vec::with_capacity_in(2 << self.log2_padded_len, self.element_allocator.clone());
tmp.resize_with(2 << self.log2_padded_len, || self.base_ring().zero());
let x_prepared =
self.convolution
.prepare_convolution_operand(&value.values[..], Some(2 * self.rank()), self.base_ring());
self.convolution.compute_convolution_prepared(
&value.values[..],
Some(&x_prepared),
&value.values[..],
Some(&x_prepared),
&mut tmp,
self.base_ring(),
);
self.reduce_mod_poly(&mut tmp);
for i in 0..self.rank() {
value.values[i] = std::mem::replace(&mut tmp[i], self.base_ring().zero());
}
}
}
impl<R, V, A, C> RingExtension for FreeAlgebraImplBase<R, V, A, C>
where
R: RingStore,
V: VectorView<El<R>>,
A: Allocator + Clone,
C: ConvolutionAlgorithm<R::Type>,
{
type BaseRing = R;
fn base_ring(&self) -> &Self::BaseRing { &self.base_ring }
fn from(&self, x: El<Self::BaseRing>) -> Self::Element {
let mut result_values = Vec::with_capacity_in(1 << self.log2_padded_len, self.element_allocator.clone());
result_values.push(x);
result_values.extend((0..((1 << self.log2_padded_len) - 1)).map(|_| self.base_ring().zero()));
return FreeAlgebraImplEl {
values: result_values.into_boxed_slice(),
};
}
fn mul_assign_base(&self, lhs: &mut Self::Element, rhs: &El<Self::BaseRing>) {
for i in 0..self.rank() {
self.base_ring().mul_assign_ref(&mut lhs.values[i], rhs);
}
}
fn fma_base(&self, lhs: &Self::Element, rhs: &El<Self::BaseRing>, summand: Self::Element) -> Self::Element {
let mut new = Vec::with_capacity_in(1 << self.log2_padded_len, self.element_allocator.clone());
new.extend(
summand
.values
.into_iter()
.take(self.rank())
.enumerate()
.map(|(i, x)| self.base_ring.fma(&lhs.values[i], rhs, x)),
);
new.resize_with(1 << self.log2_padded_len, || self.base_ring().zero());
return FreeAlgebraImplEl {
values: new.into_boxed_slice(),
};
}
}
impl<R, V, A, C> DivisibilityRing for FreeAlgebraImplBase<R, V, A, C>
where
R: RingStore,
R::Type: LinSolveRing,
V: VectorView<El<R>>,
A: Allocator + Clone,
C: ConvolutionAlgorithm<R::Type>,
{
fn checked_left_div(&self, lhs: &Self::Element, rhs: &Self::Element) -> Option<Self::Element> {
let mut mul_matrix: OwnedMatrix<_, _> =
create_multiplication_matrix(RingRef::new(self), rhs, self.allocator().clone());
let mut lhs_matrix: OwnedMatrix<_, _> =
OwnedMatrix::zero_in(self.rank(), 1, self.base_ring(), self.allocator().clone());
let data = self.wrt_canonical_basis(lhs);
for j in 0..self.rank() {
*lhs_matrix.at_mut(j, 0) = data.at(j);
}
let mut solution: OwnedMatrix<_, _> =
OwnedMatrix::zero_in(self.rank(), 1, self.base_ring(), self.allocator().clone());
let has_sol = self.base_ring().get_ring().solve_right(
mul_matrix.data_mut(),
lhs_matrix.data_mut(),
solution.data_mut(),
Global,
);
if has_sol.is_solved() {
return Some(
self.from_canonical_basis((0..self.rank()).map(|i| self.base_ring().clone_el(solution.at(i, 0)))),
);
} else {
return None;
}
}
fn balance_factor<'a, I>(&self, elements: I) -> Option<Self::Element>
where
I: Iterator<Item = &'a Self::Element>,
Self: 'a,
{
self.base_ring()
.get_ring()
.balance_factor(elements.flat_map(|x| x.values.iter()))
.map(|c| RingRef::new(self).inclusion().map(c))
}
default fn invert(&self, el: &Self::Element) -> Option<Self::Element> { self.checked_left_div(&self.one(), el) }
}
impl<R, V, A, C> Debug for FreeAlgebraImplBase<R, V, A, C>
where
R: RingStore + Debug,
V: VectorView<El<R>>,
A: Allocator + Clone,
C: ConvolutionAlgorithm<R::Type>,
{
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
let poly_ring = DensePolyRing::new(self.base_ring(), self.gen_name);
f.debug_struct("FreeAlgebraImplBase")
.field("base_ring", &self.base_ring)
.field(
"modulus",
&poly_ring.format(&RingRef::new(self).generating_poly(&poly_ring, self.base_ring.identity())),
)
.finish()
}
}
impl<R, V> Serialize for FreeAlgebraImplBase<R, V, Global, KaratsubaAlgorithm>
where
R: RingStore + Serialize,
R::Type: SerializableElementRing,
V: VectorView<El<R>>,
{
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let poly_ring = DensePolyRing::new(self.base_ring(), "X");
SerializableNewtypeStruct::new(
"FreeAlgebraImpl",
(
self.base_ring(),
SerializeOwnedWithRing::new(
RingRef::new(self).generating_poly(&poly_ring, self.base_ring().identity()),
poly_ring,
),
),
)
.serialize(serializer)
}
}
impl<'de, R> Deserialize<'de> for FreeAlgebraImplBase<R, SparseMapVector<R>, Global, KaratsubaAlgorithm>
where
R: RingStore + Deserialize<'de> + Clone,
R::Type: SerializableElementRing,
{
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
let ring_cell = OnceCell::new();
let poly = <_ as DeserializeSeed<'de>>::deserialize(
DeserializeSeedNewtypeStruct::new(
"FreeAlgebraImpl",
DeserializeSeedDependentTuple::new(PhantomData::<R>, |ring| {
let poly_ring = DensePolyRing::new(ring, "X");
ring_cell.set(poly_ring).ok().unwrap();
DeserializeWithRing::new(ring_cell.get().unwrap())
}),
),
deserializer,
)?;
let poly_ring = ring_cell.into_inner().unwrap();
assert!(poly_ring.base_ring().is_one(poly_ring.lc(&poly).unwrap()));
let rank = poly_ring.degree(&poly).unwrap();
let mut x_pow_rank = SparseMapVector::new(rank, (*poly_ring.base_ring()).clone());
for (c, i) in poly_ring.terms(&poly) {
*x_pow_rank.at_mut(i) = poly_ring.base_ring().negate(poly_ring.base_ring().clone_el(c));
}
_ = x_pow_rank.at_mut(0);
return Ok(FreeAlgebraImpl::new_with_convolution(
poly_ring.into().into_base_ring(),
rank,
x_pow_rank,
"θ",
Global,
STANDARD_CONVOLUTION,
)
.into());
}
}
impl<'de, R> Deserialize<'de> for FreeAlgebraImplBase<R, Vec<El<R>>, Global, KaratsubaAlgorithm>
where
R: RingStore + Deserialize<'de>,
R::Type: SerializableElementRing,
{
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
let ring_cell = OnceCell::new();
let poly = <_ as DeserializeSeed<'de>>::deserialize(
DeserializeSeedNewtypeStruct::new(
"FreeAlgebraImpl",
DeserializeSeedDependentTuple::new(PhantomData::<R>, |ring| {
let poly_ring = DensePolyRing::new(ring, "X");
ring_cell.set(poly_ring).ok().unwrap();
DeserializeWithRing::new(ring_cell.get().unwrap())
}),
),
deserializer,
)?;
let poly_ring = ring_cell.into_inner().unwrap();
assert!(poly_ring.base_ring().is_one(poly_ring.lc(&poly).unwrap()));
let rank = poly_ring.degree(&poly).unwrap();
let x_pow_rank = (0..rank)
.map(|i| {
poly_ring
.base_ring()
.negate(poly_ring.base_ring().clone_el(poly_ring.coefficient_at(&poly, i)))
})
.collect::<Vec<_>>();
return Ok(FreeAlgebraImpl::new_with_convolution(
poly_ring.into().into_base_ring(),
rank,
x_pow_rank,
"θ",
Global,
STANDARD_CONVOLUTION,
)
.into());
}
}
impl<R, V, A, C> SerializableElementRing for FreeAlgebraImplBase<R, V, A, C>
where
R: RingStore,
V: VectorView<El<R>>,
A: Allocator + Clone,
C: ConvolutionAlgorithm<R::Type>,
R::Type: SerializableElementRing,
{
fn deserialize<'de, D>(&self, deserializer: D) -> Result<Self::Element, D::Error>
where
D: Deserializer<'de>,
{
DeserializeSeedNewtypeStruct::new(
"ExtensionRingEl",
DeserializeSeedSeq::new(
std::iter::repeat_n(DeserializeWithRing::new(self.base_ring()), self.rank() + 1),
Vec::with_capacity_in(1 << self.log2_padded_len, self.element_allocator.clone()),
|mut current, next| {
current.push(next);
current
},
),
)
.deserialize(deserializer)
.map(|mut values| {
values.resize_with(1 << self.log2_padded_len, || self.base_ring().zero());
FreeAlgebraImplEl {
values: values.into_boxed_slice(),
}
})
}
fn serialize<S>(&self, el: &Self::Element, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
debug_assert_eq!(1 << self.log2_padded_len, el.values.len());
SerializableNewtypeStruct::new(
"ExtensionRingEl",
SerializableSeq::new_with_len(
(0..self.rank()).map(|i| SerializeWithRing::new(&el.values[i], self.base_ring())),
self.rank(),
),
)
.serialize(serializer)
}
}
impl<R, V, A, C> FreeAlgebra for FreeAlgebraImplBase<R, V, A, C>
where
R: RingStore,
V: VectorView<El<R>>,
A: Allocator + Clone,
C: ConvolutionAlgorithm<R::Type>,
{
type VectorRepresentation<'a>
= CloneElFn<&'a [El<R>], El<R>, CloneRingEl<&'a R>>
where
Self: 'a;
fn canonical_gen(&self) -> Self::Element {
if self.rank() == 1 {
if self.x_pow_rank.len() == 1 {
self.from_canonical_basis([self.base_ring.clone_el(self.x_pow_rank.at(0))])
} else {
assert!(self.x_pow_rank.len() == 0);
self.zero()
}
} else {
self.from_canonical_basis((0..self.rank()).map(|i| {
if i == 1 {
self.base_ring.one()
} else {
self.base_ring.zero()
}
}))
}
}
fn wrt_canonical_basis<'a>(&'a self, el: &'a Self::Element) -> Self::VectorRepresentation<'a> {
el.values[..self.rank].clone_ring_els(self.base_ring())
}
fn from_canonical_basis<W>(&self, vec: W) -> Self::Element
where
W: IntoIterator<Item = El<Self::BaseRing>>,
W::IntoIter: DoubleEndedIterator,
{
let mut given_len = 0;
let mut vec_it = vec.into_iter().inspect(|_| given_len += 1);
let mut result = Vec::with_capacity_in(1 << self.log2_padded_len, self.element_allocator.clone());
result.extend(vec_it.by_ref());
result.extend((0..((1 << self.log2_padded_len) - self.rank())).map(|_| self.base_ring().zero()));
assert!(vec_it.next().is_none());
assert_eq!(given_len, self.rank());
FreeAlgebraImplEl {
values: result.into_boxed_slice(),
}
}
fn rank(&self) -> usize { self.rank }
fn mul_assign_gen_power(&self, el: &mut Self::Element, power: usize) {
let mut gen_pow_d = Vec::new_in(self.allocator().clone());
gen_pow_d.extend(self.x_pow_rank.as_iter().map(|c| self.base_ring.clone_el(c)));
gen_pow_d.resize_with(1 << self.log2_padded_len, || self.base_ring.zero());
if !power.is_multiple_of(self.rank()) {
let mut gen_pow_rem = Vec::new_in(self.allocator().clone());
gen_pow_rem.extend((0..((power % self.rank()) - 1)).map(|_| self.base_ring.zero()));
gen_pow_rem.push(self.base_ring.one());
gen_pow_rem.resize_with(1 << self.log2_padded_len, || self.base_ring.zero());
*el = self.prod([
FreeAlgebraImplEl {
values: replace(&mut el.values, Box::new_in([], self.allocator().clone())),
},
RingValue::from_ref(self).pow(
FreeAlgebraImplEl {
values: gen_pow_d.into_boxed_slice(),
},
power / self.rank,
),
FreeAlgebraImplEl {
values: gen_pow_rem.into_boxed_slice(),
},
]);
} else {
self.mul_assign(
el,
RingValue::from_ref(self).pow(
FreeAlgebraImplEl {
values: gen_pow_d.into_boxed_slice(),
},
power / self.rank,
),
);
}
}
}
impl<R, V, A, C> HashableElRing for FreeAlgebraImplBase<R, V, A, C>
where
R: RingStore,
R::Type: HashableElRing,
V: VectorView<El<R>>,
A: Allocator + Clone,
C: ConvolutionAlgorithm<R::Type>,
{
fn hash<H: std::hash::Hasher>(&self, el: &Self::Element, h: &mut H) {
for x in &el.values[..self.rank()] {
self.base_ring().get_ring().hash(x, h)
}
}
}
pub struct WRTCanonicalBasisElementCreator<'a, R, V, A, C>
where
R: RingStore,
R::Type: FiniteRing,
V: VectorView<El<R>>,
A: Allocator + Clone,
C: ConvolutionAlgorithm<R::Type>,
{
base_ring: &'a FreeAlgebraImplBase<R, V, A, C>,
}
impl<'a, R, V, A, C> Clone for WRTCanonicalBasisElementCreator<'a, R, V, A, C>
where
R: RingStore,
R::Type: FiniteRing,
V: VectorView<El<R>>,
A: Allocator + Clone,
C: ConvolutionAlgorithm<R::Type>,
{
fn clone(&self) -> Self { *self }
}
impl<'a, 'b, R, V, A, C> FnOnce<(&'b [El<R>],)> for WRTCanonicalBasisElementCreator<'a, R, V, A, C>
where
R: RingStore,
R::Type: FiniteRing,
V: VectorView<El<R>>,
A: Allocator + Clone,
C: ConvolutionAlgorithm<R::Type>,
{
type Output = El<FreeAlgebraImpl<R, V, A, C>>;
extern "rust-call" fn call_once(mut self, args: (&'b [El<R>],)) -> Self::Output { self.call_mut(args) }
}
impl<'a, 'b, R, V, A, C> FnMut<(&'b [El<R>],)> for WRTCanonicalBasisElementCreator<'a, R, V, A, C>
where
R: RingStore,
R::Type: FiniteRing,
V: VectorView<El<R>>,
A: Allocator + Clone,
C: ConvolutionAlgorithm<R::Type>,
{
extern "rust-call" fn call_mut(&mut self, args: (&'b [El<R>],)) -> Self::Output {
self.base_ring
.from_canonical_basis(args.0.iter().map(|x| self.base_ring.base_ring().clone_el(x)))
}
}
impl<'a, R, V, A, C> Copy for WRTCanonicalBasisElementCreator<'a, R, V, A, C>
where
R: RingStore,
R::Type: FiniteRing,
V: VectorView<El<R>>,
A: Allocator + Clone,
C: ConvolutionAlgorithm<R::Type>,
{
}
impl<R, V, A, C> FiniteRingSpecializable for FreeAlgebraImplBase<R, V, A, C>
where
R: RingStore,
R::Type: FiniteRingSpecializable,
V: VectorView<El<R>>,
A: Allocator + Clone,
C: ConvolutionAlgorithm<R::Type>,
{
fn specialize<O: FiniteRingOperation<Self>>(op: O) -> O::Output {
struct OpWrapper<R, V, A, C, O>
where
R: RingStore,
R::Type: FiniteRingSpecializable,
V: VectorView<El<R>>,
A: Allocator + Clone,
C: ConvolutionAlgorithm<R::Type>,
O: FiniteRingOperation<FreeAlgebraImplBase<R, V, A, C>>,
{
operation: O,
ring: PhantomData<FreeAlgebraImpl<R, V, A, C>>,
}
impl<R, V, A, C, O> FiniteRingOperation<R::Type> for OpWrapper<R, V, A, C, O>
where
R: RingStore,
R::Type: FiniteRingSpecializable,
V: VectorView<El<R>>,
A: Allocator + Clone,
C: ConvolutionAlgorithm<R::Type>,
O: FiniteRingOperation<FreeAlgebraImplBase<R, V, A, C>>,
{
type Output = O::Output;
fn execute(self) -> Self::Output
where
R::Type: FiniteRing,
{
self.operation.execute()
}
fn fallback(self) -> Self::Output { self.operation.fallback() }
}
<R::Type as FiniteRingSpecializable>::specialize(OpWrapper {
operation: op,
ring: PhantomData,
})
}
}
impl<R, V, A, C> FiniteRing for FreeAlgebraImplBase<R, V, A, C>
where
R: RingStore,
R::Type: FiniteRing,
V: VectorView<El<R>>,
A: Allocator + Clone,
C: ConvolutionAlgorithm<R::Type>,
{
type ElementsIter<'a>
= MultiProduct<
<R::Type as FiniteRing>::ElementsIter<'a>,
WRTCanonicalBasisElementCreator<'a, R, V, A, C>,
CloneRingEl<&'a R>,
Self::Element,
>
where
Self: 'a;
fn elements<'a>(&'a self) -> Self::ElementsIter<'a> {
multi_cartesian_product(
(0..self.rank()).map(|_| self.base_ring().elements()),
WRTCanonicalBasisElementCreator { base_ring: self },
CloneRingEl(self.base_ring()),
)
}
fn size<I: IntegerRingStore + Copy>(&self, ZZ: I) -> Option<El<I>>
where
I::Type: IntegerRing,
{
let base_ring_size = self.base_ring().size(ZZ)?;
if ZZ.get_ring().representable_bits().is_none()
|| ZZ.abs_log2_ceil(&base_ring_size).unwrap() * self.rank() < ZZ.get_ring().representable_bits().unwrap()
{
Some(ZZ.pow(base_ring_size, self.rank()))
} else {
None
}
}
fn random_element<G: FnMut() -> u64>(&self, mut rng: G) -> <Self as RingBase>::Element {
self.from_canonical_basis((0..self.rank()).map(|_| self.base_ring().random_element(&mut rng)))
}
}
impl_field_wrap_unwrap_homs! {
<{R1, V1, A1, C1, R2, V2, A2, C2}> FreeAlgebraImplBase<R1, V1, A1, C1>, FreeAlgebraImplBase<R2, V2, A2, C2>
where R1: RingStore, R1::Type: LinSolveRing, V1: VectorView<El<R1>>, A1: Allocator + Clone, C1: ConvolutionAlgorithm<R1::Type>,
R2: RingStore, R2::Type: LinSolveRing, V2: VectorView<El<R2>>, A2: Allocator + Clone, C2: ConvolutionAlgorithm<R2::Type>,
R2::Type: CanHomFrom<R1::Type>
}
impl_field_wrap_unwrap_isos! {
<{R1, V1, A1, C1, R2, V2, A2, C2}> FreeAlgebraImplBase<R1, V1, A1, C1>, FreeAlgebraImplBase<R2, V2, A2, C2>
where R1: RingStore, R1::Type: LinSolveRing, V1: VectorView<El<R1>>, A1: Allocator + Clone, C1: ConvolutionAlgorithm<R1::Type>,
R2: RingStore, R2::Type: LinSolveRing, V2: VectorView<El<R2>>, A2: Allocator + Clone, C2: ConvolutionAlgorithm<R2::Type>,
R2::Type: CanIsoFromTo<R1::Type>
}
impl_localpir_wrap_unwrap_homs! {
<{R1, V1, A1, C1, R2, V2, A2, C2}> FreeAlgebraImplBase<R1, V1, A1, C1>, FreeAlgebraImplBase<R2, V2, A2, C2>
where R1: RingStore, R1::Type: LinSolveRing, V1: VectorView<El<R1>>, A1: Allocator + Clone, C1: ConvolutionAlgorithm<R1::Type>,
R2: RingStore, R2::Type: LinSolveRing, V2: VectorView<El<R2>>, A2: Allocator + Clone, C2: ConvolutionAlgorithm<R2::Type>,
R2::Type: CanHomFrom<R1::Type>
}
impl_localpir_wrap_unwrap_isos! {
<{R1, V1, A1, C1, R2, V2, A2, C2}> FreeAlgebraImplBase<R1, V1, A1, C1>, FreeAlgebraImplBase<R2, V2, A2, C2>
where R1: RingStore, R1::Type: LinSolveRing, V1: VectorView<El<R1>>, A1: Allocator + Clone, C1: ConvolutionAlgorithm<R1::Type>,
R2: RingStore, R2::Type: LinSolveRing, V2: VectorView<El<R2>>, A2: Allocator + Clone, C2: ConvolutionAlgorithm<R2::Type>,
R2::Type: CanIsoFromTo<R1::Type>
}
impl<R1, V1, A1, C1, R2, V2, A2, C2> CanHomFrom<FreeAlgebraImplBase<R1, V1, A1, C1>>
for FreeAlgebraImplBase<R2, V2, A2, C2>
where
R1: RingStore,
V1: VectorView<El<R1>>,
A1: Allocator + Clone,
C1: ConvolutionAlgorithm<R1::Type>,
R2: RingStore,
V2: VectorView<El<R2>>,
A2: Allocator + Clone,
C2: ConvolutionAlgorithm<R2::Type>,
R2::Type: CanHomFrom<R1::Type>,
{
type Homomorphism = <R2::Type as CanHomFrom<R1::Type>>::Homomorphism;
fn has_canonical_hom(&self, from: &FreeAlgebraImplBase<R1, V1, A1, C1>) -> Option<Self::Homomorphism> {
if self.rank() == from.rank() {
let hom = self.base_ring.get_ring().has_canonical_hom(from.base_ring.get_ring())?;
if (0..self.rank()).all(|i| {
(i >= self.x_pow_rank.len() && i >= from.x_pow_rank.len())
|| (i >= self.x_pow_rank.len() && from.base_ring().is_zero(from.x_pow_rank.at(i)))
|| (i >= from.x_pow_rank.len() && self.base_ring().is_zero(self.x_pow_rank.at(i)))
|| self.base_ring.eq_el(
self.x_pow_rank.at(i),
&self
.base_ring
.get_ring()
.map_in_ref(from.base_ring.get_ring(), from.x_pow_rank.at(i), &hom),
)
}) {
Some(hom)
} else {
None
}
} else {
None
}
}
fn map_in(
&self,
from: &FreeAlgebraImplBase<R1, V1, A1, C1>,
el: <FreeAlgebraImplBase<R1, V1, A1> as RingBase>::Element,
hom: &Self::Homomorphism,
) -> Self::Element {
self.from_canonical_basis((0..self.rank()).map(|i| {
self.base_ring
.get_ring()
.map_in_ref(from.base_ring.get_ring(), &el.values[i], hom)
}))
}
}
impl<R1, V1, A1, C1, R2, V2, A2, C2> CanIsoFromTo<FreeAlgebraImplBase<R1, V1, A1, C1>>
for FreeAlgebraImplBase<R2, V2, A2, C2>
where
R1: RingStore,
V1: VectorView<El<R1>>,
A1: Allocator + Clone,
C1: ConvolutionAlgorithm<R1::Type>,
R2: RingStore,
V2: VectorView<El<R2>>,
A2: Allocator + Clone,
C2: ConvolutionAlgorithm<R2::Type>,
R2::Type: CanIsoFromTo<R1::Type>,
{
type Isomorphism = <R2::Type as CanIsoFromTo<R1::Type>>::Isomorphism;
fn has_canonical_iso(&self, from: &FreeAlgebraImplBase<R1, V1, A1, C1>) -> Option<Self::Isomorphism> {
if self.rank() == from.rank() {
let iso = self.base_ring.get_ring().has_canonical_iso(from.base_ring.get_ring())?;
if (0..self.rank()).all(|i| {
(i >= self.x_pow_rank.len() && i >= from.x_pow_rank.len())
|| (i >= self.x_pow_rank.len() && from.base_ring().is_zero(from.x_pow_rank.at(i)))
|| (i >= from.x_pow_rank.len() && self.base_ring().is_zero(self.x_pow_rank.at(i)))
|| from.base_ring.eq_el(
&self.base_ring.get_ring().map_out(
from.base_ring.get_ring(),
self.base_ring.clone_el(self.x_pow_rank.at(i)),
&iso,
),
from.x_pow_rank.at(i),
)
}) {
Some(iso)
} else {
None
}
} else {
None
}
}
fn map_out(
&self,
from: &FreeAlgebraImplBase<R1, V1, A1, C1>,
el: <FreeAlgebraImplBase<R2, V2, A2> as RingBase>::Element,
iso: &Self::Isomorphism,
) -> <FreeAlgebraImplBase<R1, V1, A1, C1> as RingBase>::Element {
from.from_canonical_basis((0..self.rank()).map(|i| {
self.base_ring
.get_ring()
.map_out(from.base_ring.get_ring(), self.base_ring.clone_el(&el.values[i]), iso)
}))
}
}
#[cfg(test)]
use crate::algorithms::convolution::fft::FFTConvolution;
#[cfg(test)]
use crate::rings::zn::ZnRingStore;
#[cfg(test)]
use crate::rings::zn::zn_64::{Zn, ZnEl};
#[cfg(test)]
use crate::rings::zn::zn_static;
#[cfg(test)]
fn test_ring0_and_elements() -> (FreeAlgebraImpl<Zn, Vec<ZnEl>>, Vec<FreeAlgebraImplEl<Zn>>) {
let R = FreeAlgebraImpl::new(Zn::new(7), 1, vec![Zn::new(7).neg_one()]);
let elements = R.elements().collect();
return (R, elements);
}
#[cfg(test)]
fn test_ring1_and_elements() -> (
FreeAlgebraImpl<StaticRing<i64>, [i64; 2]>,
Vec<FreeAlgebraImplEl<StaticRing<i64>>>,
) {
let ZZ = StaticRing::<i64>::RING;
let R = FreeAlgebraImpl::new(ZZ, 2, [1, 1]);
let mut elements = Vec::new();
for a in -3..=3 {
for b in -3..=3 {
elements.push(R.from_canonical_basis([a, b]));
}
}
return (R, elements);
}
#[cfg(test)]
fn test_ring2_and_elements() -> (
FreeAlgebraImpl<StaticRing<i64>, Vec<i64>>,
Vec<FreeAlgebraImplEl<StaticRing<i64>>>,
) {
let ZZ = StaticRing::<i64>::RING;
let R = FreeAlgebraImpl::new(ZZ, 3, vec![1, -1, 1]);
let mut elements = Vec::new();
for a in [-2, 0, 1, 2] {
for b in [-2, 0, 2] {
for c in [-2, 0, 2] {
elements.push(R.from_canonical_basis([a, b, c]));
}
}
}
return (R, elements);
}
#[cfg(test)]
fn test_ring3_and_elements() -> (
FreeAlgebraImpl<StaticRing<i64>, Vec<i64>>,
Vec<FreeAlgebraImplEl<StaticRing<i64>>>,
) {
let ZZ = StaticRing::<i64>::RING;
let R = FreeAlgebraImpl::new(ZZ, 3, vec![1]);
let mut elements = Vec::new();
for a in [-2, 0, 1, 2] {
for b in [-2, 0, 2] {
for c in [-2, 0, 2] {
elements.push(R.from_canonical_basis([a, b, c]));
}
}
}
return (R, elements);
}
#[cfg(test)]
fn test_ring4_and_elements() -> (
FreeAlgebraImpl<StaticRing<i64>, SparseMapVector<StaticRing<i64>>>,
Vec<FreeAlgebraImplEl<StaticRing<i64>>>,
) {
let ZZ = StaticRing::<i64>::RING;
let mut modulus = SparseMapVector::new(3, StaticRing::<i64>::RING);
*modulus.at_mut(1) = 1;
let R = FreeAlgebraImpl::new(ZZ, 3, modulus);
let mut elements = Vec::new();
for a in [-2, 0, 1, 2] {
for b in [-2, 0, 2] {
for c in [-2, 0, 2] {
elements.push(R.from_canonical_basis([a, b, c]));
}
}
}
return (R, elements);
}
#[test]
fn test_sparse() {
let (ring, _) = test_ring4_and_elements();
assert_el_eq!(ring, ring.canonical_gen(), ring.pow(ring.canonical_gen(), 3));
}
#[test]
fn test_ring_axioms() {
let (ring, els) = test_ring2_and_elements();
crate::ring::generic_tests::test_ring_axioms(ring, els.into_iter());
let (ring, els) = test_ring3_and_elements();
crate::ring::generic_tests::test_ring_axioms(ring, els.into_iter());
let (ring, els) = test_ring4_and_elements();
crate::ring::generic_tests::test_ring_axioms(ring, els.into_iter());
let (ring, els) = test_ring2_and_elements();
let ring = ring.into().set_convolution(FFTConvolution::new_with_alloc(Global));
crate::ring::generic_tests::test_ring_axioms(ring, els.into_iter());
let base_ring = zn_static::Fp::<257>::RING;
let x_pow_rank = vec![base_ring.neg_one(); 64];
let ring = FreeAlgebraImpl::new(base_ring, 64, x_pow_rank);
let mut rng = oorandom::Rand64::new(1);
let els =
(0..10).map(|_| ring.from_canonical_basis((0..64).map(|_| ring.base_ring().random_element(|| rng.rand_u64()))));
crate::ring::generic_tests::test_ring_axioms(&ring, els);
}
#[test]
fn test_rank_1_ring() {
let base_ring = Zn::new(5).as_field().ok().unwrap();
let ring = FreeAlgebraImpl::new(base_ring, 1, [base_ring.int_hom().map(1)])
.as_field()
.ok()
.unwrap();
crate::field::generic_tests::test_field_axioms(&ring, ring.elements());
assert_el_eq!(ring, ring.one(), ring.canonical_gen());
}
#[test]
fn test_free_algebra_axioms() {
let (ring, _) = test_ring0_and_elements();
super::generic_tests::test_free_algebra_axioms(ring);
let (ring, _) = test_ring1_and_elements();
super::generic_tests::test_free_algebra_axioms(ring);
let (ring, _) = test_ring2_and_elements();
super::generic_tests::test_free_algebra_axioms(ring);
let (ring, _) = test_ring3_and_elements();
super::generic_tests::test_free_algebra_axioms(ring);
let (ring, _) = test_ring4_and_elements();
super::generic_tests::test_free_algebra_axioms(ring);
}
#[test]
fn test_division() {
let base_ring = Zn::new(4);
let i = base_ring.int_hom();
let ring = FreeAlgebraImpl::new(base_ring, 2, [i.map(-1), i.map(-1)]);
assert_el_eq!(
ring,
&ring.from_canonical_basis([i.map(1), i.map(3)]),
&ring
.checked_div(&ring.one(), &ring.from_canonical_basis([i.map(2), i.map(3)]))
.unwrap()
);
let a = ring.from_canonical_basis([i.map(2), i.map(2)]);
let b = ring.from_canonical_basis([i.map(0), i.map(2)]);
assert_el_eq!(ring, a, ring.mul(ring.checked_div(&a, &b).unwrap(), b));
assert!(ring.checked_div(&ring.one(), &a).is_none());
}
#[test]
fn test_division_ring_of_integers() {
let base_ring = StaticRing::<i64>::RING;
let ring = FreeAlgebraImpl::new(base_ring, 2, [11, 0]);
let u = ring.from_canonical_basis([10, 3]);
let u_inv = ring.from_canonical_basis([10, -3]);
assert_el_eq!(ring, u_inv, ring.checked_div(&ring.one(), &u).unwrap());
assert_el_eq!(
ring,
ring.pow(u_inv, 3),
&ring.checked_div(&ring.one(), &ring.pow(u, 3)).unwrap()
);
assert!(
ring.checked_div(&ring.from_canonical_basis([2, 0]), &ring.from_canonical_basis([3, 0]))
.is_none()
);
}
#[test]
fn test_divisibility_axioms() {
let (ring, els) = test_ring0_and_elements();
crate::divisibility::generic_tests::test_divisibility_axioms(ring, els.into_iter());
let (ring, els) = test_ring1_and_elements();
crate::divisibility::generic_tests::test_divisibility_axioms(ring, els.into_iter());
let (ring, els) = test_ring2_and_elements();
crate::divisibility::generic_tests::test_divisibility_axioms(ring, els.into_iter());
let (ring, els) = test_ring3_and_elements();
crate::divisibility::generic_tests::test_divisibility_axioms(ring, els.into_iter());
let (ring, els) = test_ring4_and_elements();
crate::divisibility::generic_tests::test_divisibility_axioms(ring, els.into_iter());
}
#[test]
fn test_cubic_mul() {
let base_ring = zn_static::Zn::<256>::RING;
let modulo = base_ring.int_hom();
let ring = FreeAlgebraImpl::new(base_ring, 3, [modulo.map(-1), modulo.map(-1), modulo.map(-1)]);
let a = ring.from_canonical_basis([modulo.map(0), modulo.map(-1), modulo.map(-1)]);
let b = ring.from_canonical_basis([modulo.map(-1), modulo.map(-2), modulo.map(-1)]);
assert_el_eq!(ring, b, ring.pow(a, 2));
}
#[test]
fn test_as_field() {
let base_ring = Zn::new(5).as_field().ok().unwrap();
let ring = FreeAlgebraImpl::new(base_ring, 1, [base_ring.int_hom().map(1)])
.as_field()
.ok()
.unwrap();
crate::field::generic_tests::test_field_axioms(&ring, ring.elements());
let base_ring = Zn::new(3).as_field().ok().unwrap();
let ring = FreeAlgebraImpl::new(base_ring, 2, [base_ring.int_hom().map(2)])
.as_field()
.ok()
.unwrap();
crate::field::generic_tests::test_field_axioms(&ring, ring.elements());
assert!(
FreeAlgebraImpl::new(base_ring, 2, [base_ring.int_hom().map(1)])
.as_field()
.is_err()
);
}
#[test]
fn test_serialization() {
let (ring, els) = test_ring0_and_elements();
crate::serialization::generic_tests::test_serialization(ring, els.into_iter());
let (ring, els) = test_ring1_and_elements();
crate::serialization::generic_tests::test_serialization(ring, els.into_iter());
let (ring, els) = test_ring2_and_elements();
crate::serialization::generic_tests::test_serialization(ring, els.into_iter());
let (ring, els) = test_ring3_and_elements();
crate::serialization::generic_tests::test_serialization(ring, els.into_iter());
let (ring, els) = test_ring4_and_elements();
crate::serialization::generic_tests::test_serialization(ring, els.into_iter());
}
#[test]
#[should_panic]
fn test_from_canonical_basis_enforce_len() {
let (ring, _) = test_ring1_and_elements();
_ = ring.from_canonical_basis([0, 1, 2]);
}
#[test]
fn test_serialize_deserialize() {
crate::serialization::generic_tests::test_serialize_deserialize(test_ring0_and_elements().0.into());
crate::serialization::generic_tests::test_serialize_deserialize(test_ring2_and_elements().0.into());
crate::serialization::generic_tests::test_serialize_deserialize(test_ring3_and_elements().0.into());
}