use crate::array::*;
use crate::category::*;
use core::fmt::Debug;
use core::ops::{Add, BitOr, Shr};
use num_traits::{One, Zero};
#[derive(Eq)]
pub struct FiniteFunction<K: ArrayKind> {
pub table: K::Index,
pub target: K::I,
}
impl<K: ArrayKind> PartialEq for FiniteFunction<K> {
fn eq(&self, other: &Self) -> bool {
self.table == other.table && self.target == other.target
}
}
impl<K: ArrayKind> FiniteFunction<K> {
pub fn new(table: K::Index, target: K::I) -> Option<FiniteFunction<K>> {
if let Some(true) = table.max().map(|m| m >= target) {
return None;
}
Some(FiniteFunction { table, target })
}
pub fn terminal(a: K::I) -> Self {
let table = K::Index::fill(K::I::zero(), a);
let target = K::I::one();
FiniteFunction { table, target }
}
pub fn constant(a: K::I, x: K::I, b: K::I) -> Self {
let table = K::Index::fill(x.clone(), a);
let target = x + b + K::I::one(); FiniteFunction { table, target }
}
pub fn inject0(&self, b: K::I) -> FiniteFunction<K> {
FiniteFunction {
table: self.table.clone(),
target: b + self.target(),
}
}
pub fn inject1(&self, a: K::I) -> FiniteFunction<K> {
FiniteFunction {
table: a.clone() + &self.table,
target: a + self.target.clone(),
}
}
pub fn to_initial(&self) -> FiniteFunction<K> {
Self::initial(self.target.clone())
}
pub fn coequalizer(&self, other: &Self) -> Option<FiniteFunction<K>> {
if self.source() != other.source() || self.target() != other.target() {
return None;
}
let (table, target) =
K::Index::connected_components(&self.table, &other.table, self.target());
Some(FiniteFunction { table, target })
}
pub fn coequalizer_universal(&self, f: &Self) -> Option<Self>
where
K::Type<K::I>: Array<K, K::I>,
{
let table = coequalizer_universal(self, f.table.as_ref())?.into();
let target = f.target();
Some(FiniteFunction { table, target })
}
pub fn transpose(a: K::I, b: K::I) -> FiniteFunction<K> {
if a.is_zero() {
return Self::initial(a);
}
let n = b.clone() * a.clone();
let i = K::Index::arange(&K::I::zero(), &n);
let (q, r) = i.quot_rem(a);
FiniteFunction {
target: n,
table: r.mul_constant_add(b, &q),
}
}
pub fn injections(&self, a: &FiniteFunction<K>) -> Option<Self> {
let s = self;
let p = self.table.cumulative_sum();
let k = (a >> s)?;
let r = k.table.segmented_arange();
let repeats = k.table;
let values = p.gather(a.table.get_range(..));
let z = repeats.repeat(values.get_range(..));
Some(FiniteFunction {
table: r + z,
target: p.get(p.len() - K::I::one()),
})
}
pub fn cumulative_sum(&self) -> Self {
let extended_table = self.table.cumulative_sum();
let target = extended_table.get(self.source());
let table = Array::from_slice(extended_table.get_range(..self.source()));
FiniteFunction { table, target }
}
}
pub fn coequalizer_universal<K: ArrayKind, T>(
q: &FiniteFunction<K>,
f: &K::Type<T>,
) -> Option<K::Type<T>>
where
K::Type<T>: Array<K, T>,
{
if q.source() != f.len() {
return None;
}
let table = f.scatter(q.table.get_range(..), q.target());
use crate::semifinite::SemifiniteFunction;
let u = SemifiniteFunction(table);
let f_prime = (q >> &u).expect("by construction");
if f_prime.0 == *f {
Some(u.0)
} else {
None
}
}
impl<K: ArrayKind> Arrow for FiniteFunction<K> {
type Object = K::I;
fn source(&self) -> Self::Object {
self.table.len()
}
fn target(&self) -> Self::Object {
self.target.clone()
}
fn identity(a: Self::Object) -> Self {
let table = K::Index::arange(&K::I::zero(), &a);
let target = a.clone();
FiniteFunction { table, target }
}
fn compose(&self, other: &Self) -> Option<Self> {
if self.target == other.source() {
let table = other.table.gather(self.table.get_range(..));
let target = other.target.clone();
Some(FiniteFunction { table, target })
} else {
None
}
}
}
impl<K: ArrayKind> Coproduct for FiniteFunction<K> {
fn initial_object() -> Self::Object {
K::I::zero()
}
fn initial(a: Self::Object) -> Self {
Self {
table: K::Index::empty(),
target: a.clone(),
}
}
fn coproduct(&self, other: &Self) -> Option<Self> {
if self.target != other.target {
return None;
}
Some(Self {
table: self.table.concatenate(&other.table),
target: self.target.clone(),
})
}
fn inj0(a: Self::Object, b: Self::Object) -> Self {
let table = K::Index::arange(&K::I::zero(), &a);
let target = a.clone() + b.clone();
Self { table, target }
}
fn inj1(a: Self::Object, b: Self::Object) -> Self {
let target = a.clone() + b.clone();
let table = K::Index::arange(&a, &target);
Self { table, target }
}
}
impl<K: ArrayKind> Monoidal for FiniteFunction<K> {
fn unit() -> Self::Object {
K::I::zero()
}
fn tensor(&self, other: &Self) -> Self {
let table = self
.table
.concatenate(&(self.target.clone() + &other.table));
let target = self.target.clone() + other.target.clone();
Self { table, target }
}
}
impl<K: ArrayKind> SymmetricMonoidal for FiniteFunction<K> {
fn twist(a: K::I, b: K::I) -> Self {
let target = a.clone() + b.clone();
let lhs = K::Index::arange(&b, &target);
let rhs = K::Index::arange(&K::I::zero(), &b);
let table = lhs.concatenate(&rhs);
Self { table, target }
}
}
impl<K: ArrayKind> Shr<&FiniteFunction<K>> for &FiniteFunction<K> {
type Output = Option<FiniteFunction<K>>;
fn shr(self, rhs: &FiniteFunction<K>) -> Option<FiniteFunction<K>> {
self.compose(rhs)
}
}
impl<K: ArrayKind> Add<&FiniteFunction<K>> for &FiniteFunction<K> {
type Output = Option<FiniteFunction<K>>;
fn add(self, rhs: &FiniteFunction<K>) -> Option<FiniteFunction<K>> {
self.coproduct(rhs)
}
}
impl<K: ArrayKind> BitOr<&FiniteFunction<K>> for &FiniteFunction<K> {
type Output = FiniteFunction<K>;
fn bitor(self, rhs: &FiniteFunction<K>) -> FiniteFunction<K> {
self.tensor(rhs)
}
}
impl<K: ArrayKind> Clone for FiniteFunction<K> {
fn clone(&self) -> Self {
Self {
table: self.table.clone(),
target: self.target.clone(),
}
}
}
impl<K: ArrayKind> Debug for FiniteFunction<K>
where
K::Index: Debug,
{
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("FiniteFunction")
.field("table", &self.table)
.field("target", &self.target)
.finish()
}
}