use feanor_math::algorithms::linsolve::LinSolveRingStore;
use feanor_math::divisibility::DivisibilityRingStore;
use feanor_math::group::AbelianGroupStore;
use feanor_math::homomorphism::*;
use feanor_math::matrix::OwnedMatrix;
use feanor_math::ring::*;
use feanor_math::rings::zn::*;
use feanor_math::rings::extension::FreeAlgebraStore;
use feanor_math::primitive_int::*;
use feanor_math::seq::VectorFn;
use tracing::instrument;
use crate::circuit::CircuitEvaluatorCosts;
use crate::circuit::PlaintextCircuit;
use crate::lin_transform::trace::trace_circuit;
use crate::number_ring::galois::*;
use crate::number_ring::hypercube::structure::HypercubeStructure;
use crate::number_ring::hypercube::isomorphism::*;
use crate::number_ring::*;
use crate::lin_transform::matmul::*;
use crate::lin_transform::*;
use crate::NiceZn;
fn assert_hypercube_supported(H: &HypercubeStructure) {
assert!(H.galois_group().m() % 2 == 1);
assert!(H.galois_group().is_full_cyclotomic_galois_group());
assert!(H.is_tensor_product_compatible());
}
#[instrument(skip_all)]
fn dwt1d_matrix<R>(H: &HypercubeStructure, slot_ring: &SlotRingOver<R>, dim_index: usize, zeta_powertable: &PowerTable<&SlotRingOver<R>>) -> OwnedMatrix<El<SlotRingOver<R>>>
where R: RingStore,
R::Type: NiceZn
{
assert_hypercube_supported(H);
let Gal = H.galois_group();
let ZZ_to_Zn = Gal.underlying_ring().can_hom(&StaticRing::<i64>::RING).unwrap();
OwnedMatrix::from_fn(H.dim_length(dim_index), H.dim_length(dim_index), |i, j| {
let exponent = Gal.underlying_ring().prod([
*Gal.as_ring_el(&H.map_1d(dim_index, -(i as i64))),
ZZ_to_Zn.map(j as i64),
ZZ_to_Zn.map(H.galois_group().m() as i64 / H.factor_of_m(dim_index).unwrap())
]);
return slot_ring.clone_el(&*zeta_powertable.get_power(Gal.underlying_ring().smallest_lift(exponent)));
})
}
#[instrument(skip_all)]
fn dwt1d_inv_matrix<R>(H: &HypercubeStructure, slot_ring: &SlotRingOver<R>, dim_index: usize, zeta_powertable: &PowerTable<&SlotRingOver<R>>) -> OwnedMatrix<El<SlotRingOver<R>>>
where R: RingStore,
R::Type: NiceZn
{
assert_hypercube_supported(H);
let mut A = dwt1d_matrix(H, slot_ring, dim_index, zeta_powertable);
let mut rhs = OwnedMatrix::identity(H.dim_length(dim_index), H.dim_length(dim_index), slot_ring);
let mut sol = OwnedMatrix::zero(H.dim_length(dim_index), H.dim_length(dim_index), slot_ring);
<_ as LinSolveRingStore>::solve_right(slot_ring, A.data_mut(), rhs.data_mut(), sol.data_mut()).assert_solved();
return sol;
}
#[instrument(skip_all)]
fn dwt1d<'a, R>(H: &HypercubeIsomorphism<R>, dim_index: usize, zeta_powertable: &PowerTable<&SlotRingOf<R>>) -> Vec<MatmulTransform<R::Type>>
where R: RingStore,
R::Type: Sized + NumberRingQuotient,
BaseRing<R>: NiceZn
{
assert_hypercube_supported(H.hypercube());
if H.hypercube().dim_length(dim_index) == 1{
Vec::new()
} else {
let A = dwt1d_matrix(H.hypercube(), H.slot_ring(), dim_index, zeta_powertable);
vec![MatmulTransform::matmul1d(
H,
dim_index,
|i, j, _idxs| H.slot_ring().clone_el(A.at(i, j))
)]
}
}
#[instrument(skip_all)]
fn dwt1d_inv<'a, R>(H: &HypercubeIsomorphism<R>, dim_index: usize, zeta_powertable: &PowerTable<&SlotRingOf<R>>) -> Vec<MatmulTransform<R::Type>>
where R: RingStore,
R::Type: Sized + NumberRingQuotient,
BaseRing<R>: NiceZn
{
assert_hypercube_supported(H.hypercube());
if H.hypercube().dim_length(dim_index) == 1{
Vec::new()
} else {
let A = dwt1d_inv_matrix(H.hypercube(), H.slot_ring(), dim_index, zeta_powertable);
vec![MatmulTransform::matmul1d(
H,
dim_index,
|i, j, _idxs| H.slot_ring().clone_el(A.at(i, j))
)]
}
}
#[instrument(skip_all)]
fn slots_to_powcoeffs_fat_fst_step<R>(H: &HypercubeIsomorphism<R>, dim_index: usize, zeta_powertable: &PowerTable<&SlotRingOf<R>>) -> OwnedMatrix<El<<R::Type as RingExtension>::BaseRing>>
where R: RingStore,
R::Type: Sized + NumberRingQuotient,
BaseRing<R>: NiceZn
{
assert_hypercube_supported(H.hypercube());
let Gal = H.galois_group();
let ZZ_to_Gal = Gal.underlying_ring().can_hom(&StaticRing::<i64>::RING).unwrap();
OwnedMatrix::from_fn(H.hypercube().dim_length(dim_index) * H.slot_ring().rank(), H.hypercube().dim_length(dim_index) * H.slot_ring().rank(), |row_idx, col_idx| {
let i = row_idx / H.slot_ring().rank();
let k = row_idx % H.slot_ring().rank();
let j = col_idx / H.slot_ring().rank();
let l = col_idx % H.slot_ring().rank();
let exponent = Gal.underlying_ring().prod([
*Gal.as_ring_el(&H.hypercube().map_1d(0, -(i as i64))),
ZZ_to_Gal.map(H.galois_group().m() as i64 / H.hypercube().factor_of_m(0).unwrap()),
ZZ_to_Gal.map((j + l * H.hypercube().dim_length(0)) as i64)
]);
return H.slot_ring().wrt_canonical_basis(&*zeta_powertable.get_power(Gal.underlying_ring().smallest_positive_lift(exponent))).at(k);
})
}
#[instrument(skip_all)]
pub fn slots_to_powcoeffs_fat<R>(H: &HypercubeIsomorphism<R>, max_levels: usize, cost_model: &CircuitEvaluatorCosts) -> PlaintextCircuit<R::Type>
where R: RingStore,
R::Type: Sized + NumberRingQuotient,
BaseRing<R>: NiceZn
{
MatmulTransform::to_circuit_many(H.ring(), H.hypercube(), slots_to_powcoeffs_fat_impl(H), max_levels, cost_model)
}
fn slots_to_powcoeffs_fat_impl<R>(H: &HypercubeIsomorphism<R>) -> Vec<MatmulTransform<R::Type>>
where R: RingStore,
R::Type: Sized + NumberRingQuotient,
BaseRing<R>: NiceZn
{
assert_hypercube_supported(H.hypercube());
let mut result = Vec::new();
let zeta_powertable = PowerTable::new(H.slot_ring(), H.slot_ring().canonical_gen(), H.galois_group().m() as usize);
let fst_step_matrix = slots_to_powcoeffs_fat_fst_step(H, 0, &zeta_powertable);
result.push(MatmulTransform::blockmatmul1d(
H,
0,
|(i, k), (j, l), _idxs| H.slot_ring().base_ring().clone_el(fst_step_matrix.at(i * H.slot_ring().rank() + k, j * H.slot_ring().rank() + l))
));
for i in 1..H.hypercube().dim_count() {
result.extend(dwt1d(H, i, &zeta_powertable));
}
return result;
}
#[instrument(skip_all)]
pub fn powcoeffs_to_slots_fat<R>(H: &HypercubeIsomorphism<R>, max_levels: usize, cost_model: &CircuitEvaluatorCosts) -> PlaintextCircuit<R::Type>
where R: RingStore,
R::Type: Sized + NumberRingQuotient,
BaseRing<R>: NiceZn
{
MatmulTransform::to_circuit_many(H.ring(), H.hypercube(), powcoeffs_to_slots_fat_impl(H), max_levels, cost_model)
}
fn powcoeffs_to_slots_fat_impl<R>(H: &HypercubeIsomorphism<R>) -> Vec<MatmulTransform<R::Type>>
where R: RingStore,
R::Type: Sized + NumberRingQuotient,
BaseRing<R>: NiceZn
{
assert_hypercube_supported(H.hypercube());
let mut result = Vec::new();
let zeta_powertable = PowerTable::new(H.slot_ring(), H.slot_ring().canonical_gen(), H.galois_group().m() as usize);
for i in (1..H.hypercube().dim_count()).rev() {
result.extend(dwt1d_inv(H, i, &zeta_powertable));
}
let mut A = slots_to_powcoeffs_fat_fst_step(H, 0, &zeta_powertable);
let mut rhs = OwnedMatrix::identity(H.hypercube().dim_length(0) * H.slot_ring().rank(), H.hypercube().dim_length(0) * H.slot_ring().rank(), H.slot_ring().base_ring());
let mut sol = OwnedMatrix::zero(H.hypercube().dim_length(0) * H.slot_ring().rank(), H.hypercube().dim_length(0) * H.slot_ring().rank(), H.slot_ring().base_ring());
<_ as LinSolveRingStore>::solve_right(H.slot_ring().base_ring(), A.data_mut(), rhs.data_mut(), sol.data_mut()).assert_solved();
result.push(MatmulTransform::blockmatmul1d(
H,
0,
|(i, k), (j, l), _idxs| H.slot_ring().base_ring().clone_el(sol.at(i * H.slot_ring().rank() + k, j * H.slot_ring().rank() + l))
));
return result;
}
#[instrument(skip_all)]
pub fn slots_to_powcoeffs_thin<R>(H: &HypercubeIsomorphism<R>, max_levels: usize, cost_model: &CircuitEvaluatorCosts) -> PlaintextCircuit<R::Type>
where R: RingStore,
R::Type: Sized + NumberRingQuotient,
BaseRing<R>: NiceZn
{
MatmulTransform::to_circuit_many(H.ring(), H.hypercube(), slots_to_powcoeffs_thin_impl(H), max_levels, cost_model)
}
fn slots_to_powcoeffs_thin_impl<R>(H: &HypercubeIsomorphism<R>) -> Vec<MatmulTransform<R::Type>>
where R: RingStore,
R::Type: Sized + NumberRingQuotient,
BaseRing<R>: NiceZn
{
assert_hypercube_supported(H.hypercube());
let zeta_powertable = PowerTable::new(H.slot_ring(), H.slot_ring().canonical_gen(), H.galois_group().m() as usize);
let mut result = Vec::new();
for i in 0..H.hypercube().dim_count() {
result.extend(dwt1d(H, i, &zeta_powertable));
}
return result;
}
#[instrument(skip_all)]
fn powcoeffs_to_slots_thin_last_step<R>(H: &HypercubeIsomorphism<R>, dim_index: usize, zeta_powertable: &PowerTable<&SlotRingOf<R>>) -> OwnedMatrix<El<SlotRingOf<R>>>
where R: RingStore,
R::Type: Sized + NumberRingQuotient,
BaseRing<R>: NiceZn
{
assert_hypercube_supported(H.hypercube());
assert_eq!(0, dim_index);
let Gal = H.galois_group();
let ZZ_to_Zn = Gal.underlying_ring().can_hom(&StaticRing::<i64>::RING).unwrap();
let slot_ring = H.slot_ring();
let scale = slot_ring.base_ring().invert(&slot_ring.base_ring().coerce(&StaticRing::<i64>::RING, H.hypercube().factor_of_m(dim_index).unwrap())).unwrap();
OwnedMatrix::from_fn(H.hypercube().dim_length(dim_index), H.hypercube().dim_length(dim_index), |k, j| {
let exponent1 = Gal.underlying_ring().prod([
*Gal.as_ring_el(&H.hypercube().map_1d(dim_index, -(j as i64))),
ZZ_to_Zn.map(-(k as i64)),
ZZ_to_Zn.map(H.galois_group().m() as i64 / H.hypercube().factor_of_m(dim_index).unwrap())
]);
let exponent2 = Gal.underlying_ring().prod([
*Gal.as_ring_el(&H.hypercube().map_1d(dim_index, -(j as i64))),
ZZ_to_Zn.map(H.galois_group().m() as i64 / H.hypercube().factor_of_m(dim_index).unwrap())
]);
return slot_ring.inclusion().mul_ref_map(&slot_ring.sub_ref(
&*zeta_powertable.get_power(Gal.underlying_ring().smallest_lift(exponent1)),
&*zeta_powertable.get_power(Gal.underlying_ring().smallest_lift(exponent2))
), &scale);
})
}
#[instrument(skip_all)]
pub fn powcoeffs_to_slots_thin<R>(H: &HypercubeIsomorphism<R>, max_levels: usize, cost_model: &CircuitEvaluatorCosts) -> PlaintextCircuit<R::Type>
where R: RingStore,
R::Type: Sized + NumberRingQuotient,
BaseRing<R>: NiceZn
{
let frobenius_subgroup = H.galois_group().parent().get_group().clone().subgroup([H.hypercube().frobenius(1)]);
debug_assert_eq!(frobenius_subgroup.group_order(), H.slot_ring().rank());
let trace_circuit = trace_circuit(H.ring(), &frobenius_subgroup, cost_model);
trace_circuit.compose(MatmulTransform::to_circuit_many(H.ring(), H.hypercube(), powcoeffs_to_slots_thin_base(H), max_levels, cost_model), H.ring())
}
fn powcoeffs_to_slots_thin_base<R>(H: &HypercubeIsomorphism<R>) -> Vec<MatmulTransform<R::Type>>
where R: RingStore,
R::Type: Sized + NumberRingQuotient,
BaseRing<R>: NiceZn
{
assert_hypercube_supported(H.hypercube());
let mut result = Vec::new();
let zeta_powertable = PowerTable::new(H.slot_ring(), H.slot_ring().canonical_gen(), H.galois_group().m() as usize);
for i in (1..H.hypercube().dim_count()).rev() {
result.extend(dwt1d_inv(H, i, &zeta_powertable));
}
let last_step_matrix = powcoeffs_to_slots_thin_last_step(H, 0, &zeta_powertable);
result.push(MatmulTransform::matmul1d(
H,
0,
|i, j, _| H.slot_ring().clone_el(last_step_matrix.at(i, j))
));
return result;
}
#[cfg(test)]
use crate::ring_literal;
#[cfg(test)]
use crate::number_ring::quotient_by_int::NumberRingQuotientByIntBase;
#[cfg(test)]
use feanor_math::assert_el_eq;
#[cfg(test)]
use feanor_math::integer::*;
#[cfg(test)]
use crate::number_ring::tensor_ring::TensorProductNumberRing;
#[cfg(test)]
use crate::{ZZi64, ZZbig};
#[cfg(test)]
use crate::circuit::DEFAULT_EVALUATOR_COSTS;
#[test]
fn test_slots_to_powcoeffs_thin() {
feanor_tracing::DelayedLogger::init_test();
let ring = NumberRingQuotientByIntBase::new(TensorProductNumberRing::new(5, 7), Zn::new(11));
let hypercube = HypercubeStructure::halevi_shoup_hypercube(ring.acting_galois_group(), int_cast(11, ZZbig, ZZi64));
let H = HypercubeIsomorphism::new(&ring, &hypercube, None);
let mut current = ring_literal(&ring, &[1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
for transform in slots_to_powcoeffs_thin_impl(&H) {
current = ring.get_ring().compute_linear_transform(H.hypercube(), ¤t, &transform);
}
let expected = ring.sum([0, 5, 7, 12, 14, 19, 21, 26].into_iter().map(|k| ring.pow(ring.canonical_gen(), k)));
assert_el_eq!(ring, expected, current);
assert_eq!(7, H.hypercube().factor_of_m(0).unwrap());
assert_eq!(2, H.hypercube().dim_length(0));
assert_eq!(5, H.hypercube().factor_of_m(1).unwrap());
assert_eq!(4, H.hypercube().dim_length(1));
let mut current = H.from_slot_values((1..9).map(|m| H.slot_ring().int_hom().map(m)));
for transform in slots_to_powcoeffs_thin_impl(&H) {
current = ring.get_ring().compute_linear_transform(H.hypercube(), ¤t, &transform);
}
let ring_ref = ˚
let expected = ring.sum((0..2).flat_map(|i| (0..4).map(move |j| ring_ref.mul(ring_ref.pow(ring_ref.canonical_gen(), i * 5 + j * 7), ring_ref.int_hom().map((1 + j + i * 4) as i32)))));
assert_el_eq!(ring, expected, current);
let ring = NumberRingQuotientByIntBase::new(TensorProductNumberRing::new(5, 7), Zn::new(71));
let hypercube = HypercubeStructure::halevi_shoup_hypercube(ring.acting_galois_group(), int_cast(71, ZZbig, ZZi64));
let H = HypercubeIsomorphism::new(&ring, &hypercube, None);
let mut current = H.from_slot_values((1..25).map(|m| H.slot_ring().int_hom().map(m)));
for transform in slots_to_powcoeffs_thin_impl(&H) {
current = ring.get_ring().compute_linear_transform(H.hypercube(), ¤t, &transform);
}
let ring_ref = ˚
let expected = ring.sum((0..4).flat_map(|i| (0..6).map(move |j| ring_ref.mul(ring_ref.pow(ring_ref.canonical_gen(), i * 7 + j * 5), ring_ref.int_hom().map((1 + j + i * 6) as i32)))));
assert_el_eq!(ring, expected, current);
let ring = NumberRingQuotientByIntBase::new(TensorProductNumberRing::new(11, 31), Zn::new(8));
let hypercube = HypercubeStructure::halevi_shoup_hypercube(ring.acting_galois_group(), int_cast(2, ZZbig, ZZi64));
let H = HypercubeIsomorphism::new(&ring, &hypercube, None);
let mut current = H.from_slot_values((1..=30).map(|m| H.slot_ring().int_hom().map(m)));
for transform in slots_to_powcoeffs_thin_impl(&H) {
current = ring.get_ring().compute_linear_transform(H.hypercube(), ¤t, &transform);
}
let expected = ring.sum((0..30).map(|j| ring.mul(ring.pow(ring.canonical_gen(), j * 11), ring.int_hom().map((j + 1) as i32))));
assert_el_eq!(ring, expected, current);
}
#[test]
fn test_powcoeffs_to_slots_thin() {
feanor_tracing::DelayedLogger::init_test();
let ring = NumberRingQuotientByIntBase::new(TensorProductNumberRing::new(5, 7), Zn::new(11));
let hypercube = HypercubeStructure::halevi_shoup_hypercube(ring.acting_galois_group(), int_cast(11, ZZbig, ZZi64));
let H = HypercubeIsomorphism::new(&ring, &hypercube, None);
let trace = MatmulTransform::linear_combine_shifts_with_frobenius(&H, (0..H.slot_ring().rank()).map(|i| (vec![i as i64, 0, 0], H.ring().one())));
assert_eq!(7, H.hypercube().factor_of_m(0).unwrap());
assert_eq!(2, H.hypercube().dim_length(0));
assert_eq!(5, H.hypercube().factor_of_m(1).unwrap());
assert_eq!(4, H.hypercube().dim_length(1));
let mut current = ring_literal(&ring, &[1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
for transform in powcoeffs_to_slots_thin_base(&H) {
current = ring.get_ring().compute_linear_transform(H.hypercube(), ¤t, &transform);
}
current = ring.get_ring().compute_linear_transform(H.hypercube(), ¤t, &trace);
let expected = H.from_slot_values([H.slot_ring().one()].into_iter().chain((2..9).map(|_| H.slot_ring().zero())));
assert_el_eq!(ring, expected, current);
let ring_ref = ˚
let mut current = ring.sum((0..6).flat_map(|i| (0..4).map(move |j| ring_ref.mul(ring_ref.pow(ring_ref.canonical_gen(), i * 5 + j * 7), ring_ref.int_hom().map((1 + j + i * 4) as i32)))));
for transform in powcoeffs_to_slots_thin_base(&H) {
current = ring.get_ring().compute_linear_transform(H.hypercube(), ¤t, &transform);
}
current = ring.get_ring().compute_linear_transform(H.hypercube(), ¤t, &trace);
let expected = H.from_slot_values([1, 2, 3, 4, 5, 6, 7, 8].into_iter().map(|i| H.slot_ring().int_hom().map(i)));
assert_el_eq!(ring, expected, current);
let ring = NumberRingQuotientByIntBase::new(TensorProductNumberRing::new(5, 7), Zn::new(71));
let hypercube = HypercubeStructure::halevi_shoup_hypercube(ring.acting_galois_group(), int_cast(71, ZZbig, ZZi64));
let H = HypercubeIsomorphism::new(&ring, &hypercube, None);
let trace = MatmulTransform::linear_combine_shifts_with_frobenius(&H, (0..H.slot_ring().rank()).map(|i| (vec![i as i64, 0, 0], H.ring().one())));
let ring_ref = ˚
let mut current = ring.sum((0..4).flat_map(|i| (0..6).map(move |j| ring_ref.mul(ring_ref.pow(ring_ref.canonical_gen(), i * 7 + j * 5), ring_ref.int_hom().map((1 + j + i * 6) as i32)))));
for transform in powcoeffs_to_slots_thin_base(&H) {
current = ring.get_ring().compute_linear_transform(H.hypercube(), ¤t, &transform);
}
current = ring.get_ring().compute_linear_transform(H.hypercube(), ¤t, &trace);
let expected = H.from_slot_values((1..25).map(|i| H.slot_ring().int_hom().map(i)));
assert_el_eq!(ring, expected, current);
}
#[test]
fn test_slots_to_powcoeffs_fat() {
feanor_tracing::DelayedLogger::init_test();
let ring = NumberRingQuotientByIntBase::new(TensorProductNumberRing::new(5, 7), Zn::new(11));
let hypercube = HypercubeStructure::halevi_shoup_hypercube(ring.acting_galois_group(), int_cast(11, ZZbig, ZZi64));
let H = HypercubeIsomorphism::new(&ring, &hypercube, None);
let mut current = ring_literal(&ring, &[1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
for transform in slots_to_powcoeffs_fat_impl(&H) {
current = ring.get_ring().compute_linear_transform(H.hypercube(), ¤t, &transform);
}
let expected = ring.sum([0, 5, 7, 12, 14, 19, 21, 26].into_iter().map(|k| ring.pow(ring.canonical_gen(), k)));
assert_el_eq!(ring, expected, current);
assert_eq!(7, H.hypercube().factor_of_m(0).unwrap());
assert_eq!(2, H.hypercube().dim_length(0));
assert_eq!(5, H.hypercube().factor_of_m(1).unwrap());
assert_eq!(4, H.hypercube().dim_length(1));
let mut current = H.from_slot_values((1..9).map(|i| H.slot_ring().int_hom().map(i)));
for transform in slots_to_powcoeffs_fat_impl(&H) {
current = ring.get_ring().compute_linear_transform(H.hypercube(), ¤t, &transform);
}
let ring_ref = ˚
let expected = ring.sum((0..2).flat_map(|i| (0..4).map(move |j| ring_ref.mul(ring_ref.pow(ring_ref.canonical_gen(), i * 5 + j * 7), ring_ref.int_hom().map((1 + j + i * 4) as i32)))));
assert_el_eq!(ring, expected, current);
let hom = H.slot_ring().base_ring().int_hom();
let mut current = H.from_slot_values((1..9).map(|i| H.slot_ring().from_canonical_basis([hom.map(i), hom.map(i + 100), hom.map(i + 200)])));
for transform in slots_to_powcoeffs_fat_impl(&H) {
current = ring.get_ring().compute_linear_transform(H.hypercube(), ¤t, &transform);
}
let ring_ref = ˚
let expected = ring.sum((0..3).flat_map(|k| (0..2).flat_map(move |i| (0..4).map(move |j| ring_ref.mul(ring_ref.pow(ring_ref.canonical_gen(), (i + k * 2) * 5 + j * 7), ring_ref.int_hom().map((1 + j + i * 4 + k * 100) as i32))))));
assert_el_eq!(ring, expected, current);
let ring = NumberRingQuotientByIntBase::new(TensorProductNumberRing::new(5, 7), Zn::new(71));
let hypercube = HypercubeStructure::halevi_shoup_hypercube(ring.acting_galois_group(), int_cast(71, ZZbig, ZZi64));
let H = HypercubeIsomorphism::new(&ring, &hypercube, None);
assert_eq!(5, H.hypercube().factor_of_m(0).unwrap());
assert_eq!(4, H.hypercube().dim_length(0));
assert_eq!(7, H.hypercube().factor_of_m(1).unwrap());
assert_eq!(6, H.hypercube().dim_length(1));
let mut current = H.from_slot_values((1..25).map(|i| H.slot_ring().int_hom().map(i)));
for transform in slots_to_powcoeffs_fat_impl(&H) {
current = ring.get_ring().compute_linear_transform(H.hypercube(), ¤t, &transform);
}
let ring_ref = ˚
let expected = ring.sum((0..4).flat_map(|i| (0..6).map(move |j| ring_ref.mul(ring_ref.pow(ring_ref.canonical_gen(), i * 7 + j * 5), ring_ref.int_hom().map((1 + j + i * 6) as i32)))));
assert_el_eq!(ring, expected, current);
}
#[test]
fn test_powcoeffs_to_slots_fat() {
feanor_tracing::DelayedLogger::init_test();
let ring = NumberRingQuotientByIntBase::new(TensorProductNumberRing::new(5, 7), Zn::new(11));
let hypercube = HypercubeStructure::halevi_shoup_hypercube(ring.acting_galois_group(), int_cast(11, ZZbig, ZZi64));
let H = HypercubeIsomorphism::new(&ring, &hypercube, None);
assert_eq!(7, H.hypercube().factor_of_m(0).unwrap());
assert_eq!(2, H.hypercube().dim_length(0));
assert_eq!(5, H.hypercube().factor_of_m(1).unwrap());
assert_eq!(4, H.hypercube().dim_length(1));
let ring_ref = ˚
let mut current = ring.sum((0..6).flat_map(|i| (0..4).map(move |j| ring_ref.mul(ring_ref.pow(ring_ref.canonical_gen(), i * 5 + j * 7), ring_ref.int_hom().map((1 + j + i * 4) as i32)))));
for transform in powcoeffs_to_slots_fat_impl(&H) {
current = ring.get_ring().compute_linear_transform(H.hypercube(), ¤t, &transform);
}
let expected = H.from_slot_values([1, 2, 3, 4, 5, 6, 7, 8].into_iter().map(|i| H.slot_ring().from_canonical_basis([i, i + 8, i + 16].into_iter().map(|i| H.slot_ring().base_ring().int_hom().map(i)))));
assert_el_eq!(ring, expected, current);
}
#[test]
#[ignore]
fn test_powcoeffs_to_slots_thin_large() {
feanor_tracing::DelayedLogger::init_test();
let ring = RingValue::from(NumberRingQuotientByIntBase::new(TensorProductNumberRing::new(337, 127), Zn::new(65536)).into());
let h = HypercubeStructure::halevi_shoup_hypercube(ring.acting_galois_group(), int_cast(2, ZZbig, ZZi64));
let H = HypercubeIsomorphism::new(&ring, &h, Some("./cache"));
assert_eq!(337, H.hypercube().factor_of_m(0).unwrap());
assert_eq!(16, H.hypercube().dim_length(0));
assert_eq!(127, H.hypercube().factor_of_m(1).unwrap());
assert_eq!(126, H.hypercube().dim_length(1));
let transform = powcoeffs_to_slots_thin_base(&H);
println!("{}", transform.len());
println!("{}", transform.last().unwrap().automorphism_count());
let circuit = transform.last().unwrap().clone(&ring).to_circuit(&ring, &h, &DEFAULT_EVALUATOR_COSTS);
println!("{}", circuit.galois_gate_output_sum());
let ring_ref = ˚
let mut current = ring.pow(ring_ref.canonical_gen(), 7 * 127 + 2 * 337);
for transform in &transform {
current = ring.get_ring().compute_linear_transform(H.hypercube(), ¤t, &transform);
}
let expected = H.from_slot_values(H.hypercube().hypercube_iter(|idxs| if idxs[0] == 7 && idxs[1] == 2 {
H.slot_ring().one()
} else {
H.slot_ring().zero()
}));
assert_el_eq!(ring, expected, current);
}