use std::alloc::Allocator;
use feanor_math::integer::BigIntRing;
use feanor_math::matrix::*;
use feanor_math::ring::*;
use feanor_math::rings::extension::{FreeAlgebra, FreeAlgebraStore};
use feanor_math::rings::zn::zn_64::{ZnEl, Zn, ZnBase};
use feanor_math::rings::zn::zn_rns;
use feanor_math::seq::{VectorView, VectorFn};
use tracing::instrument;
use crate::number_ring::quotient::{NumberRingQuotient, NumberRingQuotientEl};
use crate::number_ring::HECyclotomicNumberRing;
use crate::rnsconv::RNSOperation;
pub mod poly_remainder;
pub mod serialization;
pub mod double_rns_ring;
pub mod single_rns_ring;
pub mod double_rns_managed;
pub trait PreparedMultiplicationRing: RingBase {
type PreparedMultiplicant;
fn prepare_multiplicant(&self, x: &Self::Element) -> Self::PreparedMultiplicant;
fn mul_prepared(&self, lhs: &Self::PreparedMultiplicant, rhs: &Self::PreparedMultiplicant) -> Self::Element;
fn inner_product_prepared<'a, I>(&self, parts: I) -> Self::Element
where I: IntoIterator<Item = (&'a Self::PreparedMultiplicant, &'a Self::PreparedMultiplicant)>,
Self: 'a
{
parts.into_iter().fold(self.zero(), |current, (lhs, rhs)| self.add(current, self.mul_prepared(lhs, rhs)))
}
}
pub trait BGFVCiphertextRing: PreparedMultiplicationRing + FreeAlgebra + RingExtension<BaseRing = zn_rns::Zn<Zn, BigIntRing>> {
type NumberRing: HECyclotomicNumberRing;
fn number_ring(&self) -> &Self::NumberRing;
fn drop_rns_factor(&self, drop_rns_factors: &[usize]) -> Self;
fn drop_rns_factor_element(&self, from: &Self, dropped_rns_factors: &[usize], value: Self::Element) -> Self::Element;
fn drop_rns_factor_prepared(&self, from: &Self, dropped_rns_factors: &[usize], value: Self::PreparedMultiplicant) -> Self::PreparedMultiplicant;
fn small_generating_set_len(&self) -> usize;
fn as_representation_wrt_small_generating_set<V>(&self, x: &Self::Element, output: SubmatrixMut<V, ZnEl>)
where V: AsPointerToSlice<ZnEl>;
fn partial_representation_wrt_small_generating_set<V>(&self, x: &Self::Element, row_indices: &[usize], output: SubmatrixMut<V, ZnEl>)
where V: AsPointerToSlice<ZnEl>;
fn from_representation_wrt_small_generating_set<V>(&self, data: Submatrix<V, ZnEl>) -> Self::Element
where V: AsPointerToSlice<ZnEl>;
#[instrument(skip_all)]
fn two_by_two_convolution(&self, lhs: [&Self::Element; 2], rhs: [&Self::Element; 2]) -> [Self::Element; 3] {
let mut lhs_it = lhs.into_iter();
let mut rhs_it = rhs.into_iter();
let lhs: [_; 2] = std::array::from_fn(|_| self.prepare_multiplicant(lhs_it.next().unwrap()));
let rhs: [_; 2] = std::array::from_fn(|_| self.prepare_multiplicant(rhs_it.next().unwrap()));
[
self.mul_prepared(&lhs[0], &rhs[0]),
self.inner_product_prepared([(&lhs[0], &rhs[1]), (&lhs[1], &rhs[0])]),
self.mul_prepared(&lhs[1], &rhs[1])
]
}
}
#[instrument(skip_all)]
pub fn perform_rns_op<R, Op>(to: &R, from: &R, el: &R::Element, op: &Op) -> R::Element
where R: BGFVCiphertextRing,
Op: RNSOperation<RingType = ZnBase>
{
assert!(from.number_ring() == to.number_ring());
assert_eq!(op.input_rings().len(), from.base_ring().len());
assert_eq!(op.output_rings().len(), to.base_ring().len());
assert!(op.input_rings().iter().zip(from.base_ring().as_iter()).all(|(l, r)| l.get_ring() == r.get_ring()));
assert!(op.output_rings().iter().zip(to.base_ring().as_iter()).all(|(l, r)| l.get_ring() == r.get_ring()));
let mut el_repr = OwnedMatrix::zero(from.base_ring().len(), from.small_generating_set_len(), from.base_ring().at(0));
from.as_representation_wrt_small_generating_set(el, el_repr.data_mut());
let mut res_repr = Vec::with_capacity(el_repr.col_count() * to.base_ring().len());
res_repr.resize(el_repr.col_count() * to.base_ring().len(), to.base_ring().at(0).zero());
let mut res_repr = SubmatrixMut::from_1d(&mut res_repr, to.base_ring().len(), el_repr.col_count());
op.apply(el_repr.data(), res_repr.reborrow());
return to.from_representation_wrt_small_generating_set(res_repr.as_const());
}
#[instrument(skip_all)]
pub fn perform_rns_op_to_plaintext_ring<R, Op, A>(to: &NumberRingQuotient<R::NumberRing, Zn, A>, from: &R, el: &R::Element, op: &Op) -> NumberRingQuotientEl<R::NumberRing, Zn, A>
where R: BGFVCiphertextRing,
Op: RNSOperation<RingType = ZnBase>,
A: Allocator + Clone
{
assert!(from.number_ring() == to.get_ring().number_ring());
assert_eq!(op.input_rings().len(), from.base_ring().len());
assert_eq!(op.output_rings().len(), 1);
assert!(op.input_rings().iter().zip(from.base_ring().as_iter()).all(|(l, r)| l.get_ring() == r.get_ring()));
assert!(op.output_rings().at(0).get_ring() == to.base_ring().get_ring());
let mut el_repr = Vec::with_capacity(from.rank() * from.base_ring().len());
el_repr.resize(from.rank() * from.base_ring().len(), from.base_ring().at(0).zero());
let mut el_repr = SubmatrixMut::from_1d(&mut el_repr, from.base_ring().len(), from.rank());
for (j, c) in from.wrt_canonical_basis(el).iter().enumerate() {
for (i, x) in from.base_ring().get_congruence(&c).as_iter().enumerate() {
*el_repr.at_mut(i, j) = *x;
}
}
let mut res_repr = Vec::with_capacity(el_repr.col_count());
res_repr.resize(el_repr.col_count(), to.base_ring().zero());
let mut res_repr = SubmatrixMut::from_1d(&mut res_repr, 1, el_repr.col_count());
op.apply(el_repr.as_const(), res_repr.reborrow());
return to.from_canonical_basis(res_repr.row_at(0).copy_els().iter());
}