1use std::collections::{HashMap, HashSet};
16
17use super::ratio::{Ratio};
18use super::super::{Binary, Expression};
19
20macro_rules! binary {
27 ($left:expr, $right:expr, $operation:ident) => ({
28 match ($left, $right) {
29 (Some(left), Some(right)) => left.$operation(right),
30 _ => None,
31 }
32 });
33}
34
35impl Expression {
42 pub fn minimum(&self) -> Option<i32> {
46 match *self {
47 Expression::Binary(binary, ref left, ref right) => match binary {
48 Binary::Add => binary!(left.minimum(), right.minimum(), checked_add),
49 Binary::Divide => binary!(left.minimum(), right.maximum(), checked_div),
50 Binary::Multiply => binary!(left.minimum(), right.minimum(), checked_mul),
51 Binary::Subtract => binary!(left.minimum(), right.maximum(), checked_sub),
52 },
53 Expression::Constant(constant) => Some(constant as i32),
54 Expression::Dice(dice, _, fold) => Some(dice.minimum(fold) as i32),
55 Expression::Die(die, _) => Some(die.minimum() as i32),
56 }
57 }
58
59 pub fn maximum(&self) -> Option<i32> {
61 match *self {
62 Expression::Binary(binary, ref left, ref right) => match binary {
63 Binary::Add => binary!(left.maximum(), right.maximum(), checked_add),
64 Binary::Divide => binary!(left.maximum(), right.minimum(), checked_div),
65 Binary::Multiply => binary!(left.maximum(), right.maximum(), checked_mul),
66 Binary::Subtract => binary!(left.maximum(), right.minimum(), checked_sub),
67 },
68 Expression::Constant(constant) => Some(constant as i32),
69 Expression::Dice(dice, _, fold) => Some(dice.maximum(fold) as i32),
70 Expression::Die(die, _) => Some(die.maximum() as i32),
71 }
72 }
73
74 pub fn average(&self) -> Option<Ratio> {
76 match *self {
77 Expression::Binary(binary, ref left, ref right) => match binary {
78 Binary::Add => binary!(left.average(), right.average(), checked_add),
79 Binary::Divide => {
80 let left = try_opt!(left.probabilities());
81 let right = try_opt!(right.probabilities());
82 let mut average = Ratio::zero();
83 for (lr, lp) in left {
84 for &(rr, rp) in &right {
85 average += Ratio::new((lr / rr) as i128, 1) * (lp * rp);
86 }
87 }
88 Some(average)
89 },
90 Binary::Multiply => binary!(left.average(), right.average(), checked_mul),
91 Binary::Subtract => binary!(left.average(), right.average(), checked_sub),
92 },
93 Expression::Constant(constant) => Some(Ratio::new(constant as i128, 1)),
94 Expression::Dice(dice, reroll, fold) => Some(dice.average(reroll, fold)),
95 Expression::Die(die, reroll) => Some(die.average(reroll)),
96 }
97 }
98
99 pub fn results(&self) -> Option<Vec<i32>> {
101 match *self {
102 Expression::Binary(binary, ref left, ref right) => {
103 let left = try_opt!(left.results());
104 let right = try_opt!(right.results());
105 let mut results = HashSet::new();
106 for lr in left {
107 for rr in &right {
108 results.insert(try_opt!(binary.combine(lr, *rr)));
109 }
110 }
111 let mut results = results.into_iter().collect::<Vec<_>>();
112 results.sort();
113 Some(results)
114 },
115 Expression::Constant(constant) => Some(vec![constant as i32]),
116 Expression::Dice(dice, _, fold) => Some(dice.results(fold).collect()),
117 Expression::Die(die, _) => Some(die.results().collect()),
118 }
119 }
120
121 pub fn probabilities(&self) -> Option<Vec<(i32, Ratio)>> {
123 match *self {
124 Expression::Binary(binary, ref left, ref right) => {
125 let left = try_opt!(left.probabilities());
126 let right = try_opt!(right.probabilities());
127 let mut probabilities = try_opt!(self.results()).into_iter().map(|r| {
128 (r, Ratio::zero())
129 }).collect::<HashMap<_, _>>();
130 for (lr, lp) in left {
131 for &(rr, rp) in &right {
132 let result = try_opt!(binary.combine(lr, rr));
133 *probabilities.get_mut(&result).unwrap() += lp * rp;
134 }
135 }
136 let mut probabilities = probabilities.into_iter().collect::<Vec<_>>();
137 probabilities.sort();
138 Some(probabilities)
139 },
140 Expression::Constant(constant) => Some(vec![(constant as i32, Ratio::new(1, 0))]),
141 Expression::Dice(dice, reroll, fold) => Some(dice.probabilities(reroll, fold)),
142 Expression::Die(die, reroll) => Some(die.probabilities(reroll)),
143 }
144 }
145}