bug fixes and performance improvements

This commit is contained in:
2023-05-07 22:35:38 +01:00
parent f3ce910f66
commit a604e40bad
167 changed files with 5965 additions and 4229 deletions

View File

@@ -1,115 +0,0 @@
use std::collections::{VecDeque, HashSet};
use std::iter;
use std::hash::Hash;
use crate::unwrap_or;
use crate::utils::BoxedIter;
// TODO: move to own crate
/// Two-stage breadth-first search;
/// Instead of enumerating neighbors before returning a node, it puts visited but not yet
/// enumerated nodes in a separate queue and only enumerates them to refill the queue of children
/// one by one once it's empty. This method is preferable for generated graphs because it doesn't
/// allocate memory for the children until necessary, but it's also probably a bit slower since
/// it involves additional processing.
///
/// # Performance
/// `T` is cloned twice for each returned value.
pub fn bfs<T, F, I>(init: T, neighbors: F)
-> impl Iterator<Item = T>
where T: Eq + Hash + Clone + std::fmt::Debug,
F: Fn(T) -> I, I: Iterator<Item = T>
{
let mut visited: HashSet<T> = HashSet::new();
let mut visit_queue: VecDeque<T> = VecDeque::from([init]);
let mut unpack_queue: VecDeque<T> = VecDeque::new();
iter::from_fn(move || {
let next = {loop {
let next = unwrap_or!(visit_queue.pop_front(); break None);
if !visited.contains(&next) { break Some(next) }
}}.or_else(|| loop {
let unpacked = unwrap_or!(unpack_queue.pop_front(); break None);
let mut nbv = neighbors(unpacked).filter(|t| !visited.contains(t));
if let Some(next) = nbv.next() {
visit_queue.extend(nbv);
break Some(next)
}
})?;
visited.insert(next.clone());
unpack_queue.push_back(next.clone());
Some(next)
})
}
/// Same as [bfs] but with a recursion depth limit
///
/// The main intent is to effectively walk infinite graphs of unknown breadth without making the
/// recursion depth dependent on the number of nodes. If predictable runtime is more important
/// than predictable depth, [bfs] with [std::iter::Iterator::take] should be used instead
pub fn bfs_upto<'a, T: 'a, F: 'a, I: 'a>(init: T, neighbors: F, limit: usize)
-> impl Iterator<Item = T> + 'a
where T: Eq + Hash + Clone + std::fmt::Debug,
F: Fn(T) -> I, I: Iterator<Item = T>
{
/// Newtype to store the recursion depth but exclude it from equality comparisons
/// Because BFS visits nodes in increasing distance order, when a node is visited for the
/// second time it will never override the earlier version of itself. This is not the case
/// with Djikstra's algorithm, which can be conceptualised as a "weighted BFS".
#[derive(Eq, Clone, Debug)]
struct Wrap<U>(usize, U);
impl<U: PartialEq> PartialEq for Wrap<U> {
fn eq(&self, other: &Self) -> bool { self.1.eq(&other.1) }
}
impl<U: Hash> Hash for Wrap<U> {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) { self.1.hash(state) }
}
bfs(Wrap(0, init), move |Wrap(dist, t)| -> BoxedIter<Wrap<T>> { // boxed because we branch
if dist == limit {Box::new(iter::empty())}
else {Box::new(neighbors(t).map(move |t| Wrap(dist + 1, t)))}
}).map(|Wrap(_, t)| t)
}
#[cfg(test)]
mod tests {
use itertools::Itertools;
use super::*;
type Graph = Vec<Vec<usize>>;
fn neighbors(graph: &Graph, pt: usize) -> impl Iterator<Item = usize> + '_ {
graph[pt].iter().copied()
}
fn from_neighborhood_matrix(matrix: Vec<Vec<usize>>) -> Graph {
matrix.into_iter().map(|v| {
v.into_iter().enumerate().filter_map(|(i, ent)| {
if ent > 1 {panic!("Neighborhood matrices must contain binary values")}
else if ent == 1 {Some(i)}
else {None}
}).collect()
}).collect()
}
#[test]
fn test_square() {
let simple_graph = from_neighborhood_matrix(vec![
vec![0,1,0,1,1,0,0,0],
vec![1,0,1,0,0,1,0,0],
vec![0,1,0,1,0,0,1,0],
vec![1,0,1,0,0,0,0,1],
vec![1,0,0,0,0,1,0,1],
vec![0,1,0,0,1,0,1,0],
vec![0,0,1,0,0,1,0,1],
vec![0,0,0,1,1,0,1,0],
]);
let scan = bfs(0, |n| neighbors(&simple_graph, n)).collect_vec();
assert_eq!(scan, vec![0, 1, 3, 4, 2, 5, 7, 6])
}
#[test]
fn test_stringbuilder() {
let scan = bfs("".to_string(), |s| {
vec![s.clone()+";", s.clone()+"a", s+"aaa"].into_iter()
}).take(30).collect_vec();
println!("{scan:?}")
}
}

View File

@@ -1,4 +1,6 @@
use std::{hash::Hash, cell::RefCell, rc::Rc};
use std::cell::RefCell;
use std::hash::Hash;
use std::rc::Rc;
use hashbrown::HashMap;
// TODO: make this a crate
@@ -20,6 +22,7 @@ impl<'a, I, O> Cache<'a, I, O> where
}
}
#[allow(unused)]
pub fn rc<F: 'a>(closure: F) -> Rc<Self> where F: Fn(I, &Self) -> O {
Rc::new(Self::new(closure))
}
@@ -44,4 +47,27 @@ impl<'a, I, O> Cache<'a, I, O> where
let store = self.store.borrow();
store.get(i).cloned()
}
/// Convert this cache into a cached [Fn(&I) -> O]
#[allow(unused)]
pub fn into_fn(self) -> impl Fn(&I) -> O + 'a where I: 'a {
move |i| self.find(i)
}
/// Borrow this cache with a cached [Fn(&I) -> O]
#[allow(unused)]
pub fn as_fn<'b: 'a>(&'b self) -> impl Fn(&I) -> O + 'b where I: 'b {
move |i| self.find(i)
}
}
impl<'a, I, O> IntoIterator for Cache<'a, I, O> {
type IntoIter = hashbrown::hash_map::IntoIter<I, O>;
type Item = (I, O);
fn into_iter(self) -> Self::IntoIter {
let Cache{ store, .. } = self;
let map = store.into_inner();
map.into_iter()
}
}

0
src/utils/coprefix.rs Normal file
View File

View File

@@ -1,19 +0,0 @@
use std::fmt::Display;
use lasso::RodeoResolver;
pub trait InternedDisplay {
fn fmt(&self,
f: &mut std::fmt::Formatter<'_>,
rr: RodeoResolver
) -> std::fmt::Result;
}
impl<T> InternedDisplay for T where T: Display {
fn fmt(&self,
f: &mut std::fmt::Formatter<'_>,
rr: RodeoResolver
) -> std::fmt::Result {
<Self as Display>::fmt(&self, f)
}
}

181
src/utils/interner.rs Normal file
View File

@@ -0,0 +1,181 @@
use std::sync::{Mutex, Arc};
use std::num::NonZeroU32;
use std::hash::Hash;
use lasso::{Rodeo, Spur, Key};
use base64::{engine::general_purpose::STANDARD_NO_PAD as BASE64, Engine};
/// A token representing an interned string or sequence in an interner
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, PartialOrd, Ord)]
pub struct Token<const RANK: u8>(pub Spur);
/// An interner that can intern strings, and sequences of things it
/// interned as long as they have the same rank
pub trait Interner: Sync {
fn str2tok(&self, str: &str) -> Token<String>;
fn tok2str(&self, token: Token<String>) -> String;
fn slc2tok<const RANK: u8>(&self, slice: &[Token<RANK>]) -> Token<{RANK + 1}>;
fn tok2slc<const RANK: u8>(&self, token: Token<RANK>) -> Vec<Token<{RANK - 1}>>;
fn tok2strv(&self, token: Token<Vec<Token<String>>>) -> Vec<String> {
self.tok2slc(token).into_iter().map(|t| self.tok2str(t)).collect()
}
fn tokv2strv(&self, slice: &[Token<String>]) -> Vec<String> {
slice.iter().map(|t| self.tok2str(*t)).collect()
}
/// Get the first token of a sequence
fn head<const RANK: u8>(&self, token: Token<RANK>) -> Token<{RANK - 1}>;
/// Returns the length of a sequence
fn len<const RANK: u8>(&self, token: Token<RANK>) -> usize
where Token<{RANK - 1}>: Clone;
/// Returns the length of the longest identical prefix of the two sequences
fn coprefix<const RANK: u8>(&self, a: Token<RANK>, b: Token<RANK>) -> usize
where Token<{RANK - 1}>: Clone;
}
fn serialize_seq<const RANK: u8>(seq: &[Token<RANK>]) -> String {
let data: Vec<u8> = seq.iter()
.map(|t| u32::from(t.0.into_inner()).to_le_bytes().into_iter())
.flatten()
.collect();
BASE64.encode(data)
}
fn deserialize_seq<const RANK: u8>(string: &str) -> Vec<Token<RANK>> {
let data = BASE64.decode(string)
.expect("String is not valid base64");
assert!(data.len() % 4 == 0, "Tokens should serialize to 3 bytes each");
data.array_chunks::<4>().map(|a| {
let bytes = [a[0], a[1], a[2], a[3]];
let nz32 = NonZeroU32::new(u32::from_le_bytes(bytes))
.expect("Token representation should never be zero");
Token(Spur::try_from_usize(u32::from(nz32) as usize).unwrap())
}).collect()
}
/// An interner that delegates the actual work to Lasso
#[derive(Clone)]
pub struct LassoInterner {
strings: Arc<Mutex<Rodeo>>,
slices: Arc<Mutex<Rodeo>>
}
impl LassoInterner {
/// Create an empty interner. Called to create the singleton.
fn new() -> Self {
Self{
slices: Arc::new(Mutex::new(Rodeo::new())),
strings: Arc::new(Mutex::new(Rodeo::new()))
}
}
}
impl Interner for LassoInterner {
fn str2tok(&self, str: &str) -> Token<String> {
let mut strings = self.strings.lock().unwrap();
let key = strings.get_or_intern(str);
Token(key)
}
fn tok2str<'a>(&'a self, token: Token<String>) -> String {
let key = token.0;
let strings = self.strings.lock().unwrap();
strings.resolve(&key).to_string()
}
fn slc2tok<const RANK: u8>(&self, slice: &[Token<RANK>]) -> Token<{RANK + 1}> {
let data = serialize_seq(slice);
let mut slices = self.slices.lock().unwrap();
let key = slices.get_or_intern(data);
Token(key)
}
fn tok2slc<'a, const RANK: u8>(&'a self, token: Token<RANK>) -> Vec<Token<{RANK - 1}>> {
let key = token.0;
let slices = self.slices.lock().unwrap();
let string = slices.resolve(&key);
deserialize_seq(string)
}
fn head<const RANK: u8>(&self, token: Token<RANK>) -> Token<{RANK - 1}> {
let key = token.0;
let slices = self.slices.lock().unwrap();
let string = slices.resolve(&key);
deserialize_seq(&string[0..5])[0]
}
fn len<const RANK: u8>(&self, token: Token<RANK>) -> usize where Token<{RANK - 1}>: Clone {
let key = token.0;
let slices = self.slices.lock().unwrap();
let string = slices.resolve(&key);
assert!(string.len() % 4 == 0, "Tokens should serialize to 3 characters");
string.len() / 4
}
fn coprefix<const RANK: u8>(&self, a: Token<RANK>, b: Token<RANK>) -> usize where Token<{RANK - 1}>: Clone {
let keya = a.0;
let keyb = b.0;
let slices = self.slices.lock().unwrap();
let sa = slices.resolve(&keya);
let sb = slices.resolve(&keyb);
sa.bytes()
.zip(sb.bytes())
.take_while(|(a, b)| a == b)
.count() / 4
}
}
/// Create an interner that inherits the singleton's data, and
/// block all future interaction with the singleton.
///
/// DO NOT call within [dynamic] or elsewhere pre-main
pub fn mk_interner() -> impl Interner {
LassoInterner::new()
}
pub trait StringLike: Clone + Eq + Hash {
fn into_str(self, i: &Interner) -> String;
fn into_tok(self, i: &Interner) -> Token<String>;
}
impl StringLike for String {
fn into_str(self, _i: &Interner) -> String {self}
fn into_tok(self, i: &Interner) -> Token<String> {i.str2tok(&self)}
}
impl StringLike for Token<String> {
fn into_str(self, i: &Interner) -> String {i.tok2str(self)}
fn into_tok(self, _i: &Interner) -> Token<String> {self}
}
pub trait StringVLike: Clone + Eq + Hash {
fn into_strv(self, i: &Interner) -> Vec<String>;
fn into_tok(self, i: &Interner) -> Token<Vec<Token<String>>>;
fn into_tokv(self, i: &Interner) -> Vec<Token<String>>;
}
impl StringVLike for Vec<String> {
fn into_strv(self, _i: &Interner) -> Vec<String> {self}
fn into_tok(self, i: &Interner) -> Token<Vec<Token<String>>> {
let tokv = self.into_iter()
.map(|s| i.str2tok(&s))
.collect::<Vec<_>>();
i.slc2tok(&tokv)
}
fn into_tokv(self, i: &Interner) -> Vec<Token<String>> {
self.into_iter()
.map(|s| i.str2tok(&s))
.collect()
}
}
impl StringVLike for Vec<Token<String>> {
fn into_strv(self, i: &Interner) -> Vec<String> {i.tokv2strv(&self)}
fn into_tok(self, i: &Interner) -> Token<Vec<Token<String>>> {i.slc2tok(&self)}
fn into_tokv(self, _i: &Interner) -> Vec<Token<String>> {self}
}
impl StringVLike for Token<Vec<Token<String>>> {
fn into_strv(self, i: &Interner) -> Vec<String> {i.tok2strv(self)}
fn into_tok(self, _i: &Interner) -> Token<Vec<Token<String>>> {self}
fn into_tokv(self, i: &Interner) -> Vec<Token<String>> {i.tok2slc(self)}
}

View File

@@ -1,6 +1,6 @@
/// Utility functions to get rid of explicit casts to BoxedIter which are tedious
use std::{iter, mem};
use std::iter;
pub type BoxedIter<'a, T> = Box<dyn Iterator<Item = T> + 'a>;
pub type BoxedIterIter<'a, T> = BoxedIter<'a, BoxedIter<'a, T>>;

View File

@@ -1,87 +1,26 @@
mod cache;
pub mod translate;
mod replace_first;
mod interned_display;
pub use interned_display::InternedDisplay;
// mod visitor;
// mod interned_display;
// mod interner;
mod variant;
mod print_nname;
mod pushed;
pub use pushed::pushed;
pub use print_nname::{print_nname_seq, print_nname};
// pub use interner::*;
// pub use interned_display::InternedDisplay;
pub use replace_first::replace_first;
pub use cache::Cache;
mod substack;
pub use substack::Stackframe;
pub use substack::{Stackframe, Substack, SubstackIterator};
mod side;
pub use side::Side;
mod unwrap_or;
pub mod iter;
pub use iter::BoxedIter;
mod bfs;
mod string_from_charset;
pub use string_from_charset::string_from_charset;
mod xloop;
mod protomap;
pub use protomap::ProtoMap;
mod product2;
pub use product2::Product2;
use mappable_rc::Mrc;
pub fn mrc_derive<T: ?Sized, P, U: ?Sized>(m: &Mrc<T>, p: P) -> Mrc<U>
where P: for<'a> FnOnce(&'a T) -> &'a U {
Mrc::map(Mrc::clone(m), p)
}
pub fn mrc_try_derive<T: ?Sized, P, U: ?Sized>(m: &Mrc<T>, p: P) -> Option<Mrc<U>>
where P: for<'a> FnOnce(&'a T) -> Option<&'a U> {
Mrc::try_map(Mrc::clone(m), p).ok()
}
pub fn mrc_empty_slice<T>() -> Mrc<[T]> {
mrc_derive_slice(&Mrc::new(Vec::new()))
}
pub fn to_mrc_slice<T>(v: Vec<T>) -> Mrc<[T]> {
Mrc::map(Mrc::new(v), |v| v.as_slice())
}
pub fn collect_to_mrc<I>(iter: I) -> Mrc<[I::Item]> where I: Iterator {
to_mrc_slice(iter.collect())
}
pub fn mrc_derive_slice<T>(mv: &Mrc<Vec<T>>) -> Mrc<[T]> {
mrc_derive(mv, |v| v.as_slice())
}
pub fn one_mrc_slice<T>(t: T) -> Mrc<[T]> {
Mrc::map(Mrc::new([t; 1]), |v| v.as_slice())
}
pub fn mrc_to_iter<T>(ms: Mrc<[T]>) -> impl Iterator<Item = Mrc<T>> {
let mut i = 0;
std::iter::from_fn(move || if i < ms.len() {
let out = Some(mrc_derive(&ms, |s| &s[i]));
i += 1;
out
} else {None})
}
pub fn mrc_unnest<T>(m: &Mrc<Mrc<T>>) -> Mrc<T> {
Mrc::clone(m.as_ref())
}
pub fn mrc_slice_to_only<T>(m: Mrc<[T]>) -> Result<Mrc<T>, ()> {
Mrc::try_map(m, |slice| {
if slice.len() != 1 {None}
else {Some(&slice[0])}
}).map_err(|_| ())
}
pub fn mrc_slice_to_only_option<T>(m: Mrc<[T]>) -> Result<Option<Mrc<T>>, ()> {
if m.len() > 1 {return Err(())}
Ok(Mrc::try_map(m, |slice| {
if slice.len() == 0 {None}
else {Some(&slice[0])}
}).ok())
}
pub fn mrc_concat<T: Clone>(a: &Mrc<[T]>, b: &Mrc<[T]>) -> Mrc<[T]> {
collect_to_mrc(a.iter().chain(b.iter()).cloned())
}
pub use protomap::ProtoMap;

16
src/utils/print_nname.rs Normal file
View File

@@ -0,0 +1,16 @@
use itertools::Itertools;
use crate::interner::{Interner, Token};
#[allow(unused)]
pub fn print_nname(t: Token<Vec<Token<String>>>, i: &Interner) -> String {
i.r(t).iter().map(|t| i.r(*t)).join("::")
}
#[allow(unused)]
pub fn print_nname_seq<'a>(
tv: impl Iterator<Item = &'a Token<Vec<Token<String>>>>,
i: &Interner
) -> String {
tv.map(|t| print_nname(*t, i)).join(", ")
}

View File

@@ -1,55 +0,0 @@
use super::Side;
/// The output of a two-part algorithm. The values are
///
/// - [Product2::Left] or [Product2::Right] if one of the arguments is the product
/// - [Product2::Either] if the arguments are identical
/// - [Product2::New] if the product is a different value from either
pub enum Product2<T> {
Left,
Right,
#[allow(unused)]
Either,
#[allow(unused)]
New(T)
}
impl<T> Product2<T> {
/// Convert the product into a concrete value by providing the original arguments
pub fn pick(self, left: T, right: T) -> T {
match self {
Self::Left | Self::Either => left,
Self::Right => right,
Self::New(t) => t
}
}
/// Combine some subresults into a tuple representing a greater result
pub fn join<U>(
self, (lt, rt): (T, T),
second: Product2<U>, (lu, ru): (U, U)
) -> Product2<(T, U)> {
match (self, second) {
(Self::Either, Product2::Either) => Product2::Either,
(Self::Left | Self::Either, Product2::Left | Product2::Either) => Product2::Left,
(Self::Right | Self::Either, Product2::Right | Product2::Either) => Product2::Right,
(t, u) => Product2::New((t.pick(lt, rt), u.pick(lu, ru)))
}
}
/// Translate results back into the type of the original problem.
pub fn map<A, F: FnOnce(T) -> A>(self, f: F) -> Product2<A> {
match self {
Product2::Left => Product2::Left, Product2::Right => Product2::Right,
Product2::Either => Product2::Either,
Product2::New(t) => Product2::New(f(t))
}
}
}
/// Technically very different but sometimes neecessary to translate
impl<T> From<Side> for Product2<T> {
fn from(value: Side) -> Self {match value {
Side::Left => Self::Left,
Side::Right => Self::Right
}}
}

8
src/utils/pushed.rs Normal file
View File

@@ -0,0 +1,8 @@
/// Create a new vector consisting of the provided vector with the
/// element appended
pub fn pushed<T: Clone>(vec: &Vec<T>, t: T) -> Vec<T> {
let mut next = Vec::with_capacity(vec.len() + 1);
next.extend_from_slice(&vec[..]);
next.push(t);
next
}

View File

@@ -1,5 +1,7 @@
use std::fmt::Display;
use super::BoxedIter;
/// A primitive for encoding the two sides Left and Right. While booleans
/// are technically usable for this purpose, they're less descriptive.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
@@ -55,4 +57,36 @@ impl Side {
Side::Right => (opposite, this)
}
}
}
/// Produces an increasing sequence on Right, and a decreasing sequence
/// on Left
pub fn walk<'a, I: DoubleEndedIterator + 'a>(&self, iter: I)
-> BoxedIter<'a, I::Item>
{
match self {
Side::Right => Box::new(iter) as BoxedIter<I::Item>,
Side::Left => Box::new(iter.rev()),
}
}
}
#[cfg(test)]
mod test {
use itertools::Itertools;
use super::*;
/// I apparently have a tendency to mix these up so it's best if
/// the sides are explicitly stated
#[test]
fn test_walk() {
assert_eq!(
Side::Right.walk(0..4).collect_vec(),
vec![0, 1, 2, 3],
"can walk a range"
);
assert_eq!(
Side::Left.walk(0..4).collect_vec(),
vec![3, 2, 1, 0],
"can walk a range backwards"
)
}
}

View File

@@ -1,3 +1,4 @@
use std::collections::VecDeque;
use std::fmt::Debug;
// TODO: extract to crate
@@ -8,71 +9,60 @@ use std::fmt::Debug;
#[derive(Clone, Copy)]
pub struct Stackframe<'a, T> {
pub item: T,
pub prev: Option<&'a Stackframe<'a, T>>,
pub prev: &'a Substack<'a, T>,
pub len: usize
}
impl<'a, T: 'a> Stackframe<'a, T> {
pub fn new(item: T) -> Self {
Self {
item,
prev: None,
len: 1
}
}
/// Get the item owned by this listlike, very fast O(1)
pub fn item(&self) -> &T { &self.item }
/// Get the next link in the list, very fast O(1)
pub fn prev(&self) -> Option<&'a Stackframe<T>> { self.prev }
/// Construct an iterator over the listlike, very fast O(1)
pub fn iter(&self) -> StackframeIterator<T> {
StackframeIterator { curr: Some(self) }
}
pub fn push(&self, item: T) -> Stackframe<'_, T> {
Stackframe {
item,
prev: Some(self),
len: self.len + 1
}
}
#[allow(unused)]
pub fn opush(prev: Option<&'a Self>, item: T) -> Self {
Self {
item,
prev,
len: prev.map_or(1, |s| s.len)
}
}
#[allow(unused)]
pub fn len(&self) -> usize { self.len }
#[allow(unused)]
pub fn pop(&self, count: usize) -> Option<&Self> {
if count == 0 {Some(self)}
else {self.prev.expect("Index out of range").pop(count - 1)}
}
#[allow(unused)]
pub fn opop(cur: Option<&Self>, count: usize) -> Option<&Self> {
if count == 0 {cur}
else {Self::opop(cur.expect("Index out of range").prev, count - 1)}
}
#[allow(unused)]
pub fn o_into_iter(curr: Option<&Self>) -> StackframeIterator<T> {
StackframeIterator { curr }
}
#[derive(Clone, Copy)]
pub enum Substack<'a, T> {
Frame(Stackframe<'a, T>),
Bottom
}
impl<'a, T> Debug for Stackframe<'a, T> where T: Debug {
impl<'a, T> Substack<'a, T> {
/// Convert the substack into an option of stackframe
pub fn opt(&'a self) -> Option<&'a Stackframe<'a, T>> { match self {
Self::Frame(f) => Some(f),
Self::Bottom => None
} }
/// Construct an iterator over the listlike, very fast O(1)
pub fn iter(&self) -> SubstackIterator<T> {
SubstackIterator { curr: self }
}
pub fn push(&'a self, item: T) -> Self {
Self::Frame(self.new_frame(item))
}
pub fn new_frame(&'a self, item: T) -> Stackframe<'a, T> {
Stackframe {
item,
prev: &self,
len: self.opt().map_or(1, |s| s.len)
}
}
pub fn pop(&'a self, count: usize) -> Option<&'a Stackframe<'a, T>> {
if let Self::Frame(p) = self {
if count == 0 {Some(&p)}
else {p.prev.pop(count - 1)}
} else {None}
}
pub fn len(&self) -> usize { match self {
Self::Frame(f) => f.len,
Self::Bottom => 0
} }
}
impl<'a, T> Debug for Substack<'a, T> where T: Debug {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "Substack")?;
f.debug_list().entries(self.iter()).finish()
}
}
pub struct StackframeIterator<'a, T> {
curr: Option<&'a Stackframe<'a, T>>
pub struct SubstackIterator<'a, T> {
curr: &'a Substack<'a, T>
}
impl<'a, T> StackframeIterator<'a, T> {
impl<'a, T> SubstackIterator<'a, T> {
#[allow(unused)]
pub fn first_some<U, F>(&mut self, f: F) -> Option<U>
where F: Fn(&T) -> Option<U> {
@@ -83,15 +73,38 @@ impl<'a, T> StackframeIterator<'a, T> {
}
None
}
/// Returns an iterator that starts from the bottom of the stack
/// and ends at the current position. This moves all items to the
/// heap by copying them to a [Vec]
pub fn rev_vec_clone(self) -> Vec<T> where T: Clone {
let mut deque = VecDeque::with_capacity(self.curr.len());
for item in self { deque.push_front(item.clone()) }
deque.into()
}
}
impl<'a, T> Iterator for StackframeIterator<'a, T> {
impl<'a, T> Copy for SubstackIterator<'a, T> {}
impl<'a, T> Clone for SubstackIterator<'a, T> {
fn clone(&self) -> Self {
Self { curr: self.curr }
}
}
impl<'a, T> Iterator for SubstackIterator<'a, T> {
type Item = &'a T;
fn next(&mut self) -> Option<&'a T> {
let curr = self.curr?;
let item = curr.item();
let prev = curr.prev();
let curr = self.curr.opt()?;
let item = &curr.item;
let prev = curr.prev;
self.curr = prev;
Some(item)
}
}
fn size_hint(&self) -> (usize, Option<usize>) {
(self.curr.len(), Some(self.curr.len()))
}
}

View File

@@ -2,9 +2,9 @@ use std::mem;
// TODO: extract to crate
#[allow(unused)]
/// Map over a `&mut` with a mapper function that takes ownership of
/// the value
#[allow(unused)]
pub fn translate<T, F: FnOnce(T) -> T>(data: &mut T, f: F) {
unsafe {
let mut acc = mem::MaybeUninit::<T>::uninit().assume_init();
@@ -17,6 +17,7 @@ pub fn translate<T, F: FnOnce(T) -> T>(data: &mut T, f: F) {
/// Map over a `&mut` with a mapper function that takes ownership of
/// the value and also produces some unrelated data.
#[allow(unused)]
pub fn process<T, U, F: FnOnce(T) -> (T, U)>(data: &mut T, f: F) -> U {
unsafe {
let mut acc = mem::MaybeUninit::<T>::uninit().assume_init();

19
src/utils/variant.rs Normal file
View File

@@ -0,0 +1,19 @@
// trait Var<T> {
// type With<U>: Var<U>;
// fn map<U>(self, f: impl FnOnce(T) -> U) -> Self::With<U>;
// fn map_multi<U, V, Ret: Var<U> + Var<V>>(
// self, f: impl FnOnce(T) -> Ret
// ) -> <Self::With<U> as Var<U>>::With<V>;
// }
// enum Variant<T, U> {
// Head(T),
// Tail(U)
// }
// impl<H, T: Var<_>> Var<H> for Variant<H, T> {
// fn map<U>(self, f: impl FnOnce(H) -> U) -> Self::With<U> {
// match
// }
// }

View File

@@ -78,8 +78,8 @@ macro_rules! xloop {
(while $cond:expr; $body:stmt; $exit:stmt) => {
{
loop {
if $cond { break { $exit } }
else { $body }
if $cond { $body }
else { break { $exit } }
}
}
};
@@ -87,6 +87,6 @@ macro_rules! xloop {
xloop!(for ( $init; $cond; $step ) $body; ())
};
($init:stmt; $cond:expr; $step:stmt; $body:stmt; $exit:stmt) => {
{ $init; xloop!(while !($cond); { $body; $step }; $exit) }
{ $init; xloop!(while $cond; { $body; $step }; $exit) }
};
}