#[cfg(test)]
use proptest::prelude::*;
use std::collections::VecDeque;
#[derive(Debug, Clone)]
pub enum ExprBoxed {
Add {
a: Box<ExprBoxed>,
b: Box<ExprBoxed>,
},
Sub {
a: Box<ExprBoxed>,
b: Box<ExprBoxed>,
},
Mul {
a: Box<ExprBoxed>,
b: Box<ExprBoxed>,
},
LiteralInt {
literal: i64,
},
}
impl ExprBoxed {
pub fn eval(&self) -> i64 {
match &self {
ExprBoxed::Add { a, b } => a.eval() + b.eval(),
ExprBoxed::Sub { a, b } => a.eval() - b.eval(),
ExprBoxed::Mul { a, b } => a.eval() * b.eval(),
ExprBoxed::LiteralInt { literal } => *literal,
}
}
}
#[derive(Debug, Clone, Copy)]
pub enum ExprLayer<A> {
Add { a: A, b: A },
Sub { a: A, b: A },
Mul { a: A, b: A },
LiteralInt { literal: i64 },
}
#[derive(Eq, Hash, PartialEq)]
pub struct ExprIdx(usize);
impl ExprIdx {
fn head() -> Self {
ExprIdx(0)
}
}
pub struct Expr {
elems: Vec<ExprLayer<ExprIdx>>,
}
impl Expr {
fn generate_from_boxed_inline(a: &ExprBoxed) -> Self {
let mut frontier: VecDeque<&ExprBoxed> = VecDeque::new();
let mut elems = vec![];
fn push_to_frontier<'a>(
elems: &Vec<ExprLayer<ExprIdx>>,
frontier: &mut VecDeque<&'a ExprBoxed>,
a: &'a ExprBoxed,
) -> ExprIdx {
frontier.push_back(a);
ExprIdx(elems.len() + frontier.len())
}
push_to_frontier(&elems, &mut frontier, a);
while let Some(seed) = { frontier.pop_front() } {
let node = match seed {
ExprBoxed::Add { a, b } => {
let a = push_to_frontier(&elems, &mut frontier, a);
let b = push_to_frontier(&elems, &mut frontier, b);
ExprLayer::Add { a, b }
}
ExprBoxed::Sub { a, b } => {
let a = push_to_frontier(&elems, &mut frontier, a);
let b = push_to_frontier(&elems, &mut frontier, b);
ExprLayer::Sub { a, b }
}
ExprBoxed::Mul { a, b } => {
let a = push_to_frontier(&elems, &mut frontier, a);
let b = push_to_frontier(&elems, &mut frontier, b);
ExprLayer::Mul { a, b }
}
ExprBoxed::LiteralInt { literal } => {
ExprLayer::LiteralInt { literal: *literal }
}
};
elems.push(node);
}
Self { elems }
}
}
impl Expr {
fn generate_from_boxed_with_fmap(seed: &ExprBoxed) -> Self {
let mut frontier: VecDeque<&ExprBoxed> = VecDeque::from([seed]);
let mut elems = vec![];
while let Some(seed) = { frontier.pop_front() } {
let node = match seed {
ExprBoxed::Add { a, b } => ExprLayer::Add { a, b },
ExprBoxed::Sub { a, b } => ExprLayer::Sub { a, b },
ExprBoxed::Mul { a, b } => ExprLayer::Mul { a, b },
ExprBoxed::LiteralInt { literal } => ExprLayer::LiteralInt { literal: *literal },
};
let node = node.map(|seed| {
frontier.push_back(seed);
ExprIdx(elems.len() + frontier.len())
});
elems.push(node);
}
Self { elems }
}
}
impl Expr {
fn eval_inline(self) -> i64 {
use std::mem::MaybeUninit;
let mut results = std::iter::repeat_with(MaybeUninit::<i64>::uninit)
.take(self.elems.len())
.collect::<Vec<_>>();
fn get_result_unsafe(results: &mut [MaybeUninit<i64>], idx: ExprIdx) -> i64 {
unsafe {
let maybe_uninit =
std::mem::replace(results.get_unchecked_mut(idx.0), MaybeUninit::uninit());
maybe_uninit.assume_init()
}
}
for (idx, node) in self.elems.into_iter().enumerate().rev() {
let alg_res = {
match node {
ExprLayer::Add { a, b } => {
let a = get_result_unsafe(&mut results, a);
let b = get_result_unsafe(&mut results, b);
a + b
}
ExprLayer::Sub { a, b } => {
let a = get_result_unsafe(&mut results, a);
let b = get_result_unsafe(&mut results, b);
a - b
}
ExprLayer::Mul { a, b } => {
let a = get_result_unsafe(&mut results, a);
let b = get_result_unsafe(&mut results, b);
a * b
}
ExprLayer::LiteralInt { literal } => literal,
}
};
results[idx].write(alg_res);
}
unsafe {
let maybe_uninit =
std::mem::replace(results.get_unchecked_mut(0), MaybeUninit::uninit());
maybe_uninit.assume_init()
}
}
}
impl<A> ExprLayer<A> {
#[inline(always)]
fn map<B, F: FnMut(A) -> B>(self, mut f: F) -> ExprLayer<B> {
match self {
ExprLayer::Add { a, b } => ExprLayer::Add { a: f(a), b: f(b) },
ExprLayer::Sub { a, b } => ExprLayer::Sub { a: f(a), b: f(b) },
ExprLayer::Mul { a, b } => ExprLayer::Mul { a: f(a), b: f(b) },
ExprLayer::LiteralInt { literal } => ExprLayer::LiteralInt { literal },
}
}
}
impl Expr {
fn eval_inline_fmap(self) -> i64 {
use std::mem::MaybeUninit;
let mut results = std::iter::repeat_with(MaybeUninit::<i64>::uninit)
.take(self.elems.len())
.collect::<Vec<_>>();
for (idx, node) in self.elems.into_iter().enumerate().rev() {
let node = node.map(|idx| unsafe {
let maybe_uninit =
std::mem::replace(results.get_unchecked_mut(idx.0), MaybeUninit::uninit());
maybe_uninit.assume_init()
});
let alg_res = match node {
ExprLayer::Add { a, b } => a + b,
ExprLayer::Sub { a, b } => a - b,
ExprLayer::Mul { a, b } => a * b,
ExprLayer::LiteralInt { literal } => literal,
};
results[idx].write(alg_res);
}
unsafe {
let maybe_uninit =
std::mem::replace(results.get_unchecked_mut(0), MaybeUninit::uninit());
maybe_uninit.assume_init()
}
}
}
impl Expr {
fn fold<A: std::fmt::Debug, F: FnMut(ExprLayer<A>) -> A>(self, mut alg: F) -> A {
use std::mem::MaybeUninit;
let mut results = std::iter::repeat_with(|| MaybeUninit::<A>::uninit())
.take(self.elems.len())
.collect::<Vec<_>>();
for (idx, node) in self.elems.into_iter().enumerate().rev() {
let alg_res = {
let node = node.map(|x| unsafe {
let maybe_uninit =
std::mem::replace(results.get_unchecked_mut(x.0), MaybeUninit::uninit());
maybe_uninit.assume_init()
});
alg(node)
};
results[idx].write(alg_res);
}
unsafe {
let maybe_uninit = std::mem::replace(
results.get_unchecked_mut(ExprIdx::head().0),
MaybeUninit::uninit(),
);
maybe_uninit.assume_init()
}
}
}
impl Expr {
pub fn eval(self) -> i64 {
self.fold(|expr| match expr {
ExprLayer::Add { a, b } => a + b,
ExprLayer::Sub { a, b } => a - b,
ExprLayer::Mul { a, b } => a * b,
ExprLayer::LiteralInt { literal } => literal,
})
}
}
impl Expr {
fn generate<A, F: Fn(A) -> ExprLayer<A>>(a: A, coalg: F) -> Self {
let mut frontier = VecDeque::from([a]);
let mut elems = vec![];
while let Some(seed) = frontier.pop_front() {
let node = coalg(seed);
let node = node.map(|aa| {
frontier.push_back(aa);
ExprIdx(elems.len() + frontier.len())
});
elems.push(node);
}
Self { elems }
}
}
impl Expr {
pub fn generate_from_boxed(ast: &ExprBoxed) -> Self {
Self::generate(ast, |seed| match seed {
ExprBoxed::Add { a, b } => ExprLayer::Add { a, b },
ExprBoxed::Sub { a, b } => ExprLayer::Sub { a, b },
ExprBoxed::Mul { a, b } => ExprLayer::Mul { a, b },
ExprBoxed::LiteralInt { literal } => ExprLayer::LiteralInt { literal: *literal },
})
}
}
#[cfg(test)]
proptest! {
#[test]
fn expr_eval(boxed_expr in arb_expr()) {
let eval_boxed = boxed_expr.eval();
let eval_inlined = Expr::generate_from_boxed_inline(&boxed_expr).eval_inline();
let eval_inlined_fmap = Expr::generate_from_boxed_with_fmap(&boxed_expr).eval_inline_fmap();
let eval_via_fold = Expr::generate_from_boxed(&boxed_expr).eval();
assert_eq!(eval_boxed, eval_inlined);
assert_eq!(eval_boxed, eval_inlined_fmap);
assert_eq!(eval_boxed, eval_via_fold);
}
}
#[cfg(test)]
pub fn arb_expr() -> impl Strategy<Value = ExprBoxed> {
let leaf = any::<i8>().prop_map(|x| ExprBoxed::LiteralInt { literal: x as i64 });
leaf.prop_recursive(
8, 256, 10, |inner| {
prop_oneof![
(inner.clone(), inner.clone()).prop_map(|(a, b)| ExprBoxed::Add {
a: Box::new(a),
b: Box::new(b)
}),
(inner.clone(), inner.clone()).prop_map(|(a, b)| ExprBoxed::Sub {
a: Box::new(a),
b: Box::new(b)
}),
(inner.clone(), inner).prop_map(|(a, b)| ExprBoxed::Mul {
a: Box::new(a),
b: Box::new(b)
}),
]
},
)
}