use std::cell::RefCell;
use std::collections::{HashMap, HashSet};
use std::fmt;
use std::sync::{Arc, Mutex, OnceLock};
use super::functions::TailFn;
#[derive(Default, Debug)]
pub struct BlackHoleDetector {
in_progress: std::collections::HashSet<String>,
}
impl BlackHoleDetector {
pub fn new() -> Self {
BlackHoleDetector::default()
}
pub fn enter(&mut self, name: impl Into<String>) -> Result<(), String> {
let n = name.into();
if self.in_progress.contains(&n) {
return Err(format!("black hole detected in `{}`", n));
}
self.in_progress.insert(n);
Ok(())
}
pub fn exit(&mut self, name: &str) {
self.in_progress.remove(name);
}
pub fn is_in_progress(&self, name: &str) -> bool {
self.in_progress.contains(name)
}
pub fn depth(&self) -> usize {
self.in_progress.len()
}
}
pub struct LazyMap<K: Eq + std::hash::Hash + Clone, V> {
computed: HashMap<K, V>,
pending: HashMap<K, Box<dyn FnOnce() -> V>>,
}
impl<K: Eq + std::hash::Hash + Clone, V: Clone + fmt::Debug> LazyMap<K, V> {
pub fn new() -> Self {
LazyMap {
computed: HashMap::new(),
pending: HashMap::new(),
}
}
pub fn insert_lazy<F>(&mut self, key: K, f: F)
where
F: FnOnce() -> V + 'static,
{
self.pending.insert(key, Box::new(f));
}
pub fn insert_ready(&mut self, key: K, value: V) {
self.computed.insert(key, value);
}
pub fn get(&mut self, key: &K) -> Option<&V> {
if let Some(f) = self.pending.remove(key) {
let val = f();
self.computed.insert(key.clone(), val);
}
self.computed.get(key)
}
pub fn contains(&self, key: &K) -> bool {
self.computed.contains_key(key) || self.pending.contains_key(key)
}
pub fn len(&self) -> usize {
self.computed.len() + self.pending.len()
}
pub fn is_empty(&self) -> bool {
self.computed.is_empty() && self.pending.is_empty()
}
pub fn computed_count(&self) -> usize {
self.computed.len()
}
pub fn pending_count(&self) -> usize {
self.pending.len()
}
}
pub struct LazySieve {
primes: Vec<u64>,
candidate: u64,
}
impl LazySieve {
pub fn new() -> Self {
LazySieve {
primes: Vec::new(),
candidate: 2,
}
}
pub fn next_prime(&mut self) -> u64 {
loop {
let c = self.candidate;
self.candidate += 1;
let is_prime = self.primes.iter().all(|&p| c % p != 0);
if is_prime {
self.primes.push(c);
return c;
}
}
}
pub fn take_primes(&mut self, n: usize) -> Vec<u64> {
(0..n).map(|_| self.next_prime()).collect()
}
}
#[allow(dead_code)]
#[derive(Debug, Default)]
pub struct MemoCache<V> {
entries: std::collections::HashMap<u64, V>,
pub(crate) hits: usize,
pub(crate) misses: usize,
}
#[allow(dead_code)]
impl<V: Clone> MemoCache<V> {
pub fn new() -> Self {
Self {
entries: std::collections::HashMap::new(),
hits: 0,
misses: 0,
}
}
pub fn get(&mut self, key: u64) -> Option<&V> {
if self.entries.contains_key(&key) {
self.hits += 1;
self.entries.get(&key)
} else {
self.misses += 1;
None
}
}
pub fn insert(&mut self, key: u64, value: V) {
self.entries.insert(key, value);
}
pub fn hit_rate(&self) -> f64 {
let total = self.hits + self.misses;
if total == 0 {
0.0
} else {
self.hits as f64 / total as f64
}
}
pub fn len(&self) -> usize {
self.entries.len()
}
pub fn is_empty(&self) -> bool {
self.entries.is_empty()
}
pub fn clear(&mut self) {
self.entries.clear();
self.hits = 0;
self.misses = 0;
}
}
pub(super) enum ThunkState<T> {
Unevaluated(Box<dyn FnOnce() -> T>),
Evaluated(T),
BlackHole,
}
pub struct LazyAccumulator<T> {
pending: Vec<Box<dyn FnOnce() -> T>>,
collected: Vec<T>,
}
impl<T: Clone + fmt::Debug> LazyAccumulator<T> {
pub fn new() -> Self {
LazyAccumulator {
pending: Vec::new(),
collected: Vec::new(),
}
}
pub fn add<F: FnOnce() -> T + 'static>(&mut self, f: F) {
self.pending.push(Box::new(f));
}
pub fn flush(&mut self) {
while let Some(f) = self.pending.pop() {
self.collected.push(f());
}
}
pub fn collect(&mut self) -> &[T] {
self.flush();
&self.collected
}
pub fn pending_count(&self) -> usize {
self.pending.len()
}
pub fn collected_count(&self) -> usize {
self.collected.len()
}
}
pub struct StreamThunk<S: Clone, T> {
state: S,
gen: Box<dyn Fn(&S) -> (T, S)>,
}
impl<S: Clone + fmt::Debug, T: Clone + fmt::Debug> StreamThunk<S, T> {
pub fn new<F>(init: S, f: F) -> Self
where
F: Fn(&S) -> (T, S) + 'static,
{
StreamThunk {
state: init,
gen: Box::new(f),
}
}
pub fn next(&mut self) -> T {
let (val, new_state) = (self.gen)(&self.state);
self.state = new_state;
val
}
pub fn take_n(&mut self, n: usize) -> Vec<T> {
(0..n).map(|_| self.next()).collect()
}
pub fn current_state(&self) -> &S {
&self.state
}
}
#[derive(Clone, Debug)]
pub enum DeferredValue<T> {
Ready(T),
Deferred { name: String },
Failed { error: String },
}
impl<T: Clone + fmt::Debug> DeferredValue<T> {
pub fn unwrap_ready(self) -> T {
match self {
DeferredValue::Ready(v) => v,
DeferredValue::Deferred { name } => {
panic!(
"DeferredValue::unwrap_ready: value '{}' not yet computed",
name
)
}
DeferredValue::Failed { error } => {
panic!("DeferredValue::unwrap_ready: computation failed: {}", error)
}
}
}
pub fn is_ready(&self) -> bool {
matches!(self, DeferredValue::Ready(_))
}
pub fn is_deferred(&self) -> bool {
matches!(self, DeferredValue::Deferred { .. })
}
pub fn is_failed(&self) -> bool {
matches!(self, DeferredValue::Failed { .. })
}
pub fn map<U, F>(self, f: F) -> DeferredValue<U>
where
F: FnOnce(T) -> U,
{
match self {
DeferredValue::Ready(v) => DeferredValue::Ready(f(v)),
DeferredValue::Deferred { name } => DeferredValue::Deferred { name },
DeferredValue::Failed { error } => DeferredValue::Failed { error },
}
}
pub fn get(&self) -> Option<&T> {
match self {
DeferredValue::Ready(v) => Some(v),
_ => None,
}
}
}
pub struct LazyRange {
pub(super) current: i64,
pub(super) end: i64,
pub(super) step: i64,
}
impl LazyRange {
pub fn new(start: i64, end: i64, step: i64) -> Self {
assert!(step != 0, "step must be non-zero");
LazyRange {
current: start,
end,
step,
}
}
pub fn up_to(n: i64) -> Self {
Self::new(0, n, 1)
}
pub fn collect_all(self) -> Vec<i64> {
self.collect()
}
}
pub struct ThunkCache {
pub(super) entries: HashMap<String, Arc<dyn std::any::Any + Send + Sync>>,
}
impl ThunkCache {
pub fn new() -> Self {
ThunkCache {
entries: HashMap::new(),
}
}
pub fn insert<T, F>(&mut self, name: impl Into<String>, f: F)
where
T: Clone + fmt::Debug + Send + Sync + 'static,
F: FnOnce() -> T + Send + 'static,
{
let thunk = Arc::new(SharedThunk::new(f));
self.entries.insert(name.into(), thunk);
}
pub fn insert_pure<T>(&mut self, name: impl Into<String>, value: T)
where
T: Clone + fmt::Debug + Send + Sync + 'static,
{
let thunk = Arc::new(SharedThunk::pure(value));
self.entries.insert(name.into(), thunk);
}
pub fn force<T>(&self, name: &str) -> Option<T>
where
T: Clone + fmt::Debug + Send + Sync + 'static,
{
let entry = self.entries.get(name)?;
let thunk = entry.downcast_ref::<SharedThunk<T>>()?;
Some(thunk.force())
}
pub fn contains(&self, name: &str) -> bool {
self.entries.contains_key(name)
}
pub fn len(&self) -> usize {
self.entries.len()
}
pub fn is_empty(&self) -> bool {
self.entries.is_empty()
}
}
pub struct TakeIter<T: Clone + 'static> {
pub(super) current: Option<(T, Option<TailFn<T>>)>,
pub(super) remaining: usize,
}
pub struct FutureChain<T> {
initial: Box<dyn FnOnce() -> T>,
steps: Vec<Box<dyn FnOnce(T) -> T>>,
}
impl<T: 'static> FutureChain<T> {
pub fn new<F: FnOnce() -> T + 'static>(init: F) -> Self {
FutureChain {
initial: Box::new(init),
steps: Vec::new(),
}
}
pub fn then<F: FnOnce(T) -> T + 'static>(mut self, f: F) -> Self {
self.steps.push(Box::new(f));
self
}
pub fn force(self) -> T {
let mut val = (self.initial)();
for step in self.steps {
val = step(val);
}
val
}
pub fn step_count(&self) -> usize {
self.steps.len()
}
}
pub struct BatchEval<T> {
tasks: Vec<(String, Box<dyn FnOnce() -> T>)>,
results: Vec<(String, T)>,
}
impl<T: Clone + fmt::Debug> BatchEval<T> {
pub fn new() -> Self {
BatchEval {
tasks: Vec::new(),
results: Vec::new(),
}
}
pub fn add<F: FnOnce() -> T + 'static>(&mut self, name: impl Into<String>, f: F) {
self.tasks.push((name.into(), Box::new(f)));
}
pub fn run_all(&mut self) {
while let Some((name, f)) = self.tasks.pop() {
let val = f();
self.results.push((name, val));
}
}
pub fn result(&self, name: &str) -> Option<&T> {
self.results.iter().find(|(n, _)| n == name).map(|(_, v)| v)
}
pub fn pending(&self) -> usize {
self.tasks.len()
}
pub fn completed(&self) -> usize {
self.results.len()
}
pub fn all_results(&self) -> &[(String, T)] {
&self.results
}
}
#[allow(dead_code)]
#[derive(Debug, Default)]
pub struct TrackedCell<T> {
value: Option<T>,
force_count: usize,
}
#[allow(dead_code)]
impl<T: Clone> TrackedCell<T> {
pub fn new() -> Self {
Self {
value: None,
force_count: 0,
}
}
pub fn set(&mut self, val: T) {
if self.value.is_none() {
self.value = Some(val);
}
}
pub fn get(&mut self) -> Option<&T> {
if self.value.is_some() {
self.force_count += 1;
}
self.value.as_ref()
}
pub fn force_count(&self) -> usize {
self.force_count
}
pub fn is_initialized(&self) -> bool {
self.value.is_some()
}
}
pub struct MemoFn2<I1, I2, O> {
cache: HashMap<(I1, I2), O>,
func: Box<dyn Fn(I1, I2) -> O>,
}
impl<I1, I2, O> MemoFn2<I1, I2, O>
where
I1: Eq + std::hash::Hash + Clone,
I2: Eq + std::hash::Hash + Clone,
O: Clone,
{
pub fn new<F: Fn(I1, I2) -> O + 'static>(f: F) -> Self {
MemoFn2 {
cache: HashMap::new(),
func: Box::new(f),
}
}
pub fn call(&mut self, a: I1, b: I2) -> O {
let key = (a.clone(), b.clone());
if let Some(v) = self.cache.get(&key) {
return v.clone();
}
let result = (self.func)(a, b);
self.cache.insert(key, result.clone());
result
}
pub fn cache_size(&self) -> usize {
self.cache.len()
}
pub fn clear_cache(&mut self) {
self.cache.clear();
}
}
pub struct ThunkVec<T> {
thunks: Vec<std::cell::RefCell<ThunkVecState<T>>>,
}
impl<T: Clone + fmt::Debug> ThunkVec<T> {
pub fn new() -> Self {
ThunkVec { thunks: Vec::new() }
}
pub fn push_lazy<F: FnOnce() -> T + 'static>(&mut self, f: F) {
self.thunks
.push(std::cell::RefCell::new(ThunkVecState::Pending(Box::new(f))));
}
pub fn push_ready(&mut self, value: T) {
self.thunks
.push(std::cell::RefCell::new(ThunkVecState::Computed(value)));
}
pub fn get(&self, index: usize) -> Option<T> {
let cell = self.thunks.get(index)?;
let state = cell.borrow();
if let ThunkVecState::Computed(ref v) = *state {
return Some(v.clone());
}
drop(state);
let old = cell.replace(ThunkVecState::Computed(
match cell.replace(ThunkVecState::Computed(unsafe { std::mem::zeroed() })) {
ThunkVecState::Pending(f) => f(),
ThunkVecState::Computed(v) => v,
},
));
drop(old);
let state2 = cell.borrow();
if let ThunkVecState::Computed(ref v) = *state2 {
Some(v.clone())
} else {
None
}
}
pub fn len(&self) -> usize {
self.thunks.len()
}
pub fn is_empty(&self) -> bool {
self.thunks.is_empty()
}
pub fn force_all(&self) -> Vec<T> {
(0..self.len()).filter_map(|i| self.get(i)).collect()
}
}
pub struct CycleSafeMemo<T: Clone + Default> {
cache: HashMap<String, T>,
in_progress: std::collections::HashSet<String>,
}
impl<T: Clone + Default + fmt::Debug> CycleSafeMemo<T> {
pub fn new() -> Self {
CycleSafeMemo {
cache: HashMap::new(),
in_progress: std::collections::HashSet::new(),
}
}
pub fn get_or_compute<F>(&mut self, key: impl Into<String>, f: F) -> T
where
F: FnOnce(&mut Self) -> T,
{
let k = key.into();
if let Some(v) = self.cache.get(&k) {
return v.clone();
}
if self.in_progress.contains(&k) {
return T::default();
}
self.in_progress.insert(k.clone());
let result = f(self);
self.in_progress.remove(&k);
self.cache.insert(k, result.clone());
result
}
pub fn cache_size(&self) -> usize {
self.cache.len()
}
}
pub struct SharedThunk<T> {
pub(super) inner: Arc<Mutex<SharedThunkInner<T>>>,
}
impl<T: Clone + fmt::Debug + Send + Sync + 'static> SharedThunk<T> {
pub fn new<F: FnOnce() -> T + Send + 'static>(f: F) -> Self {
SharedThunk {
inner: Arc::new(Mutex::new(SharedThunkInner {
value: OnceLock::new(),
thunk: Some(Box::new(f)),
})),
}
}
pub fn pure(value: T) -> Self {
let lock = OnceLock::new();
let _ = lock.set(value);
SharedThunk {
inner: Arc::new(Mutex::new(SharedThunkInner {
value: lock,
thunk: None,
})),
}
}
pub fn force(&self) -> T {
let mut guard = self.inner.lock().unwrap_or_else(|e| e.into_inner());
if let Some(v) = guard.value.get() {
return v.clone();
}
let f = guard.thunk.take().expect("thunk already consumed");
let result = f();
let _ = guard.value.set(result.clone());
result
}
pub fn is_evaluated(&self) -> bool {
let guard = self.inner.lock().unwrap_or_else(|e| e.into_inner());
guard.value.get().is_some()
}
pub fn share(&self) -> Self {
SharedThunk {
inner: Arc::clone(&self.inner),
}
}
}
enum ThunkVecState<T> {
Pending(Box<dyn FnOnce() -> T>),
Computed(T),
}
pub struct LazyTree<T: Clone + fmt::Debug + 'static> {
pub value: T,
children_thunk: Option<Arc<dyn Fn() -> Vec<LazyTree<T>> + Send + Sync>>,
}
impl<T: Clone + fmt::Debug + 'static> LazyTree<T> {
pub fn leaf(value: T) -> Self {
LazyTree {
value,
children_thunk: None,
}
}
pub fn node<F>(value: T, children: F) -> Self
where
F: Fn() -> Vec<LazyTree<T>> + Send + Sync + 'static,
{
LazyTree {
value,
children_thunk: Some(Arc::new(children)),
}
}
pub fn children(&self) -> Vec<LazyTree<T>> {
match &self.children_thunk {
None => Vec::new(),
Some(f) => f(),
}
}
pub fn is_leaf(&self) -> bool {
self.children_thunk.is_none()
}
pub fn dfs(&self) -> Vec<T> {
let mut result = vec![self.value.clone()];
for child in self.children() {
result.extend(child.dfs());
}
result
}
}
pub struct MemoTable {
pub(super) entries: HashMap<String, Box<dyn std::any::Any>>,
}
impl MemoTable {
pub fn new() -> Self {
MemoTable {
entries: HashMap::new(),
}
}
pub fn insert<V: 'static>(&mut self, key: impl Into<String>, value: V) {
self.entries.insert(key.into(), Box::new(value));
}
pub fn get<V: 'static>(&self, key: &str) -> Option<&V> {
self.entries.get(key)?.downcast_ref::<V>()
}
pub fn contains(&self, key: &str) -> bool {
self.entries.contains_key(key)
}
pub fn remove(&mut self, key: &str) -> bool {
self.entries.remove(key).is_some()
}
pub fn len(&self) -> usize {
self.entries.len()
}
pub fn is_empty(&self) -> bool {
self.entries.is_empty()
}
pub fn clear(&mut self) {
self.entries.clear();
}
}
pub struct LazyList<T: Clone + 'static> {
pub(super) head: Option<T>,
pub(super) tail: Option<Arc<dyn Fn() -> LazyList<T> + Send + Sync>>,
}
impl<T: Clone + fmt::Debug + 'static> LazyList<T> {
pub fn empty() -> Self {
LazyList {
head: None,
tail: None,
}
}
pub fn cons<F>(head: T, tail: F) -> Self
where
F: Fn() -> LazyList<T> + Send + Sync + 'static,
{
LazyList {
head: Some(head),
tail: Some(Arc::new(tail)),
}
}
pub fn from_fn(seed: T, next: impl Fn(T) -> T + Send + Sync + Clone + 'static) -> Self
where
T: Send + Sync,
{
let next2 = next.clone();
let seed2 = next(seed.clone());
LazyList::cons(seed, move || {
LazyList::from_fn(seed2.clone(), next2.clone())
})
}
pub fn is_empty(&self) -> bool {
self.head.is_none()
}
pub fn head(&self) -> Option<&T> {
self.head.as_ref()
}
pub fn tail(&self) -> LazyList<T> {
match &self.tail {
Some(f) => f(),
None => LazyList::empty(),
}
}
pub fn take(&self, n: usize) -> TakeIter<T> {
TakeIter {
current: self.head.clone().map(|h| {
let tail = self.tail.clone();
(h, tail)
}),
remaining: n,
}
}
}
pub struct LazyFilter<'a, A> {
pub(super) data: &'a [A],
pub(super) pred: Box<dyn Fn(&A) -> bool>,
pub(super) index: usize,
}
impl<'a, A: Clone + fmt::Debug> LazyFilter<'a, A> {
pub fn new<F: Fn(&A) -> bool + 'static>(data: &'a [A], pred: F) -> Self {
LazyFilter {
data,
pred: Box::new(pred),
index: 0,
}
}
pub fn collect_all(&self) -> Vec<A> {
self.data
.iter()
.filter(|x| (self.pred)(x))
.cloned()
.collect()
}
}
pub struct LazyCell<T> {
pub(super) inner: std::cell::OnceCell<T>,
}
impl<T: Clone + fmt::Debug> LazyCell<T> {
pub fn new() -> Self {
LazyCell {
inner: std::cell::OnceCell::new(),
}
}
pub fn init(&self, value: T) -> Result<(), T> {
self.inner.set(value)
}
pub fn get(&self) -> Option<&T> {
self.inner.get()
}
pub fn get_or_init<F: FnOnce() -> T>(&self, f: F) -> &T {
self.inner.get_or_init(f)
}
pub fn is_initialized(&self) -> bool {
self.inner.get().is_some()
}
}
pub struct LazyString {
parts: Vec<String>,
}
impl LazyString {
pub fn new() -> Self {
LazyString { parts: Vec::new() }
}
pub fn push(mut self, s: impl Into<String>) -> Self {
self.parts.push(s.into());
self
}
pub fn build(self) -> String {
self.parts.concat()
}
pub fn part_count(&self) -> usize {
self.parts.len()
}
}
pub struct Once<T: Clone + Send + Sync + 'static> {
pub(super) inner: OnceLock<T>,
}
impl<T: Clone + fmt::Debug + Send + Sync + 'static> Once<T> {
pub fn new() -> Self {
Once {
inner: OnceLock::new(),
}
}
pub fn get_or_init<F: FnOnce() -> T>(&self, f: F) -> &T {
self.inner.get_or_init(f)
}
pub fn get(&self) -> Option<&T> {
self.inner.get()
}
pub fn is_initialized(&self) -> bool {
self.inner.get().is_some()
}
}
pub struct MemoFn<I, O> {
cache: HashMap<I, O>,
func: Box<dyn Fn(I) -> O>,
}
impl<I: Eq + std::hash::Hash + Clone, O: Clone> MemoFn<I, O> {
pub fn new<F: Fn(I) -> O + 'static>(f: F) -> Self {
MemoFn {
cache: HashMap::new(),
func: Box::new(f),
}
}
pub fn call(&mut self, arg: I) -> O {
if let Some(v) = self.cache.get(&arg) {
return v.clone();
}
let result = (self.func)(arg.clone());
self.cache.insert(arg, result.clone());
result
}
pub fn clear_cache(&mut self) {
self.cache.clear();
}
pub fn cache_size(&self) -> usize {
self.cache.len()
}
}
pub struct Thunk<T> {
pub(super) state: RefCell<ThunkState<T>>,
}
impl<T: Clone + fmt::Debug> Thunk<T> {
pub fn new<F: FnOnce() -> T + 'static>(f: F) -> Self {
Thunk {
state: RefCell::new(ThunkState::Unevaluated(Box::new(f))),
}
}
pub fn pure(value: T) -> Self {
Thunk {
state: RefCell::new(ThunkState::Evaluated(value)),
}
}
pub fn force(&self) -> std::cell::Ref<'_, T> {
{
let s = self.state.borrow();
if matches!(*s, ThunkState::Evaluated(_)) {
return std::cell::Ref::map(s, |state| {
if let ThunkState::Evaluated(ref v) = state {
v
} else {
unreachable!()
}
});
}
}
let old_state = self.state.replace(ThunkState::BlackHole);
let result = match old_state {
ThunkState::Unevaluated(f) => f(),
ThunkState::BlackHole => {
panic!("lazy evaluation cycle detected (black hole)")
}
ThunkState::Evaluated(_) => unreachable!(),
};
self.state.replace(ThunkState::Evaluated(result));
std::cell::Ref::map(self.state.borrow(), |state| {
if let ThunkState::Evaluated(ref v) = state {
v
} else {
unreachable!()
}
})
}
pub fn is_evaluated(&self) -> bool {
matches!(*self.state.borrow(), ThunkState::Evaluated(_))
}
}
pub(super) struct SharedThunkInner<T> {
pub(super) value: OnceLock<T>,
thunk: Option<Box<dyn FnOnce() -> T + Send>>,
}
pub struct LazyMap2<'a, A, B> {
pub(super) data: &'a [A],
pub(super) func: Box<dyn Fn(&A) -> B>,
pub(super) index: usize,
}
impl<'a, A, B: fmt::Debug> LazyMap2<'a, A, B> {
pub fn new<F: Fn(&A) -> B + 'static>(data: &'a [A], f: F) -> Self {
LazyMap2 {
data,
func: Box::new(f),
index: 0,
}
}
pub fn get(&self, i: usize) -> Option<B> {
self.data.get(i).map(|x| (self.func)(x))
}
pub fn collect_all(&self) -> Vec<B> {
self.data.iter().map(|x| (self.func)(x)).collect()
}
}