use crate::array::*;
use crate::category::*;
use crate::finite_function::*;
use crate::semifinite::*;
use core::fmt::Debug;
use core::ops::{Add, Shr};
use num_traits::{One, Zero};
pub trait HasLen<K: ArrayKind> {
fn len(&self) -> K::I;
fn is_empty(&self) -> bool {
self.len() == K::I::zero()
}
}
impl<K: ArrayKind> HasLen<K> for FiniteFunction<K> {
fn len(&self) -> K::I {
self.source()
}
}
impl<K: ArrayKind, T> HasLen<K> for SemifiniteFunction<K, T>
where
K::Type<T>: Array<K, T>,
{
fn len(&self) -> K::I {
self.0.len()
}
}
#[non_exhaustive] pub struct IndexedCoproduct<K: ArrayKind, F> {
pub sources: FiniteFunction<K>,
pub values: F,
}
impl<K: ArrayKind, F: Clone> Clone for IndexedCoproduct<K, F>
where
K::Type<K::I>: Clone,
{
fn clone(&self) -> Self {
Self {
sources: self.sources.clone(),
values: self.values.clone(),
}
}
}
impl<K: ArrayKind, F: PartialEq> PartialEq for IndexedCoproduct<K, F> {
fn eq(&self, other: &Self) -> bool {
self.sources == other.sources && self.values == other.values
}
}
impl<K: ArrayKind, F: Clone + HasLen<K>> IndexedCoproduct<K, F>
where
K::Type<K::I>: NaturalArray<K>,
{
pub fn new(sources: FiniteFunction<K>, values: F) -> Option<Self> {
IndexedCoproduct { sources, values }.validate()
}
pub fn from_semifinite(sources: SemifiniteFunction<K, K::I>, values: F) -> Option<Self> {
let sources = FiniteFunction::new(sources.0.into(), values.len() + K::I::one())?;
IndexedCoproduct { sources, values }.validate()
}
fn validate(self) -> Option<Self> {
let sum = self.sources.table.sum();
if self.sources.target != sum.clone() + K::I::one() {
return None;
}
if sum != self.values.len() {
return None;
}
Some(self)
}
}
impl<K: ArrayKind, F: Clone + HasLen<K>> IndexedCoproduct<K, F>
where
K::Type<K::I>: NaturalArray<K>,
{
pub fn singleton(values: F) -> Self {
let n = values.len();
let sources = FiniteFunction::constant(K::I::one(), n, K::I::zero());
IndexedCoproduct { sources, values }
}
pub fn elements(values: F) -> Self {
let n = values.len();
let sources =
FiniteFunction::new(K::Index::fill(K::I::one(), n.clone()), n + K::I::one()).unwrap();
IndexedCoproduct::new(sources, values).expect("by construction")
}
pub fn len(&self) -> K::I {
self.sources.source()
}
pub fn flatmap_sources<G: Clone>(
&self,
other: &IndexedCoproduct<K, G>,
) -> IndexedCoproduct<K, G> {
assert_eq!(self.values.len(), other.len());
let sources = FiniteFunction {
table: self.sources.table.segmented_sum(&other.sources.table),
target: other.sources.target.clone(), };
let values = other.values.clone();
IndexedCoproduct { sources, values }
}
}
impl<K: ArrayKind, F> HasLen<K> for IndexedCoproduct<K, F>
where
K::Type<K::I>: NaturalArray<K>,
{
fn len(&self) -> K::I {
self.sources.len()
}
}
impl<K: ArrayKind> IndexedCoproduct<K, FiniteFunction<K>>
where
K::Type<K::I>: NaturalArray<K>,
{
pub fn initial(target: K::I) -> Self {
let sources = FiniteFunction::initial(K::I::one());
let values = FiniteFunction::initial(target);
IndexedCoproduct { sources, values }
}
pub fn tensor(
&self,
other: &IndexedCoproduct<K, FiniteFunction<K>>,
) -> IndexedCoproduct<K, FiniteFunction<K>> {
let table = self.sources.table.concatenate(&other.sources.table);
let target = (self.sources.target.clone() + other.sources.target.clone()) - K::I::one();
IndexedCoproduct {
sources: FiniteFunction { table, target },
values: &self.values | &other.values,
}
}
pub fn map_values(&self, x: &FiniteFunction<K>) -> Option<Self> {
Some(Self {
sources: self.sources.clone(),
values: (&self.values >> x)?,
})
}
pub fn map_semifinite<T>(
&self,
x: &SemifiniteFunction<K, T>,
) -> Option<IndexedCoproduct<K, SemifiniteFunction<K, T>>>
where
K::Type<T>: Array<K, T>,
{
Some(IndexedCoproduct::<K, SemifiniteFunction<K, T>> {
sources: self.sources.clone(),
values: (&self.values >> x)?,
})
}
pub fn flatmap(&self, other: &Self) -> Self {
assert_eq!(self.values.target(), other.len());
let sources_table = self
.sources
.table
.segmented_sum(&(&self.values >> &other.sources).unwrap().table);
let values = &other.sources.injections(&self.values).unwrap() >> &other.values;
let values = values.unwrap();
IndexedCoproduct::from_semifinite(SemifiniteFunction(sources_table.into()), values).unwrap()
}
}
impl<K: ArrayKind, F> IndexedCoproduct<K, F>
where
K::Type<K::I>: NaturalArray<K>,
F: HasLen<K> + Clone,
for<'a, 'b> &'a FiniteFunction<K>: Shr<&'b F, Output = Option<F>>, for<'a, 'b> &'a F: Add<&'b F, Output = Option<F>>, {
pub fn coproduct(&self, other: &Self) -> Option<Self> {
let table = self.sources.table.concatenate(&other.sources.table);
let target = (self.sources.target.clone() + other.sources.target.clone()) - K::I::one();
Some(IndexedCoproduct {
sources: FiniteFunction { table, target },
values: (&self.values + &other.values)?,
})
}
pub fn map_indexes(&self, x: &FiniteFunction<K>) -> Option<Self> {
let sources = x.compose(&self.sources)?;
let values = self.indexed_values(x)?;
IndexedCoproduct::from_semifinite(SemifiniteFunction(sources.table.into()), values)
}
pub fn indexed_values(&self, x: &FiniteFunction<K>) -> Option<F> {
&self.sources.injections(x)? >> &self.values
}
}
impl<K: ArrayKind, F: Debug> Debug for IndexedCoproduct<K, F>
where
K::Index: Debug,
{
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("IndexedCoproduct")
.field("sources", &self.sources)
.field("values", &self.values)
.finish()
}
}