1 use std::convert::TryFrom;
2 use std::ops::Range;
3 
4 use crate::coord::ranged1d::{
5     AsRangedCoord, DefaultFormatting, DiscreteRanged, KeyPointHint, NoDefaultFormatting, Ranged,
6     ReversibleRanged, ValueFormatter,
7 };
8 
9 macro_rules! impl_discrete_trait {
10     ($name:ident) => {
11         impl DiscreteRanged for $name {
12             fn size(&self) -> usize {
13                 if &self.1 < &self.0 {
14                     return 0;
15                 }
16                 let values = self.1 - self.0;
17                 (values + 1) as usize
18             }
19 
20             fn index_of(&self, value: &Self::ValueType) -> Option<usize> {
21                 if value < &self.0 {
22                     return None;
23                 }
24                 let ret = value - self.0;
25                 Some(ret as usize)
26             }
27 
28             fn from_index(&self, index: usize) -> Option<Self::ValueType> {
29                 if let Ok(index) = Self::ValueType::try_from(index) {
30                     return Some(self.0 + index);
31                 }
32                 None
33             }
34         }
35     };
36 }
37 
38 macro_rules! impl_ranged_type_trait {
39     ($value:ty, $coord:ident) => {
40         impl AsRangedCoord for Range<$value> {
41             type CoordDescType = $coord;
42             type Value = $value;
43         }
44     };
45 }
46 macro_rules! impl_reverse_mapping_trait {
47     ($type:ty, $name: ident) => {
48         impl ReversibleRanged for $name {
49             fn unmap(&self, p: i32, (min, max): (i32, i32)) -> Option<$type> {
50                 if p < min.min(max) || p > max.max(min) || min == max {
51                     return None;
52                 }
53 
54                 let logical_offset = f64::from(p - min) / f64::from(max - min);
55 
56                 return Some(((self.1 - self.0) as f64 * logical_offset + self.0 as f64) as $type);
57             }
58         }
59     };
60 }
61 macro_rules! make_numeric_coord {
62     ($type:ty, $name:ident, $key_points:ident, $doc: expr, $fmt: ident) => {
63         #[doc = $doc]
64         #[derive(Clone)]
65         pub struct $name($type, $type);
66         impl From<Range<$type>> for $name {
67             fn from(range: Range<$type>) -> Self {
68                 return $name(range.start, range.end);
69             }
70         }
71         impl Ranged for $name {
72             type FormatOption = $fmt;
73             type ValueType = $type;
74             #[allow(clippy::float_cmp)]
75             fn map(&self, v: &$type, limit: (i32, i32)) -> i32 {
76                 // Corner case: If we have a range that have only one value,
77                 // then we just assign everything to the only point
78                 if self.1 == self.0 {
79                     return (limit.1 - limit.0) / 2;
80                 }
81 
82                 let logic_length = (*v as f64 - self.0 as f64) / (self.1 as f64 - self.0 as f64);
83 
84                 let actual_length = limit.1 - limit.0;
85 
86                 if actual_length == 0 {
87                     return limit.1;
88                 }
89 
90                 return limit.0 + (actual_length as f64 * logic_length + 1e-3).floor() as i32;
91             }
92             fn key_points<Hint: KeyPointHint>(&self, hint: Hint) -> Vec<$type> {
93                 $key_points((self.0, self.1), hint.max_num_points())
94             }
95             fn range(&self) -> Range<$type> {
96                 return self.0..self.1;
97             }
98         }
99     };
100     ($type:ty, $name:ident, $key_points:ident, $doc: expr) => {
101         make_numeric_coord!($type, $name, $key_points, $doc, DefaultFormatting);
102     };
103 }
104 
105 macro_rules! gen_key_points_comp {
106     (float, $name:ident, $type:ty) => {
107         fn $name(range: ($type, $type), max_points: usize) -> Vec<$type> {
108             if max_points == 0 {
109                 return vec![];
110             }
111 
112             let range = (range.0 as f64, range.1 as f64);
113 
114             assert!(!(range.0.is_nan() || range.1.is_nan()));
115 
116             if range.0 == range.1 {
117                 return vec![range.0 as $type];
118             }
119 
120             let mut scale = (10f64).powf((range.1 - range.0).log(10.0).floor());
121             let mut digits = -(range.1 - range.0).log(10.0).floor() as i32 + 1;
122             fn rem_euclid(a: f64, b: f64) -> f64 {
123                 if b > 0.0 {
124                     a - (a / b).floor() * b
125                 } else {
126                     a - (a / b).ceil() * b
127                 }
128             }
129 
130             // At this point we need to make sure that the loop invariant:
131             // The scale must yield number of points than requested
132             if 1 + ((range.1 - range.0) / scale).floor() as usize > max_points {
133                 scale *= 10.0;
134             }
135 
136             'outer: loop {
137                 let old_scale = scale;
138                 for nxt in [2.0, 5.0, 10.0].iter() {
139                     let new_left = range.0 + scale / nxt - rem_euclid(range.0, scale / nxt);
140                     let new_right = range.1 - rem_euclid(range.1, scale / nxt);
141 
142                     let npoints = 1 + ((new_right - new_left) / old_scale * nxt) as usize;
143 
144                     if npoints > max_points {
145                         break 'outer;
146                     }
147 
148                     scale = old_scale / nxt;
149                 }
150                 scale = old_scale / 10.0;
151                 if scale < 1.0 {
152                     digits += 1;
153                 }
154             }
155 
156             let mut ret = vec![];
157             let mut left = range.0 + scale - rem_euclid(range.0, scale);
158             let right = range.1 - rem_euclid(range.1, scale);
159             while left <= right {
160                 let size = (10f64).powf(digits as f64 + 1.0);
161                 let new_left = (left * size).abs() + 1e-3;
162                 if left < 0.0 {
163                     left = -new_left.round() / size;
164                 } else {
165                     left = new_left.round() / size;
166                 }
167                 ret.push(left as $type);
168                 left += scale;
169             }
170             return ret;
171         }
172     };
173     (integer, $name:ident, $type:ty) => {
174         fn $name(range: ($type, $type), max_points: usize) -> Vec<$type> {
175             let mut scale: $type = 1;
176             let range = (range.0.min(range.1), range.0.max(range.1));
177             'outer: while (range.1 - range.0 + scale - 1) as usize / (scale as usize) > max_points {
178                 let next_scale = scale * 10;
179                 for new_scale in [scale * 2, scale * 5, scale * 10].iter() {
180                     scale = *new_scale;
181                     if (range.1 - range.0 + *new_scale - 1) as usize / (*new_scale as usize)
182                         < max_points
183                     {
184                         break 'outer;
185                     }
186                 }
187                 scale = next_scale;
188             }
189 
190             let (mut left, right) = (
191                 range.0 + (scale - range.0 % scale) % scale,
192                 range.1 - range.1 % scale,
193             );
194 
195             let mut ret = vec![];
196             while left <= right {
197                 ret.push(left as $type);
198                 left += scale;
199             }
200 
201             return ret;
202         }
203     };
204 }
205 
206 gen_key_points_comp!(float, compute_f32_key_points, f32);
207 gen_key_points_comp!(float, compute_f64_key_points, f64);
208 gen_key_points_comp!(integer, compute_i32_key_points, i32);
209 gen_key_points_comp!(integer, compute_u32_key_points, u32);
210 gen_key_points_comp!(integer, compute_i64_key_points, i64);
211 gen_key_points_comp!(integer, compute_u64_key_points, u64);
212 gen_key_points_comp!(integer, compute_i128_key_points, i128);
213 gen_key_points_comp!(integer, compute_u128_key_points, u128);
214 gen_key_points_comp!(integer, compute_isize_key_points, isize);
215 gen_key_points_comp!(integer, compute_usize_key_points, usize);
216 
217 make_numeric_coord!(
218     f32,
219     RangedCoordf32,
220     compute_f32_key_points,
221     "The ranged coordinate for type f32",
222     NoDefaultFormatting
223 );
224 impl_reverse_mapping_trait!(f32, RangedCoordf32);
225 impl ValueFormatter<f32> for RangedCoordf32 {
format(value: &f32) -> String226     fn format(value: &f32) -> String {
227         crate::data::float::FloatPrettyPrinter {
228             allow_scientific: false,
229             min_decimal: 1,
230             max_decimal: 5,
231         }
232         .print(*value as f64)
233     }
234 }
235 make_numeric_coord!(
236     f64,
237     RangedCoordf64,
238     compute_f64_key_points,
239     "The ranged coordinate for type f64",
240     NoDefaultFormatting
241 );
242 impl_reverse_mapping_trait!(f64, RangedCoordf64);
243 impl ValueFormatter<f64> for RangedCoordf64 {
format(value: &f64) -> String244     fn format(value: &f64) -> String {
245         crate::data::float::FloatPrettyPrinter {
246             allow_scientific: false,
247             min_decimal: 1,
248             max_decimal: 5,
249         }
250         .print(*value)
251     }
252 }
253 make_numeric_coord!(
254     u32,
255     RangedCoordu32,
256     compute_u32_key_points,
257     "The ranged coordinate for type u32"
258 );
259 make_numeric_coord!(
260     i32,
261     RangedCoordi32,
262     compute_i32_key_points,
263     "The ranged coordinate for type i32"
264 );
265 make_numeric_coord!(
266     u64,
267     RangedCoordu64,
268     compute_u64_key_points,
269     "The ranged coordinate for type u64"
270 );
271 make_numeric_coord!(
272     i64,
273     RangedCoordi64,
274     compute_i64_key_points,
275     "The ranged coordinate for type i64"
276 );
277 make_numeric_coord!(
278     u128,
279     RangedCoordu128,
280     compute_u128_key_points,
281     "The ranged coordinate for type u128"
282 );
283 make_numeric_coord!(
284     i128,
285     RangedCoordi128,
286     compute_i128_key_points,
287     "The ranged coordinate for type i128"
288 );
289 make_numeric_coord!(
290     usize,
291     RangedCoordusize,
292     compute_usize_key_points,
293     "The ranged coordinate for type usize"
294 );
295 make_numeric_coord!(
296     isize,
297     RangedCoordisize,
298     compute_isize_key_points,
299     "The ranged coordinate for type isize"
300 );
301 
302 impl_discrete_trait!(RangedCoordu32);
303 impl_discrete_trait!(RangedCoordi32);
304 impl_discrete_trait!(RangedCoordu64);
305 impl_discrete_trait!(RangedCoordi64);
306 impl_discrete_trait!(RangedCoordu128);
307 impl_discrete_trait!(RangedCoordi128);
308 impl_discrete_trait!(RangedCoordusize);
309 impl_discrete_trait!(RangedCoordisize);
310 
311 impl_ranged_type_trait!(f32, RangedCoordf32);
312 impl_ranged_type_trait!(f64, RangedCoordf64);
313 impl_ranged_type_trait!(i32, RangedCoordi32);
314 impl_ranged_type_trait!(u32, RangedCoordu32);
315 impl_ranged_type_trait!(i64, RangedCoordi64);
316 impl_ranged_type_trait!(u64, RangedCoordu64);
317 impl_ranged_type_trait!(i128, RangedCoordi128);
318 impl_ranged_type_trait!(u128, RangedCoordu128);
319 impl_ranged_type_trait!(isize, RangedCoordisize);
320 impl_ranged_type_trait!(usize, RangedCoordusize);
321 
322 #[cfg(test)]
323 mod test {
324     use super::*;
325     #[test]
test_key_points()326     fn test_key_points() {
327         let kp = compute_i32_key_points((0, 999), 28);
328 
329         assert!(kp.len() > 0);
330         assert!(kp.len() <= 28);
331 
332         let kp = compute_f64_key_points((-1.2, 1.2), 1);
333         assert!(kp.len() == 1);
334 
335         let kp = compute_f64_key_points((-1.2, 1.2), 0);
336         assert!(kp.len() == 0);
337     }
338 
339     #[test]
test_linear_coord_map()340     fn test_linear_coord_map() {
341         let coord: RangedCoordu32 = (0..20).into();
342         assert_eq!(coord.key_points(11).len(), 11);
343         assert_eq!(coord.key_points(11)[0], 0);
344         assert_eq!(coord.key_points(11)[10], 20);
345         assert_eq!(coord.map(&5, (0, 100)), 25);
346 
347         let coord: RangedCoordf32 = (0f32..20f32).into();
348         assert_eq!(coord.map(&5.0, (0, 100)), 25);
349     }
350 
351     #[test]
test_linear_coord_system()352     fn test_linear_coord_system() {
353         let _coord =
354             crate::coord::ranged2d::cartesian::Cartesian2d::<RangedCoordu32, RangedCoordu32>::new(
355                 0..10,
356                 0..10,
357                 (0..1024, 0..768),
358             );
359     }
360 
361     #[test]
test_coord_unmap()362     fn test_coord_unmap() {
363         let coord: RangedCoordu32 = (0..20).into();
364         let pos = coord.map(&5, (1000, 2000));
365         let value = coord.unmap(pos, (1000, 2000));
366         assert_eq!(value, Some(5));
367     }
368 
369     #[test]
test_zero_sized_coord_not_hang()370     fn test_zero_sized_coord_not_hang() {
371         let coord: RangedCoordf32 = (0.0..0.0).into();
372         let _points = coord.key_points(10);
373     }
374 
375     #[test]
test_small_coord()376     fn test_small_coord() {
377         let coord: RangedCoordf64 = (0.0..1e-25).into();
378         let points = coord.key_points(10);
379         assert!(points.len() > 0);
380     }
381 }
382