scales/
logarithmic.rs

1use super::convert::*;
2use super::linear::*;
3use super::*;
4
5#[derive(Debug, Clone, PartialEq)]
6pub struct LogarithmicScale<N> {
7    min: N,
8    max: N,
9    linear_delegate: LinearScale<N>,
10}
11
12impl<N> LogarithmicScale<N>
13where
14    N: Sub<Output = N> + Add<Output = N> + PartialOrd + FromFloat<f64> + ToFloat<f64> + Clone,
15{
16    pub fn new(min: N, max: N) -> LogarithmicScale<N> {
17        LogarithmicScale {
18            min: min.clone(),
19            max: max.clone(),
20            linear_delegate: LinearScale::new(apply_to(min, f64::log10), apply_to(max, f64::log10)),
21        }
22    }
23    pub fn inverted(min: N, max: N) -> LogarithmicScale<N> {
24        LogarithmicScale {
25            min: min.clone(),
26            max: max.clone(),
27            linear_delegate: LinearScale::inverted(
28                apply_to(min, f64::log10),
29                apply_to(max, f64::log10),
30            ),
31        }
32    }
33}
34
35impl<N> Scale<N> for LogarithmicScale<N>
36where
37    N: Sub<Output = N> + Add<Output = N> + PartialOrd + FromFloat<f64> + ToFloat<f64> + Clone,
38{
39    fn to_relative(&self, absolute: N) -> f64 {
40        let abs_log = apply_to(absolute, f64::log10);
41        self.linear_delegate.to_relative(abs_log)
42    }
43
44    fn to_absolute(&self, relative: f64) -> N {
45        let abs_log = self.linear_delegate.to_absolute(relative);
46        apply_to(abs_log, |f| 10f64.powf(f))
47    }
48
49    fn max(&self) -> N {
50        self.max.clone()
51    }
52
53    fn min(&self) -> N {
54        self.min.clone()
55    }
56}
57
58fn apply_to<N>(n: N, fun: impl Fn(f64) -> f64) -> N
59where
60    N: ToFloat<f64> + FromFloat<f64>,
61{
62    N::from_float(fun(n.to_float()))
63}
64
65#[cfg(test)]
66mod tests {
67
68    use crate::prelude::*;
69    use assert_approx_eq::*;
70    use std::time::*;
71
72    #[test]
73    fn test_log() {
74        let scale: LogarithmicScale<f64> = LogarithmicScale::new(10.0, 10240.0);
75        assert_approx_eq!(scale.to_absolute(0.0), 10.0);
76        assert_approx_eq!(scale.to_absolute(0.1), 20.0);
77        assert_approx_eq!(scale.to_absolute(0.2), 40.0);
78        assert_approx_eq!(scale.to_absolute(0.3), 80.0);
79        assert_approx_eq!(scale.to_absolute(0.4), 160.0);
80        assert_approx_eq!(scale.to_absolute(0.5), 320.0);
81        assert_approx_eq!(scale.to_absolute(0.6), 640.0);
82        assert_approx_eq!(scale.to_absolute(0.7), 1280.0);
83        assert_approx_eq!(scale.to_absolute(0.8), 2560.0);
84        assert_approx_eq!(scale.to_absolute(0.9), 5120.0);
85        assert_approx_eq!(scale.to_absolute(1.0), 10240.0);
86
87        assert_approx_eq!(scale.to_relative(10.0), 0.0);
88        assert_approx_eq!(scale.to_relative(20.0), 0.1);
89        assert_approx_eq!(scale.to_relative(40.0), 0.2);
90        assert_approx_eq!(scale.to_relative(80.0), 0.3);
91        assert_approx_eq!(scale.to_relative(160.0), 0.4);
92        assert_approx_eq!(scale.to_relative(320.0), 0.5);
93        assert_approx_eq!(scale.to_relative(640.0), 0.6);
94        assert_approx_eq!(scale.to_relative(1280.0), 0.7);
95        assert_approx_eq!(scale.to_relative(2560.0), 0.8);
96        assert_approx_eq!(scale.to_relative(5120.0), 0.9);
97        assert_approx_eq!(scale.to_relative(10240.0), 1.0);
98    }
99
100    #[test]
101    fn test_log_inverted() {
102        let scale: LogarithmicScale<f64> = LogarithmicScale::inverted(10.0, 10240.0);
103        assert_approx_eq!(scale.to_absolute(0.0), 10240.0);
104        assert_approx_eq!(scale.to_absolute(0.1), 5120.0);
105        assert_approx_eq!(scale.to_absolute(0.2), 2560.0);
106        assert_approx_eq!(scale.to_absolute(0.3), 1280.0);
107        assert_approx_eq!(scale.to_absolute(0.4), 640.0);
108        assert_approx_eq!(scale.to_absolute(0.5), 320.0);
109        assert_approx_eq!(scale.to_absolute(0.6), 160.0);
110        assert_approx_eq!(scale.to_absolute(0.7), 80.0);
111        assert_approx_eq!(scale.to_absolute(0.8), 40.0);
112        assert_approx_eq!(scale.to_absolute(0.9), 20.0);
113        assert_approx_eq!(scale.to_absolute(1.0), 10.0);
114
115        assert_approx_eq!(scale.to_relative(10.0), 1.0);
116        assert_approx_eq!(scale.to_relative(20.0), 0.9);
117        assert_approx_eq!(scale.to_relative(40.0), 0.8);
118        assert_approx_eq!(scale.to_relative(80.0), 0.7);
119        assert_approx_eq!(scale.to_relative(160.0), 0.6);
120        assert_approx_eq!(scale.to_relative(320.0), 0.5);
121        assert_approx_eq!(scale.to_relative(640.0), 0.4);
122        assert_approx_eq!(scale.to_relative(1280.0), 0.3);
123        assert_approx_eq!(scale.to_relative(2560.0), 0.2);
124        assert_approx_eq!(scale.to_relative(5120.0), 0.1);
125        assert_approx_eq!(scale.to_relative(10240.0), 0.0);
126    }
127
128    #[test]
129    fn test_log_out_of_bounds() {
130        let scale: LogarithmicScale<f64> = LogarithmicScale::new(10.0, 10240.0);
131        assert_approx_eq!(scale.to_absolute(-0.1), 5.0);
132        assert_approx_eq!(scale.to_absolute(-1.0), 0.0097656);
133        assert_approx_eq!(scale.to_absolute(-2.0), 0.0000095);
134        assert_approx_eq!(scale.to_absolute(1.1), 20480.0);
135
136        assert_approx_eq!(scale.to_relative(1.0), -0.3321928);
137        assert!(scale.to_relative(-1.0).is_nan());
138
139        let neg_inf = scale.to_relative(0.0);
140        assert!(neg_inf.is_infinite() && neg_inf.is_sign_negative());
141
142        assert_approx_eq!(scale.to_clamped_absolute(0.0), 10.0);
143        assert_approx_eq!(scale.to_clamped_absolute(-1.0), 10.0);
144        assert_approx_eq!(scale.to_clamped_absolute(1.1), 10240.0);
145
146        assert_approx_eq!(scale.to_clamped_relative(1.0), 0.0);
147        assert_approx_eq!(scale.to_clamped_relative(0.0), 0.0);
148        assert_approx_eq!(scale.to_clamped_relative(-1.0), 0.0);
149        assert_approx_eq!(scale.to_clamped_relative(20240.0), 1.0);
150    }
151
152    // #[test]
153    fn _benchmark() {
154        let loops = 100_000_000;
155
156        let upper = loops as f64;
157        let step = 1.0 / upper;
158
159        // reference run
160
161        let mut results = Vec::new();
162        let min = 10f64.log10();
163        let max = 1_000f64.log10();
164        let range = max - min;
165
166        let start = Instant::now();
167        for i in 0..loops {
168            let relative = i as f64 * step;
169            results.push(10f64.powf(min + relative * range));
170        }
171        let duration = start.elapsed();
172
173        let sample: Vec<&f64> = results.iter().take(10).collect();
174
175        eprintln!("{}", duration.as_millis());
176        eprintln!("{:?}", sample);
177
178        // actual run
179
180        let mut results = Vec::new();
181        let scale: LogarithmicScale<f64> = LogarithmicScale::new(10.0, 1_000.0);
182
183        let start = Instant::now();
184        for i in 0..loops {
185            let relative = i as f64 * step;
186            let result = scale.to_absolute(relative);
187            results.push(result);
188        }
189        let duration = start.elapsed();
190
191        let sample: Vec<&f64> = results.iter().take(10).collect();
192
193        eprintln!("{}", duration.as_millis());
194        eprintln!("{:?}", sample);
195    }
196}