1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
use crate::calc_engine::connector::Connector;

use super::{CalcValue, CalcVertex, Value};
use logisheets_parser::ast;

pub fn calc_average<C>(args: Vec<CalcVertex>, fetcher: &mut C) -> CalcVertex
where
    C: Connector,
{
    calc_mean(
        args,
        fetcher,
        &|a, b| a + b,
        &|sum, cnt| sum / cnt as f64,
        0.,
    )
}

pub fn calc_geomean<C>(args: Vec<CalcVertex>, fetcher: &mut C) -> CalcVertex
where
    C: Connector,
{
    calc_mean(
        args,
        fetcher,
        &|a, b| a * b,
        &|sum, cnt| sum.powf(1. / cnt as f64),
        1.,
    )
}

pub fn calc_harmean<C>(args: Vec<CalcVertex>, fetcher: &mut C) -> CalcVertex
where
    C: Connector,
{
    calc_mean(
        args,
        fetcher,
        &|a, b| a + 1. / b,
        &|sum, cnt| sum / cnt as f64,
        0.,
    )
}

fn calc_mean<C, F, A>(
    args: Vec<CalcVertex>,
    fetcher: &mut C,
    sum: &F,
    average: &A,
    init_value: f64,
) -> CalcVertex
where
    C: Connector,
    F: Fn(f64, f64) -> f64,
    A: Fn(f64, u32) -> f64,
{
    let result = args
        .into_iter()
        .map(|arg| fetcher.get_calc_value(arg))
        .try_fold((init_value, 0_u32), |prev, this| {
            let (s, cnt) = count_and_sum_value(this, &|a, b| a + b)?;
            Ok((sum(prev.0, s), prev.1 + cnt))
        });
    match result {
        Ok((s, c)) => {
            if c != 0 {
                let avg = average(s, c);
                CalcVertex::from_number(avg)
            } else {
                CalcVertex::from_error(ast::Error::Div0)
            }
        }
        Err(e) => CalcVertex::from_error(e),
    }
}

fn count_and_sum_value<F>(value: CalcValue, method: &F) -> Result<(f64, u32), ast::Error>
where
    F: Fn(f64, f64) -> f64,
{
    match value {
        CalcValue::Scalar(s) => match s {
            Value::Number(n) => Ok((n, 1_u32)),
            Value::Error(e) => Err(e),
            _ => Ok((0_f64, 0_u32)),
        },
        CalcValue::Range(r) => r
            .into_iter()
            .try_fold((0_f64, 0_u32), |prev, this| match this {
                Value::Number(n) => Ok((method(prev.0, n), prev.1 + 1)),
                Value::Error(e) => Err(e),
                _ => Ok(prev),
            }),
        CalcValue::Cube(cube) => {
            cube.into_iter()
                .try_fold((0_f64, 0_16), |prev, this| match this {
                    Value::Number(n) => Ok((method(prev.0, n), prev.1 + 1)),
                    Value::Error(e) => Err(e),
                    _ => Ok(prev),
                })
        }
        CalcValue::Union(values) => values
            .into_iter()
            .map(|v| count_and_sum_value(*v, method))
            .try_fold((0_f64, 0_u32), |prev, this| {
                let this = this?;
                Ok((method(prev.0, this.0), prev.1 + this.1))
            }),
    }
}