use std::sync::atomic::{AtomicPtr, Ordering};
use crate::{
checker::Checker,
expr::{Operator, PostProcessor},
tree::{BinaryTree, BinaryTreeNode},
};
unsafe fn cache_it<T, F>(cache: &AtomicPtr<T>, f: F) -> T
where
T: Copy,
F: FnOnce() -> T,
{
let x = cache.load(Ordering::SeqCst);
if x.is_null() {
let value = f();
let p = Box::into_raw(Box::new(value));
match cache.compare_exchange(
std::ptr::null::<T>() as *mut T,
p,
Ordering::SeqCst,
Ordering::SeqCst,
) {
Ok(_) => {}
Err(p) => drop(Box::from_raw(p)),
}
value
} else {
*x
}
}
#[derive(Debug)]
pub struct DiceRoll {
points: Vec<u64>,
pp: PostProcessor,
cache: AtomicPtr<u64>,
}
impl DiceRoll {
pub(crate) fn new(points: Vec<u64>, pp: PostProcessor) -> Self {
Self {
points,
pp,
cache: AtomicPtr::default(),
}
}
#[must_use]
pub const fn post_processor(&self) -> PostProcessor {
self.pp
}
#[must_use]
pub fn points(&self) -> &[u64] {
&self.points
}
#[allow(clippy::len_without_is_empty)] #[must_use]
pub fn len(&self) -> usize {
self.points.len()
}
#[allow(clippy::missing_panics_doc)] #[must_use]
fn calculate_value(&self) -> u64 {
match self.pp {
PostProcessor::Sum => self.points.iter().sum(),
PostProcessor::Avg => self.points.iter().sum::<u64>() / self.points.len() as u64,
PostProcessor::Max => *self.points.iter().max().unwrap(),
PostProcessor::Min => *self.points.iter().min().unwrap(),
}
}
pub fn value(&self) -> u64 {
unsafe { cache_it(&self.cache, || self.calculate_value()) }
}
}
#[derive(Debug)]
pub enum ItemRoll {
Dice(DiceRoll),
Number(i64),
Parentheses(Box<RollTreeNode>),
}
impl ItemRoll {
#[must_use]
pub fn value(&self) -> i64 {
match self {
#[allow(clippy::cast_possible_wrap)] Self::Dice(dice) => dice.value() as i64,
Self::Number(x) => *x,
Self::Parentheses(e) => e.value(),
}
}
}
pub type RollTree = BinaryTree<ItemRoll, Operator, AtomicPtr<i64>>;
impl RollTree {
fn calculate_value(&self) -> i64 {
match self.mid {
Operator::Add => self.left.value() + self.right.value(),
Operator::Minus => self.left.value() - self.right.value(),
Operator::Multiply => self.left.value() * self.right.value(),
}
}
pub fn value(&self) -> i64 {
unsafe { cache_it(&self.extra, || self.calculate_value()) }
}
}
pub type RollTreeNode = BinaryTreeNode<ItemRoll, Operator, AtomicPtr<i64>>;
impl RollTreeNode {
pub fn value(&self) -> i64 {
match self {
Self::Leaf(leaf) => leaf.value(),
Self::Tree(tree) => tree.value(),
}
}
}
#[derive(Debug)]
pub struct GurgleRoll<'g> {
result: RollTreeNode,
checker: Option<&'g Checker>,
cache: AtomicPtr<i64>,
}
impl<'g> GurgleRoll<'g> {
pub(crate) fn new(result: RollTreeNode, checker: Option<&'g Checker>) -> Self {
Self {
result,
checker,
cache: AtomicPtr::default(),
}
}
#[must_use]
pub const fn expr(&self) -> &RollTreeNode {
&self.result
}
pub const fn checker(&self) -> Option<&'g Checker> {
self.checker
}
#[must_use]
pub fn value(&self) -> i64 {
unsafe { cache_it(&self.cache, || self.result.value()) }
}
pub fn success(&self) -> Option<bool> {
self.checker.map(|c| c.check(self.value()))
}
}