use std::array::from_fn;
use std::convert::identity;
use feanor_math::integer::BigIntRing;
use feanor_math::matrix::*;
use feanor_math::ring::*;
use feanor_math::rings::zn::zn_64::{ZnEl, Zn, ZnBase};
use feanor_math::rings::zn::zn_rns;
use feanor_math::seq::VectorView;
use tracing::instrument;
use crate::ciphertext_ring::indices::RNSFactorIndexList;
use crate::number_ring::NumberRingQuotient;
use crate::prepared_mul::PreparedMultiplicationRing;
use crate::rns_conv::RNSOperation;
pub mod indices;
pub mod serialization;
pub mod double_rns_ring;
pub mod single_rns_ring;
pub mod double_rns_managed;
pub enum RNSFactorCongruence<'a, R: ?Sized, E> {
Zero,
CongruentTo(&'a R, usize, &'a E)
}
pub fn drop_rns_factor_list_of_congruences<'a, R, E>(from: &'a R, dropped_rns_factors: &'a RNSFactorIndexList, element: &'a E) -> impl use<'a, R, E> + Iterator<Item = RNSFactorCongruence<'a, R, E>>
where R: ?Sized + RingExtension<BaseRing = zn_rns::Zn<Zn, BigIntRing>>
{
(0..from.base_ring().len()).scan(0, |drop_idx, factor_idx| {
debug_assert!(*drop_idx == dropped_rns_factors.len() || factor_idx <= dropped_rns_factors[*drop_idx]);
if *drop_idx < dropped_rns_factors.len() && factor_idx == dropped_rns_factors[*drop_idx] {
*drop_idx += 1;
return Some(None);
} else {
return Some(Some(RNSFactorCongruence::CongruentTo(from, factor_idx, element)));
}
}).filter_map(identity)
}
pub fn add_rns_factor_list_of_congruences<'a, R, E>(to: &'a R, from: &'a R, added_rns_factors: &'a RNSFactorIndexList, element: &'a E) -> impl use<'a, R, E> + Iterator<Item = RNSFactorCongruence<'a, R, E>>
where R: ?Sized + RingExtension<BaseRing = zn_rns::Zn<Zn, BigIntRing>>
{
(0..to.base_ring().len()).scan((0, 0), |(added_idx, from_factor_idx), factor_idx| {
debug_assert!(*added_idx == added_rns_factors.len() || factor_idx <= added_rns_factors[*added_idx]);
if *added_idx < added_rns_factors.len() && factor_idx == added_rns_factors[*added_idx] {
*added_idx += 1;
return Some(RNSFactorCongruence::Zero);
} else {
*from_factor_idx += 1;
return Some(RNSFactorCongruence::CongruentTo(from, *from_factor_idx - 1, element));
}
})
}
pub trait NumberRingRNSQuotient: NumberRingQuotient + PreparedMultiplicationRing + RingExtension<BaseRing = zn_rns::Zn<Zn, BigIntRing>> {
fn collect_rns_factors<'a, I>(&self, congruences: I) -> Self::Element
where I: Iterator<Item = RNSFactorCongruence<'a, Self, Self::Element>>,
Self: 'a;
fn collect_rns_factors_prepared<'a, I>(&self, congruences: I) -> Self::PreparedMultiplicant
where I: Iterator<Item = RNSFactorCongruence<'a, Self, Self::PreparedMultiplicant>>,
Self: 'a;
fn drop_rns_factor(&self, drop_rns_factors: &RNSFactorIndexList) -> Self;
fn drop_rns_factor_element(&self, from: &Self, dropped_rns_factors: &RNSFactorIndexList, value: &Self::Element) -> Self::Element {
assert_eq!(from.base_ring().len(), self.base_ring().len() + dropped_rns_factors.len());
self.collect_rns_factors(drop_rns_factor_list_of_congruences(from, dropped_rns_factors, value))
}
fn drop_rns_factor_prepared_element(&self, from: &Self, dropped_rns_factors: &RNSFactorIndexList, value: &Self::PreparedMultiplicant) -> Self::PreparedMultiplicant {
assert_eq!(from.base_ring().len(), self.base_ring().len() + dropped_rns_factors.len());
self.collect_rns_factors_prepared(drop_rns_factor_list_of_congruences(from, dropped_rns_factors, value))
}
fn add_rns_factor_element(&self, from: &Self, added_rns_factors: &RNSFactorIndexList, value: &Self::Element) -> Self::Element {
assert_eq!(self.base_ring().len(), from.base_ring().len() + added_rns_factors.len());
self.collect_rns_factors(add_rns_factor_list_of_congruences(self, from, added_rns_factors, value))
}
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 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 lhs_prep: [_; 2] = from_fn(|i| self.prepare_multiplicant(lhs[i]));
let rhs_prep: [_; 2] = from_fn(|i| self.prepare_multiplicant(rhs[i]));
[
self.mul_prepared(lhs[0], Some(&lhs_prep[0]), rhs[0], Some(&rhs_prep[0])),
self.inner_product_prepared([(lhs[0], Some(&lhs_prep[0]), rhs[1], Some(&rhs_prep[1])), (lhs[1], Some(&lhs_prep[1]), rhs[0], Some(&rhs_prep[0]))]),
self.mul_prepared(lhs[1], Some(&lhs_prep[1]), rhs[1], Some(&rhs_prep[1]))
]
}
}
#[instrument(skip_all)]
pub fn perform_rns_op<R, Op>(to: &R, from: &R, el: &R::Element, op: &Op) -> R::Element
where R: NumberRingRNSQuotient,
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());
}
#[cfg(test)]
use feanor_math::rings::extension::extension_impl::FreeAlgebraImpl;
#[test]
fn test_drop_rns_factor_list_of_congruences() {
let from = FreeAlgebraImpl::new(zn_rns::Zn::new(vec![Zn::new(17), Zn::new(19), Zn::new(23)], BigIntRing::RING), 1, []);
let dummy = ();
let dropped_rns_factors = RNSFactorIndexList::from([1], 3);
let actual = drop_rns_factor_list_of_congruences(from.get_ring(), &dropped_rns_factors, &dummy).collect::<Vec<_>>();
assert_eq!(2, actual.len());
match actual[0] {
RNSFactorCongruence::CongruentTo(_, i, ()) => assert_eq!(0, i),
_ => unreachable!()
}
match actual[1] {
RNSFactorCongruence::CongruentTo(_, i, ()) => assert_eq!(2, i),
_ => unreachable!()
}
}