fuchsia_triage/metrics/
arithmetic.rs
1use super::{MathFunction, MetricValue, Problem};
6
7enum PromotedOperands {
8 Float(Vec<f64>),
9 Int(Vec<i64>),
10}
11
12pub fn calculate(function: &MathFunction, operands: &[MetricValue]) -> MetricValue {
15 match function {
18 MathFunction::Min | MathFunction::Max if !operands.is_empty() => {}
19 MathFunction::Min | MathFunction::Max => {
20 return super::syntax_error("No operands in math expression");
21 }
22 MathFunction::Abs if operands.len() == 1 => {}
23 MathFunction::Abs => return super::syntax_error("Abs requires exactly one operand."),
24 _ if operands.len() == 2 => {}
25 _ => return super::internal_bug("Internal bug. Function needs 2 arguments."),
26 }
27 let operands = match promote_type(operands) {
28 Ok(operands) => operands,
29 Err(value) => return value,
30 };
31 match operands {
32 PromotedOperands::Float(operands) => MetricValue::Float(match function {
33 MathFunction::Add => operands[0] + operands[1],
34 MathFunction::Sub => operands[0] - operands[1],
35 MathFunction::Mul => operands[0] * operands[1],
36 MathFunction::FloatDiv | MathFunction::IntDiv if operands[1] == 0.0 => {
37 return super::value_error("Division by zero")
38 }
39 MathFunction::FloatDivChecked | MathFunction::IntDivChecked if operands[1] == 0.0 => {
40 return MetricValue::Problem(Problem::Ignore(vec![Problem::ValueError(
41 "Division by zero".to_string(),
42 )]))
43 }
44 MathFunction::FloatDiv | MathFunction::FloatDivChecked => operands[0] / operands[1],
45 MathFunction::IntDiv | MathFunction::IntDivChecked => {
46 return match super::safe_float_to_int(operands[0] / operands[1]) {
47 Some(int) => MetricValue::Int(int),
48 None => super::value_error("Non-numeric division result"),
49 }
50 }
51 MathFunction::Greater => return MetricValue::Bool(operands[0] > operands[1]),
52 MathFunction::Less => return MetricValue::Bool(operands[0] < operands[1]),
53 MathFunction::GreaterEq => return MetricValue::Bool(operands[0] >= operands[1]),
54 MathFunction::LessEq => return MetricValue::Bool(operands[0] <= operands[1]),
55 MathFunction::Min => fold(operands, &f64::min),
56 MathFunction::Max => fold(operands, &f64::max),
57 MathFunction::Abs => operands[0].abs(),
58 }),
59 PromotedOperands::Int(operands) => MetricValue::Int(match function {
60 MathFunction::Add => operands[0] + operands[1],
61 MathFunction::Sub => operands[0] - operands[1],
62 MathFunction::Mul => operands[0] * operands[1],
63 MathFunction::FloatDiv | MathFunction::IntDiv if operands[1] == 0 => {
64 return super::value_error("Division by zero")
65 }
66 MathFunction::FloatDivChecked | MathFunction::IntDivChecked if operands[1] == 0 => {
67 return MetricValue::Problem(Problem::Ignore(vec![Problem::ValueError(
68 "Division by zero".to_string(),
69 )]))
70 }
71 MathFunction::FloatDiv | MathFunction::FloatDivChecked => {
72 return MetricValue::Float(operands[0] as f64 / operands[1] as f64)
73 }
74 MathFunction::IntDiv | MathFunction::IntDivChecked => operands[0] / operands[1],
75 MathFunction::Greater => return MetricValue::Bool(operands[0] > operands[1]),
76 MathFunction::Less => return MetricValue::Bool(operands[0] < operands[1]),
77 MathFunction::GreaterEq => return MetricValue::Bool(operands[0] >= operands[1]),
78 MathFunction::LessEq => return MetricValue::Bool(operands[0] <= operands[1]),
79 MathFunction::Min => fold(operands, &i64::min),
80 MathFunction::Max => fold(operands, &i64::max),
81 MathFunction::Abs => operands[0].abs(),
82 }),
83 }
84}
85
86fn fold<T: num_traits::Num + Copy>(operands: Vec<T>, function: &dyn (Fn(T, T) -> T)) -> T {
87 let mut iter = operands.iter();
88 let mut result = *iter.next().unwrap(); loop {
90 match iter.next() {
91 Some(next) => result = function(result, *next),
92 None => return result,
93 }
94 }
95}
96
97fn promote_type(operands: &[MetricValue]) -> Result<PromotedOperands, MetricValue> {
98 let mut int_vec = Vec::with_capacity(operands.len());
99 let mut float_vec = Vec::with_capacity(operands.len());
100 let mut error_vec = Vec::with_capacity(operands.len());
101 let mut non_numeric_error = None;
102 for o in operands.iter() {
103 match super::unwrap_for_math(o) {
104 MetricValue::Int(value) => {
105 int_vec.push(*value);
106 float_vec.push(*value as f64);
107 }
108 MetricValue::Float(value) => {
109 float_vec.push(*value);
110 }
111 MetricValue::Problem(problem) => {
112 error_vec.push(problem);
113 }
114 bad_type => {
115 non_numeric_error = Some(Problem::Missing(format!("{} not numeric", bad_type)));
116 }
117 }
118 }
119 if int_vec.len() == operands.len() {
120 return Ok(PromotedOperands::Int(int_vec));
121 }
122 if float_vec.len() == operands.len() {
123 return Ok(PromotedOperands::Float(float_vec));
124 }
125 if let Some(ref err) = non_numeric_error {
126 error_vec.push(err);
127 }
128 Err(MetricValue::Problem(super::MetricState::important_problem(error_vec)))
129}
130
131