use std::ops::Div;
use super::errors::InfinityAlgebraError;
use crate::arithmetic_utils::Ring;
use crate::infinity_algebra::exterior_power::ExteriorPower;
use crate::infinity_algebra::graded_module::GradedModule;
use crate::infinity_algebra::operation_tree::OperationTree;
pub trait LInfinityAlgebra<Coeffs: Ring>
where
Self: GradedModule<Coeffs>,
{
fn l_n_one_term_owned<const N: usize>(inputs: [Self; N]) -> Self;
fn l_n_one_term<const N: usize>(inputs: [&Self; N]) -> Self;
#[must_use = "Avoid trying to compute the higher coherences which are definitively 0"]
fn max_nonzero_arity() -> Option<usize> {
None
}
fn l_n_slice(inputs: &[Self]) -> Result<Self, InfinityAlgebraError> {
let n = inputs.len();
if let Some(bound) = Self::max_nonzero_arity()
&& n > bound
{
return Ok(Self::zero(inputs[0].ctx()));
}
match n {
0 => Err(InfinityAlgebraError::CurvedAlgebra),
1 => {
let inputs_const: [&Self; 1] = <&[Self; 1]>::try_from(inputs)
.expect("length checked by match arm")
.each_ref();
Ok(Self::l_n_one_term(inputs_const))
}
2 => {
let inputs_const: [&Self; 2] = <&[Self; 2]>::try_from(inputs)
.expect("length checked by match arm")
.each_ref();
Ok(Self::l_n_one_term(inputs_const))
}
3 => {
let inputs_const: [&Self; 3] = <&[Self; 3]>::try_from(inputs)
.expect("length checked by match arm")
.each_ref();
Ok(Self::l_n_one_term(inputs_const))
}
4 => {
let inputs_const: [&Self; 4] = <&[Self; 4]>::try_from(inputs)
.expect("length checked by match arm")
.each_ref();
Ok(Self::l_n_one_term(inputs_const))
}
5 => {
let inputs_const: [&Self; 5] = <&[Self; 5]>::try_from(inputs)
.expect("length checked by match arm")
.each_ref();
Ok(Self::l_n_one_term(inputs_const))
}
6 => {
let inputs_const: [&Self; 6] = <&[Self; 6]>::try_from(inputs)
.expect("length checked by match arm")
.each_ref();
Ok(Self::l_n_one_term(inputs_const))
}
7 => {
let inputs_const: [&Self; 7] = <&[Self; 7]>::try_from(inputs)
.expect("length checked by match arm")
.each_ref();
Ok(Self::l_n_one_term(inputs_const))
}
8 => {
let inputs_const: [&Self; 8] = <&[Self; 8]>::try_from(inputs)
.expect("length checked by match arm")
.each_ref();
Ok(Self::l_n_one_term(inputs_const))
}
_ => Err(InfinityAlgebraError::ArityTooLarge(n)),
}
}
#[must_use = "returns the algebra element resulting from the linear combination; discarding it loses the computed value"]
fn l_n<const N: usize>(ctx: Self::Ctx, wedge: ExteriorPower<Self, Coeffs, N>) -> Self {
let mut result = Self::zero(ctx);
for (pure_wedge, coeff) in wedge.into_summands() {
result += Self::l_n_one_term_owned(pure_wedge) * coeff;
}
result
}
fn evaluate<const N: usize>(
ctx: Self::Ctx,
tree: &OperationTree,
combination: ExteriorPower<Self, Coeffs, N>,
) -> Result<Self, InfinityAlgebraError>
where
Self: Clone,
{
assert_eq!(
tree.num_leaves(),
N,
"tree has {} leaves but combination is in ∧^{}",
tree.num_leaves(),
N,
);
let mut result = Self::zero(ctx);
for (pure_wedge, coeff) in combination.into_summands() {
result += tree.eval_refs_l::<Self, Coeffs>(&pure_wedge)? * coeff;
}
Ok(result)
}
fn truncated_maurer_cartan(
self,
ctx: Self::Ctx,
up_to_n: Option<usize>,
) -> Result<Self, InfinityAlgebraError>
where
Self: Clone + Div<Coeffs, Output = Self>,
{
let up_to_n = up_to_n.unwrap_or({
let max_arity = Self::max_nonzero_arity();
if let Some(max_arity) = max_arity {
max_arity
} else {
return Err(InfinityAlgebraError::ArityTooLarge(0));
}
});
let mut to_return = Self::zero(ctx);
let mut inputs = Vec::with_capacity(up_to_n);
for _ in 0..up_to_n {
inputs.push(self.clone());
}
let mut idx_factorial = Coeffs::one();
for idx in 1..up_to_n {
to_return += (Self::l_n_slice(&inputs[0..idx])?) / idx_factorial.clone();
idx_factorial *= Coeffs::natural_inclusion(idx + 1);
}
Ok(to_return)
}
}
impl OperationTree {
pub(crate) fn eval_refs_l<A, Coeffs>(&self, inputs: &[A]) -> Result<A, InfinityAlgebraError>
where
A: LInfinityAlgebra<Coeffs> + Clone,
Coeffs: Ring,
{
match self {
Self::Leaf {
absoulte_position: range,
} => Ok(inputs[*range].clone()),
Self::Node { children, .. } => {
let mut child_outputs: Vec<A> = Vec::with_capacity(children.len());
for child in children {
child_outputs.push(child.eval_refs_l(inputs)?);
}
A::l_n_slice(&child_outputs)
}
}
}
}