#[cfg(feature = "parallel-histogram")]
pub mod parallel;
pub mod serial;
use std::{
collections::{HashMap, hash_map::IntoIter},
fmt::{Display, Formatter},
ops::{Deref, DerefMut, Index},
sync::atomic::{AtomicU64, Ordering}
};
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
use crate::{
Add, AddressingMode, CanAllocate, CanVisitInstructions, Div, DropHighest,
DropLowest, EvaluationError, Evaluator, Exp, Function, Instruction,
InstructionVisitor, Mod, Mul, Neg, ProgramCounter, RegisterIndex, Return,
RollCustomDice, RollRange, RollStandardDice, RollingRecord,
RollingRecordIndex, RollingRecordKind, Sub, SumRollingRecord, add, div,
exp, r#mod, mul, neg, sub
};
#[derive(Debug, Clone, Default, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct Histogram(pub HashMap<i32, u64>);
impl Histogram
{
pub fn get(&self, outcome: i32) -> u64
{
self.0.get(&outcome).copied().unwrap_or(0)
}
#[inline]
pub fn total(&self) -> u64 { self.0.values().sum() }
#[inline]
pub fn unique(&self) -> u64 { self.0.len() as u64 }
pub fn outcomes(&self) -> Vec<i32>
{
let mut outcomes: Vec<_> = self.0.keys().copied().collect();
outcomes.sort();
outcomes
}
pub fn odds(&self, outcome: i32) -> (u64, u64)
{
let count = self.get(outcome);
let total = self.total();
(count, total - count)
}
pub fn percent_chance(&self, outcome: i32) -> f64
{
(self.get(outcome) as f64 / self.total() as f64) * 100.0
}
}
impl Deref for Histogram
{
type Target = HashMap<i32, u64>;
fn deref(&self) -> &Self::Target { &self.0 }
}
impl DerefMut for Histogram
{
fn deref_mut(&mut self) -> &mut Self::Target { &mut self.0 }
}
impl IntoIterator for Histogram
{
type Item = (i32, u64);
type IntoIter = IntoIter<i32, u64>;
fn into_iter(self) -> Self::IntoIter { self.0.into_iter() }
}
impl Index<i32> for Histogram
{
type Output = u64;
fn index(&self, outcome: i32) -> &Self::Output
{
self.0.get(&outcome).unwrap_or(&0)
}
}
impl Display for Histogram
{
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result
{
let mut outcomes: Vec<_> = self.0.iter().collect();
outcomes.sort_by_key(|(outcome, _)| *outcome);
for (outcome, count) in outcomes
{
writeln!(f, "{}: {}", outcome, count)?;
}
Ok(())
}
}
pub trait HistogramBuilder<'inst, T>
where
T: 'inst
{
fn new(evaluator: Evaluator) -> Self;
fn build(
&self,
args: impl IntoIterator<Item = i32> + Send
) -> Result<Histogram, EvaluationError<'_>>
{
self.build_while(args, |_| true)
}
fn build_with_limit(
&self,
args: impl IntoIterator<Item = i32> + Send,
limit: u64
) -> Result<Histogram, EvaluationError<'_>>
{
let count = AtomicU64::new(0);
self.build_while(args, |_| {
count.fetch_add(1, Ordering::Relaxed) < limit
})
}
fn build_while(
&self,
args: impl IntoIterator<Item = i32> + Send,
condition: impl Fn(&i32) -> bool + Send + Sync
) -> Result<Histogram, EvaluationError<'_>>;
fn iter(
&'inst self,
args: impl IntoIterator<Item = i32>
) -> Result<T, EvaluationError<'inst>>;
}
#[derive(Debug, Clone)]
pub struct EvaluationState<'inst>
{
instructions: &'inst [Instruction],
pc: ProgramCounter,
registers: Vec<i32>,
records: Vec<RollingRecord>,
substate: (i32, i32),
out_of_gas: bool,
successors: Option<Vec<EvaluationState<'inst>>>,
pub result: Option<i32>
}
trait CanBuildHistogram<'inst, T>
where
T: 'inst
{
fn function(&self) -> &Function;
fn environment(&self) -> &HashMap<usize, i32>;
fn create_iterator(&'inst self, initial_state: EvaluationState<'inst>)
-> T;
fn iter(
&'inst self,
args: impl IntoIterator<Item = i32>
) -> Result<T, EvaluationError<'inst>>
{
let function = self.function();
let arity = function.arity();
let args = args.into_iter().collect::<Vec<_>>();
if args.len() != arity
{
return Err(EvaluationError::BadArity {
expected: arity,
given: args.len()
});
}
let mut state = EvaluationState::initial(function);
for (i, arg) in args.into_iter().enumerate()
{
state.registers[i] = arg;
}
for (index, value) in self.environment()
{
state.registers[arity + *index] = *value;
}
Ok(self.create_iterator(state))
}
}
trait CanIterate<'inst>: Sized
{
fn next_state(&mut self) -> Option<EvaluationState<'inst>>;
fn inject_successors(&mut self, state: &mut EvaluationState<'inst>);
}
impl<'inst> EvaluationState<'inst>
{
fn initial(function: &'inst Function) -> Self
{
EvaluationState {
instructions: &function.instructions,
pc: ProgramCounter::default(),
registers: vec![0; function.register_count],
records: vec![
RollingRecord::default();
function.rolling_record_count
],
substate: (0, 0),
out_of_gas: false,
successors: None,
result: None
}
}
pub(crate) const MAX_SUCCESSORS: usize = 30;
fn evaluate(&mut self) -> Option<i32>
{
for inst in &self.instructions[self.pc.0..]
{
inst.visit(self).unwrap();
self.pc.allocate();
self.substate = (0, 0);
if self.out_of_gas
{
return None
}
}
self.result
}
fn value(&self, op: AddressingMode) -> i32
{
match op
{
AddressingMode::Immediate(value) => value.0,
AddressingMode::Register(reg) => self.registers[reg.0],
AddressingMode::RollingRecord(_) => unreachable!()
}
}
#[inline]
fn set_register(&mut self, reg: RegisterIndex, value: i32)
{
self.registers[reg.0] = value;
}
#[inline]
fn record_mut(&mut self, op: RollingRecordIndex) -> &mut RollingRecord
{
&mut self.records[op.0]
}
fn add_successor(&mut self, successor: EvaluationState<'inst>) -> bool
{
match &mut self.successors
{
Some(successors) =>
{
successors.push(successor);
},
_ =>
{
self.successors = Some(vec![successor]);
}
}
self.out_of_gas =
self.successors.as_ref().unwrap().len() >= Self::MAX_SUCCESSORS - 1;
!self.out_of_gas
}
fn new_successor(&self) -> Self
{
let mut state = self.clone();
state.successors = None;
state.out_of_gas = false;
state
}
fn new_iteration_successor(
&self,
record: RollingRecordIndex,
iteration: i32,
max_iterations: i32,
result: i32
) -> Self
{
let mut successor = self.new_successor();
if iteration == max_iterations
{
successor.pc.allocate();
successor.substate = (0, 0);
}
else
{
successor.substate = (iteration, 0);
}
let record = successor.record_mut(record);
record.results.push(result);
successor
}
fn new_index_successor(&self, iteration: i32, index: i32) -> Self
{
let mut successor = self.new_successor();
successor.substate = (iteration, index);
successor
}
}
impl Display for EvaluationState<'_>
{
fn fmt(&self, f: &mut Formatter) -> std::fmt::Result
{
write!(f, "{}{:?}", self.pc, self.substate)?;
if !self.records.is_empty()
{
write!(f, " {{ ")?;
for (i, record) in self.records.iter().enumerate()
{
if i > 0
{
write!(f, "; ")?;
}
write!(f, "{}", record)?;
}
write!(f, " }}")?;
}
if let Some(result) = self.result
{
write!(f, " -> {}", result)?;
}
Ok(())
}
}
impl InstructionVisitor<()> for EvaluationState<'_>
{
fn visit_roll_range(&mut self, inst: &RollRange) -> Result<(), ()>
{
let start = self.value(inst.start);
let end = self.value(inst.end);
{
let record = self.record_mut(inst.dest);
record.kind = RollingRecordKind::Range { start, end };
}
if end < start
{
let record = self.record_mut(inst.dest);
record.results.push(0);
return Ok(())
}
let (iteration, index) = self.substate;
if iteration == 0
{
for index in index..=end - start
{
if !self.add_successor(self.new_iteration_successor(
inst.dest,
iteration + 1,
1,
start + index
))
{
self.add_successor(
self.new_index_successor(iteration, index + 1)
);
return Ok(())
}
}
self.out_of_gas = true;
return Ok(())
}
unreachable!()
}
fn visit_roll_standard_dice(
&mut self,
inst: &RollStandardDice
) -> Result<(), ()>
{
let count = self.value(inst.count);
let faces = self.value(inst.faces);
{
let record = self.record_mut(inst.dest);
record.kind = RollingRecordKind::Standard { count, faces };
}
if count <= 0
{
let record = self.record_mut(inst.dest);
record.results.push(0);
return Ok(())
}
let (iteration, index) = self.substate;
if iteration < count
{
if faces <= 0
{
self.add_successor(self.new_iteration_successor(
inst.dest,
iteration + 1,
count,
0
));
}
for index in index..faces
{
if !self.add_successor(self.new_iteration_successor(
inst.dest,
iteration + 1,
count,
index + 1
))
{
self.add_successor(
self.new_index_successor(iteration, index + 1)
);
return Ok(())
}
}
self.out_of_gas = true;
return Ok(())
}
unreachable!()
}
fn visit_roll_custom_dice(
&mut self,
inst: &RollCustomDice
) -> Result<(), ()>
{
let count = self.value(inst.count);
let faces = inst.faces.len();
{
let record = self.record_mut(inst.dest);
record.kind = RollingRecordKind::Custom {
count,
faces: inst.faces.clone()
};
}
if count <= 0
{
let record = self.record_mut(inst.dest);
record.results.push(0);
return Ok(())
}
let (iteration, index) = self.substate;
if iteration < count
{
if faces == 0
{
self.add_successor(self.new_iteration_successor(
inst.dest,
iteration + 1,
count,
0
));
}
for index in index..faces as i32
{
if !self.add_successor(self.new_iteration_successor(
inst.dest,
iteration + 1,
count,
inst.faces[index as usize]
))
{
self.add_successor(
self.new_index_successor(iteration, index + 1)
);
return Ok(())
}
}
self.out_of_gas = true;
return Ok(())
}
unreachable!()
}
fn visit_drop_lowest(&mut self, inst: &DropLowest) -> Result<(), ()>
{
let count = self.value(inst.count);
let record = self.record_mut(inst.dest);
record.drop_lowest(count);
Ok(())
}
fn visit_drop_highest(&mut self, inst: &DropHighest) -> Result<(), ()>
{
let count = self.value(inst.count);
let record = self.record_mut(inst.dest);
record.drop_highest(count);
Ok(())
}
fn visit_sum_rolling_record(
&mut self,
inst: &SumRollingRecord
) -> Result<(), ()>
{
let sum = self.record_mut(inst.src).sum();
self.set_register(inst.dest, sum);
Ok(())
}
fn visit_add(&mut self, inst: &Add) -> Result<(), ()>
{
let op1 = self.value(inst.op1);
let op2 = self.value(inst.op2);
self.set_register(inst.dest, add(op1, op2));
Ok(())
}
fn visit_sub(&mut self, inst: &Sub) -> Result<(), ()>
{
let op1 = self.value(inst.op1);
let op2 = self.value(inst.op2);
self.set_register(inst.dest, sub(op1, op2));
Ok(())
}
fn visit_mul(&mut self, inst: &Mul) -> Result<(), ()>
{
let op1 = self.value(inst.op1);
let op2 = self.value(inst.op2);
self.set_register(inst.dest, mul(op1, op2));
Ok(())
}
fn visit_div(&mut self, inst: &Div) -> Result<(), ()>
{
let op1 = self.value(inst.op1);
let op2 = self.value(inst.op2);
self.set_register(inst.dest, div(op1, op2));
Ok(())
}
fn visit_mod(&mut self, inst: &Mod) -> Result<(), ()>
{
let op1 = self.value(inst.op1);
let op2 = self.value(inst.op2);
self.set_register(inst.dest, r#mod(op1, op2));
Ok(())
}
fn visit_exp(&mut self, inst: &Exp) -> Result<(), ()>
{
let op1 = self.value(inst.op1);
let op2 = self.value(inst.op2);
self.set_register(inst.dest, exp(op1, op2));
Ok(())
}
fn visit_neg(&mut self, inst: &Neg) -> Result<(), ()>
{
let op = self.value(inst.op);
self.set_register(inst.dest, neg(op));
Ok(())
}
fn visit_return(&mut self, inst: &Return) -> Result<(), ()>
{
self.result = Some(self.value(inst.src));
Ok(())
}
}