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 fn _benchmark() {
154 let loops = 100_000_000;
155
156 let upper = loops as f64;
157 let step = 1.0 / upper;
158
159 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 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}