use std::cell::OnceCell;
use super::functions::*;
use std::collections::HashMap;
#[allow(dead_code)]
#[derive(Debug, Clone)]
pub struct ThunkApp<A: Clone> {
pub val: A,
}
#[allow(dead_code)]
impl<A: Clone> ThunkApp<A> {
pub fn pure(a: A) -> Self {
ThunkApp { val: a }
}
pub fn map<B: Clone, F: Fn(A) -> B>(self, f: F) -> ThunkApp<B> {
ThunkApp { val: f(self.val) }
}
pub fn ap<B: Clone, F>(self, tf: ThunkApp<F>) -> ThunkApp<B>
where
F: Fn(A) -> B + Clone,
{
ThunkApp {
val: (tf.val)(self.val),
}
}
}
pub struct Memo<A: std::hash::Hash + Eq + Clone, B: Clone> {
f: Box<dyn Fn(&A) -> B>,
cache: std::collections::HashMap<A, B>,
}
impl<A: std::hash::Hash + Eq + Clone, B: Clone> Memo<A, B> {
pub fn new<F: Fn(&A) -> B + 'static>(f: F) -> Self {
Self {
f: Box::new(f),
cache: std::collections::HashMap::new(),
}
}
pub fn call(&mut self, arg: &A) -> &B {
if !self.cache.contains_key(arg) {
let result = (self.f)(arg);
self.cache.insert(arg.clone(), result);
}
&self.cache[arg]
}
pub fn clear_cache(&mut self) {
self.cache.clear();
}
pub fn cache_size(&self) -> usize {
self.cache.len()
}
}
pub enum ThunkTree<T> {
Leaf(T),
Node(Box<dyn Fn() -> Vec<ThunkTree<T>>>),
}
impl<T: Clone> ThunkTree<T> {
pub fn leaf(value: T) -> Self {
Self::Leaf(value)
}
pub fn node<F: Fn() -> Vec<ThunkTree<T>> + 'static>(f: F) -> Self {
Self::Node(Box::new(f))
}
pub fn leaves(&self) -> Vec<T>
where
T: Clone,
{
match self {
ThunkTree::Leaf(v) => vec![v.clone()],
ThunkTree::Node(f) => f().iter().flat_map(|c| c.leaves()).collect(),
}
}
pub fn is_leaf(&self) -> bool {
matches!(self, ThunkTree::Leaf(_))
}
}
pub struct LazyList<T> {
produced: Vec<T>,
next: Option<Box<dyn Fn(usize) -> Option<T>>>,
}
impl<T: Clone> LazyList<T> {
pub fn from_fn<F: Fn(usize) -> Option<T> + 'static>(f: F) -> Self {
Self {
produced: Vec::new(),
next: Some(Box::new(f)),
}
}
pub fn get(&mut self, i: usize) -> Option<&T> {
while self.produced.len() <= i {
let idx = self.produced.len();
if let Some(f) = &self.next {
match f(idx) {
Some(v) => self.produced.push(v),
None => {
self.next = None;
break;
}
}
} else {
break;
}
}
self.produced.get(i)
}
pub fn take(&mut self, n: usize) -> Vec<T> {
(0..n).filter_map(|i| self.get(i).cloned()).collect()
}
pub fn produced_count(&self) -> usize {
self.produced.len()
}
}
pub struct TryThunk<T, E> {
init: Option<Box<dyn FnOnce() -> Result<T, E>>>,
pub(super) value: Option<Result<T, E>>,
}
impl<T, E> TryThunk<T, E> {
pub fn new<F: FnOnce() -> Result<T, E> + 'static>(f: F) -> Self {
Self {
init: Some(Box::new(f)),
value: None,
}
}
pub fn force(&mut self) -> &Result<T, E> {
if self.value.is_none() {
if let Some(f) = self.init.take() {
self.value = Some(f());
}
}
self.value
.as_ref()
.expect("TryThunk: value missing after force")
}
pub fn is_forced(&self) -> bool {
self.value.is_some()
}
}
#[allow(dead_code)]
pub enum LazyStream<T> {
Nil,
Cons(T, Box<dyn FnOnce() -> LazyStream<T>>),
}
#[allow(dead_code)]
impl<T: Clone> LazyStream<T> {
pub fn nil() -> Self {
LazyStream::Nil
}
pub fn cons<F: FnOnce() -> LazyStream<T> + 'static>(head: T, tail: F) -> Self {
LazyStream::Cons(head, Box::new(tail))
}
pub fn take(self, n: usize) -> Vec<T> {
let mut result = Vec::new();
let mut current = self;
for _ in 0..n {
match current {
LazyStream::Nil => break,
LazyStream::Cons(h, tl) => {
result.push(h);
current = tl();
}
}
}
result
}
pub fn is_nil(&self) -> bool {
matches!(self, LazyStream::Nil)
}
}
pub struct ThunkSeq<T> {
items: Vec<Box<dyn Fn() -> T>>,
cache: Vec<Option<T>>,
}
impl<T: Clone> ThunkSeq<T> {
pub fn new() -> Self {
Self {
items: Vec::new(),
cache: Vec::new(),
}
}
pub fn push<F: Fn() -> T + 'static>(&mut self, f: F) {
self.items.push(Box::new(f));
self.cache.push(None);
}
pub fn get(&mut self, i: usize) -> Option<&T> {
if i >= self.items.len() {
return None;
}
if self.cache[i].is_none() {
self.cache[i] = Some((self.items[i])());
}
self.cache[i].as_ref()
}
pub fn len(&self) -> usize {
self.items.len()
}
pub fn is_empty(&self) -> bool {
self.items.is_empty()
}
pub fn forced_count(&self) -> usize {
self.cache.iter().filter(|c| c.is_some()).count()
}
}
pub struct OnceCellThunk<T> {
pub(super) cell: OnceCell<T>,
init: Option<Box<dyn FnOnce() -> T>>,
}
impl<T> OnceCellThunk<T> {
pub fn new<F: FnOnce() -> T + 'static>(f: F) -> Self {
Self {
cell: OnceCell::new(),
init: Some(Box::new(f)),
}
}
pub fn force(&mut self) -> &T {
if self.cell.get().is_none() {
if let Some(f) = self.init.take() {
let _ = self.cell.set(f());
}
}
self.cell.get().expect("OnceCellThunk: init was None")
}
pub fn is_forced(&self) -> bool {
self.cell.get().is_some()
}
}
#[allow(dead_code)]
#[derive(Debug, Clone)]
pub struct CofreeStream<A> {
pub elements: Vec<A>,
pub period: usize,
}
#[allow(dead_code)]
impl<A: Clone> CofreeStream<A> {
pub fn from_periodic(seed: Vec<A>) -> Self {
let len = seed.len().max(1);
CofreeStream {
elements: seed,
period: len,
}
}
pub fn extract(&self) -> &A {
&self.elements[0]
}
pub fn tail(&self) -> Self {
if self.elements.len() <= 1 {
return self.clone();
}
let mut new_elems = self.elements[1..].to_vec();
new_elems.push(self.elements[0].clone());
CofreeStream {
elements: new_elems,
period: self.period,
}
}
pub fn nth(&self, n: usize) -> &A {
&self.elements[n % self.elements.len()]
}
pub fn map<B, F: Fn(&A) -> B>(&self, f: F) -> CofreeStream<B> {
CofreeStream {
elements: self.elements.iter().map(f).collect(),
period: self.period,
}
}
}
#[allow(dead_code)]
#[derive(Debug, Clone)]
pub struct ComputeNode<T: Clone> {
pub name: String,
pub value: Option<T>,
pub deps: Vec<String>,
pub is_dirty: bool,
}
#[allow(dead_code)]
impl<T: Clone> ComputeNode<T> {
pub fn new(name: &str) -> Self {
ComputeNode {
name: name.to_string(),
value: None,
deps: Vec::new(),
is_dirty: true,
}
}
pub fn add_dep(&mut self, dep: &str) {
self.deps.push(dep.to_string());
}
pub fn set_value(&mut self, val: T) {
self.value = Some(val);
self.is_dirty = false;
}
pub fn invalidate(&mut self) {
self.value = None;
self.is_dirty = true;
}
pub fn get(&self) -> Option<&T> {
self.value.as_ref()
}
}
#[allow(dead_code)]
pub struct GameArena {
moves: Vec<(usize, String)>,
question_counter: usize,
}
#[allow(dead_code)]
impl GameArena {
pub fn new() -> Self {
Self {
moves: Vec::new(),
question_counter: 0,
}
}
pub fn ask(&mut self) -> usize {
let q = self.question_counter;
self.question_counter += 1;
q
}
pub fn answer(&mut self, q: usize, v: impl Into<String>) {
self.moves.push((q, v.into()));
}
pub fn get_answer(&self, q: usize) -> Option<&str> {
self.moves
.iter()
.find(|(qi, _)| *qi == q)
.map(|(_, a)| a.as_str())
}
pub fn move_count(&self) -> usize {
self.moves.len()
}
pub fn is_complete(&self) -> bool {
let answered: std::collections::HashSet<usize> =
self.moves.iter().map(|(q, _)| *q).collect();
(0..self.question_counter).all(|q| answered.contains(&q))
}
}
#[derive(Debug, Clone)]
pub struct Delayed<T> {
value: T,
delay_steps: usize,
elapsed: usize,
}
impl<T: Clone> Delayed<T> {
pub fn new(value: T, steps: usize) -> Self {
Self {
value,
delay_steps: steps,
elapsed: 0,
}
}
pub fn step(&mut self) -> Option<T> {
self.elapsed += 1;
if self.elapsed >= self.delay_steps {
Some(self.value.clone())
} else {
None
}
}
pub fn is_ready(&self) -> bool {
self.elapsed >= self.delay_steps
}
pub fn remaining(&self) -> usize {
self.delay_steps.saturating_sub(self.elapsed)
}
}
#[allow(dead_code)]
#[derive(Debug, Clone, PartialEq)]
pub enum ThunkState<T> {
Unevaluated,
Evaluating,
Evaluated(T),
Error(String),
}
#[allow(dead_code)]
impl<T: Clone> ThunkState<T> {
pub fn is_value(&self) -> bool {
matches!(self, ThunkState::Evaluated(_))
}
pub fn is_blackhole(&self) -> bool {
matches!(self, ThunkState::Evaluating)
}
pub fn get_value(&self) -> Option<&T> {
match self {
ThunkState::Evaluated(v) => Some(v),
_ => None,
}
}
pub fn enter(&mut self) -> bool {
match self {
ThunkState::Unevaluated => {
*self = ThunkState::Evaluating;
true
}
ThunkState::Evaluating => false,
_ => false,
}
}
pub fn fill(&mut self, val: T) {
*self = ThunkState::Evaluated(val);
}
pub fn fail(&mut self, msg: &str) {
*self = ThunkState::Error(msg.to_string());
}
}
#[allow(dead_code)]
pub struct ThunkKleisli<A, B> {
arrow: Box<dyn Fn(A) -> Option<B>>,
}
#[allow(dead_code)]
impl<A: 'static, B: Clone + 'static> ThunkKleisli<A, B> {
pub fn new<F: Fn(A) -> Option<B> + 'static>(f: F) -> Self {
Self { arrow: Box::new(f) }
}
pub fn apply(&self, a: A) -> Option<B> {
(self.arrow)(a)
}
pub fn compose<C: Clone + 'static>(self, other: ThunkKleisli<B, C>) -> ThunkKleisli<A, C> {
ThunkKleisli::new(move |a| {
let b = (self.arrow)(a)?;
other.apply(b)
})
}
}
#[allow(dead_code)]
#[derive(Debug, Clone)]
pub enum Suspension<A> {
Done(A),
Suspended(String),
Aborted(String),
}
#[allow(dead_code)]
impl<A: Clone> Suspension<A> {
pub fn pure(a: A) -> Self {
Suspension::Done(a)
}
pub fn suspend(desc: &str) -> Self {
Suspension::Suspended(desc.to_string())
}
pub fn abort(msg: &str) -> Self {
Suspension::Aborted(msg.to_string())
}
pub fn is_done(&self) -> bool {
matches!(self, Suspension::Done(_))
}
pub fn map<B: Clone, F: Fn(A) -> B>(self, f: F) -> Suspension<B> {
match self {
Suspension::Done(a) => Suspension::Done(f(a)),
Suspension::Suspended(d) => Suspension::Suspended(d),
Suspension::Aborted(e) => Suspension::Aborted(e),
}
}
pub fn and_then<B: Clone, F: Fn(A) -> Suspension<B>>(self, f: F) -> Suspension<B> {
match self {
Suspension::Done(a) => f(a),
Suspension::Suspended(d) => Suspension::Suspended(d),
Suspension::Aborted(e) => Suspension::Aborted(e),
}
}
}
#[allow(dead_code)]
pub struct ComonadCtx<E, A> {
pub(super) env: E,
pub(super) focus: A,
}
#[allow(dead_code)]
impl<E: Clone, A: Clone> ComonadCtx<E, A> {
pub fn new(env: E, focus: A) -> Self {
Self { env, focus }
}
pub fn extract(&self) -> A {
self.focus.clone()
}
pub fn duplicate(&self) -> ComonadCtx<E, ComonadCtx<E, A>> {
ComonadCtx {
env: self.env.clone(),
focus: self.clone(),
}
}
pub fn extend<B, F: Fn(&ComonadCtx<E, A>) -> B>(&self, f: F) -> ComonadCtx<E, B> {
ComonadCtx {
env: self.env.clone(),
focus: f(self),
}
}
pub fn map<B, F: FnOnce(A) -> B>(self, f: F) -> ComonadCtx<E, B> {
ComonadCtx {
env: self.env,
focus: f(self.focus),
}
}
pub fn env(&self) -> &E {
&self.env
}
}
#[derive(Default)]
pub struct DeferredPool<T> {
pending: std::collections::HashMap<usize, Box<dyn FnOnce() -> T>>,
resolved: std::collections::HashMap<usize, T>,
next_id: usize,
}
impl<T> DeferredPool<T> {
pub fn new() -> Self {
Self {
pending: std::collections::HashMap::new(),
resolved: std::collections::HashMap::new(),
next_id: 0,
}
}
pub fn submit<F: FnOnce() -> T + 'static>(&mut self, f: F) -> usize {
let id = self.next_id;
self.next_id += 1;
self.pending.insert(id, Box::new(f));
id
}
pub fn force(&mut self, id: usize) -> Option<&T> {
if !self.resolved.contains_key(&id) {
if let Some(f) = self.pending.remove(&id) {
self.resolved.insert(id, f());
}
}
self.resolved.get(&id)
}
pub fn force_all(&mut self) {
let ids: Vec<usize> = self.pending.keys().copied().collect();
for id in ids {
self.force(id);
}
}
pub fn pending_count(&self) -> usize {
self.pending.len()
}
pub fn resolved_count(&self) -> usize {
self.resolved.len()
}
}
#[allow(clippy::type_complexity)]
pub struct FixThunk<T: Clone> {
memo: std::collections::HashMap<usize, T>,
step: Box<dyn Fn(usize, &dyn Fn(usize) -> T) -> T>,
}
impl<T: Clone> FixThunk<T> {
pub fn new<F>(f: F) -> Self
where
F: Fn(usize, &dyn Fn(usize) -> T) -> T + 'static,
{
Self {
memo: std::collections::HashMap::new(),
step: Box::new(f),
}
}
pub fn compute(&mut self, n: usize) -> T {
if let Some(v) = self.memo.get(&n) {
return v.clone();
}
let memo_ref: *mut std::collections::HashMap<usize, T> = &mut self.memo;
let lookup = |k: usize| -> T {
unsafe { (*memo_ref).get(&k).cloned() }.expect("missing cached value")
};
let v = (self.step)(n, &lookup);
self.memo.insert(n, v.clone());
v
}
}
#[allow(dead_code)]
pub struct MemoThunk<T: Clone> {
cached: Option<T>,
}
#[allow(dead_code)]
impl<T: Clone> MemoThunk<T> {
pub fn new() -> Self {
MemoThunk { cached: None }
}
pub fn force_with<F: FnOnce() -> T>(&mut self, f: F) -> T {
if let Some(ref val) = self.cached {
val.clone()
} else {
let val = f();
self.cached = Some(val.clone());
val
}
}
pub fn is_forced(&self) -> bool {
self.cached.is_some()
}
pub fn reset(&mut self) {
self.cached = None;
}
}
#[allow(dead_code)]
pub struct LazyLinkedStream<T> {
pub head: T,
pub tail_fn: Box<dyn Fn() -> Option<Box<LazyLinkedStream<T>>>>,
}
#[allow(dead_code)]
impl<T: Clone + 'static> LazyLinkedStream<T> {
pub fn singleton(val: T) -> Self {
LazyLinkedStream {
head: val,
tail_fn: Box::new(|| None),
}
}
pub fn take_n(&self, n: usize) -> Vec<T> {
let mut result = vec![self.head.clone()];
let mut current = (self.tail_fn)();
while result.len() < n {
match current {
None => break,
Some(node) => {
result.push(node.head.clone());
current = (node.tail_fn)();
}
}
}
result
}
}
#[derive(Debug, Clone)]
pub enum Deferred<T> {
Now(T),
Later(usize),
}
impl<T: Clone> Deferred<T> {
pub fn now(v: T) -> Self {
Self::Now(v)
}
pub fn later(id: usize) -> Self {
Self::Later(id)
}
pub fn is_ready(&self) -> bool {
matches!(self, Deferred::Now(_))
}
pub fn resolve_or<F: FnOnce(usize) -> T>(self, f: F) -> T {
match self {
Deferred::Now(v) => v,
Deferred::Later(id) => f(id),
}
}
pub fn map<U, F: FnOnce(T) -> U>(self, f: F) -> Deferred<U> {
match self {
Deferred::Now(v) => Deferred::Now(f(v)),
Deferred::Later(id) => Deferred::Later(id),
}
}
}
#[allow(dead_code)]
pub enum ITreeNode<E, R> {
Ret(R),
Tau(Box<dyn FnOnce() -> ITreeNode<E, R>>),
Vis(E, Box<dyn Fn(usize) -> ITreeNode<E, R>>),
}
#[allow(dead_code)]
impl<E: Clone, R: Clone> ITreeNode<E, R> {
pub fn ret(r: R) -> Self {
ITreeNode::Ret(r)
}
pub fn tau<F: FnOnce() -> ITreeNode<E, R> + 'static>(f: F) -> Self {
ITreeNode::Tau(Box::new(f))
}
pub fn vis<K: Fn(usize) -> ITreeNode<E, R> + 'static>(e: E, k: K) -> Self {
ITreeNode::Vis(e, Box::new(k))
}
pub fn run(self, fuel: usize) -> Option<R> {
let mut current = self;
for _ in 0..fuel {
match current {
ITreeNode::Ret(r) => return Some(r),
ITreeNode::Tau(f) => {
current = f();
}
ITreeNode::Vis(_, _) => return None,
}
}
None
}
}