use oxilean_kernel::{
BinderInfo, Declaration, Environment, Expr, InductiveEnv, InductiveType, IntroRule, Level, Name,
};
use super::functions::*;
#[derive(Clone, Debug, Default)]
pub struct SumStats {
pub left_count: usize,
pub right_count: usize,
}
impl SumStats {
pub fn from_slice<A, B>(xs: &[Coproduct<A, B>]) -> Self {
let left_count = xs.iter().filter(|c| c.is_left()).count();
let right_count = xs.len() - left_count;
Self {
left_count,
right_count,
}
}
pub fn total(&self) -> usize {
self.left_count + self.right_count
}
pub fn left_fraction(&self) -> f64 {
if self.total() == 0 {
0.0
} else {
self.left_count as f64 / self.total() as f64
}
}
pub fn all_left(&self) -> bool {
self.right_count == 0 && self.total() > 0
}
pub fn all_right(&self) -> bool {
self.left_count == 0 && self.total() > 0
}
}
pub struct SumChain<A, B> {
inner: Coproduct<A, B>,
}
impl<A, B> SumChain<A, B> {
pub fn new(c: Coproduct<A, B>) -> Self {
Self { inner: c }
}
pub fn map<C>(self, f: impl FnOnce(B) -> C) -> SumChain<A, C> {
SumChain {
inner: self.inner.map_right(f),
}
}
pub fn flat_map<C>(self, f: impl FnOnce(B) -> Coproduct<A, C>) -> SumChain<A, C> {
SumChain {
inner: match self.inner {
Coproduct::Inl(a) => Coproduct::Inl(a),
Coproduct::Inr(b) => f(b),
},
}
}
pub fn into_inner(self) -> Coproduct<A, B> {
self.inner
}
pub fn is_ok(&self) -> bool {
self.inner.is_right()
}
pub fn is_err(&self) -> bool {
self.inner.is_left()
}
}
#[allow(dead_code)]
pub struct SumBifunctor<A, B, C, D> {
_a: std::marker::PhantomData<A>,
_b: std::marker::PhantomData<B>,
_c: std::marker::PhantomData<C>,
_d: std::marker::PhantomData<D>,
pub name: String,
}
impl<A, B, C, D> SumBifunctor<A, B, C, D> {
pub fn new(name: impl Into<String>) -> Self {
SumBifunctor {
_a: std::marker::PhantomData,
_b: std::marker::PhantomData,
_c: std::marker::PhantomData,
_d: std::marker::PhantomData,
name: name.into(),
}
}
pub fn apply(
&self,
s: Coproduct<A, B>,
f: impl FnOnce(A) -> C,
g: impl FnOnce(B) -> D,
) -> Coproduct<C, D> {
s.bimap(f, g)
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum Coproduct<A, B> {
Inl(A),
Inr(B),
}
impl<A, B> Coproduct<A, B> {
pub fn inl(a: A) -> Self {
Coproduct::Inl(a)
}
pub fn inr(b: B) -> Self {
Coproduct::Inr(b)
}
pub fn is_left(&self) -> bool {
matches!(self, Coproduct::Inl(_))
}
pub fn is_right(&self) -> bool {
matches!(self, Coproduct::Inr(_))
}
pub fn elim<C>(self, f: impl FnOnce(A) -> C, g: impl FnOnce(B) -> C) -> C {
match self {
Coproduct::Inl(a) => f(a),
Coproduct::Inr(b) => g(b),
}
}
pub fn swap(self) -> Coproduct<B, A> {
match self {
Coproduct::Inl(a) => Coproduct::Inr(a),
Coproduct::Inr(b) => Coproduct::Inl(b),
}
}
pub fn bimap<C, D>(self, f: impl FnOnce(A) -> C, g: impl FnOnce(B) -> D) -> Coproduct<C, D> {
match self {
Coproduct::Inl(a) => Coproduct::Inl(f(a)),
Coproduct::Inr(b) => Coproduct::Inr(g(b)),
}
}
pub fn map_left<C>(self, f: impl FnOnce(A) -> C) -> Coproduct<C, B> {
match self {
Coproduct::Inl(a) => Coproduct::Inl(f(a)),
Coproduct::Inr(b) => Coproduct::Inr(b),
}
}
pub fn map_right<D>(self, g: impl FnOnce(B) -> D) -> Coproduct<A, D> {
match self {
Coproduct::Inl(a) => Coproduct::Inl(a),
Coproduct::Inr(b) => Coproduct::Inr(g(b)),
}
}
pub fn left_or(self, default: A) -> A {
match self {
Coproduct::Inl(a) => a,
Coproduct::Inr(_) => default,
}
}
pub fn right_or(self, default: B) -> B {
match self {
Coproduct::Inl(_) => default,
Coproduct::Inr(b) => b,
}
}
pub fn into_left(self) -> Option<A> {
match self {
Coproduct::Inl(a) => Some(a),
Coproduct::Inr(_) => None,
}
}
pub fn into_right(self) -> Option<B> {
match self {
Coproduct::Inl(_) => None,
Coproduct::Inr(b) => Some(b),
}
}
pub fn into_result(self) -> Result<B, A> {
match self {
Coproduct::Inl(a) => Err(a),
Coproduct::Inr(b) => Ok(b),
}
}
pub fn from_result(r: Result<B, A>) -> Self {
match r {
Ok(b) => Coproduct::Inr(b),
Err(a) => Coproduct::Inl(a),
}
}
}
impl<A: Clone, B: Clone> Coproduct<A, B> {
pub fn merge(self) -> A
where
A: Clone,
B: Into<A>,
{
match self {
Coproduct::Inl(a) => a,
Coproduct::Inr(b) => b.into(),
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Sum4<A, B, C, D> {
In1(A),
In2(B),
In3(C),
In4(D),
}
impl<A, B, C, D> Sum4<A, B, C, D> {
#[allow(clippy::too_many_arguments)]
pub fn elim<E>(
self,
f1: impl FnOnce(A) -> E,
f2: impl FnOnce(B) -> E,
f3: impl FnOnce(C) -> E,
f4: impl FnOnce(D) -> E,
) -> E {
match self {
Sum4::In1(a) => f1(a),
Sum4::In2(b) => f2(b),
Sum4::In3(c) => f3(c),
Sum4::In4(d) => f4(d),
}
}
pub fn tag(&self) -> u8 {
match self {
Sum4::In1(_) => 1,
Sum4::In2(_) => 2,
Sum4::In3(_) => 3,
Sum4::In4(_) => 4,
}
}
pub fn is_first(&self) -> bool {
matches!(self, Sum4::In1(_))
}
pub fn is_second(&self) -> bool {
matches!(self, Sum4::In2(_))
}
pub fn is_third(&self) -> bool {
matches!(self, Sum4::In3(_))
}
pub fn is_fourth(&self) -> bool {
matches!(self, Sum4::In4(_))
}
}
#[derive(Clone, Debug, Default)]
pub struct InjectionMap {
labels: Vec<(u8, String)>,
}
impl InjectionMap {
pub fn new() -> Self {
Self::default()
}
pub fn register(&mut self, tag: u8, label: impl Into<String>) {
self.labels.push((tag, label.into()));
}
pub fn get(&self, tag: u8) -> Option<&str> {
self.labels
.iter()
.find(|(t, _)| *t == tag)
.map(|(_, l)| l.as_str())
}
pub fn len(&self) -> usize {
self.labels.len()
}
pub fn is_empty(&self) -> bool {
self.labels.is_empty()
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Tagged<T, A> {
pub tag: T,
pub value: A,
}
impl<T, A> Tagged<T, A> {
pub fn new(tag: T, value: A) -> Self {
Tagged { tag, value }
}
pub fn map<B>(self, f: impl FnOnce(A) -> B) -> Tagged<T, B> {
Tagged {
tag: self.tag,
value: f(self.value),
}
}
pub fn map_tag<U>(self, f: impl FnOnce(T) -> U) -> Tagged<U, A> {
Tagged {
tag: f(self.tag),
value: self.value,
}
}
}
#[allow(dead_code)]
pub struct TaggedUnionSm {
pub tag: String,
pub payload: String,
}
impl TaggedUnionSm {
pub fn new(tag: impl Into<String>, payload: impl Into<String>) -> Self {
TaggedUnionSm {
tag: tag.into(),
payload: payload.into(),
}
}
pub fn render(&self) -> String {
format!("{}({})", self.tag, self.payload)
}
pub fn has_tag(&self, t: &str) -> bool {
self.tag == t
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Pair<A, B> {
pub fst: A,
pub snd: B,
}
impl<A, B> Pair<A, B> {
pub fn new(fst: A, snd: B) -> Self {
Pair { fst, snd }
}
pub fn swap(self) -> Pair<B, A> {
Pair {
fst: self.snd,
snd: self.fst,
}
}
pub fn map_fst<C>(self, f: impl FnOnce(A) -> C) -> Pair<C, B> {
Pair {
fst: f(self.fst),
snd: self.snd,
}
}
pub fn map_snd<D>(self, g: impl FnOnce(B) -> D) -> Pair<A, D> {
Pair {
fst: self.fst,
snd: g(self.snd),
}
}
pub fn into_tuple(self) -> (A, B) {
(self.fst, self.snd)
}
pub fn from_tuple(t: (A, B)) -> Self {
Pair { fst: t.0, snd: t.1 }
}
}
#[allow(dead_code)]
pub struct EitherPartitionSm<L, R> {
pub lefts: Vec<L>,
pub rights: Vec<R>,
}
impl<L, R> EitherPartitionSm<L, R> {
pub fn from_vec(xs: Vec<Coproduct<L, R>>) -> Self {
let (ls, rs) = partition(xs);
EitherPartitionSm {
lefts: ls,
rights: rs,
}
}
pub fn total(&self) -> usize {
self.lefts.len() + self.rights.len()
}
pub fn all_right(&self) -> bool {
self.lefts.is_empty()
}
pub fn all_left(&self) -> bool {
self.rights.is_empty()
}
pub fn left_count(&self) -> usize {
self.lefts.len()
}
pub fn right_count(&self) -> usize {
self.rights.len()
}
}
#[allow(dead_code)]
pub struct SumTraversal<A, B> {
_a: std::marker::PhantomData<A>,
_b: std::marker::PhantomData<B>,
pub description: String,
}
impl<A, B> SumTraversal<A, B> {
pub fn new(description: impl Into<String>) -> Self {
SumTraversal {
_a: std::marker::PhantomData,
_b: std::marker::PhantomData,
description: description.into(),
}
}
pub fn traverse_option<C>(
&self,
s: Coproduct<A, B>,
f: impl FnOnce(B) -> Option<C>,
) -> Option<Coproduct<A, C>> {
match s {
Coproduct::Inl(a) => Some(Coproduct::Inl(a)),
Coproduct::Inr(b) => f(b).map(Coproduct::Inr),
}
}
}
#[allow(dead_code)]
pub struct SumUniversal<A, B, Z> {
pub left_branch: std::marker::PhantomData<fn(A) -> Z>,
pub right_branch: std::marker::PhantomData<fn(B) -> Z>,
pub description: String,
}
impl<A, B, Z> SumUniversal<A, B, Z> {
pub fn new(description: impl Into<String>) -> Self {
SumUniversal {
left_branch: std::marker::PhantomData,
right_branch: std::marker::PhantomData,
description: description.into(),
}
}
pub fn mediate(&self, s: Coproduct<A, B>, f: impl FnOnce(A) -> Z, g: impl FnOnce(B) -> Z) -> Z {
s.elim(f, g)
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Sum3<A, B, C> {
In1(A),
In2(B),
In3(C),
}
impl<A, B, C> Sum3<A, B, C> {
pub fn elim<D>(
self,
f1: impl FnOnce(A) -> D,
f2: impl FnOnce(B) -> D,
f3: impl FnOnce(C) -> D,
) -> D {
match self {
Sum3::In1(a) => f1(a),
Sum3::In2(b) => f2(b),
Sum3::In3(c) => f3(c),
}
}
pub fn is_first(&self) -> bool {
matches!(self, Sum3::In1(_))
}
pub fn is_second(&self) -> bool {
matches!(self, Sum3::In2(_))
}
pub fn is_third(&self) -> bool {
matches!(self, Sum3::In3(_))
}
}