1#![doc = include_str!("../README.md")]
2
3mod rendering;
4
5use std::{error::Error, io, iter};
6
7pub struct SumOfProducts {
9 pub products: Vec<f64>,
11 pub sum: f64,
13}
14
15const EQ: &str = " = ";
16const SEP: &str = "----";
17
18#[must_use]
20pub fn compute(parsed: &[Vec<f64>]) -> SumOfProducts {
21 let products: Vec<f64> = parsed
22 .iter()
23 .map(|values| values.iter().product())
24 .collect();
25 let sum = products.iter().sum();
26 SumOfProducts { products, sum }
27}
28
29pub fn parse<I: Iterator<Item = io::Result<String>>>(
35 lines: I,
36) -> Result<Vec<Vec<f64>>, Box<dyn Error>> {
37 lines
38 .map(|line| -> Result<Vec<f64>, _> {
39 let values: Result<Vec<f64>, _> =
41 line?.split_ascii_whitespace().map(str::parse).collect();
42 Ok(values?)
43 })
44 .filter(|result| !result.as_ref().is_ok_and(Vec::is_empty))
46 .collect()
47}
48
49#[must_use]
51pub fn render(output: SumOfProducts, input: &[Vec<f64>]) -> String {
52 let SumOfProducts { products, sum } = output;
53 let formulas = rendering::render_formulas(input);
54 let sum_width = sum.to_string().len();
55 let width = rendering::formula_width(&formulas) + EQ.len() + sum_width;
56 formulas
57 .iter()
58 .zip(products.iter())
59 .map(|(formula, product)| format!("{formula}{EQ}{product:>sum_width$}"))
60 .chain(iter::once(format!("{SEP:>width$}\n{sum:>width$}")))
61 .collect::<Vec<_>>()
62 .join("\n")
63}
64
65#[cfg(test)]
66mod tests {
67 use super::*;
68
69 #[test]
70 fn test_compute() {
71 let input = vec![vec![2.0, 3.0], vec![4.0, 5.0]];
72 let result = compute(&input);
73 assert_eq!(result.products, vec![6.0, 20.0]);
74 assert_eq!(result.sum, 26.0);
75 }
76
77 #[test]
78 fn test_parse() {
79 let lines = vec![Ok("2 3".to_string()), Ok("4 5".to_string())];
80 let result = parse(lines.into_iter()).unwrap();
81 assert_eq!(result, vec![vec![2.0, 3.0], vec![4.0, 5.0]]);
82 }
83
84 #[test]
85 fn test_parse_skips_empty_lines() {
86 let lines = vec![Ok("2 3".to_string()), Ok("".to_string()), Ok("4 5".to_string())];
87 let result = parse(lines.into_iter()).unwrap();
88 assert_eq!(result, vec![vec![2.0, 3.0], vec![4.0, 5.0]]);
89 }
90}