• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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                 if logic_length.is_infinite() {
91                     if logic_length.is_sign_positive() {
92                         return limit.1;
93                     } else {
94                         return limit.0;
95                     }
96                 }
97 
98                 if actual_length > 0 {
99                     return limit.0 + (actual_length as f64 * logic_length + 1e-3).floor() as i32;
100                 } else {
101                     return limit.0 + (actual_length as f64 * logic_length - 1e-3).ceil() as i32;
102                 }
103             }
104             fn key_points<Hint: KeyPointHint>(&self, hint: Hint) -> Vec<$type> {
105                 $key_points((self.0, self.1), hint.max_num_points())
106             }
107             fn range(&self) -> Range<$type> {
108                 return self.0..self.1;
109             }
110         }
111     };
112     ($type:ty, $name:ident, $key_points:ident, $doc: expr) => {
113         make_numeric_coord!($type, $name, $key_points, $doc, DefaultFormatting);
114     };
115 }
116 
117 macro_rules! gen_key_points_comp {
118     (float, $name:ident, $type:ty) => {
119         fn $name(range: ($type, $type), max_points: usize) -> Vec<$type> {
120             if max_points == 0 {
121                 return vec![];
122             }
123 
124             let range = (range.0.min(range.1) as f64, range.1.max(range.0) as f64);
125 
126             assert!(!(range.0.is_nan() || range.1.is_nan()));
127 
128             if (range.0 - range.1).abs() < f64::EPSILON {
129                 return vec![range.0 as $type];
130             }
131 
132             let mut scale = (10f64).powf((range.1 - range.0).log(10.0).floor());
133             // The value granularity controls how we round the values.
134             // To avoid generating key points like 1.00000000001, we round to the nearest multiple of the
135             // value granularity.
136             // By default, we make the granularity as the 1/10 of the scale.
137             let mut value_granularity = scale / 10.0;
138             fn rem_euclid(a: f64, b: f64) -> f64 {
139                 let ret = if b > 0.0 {
140                     a - (a / b).floor() * b
141                 } else {
142                     a - (a / b).ceil() * b
143                 };
144                 if (ret - b).abs() < f64::EPSILON {
145                     0.0
146                 } else {
147                     ret
148                 }
149             }
150 
151             // At this point we need to make sure that the loop invariant:
152             // The scale must yield number of points than requested
153             if 1 + ((range.1 - range.0) / scale).floor() as usize > max_points {
154                 scale *= 10.0;
155                 value_granularity *= 10.0;
156             }
157 
158             'outer: loop {
159                 let old_scale = scale;
160                 for nxt in [2.0, 5.0, 10.0].iter() {
161                     let mut new_left = range.0 - rem_euclid(range.0, old_scale / nxt);
162                     if new_left < range.0 {
163                         new_left += old_scale / nxt;
164                     }
165                     let new_right = range.1 - rem_euclid(range.1, old_scale / nxt);
166 
167                     let npoints = 1.0 + ((new_right - new_left) / old_scale * nxt);
168 
169                     if npoints.round() as usize > max_points {
170                         break 'outer;
171                     }
172 
173                     scale = old_scale / nxt;
174                 }
175                 scale = old_scale / 10.0;
176                 value_granularity /= 10.0;
177             }
178 
179             let mut ret = vec![];
180             // In some extreme cases, left might be too big, so that (left + scale) - left == 0 due to
181             // floating point error.
182             // In this case, we may loop forever. To avoid this, we need to use two variables to store
183             // the current left value. So we need keep a left_base and a left_relative.
184             let left = {
185                 let mut value = range.0 - rem_euclid(range.0, scale);
186                 if value < range.0 {
187                     value += scale;
188                 }
189                 value
190             };
191             let left_base = (left / value_granularity).floor() * value_granularity;
192             let mut left_relative = left - left_base;
193             let right = range.1 - rem_euclid(range.1, scale);
194             while (right - left_relative - left_base) >= -f64::EPSILON {
195                 let new_left_relative =
196                     (left_relative / value_granularity).round() * value_granularity;
197                 if new_left_relative < 0.0 {
198                     left_relative += value_granularity;
199                 }
200                 ret.push((left_relative + left_base) as $type);
201                 left_relative += scale;
202             }
203             return ret;
204         }
205     };
206     (integer, $name:ident, $type:ty) => {
207         fn $name(range: ($type, $type), max_points: usize) -> Vec<$type> {
208             let mut scale: $type = 1;
209             let range = (range.0.min(range.1), range.0.max(range.1));
210             let range_size = range.1 as f64 - range.0 as f64;
211             'outer: while (range_size / scale as f64).ceil() > max_points as f64 {
212                 let next_scale = scale * 10;
213                 for new_scale in [scale * 2, scale * 5, scale * 10].iter() {
214                     scale = *new_scale;
215                     if (range_size / *new_scale as f64).ceil() < max_points as f64 {
216                         break 'outer;
217                     }
218                 }
219                 scale = next_scale;
220             }
221 
222             let (mut left, right) = (
223                 range.0 + (scale - range.0 % scale) % scale,
224                 range.1 - range.1 % scale,
225             );
226 
227             let mut ret = vec![];
228             while left <= right {
229                 ret.push(left as $type);
230                 if left < right {
231                     left += scale;
232                 } else {
233                     break;
234                 }
235             }
236 
237             return ret;
238         }
239     };
240 }
241 
242 gen_key_points_comp!(float, compute_f32_key_points, f32);
243 gen_key_points_comp!(float, compute_f64_key_points, f64);
244 gen_key_points_comp!(integer, compute_i32_key_points, i32);
245 gen_key_points_comp!(integer, compute_u32_key_points, u32);
246 gen_key_points_comp!(integer, compute_i64_key_points, i64);
247 gen_key_points_comp!(integer, compute_u64_key_points, u64);
248 gen_key_points_comp!(integer, compute_i128_key_points, i128);
249 gen_key_points_comp!(integer, compute_u128_key_points, u128);
250 gen_key_points_comp!(integer, compute_isize_key_points, isize);
251 gen_key_points_comp!(integer, compute_usize_key_points, usize);
252 
253 make_numeric_coord!(
254     f32,
255     RangedCoordf32,
256     compute_f32_key_points,
257     "The ranged coordinate for type f32",
258     NoDefaultFormatting
259 );
260 impl_reverse_mapping_trait!(f32, RangedCoordf32);
261 impl ValueFormatter<f32> for RangedCoordf32 {
format(value: &f32) -> String262     fn format(value: &f32) -> String {
263         crate::data::float::FloatPrettyPrinter {
264             allow_scientific: false,
265             min_decimal: 1,
266             max_decimal: 5,
267         }
268         .print(*value as f64)
269     }
270 }
271 make_numeric_coord!(
272     f64,
273     RangedCoordf64,
274     compute_f64_key_points,
275     "The ranged coordinate for type f64",
276     NoDefaultFormatting
277 );
278 impl_reverse_mapping_trait!(f64, RangedCoordf64);
279 impl ValueFormatter<f64> for RangedCoordf64 {
format(value: &f64) -> String280     fn format(value: &f64) -> String {
281         crate::data::float::FloatPrettyPrinter {
282             allow_scientific: false,
283             min_decimal: 1,
284             max_decimal: 5,
285         }
286         .print(*value)
287     }
288 }
289 make_numeric_coord!(
290     u32,
291     RangedCoordu32,
292     compute_u32_key_points,
293     "The ranged coordinate for type u32"
294 );
295 make_numeric_coord!(
296     i32,
297     RangedCoordi32,
298     compute_i32_key_points,
299     "The ranged coordinate for type i32"
300 );
301 make_numeric_coord!(
302     u64,
303     RangedCoordu64,
304     compute_u64_key_points,
305     "The ranged coordinate for type u64"
306 );
307 make_numeric_coord!(
308     i64,
309     RangedCoordi64,
310     compute_i64_key_points,
311     "The ranged coordinate for type i64"
312 );
313 make_numeric_coord!(
314     u128,
315     RangedCoordu128,
316     compute_u128_key_points,
317     "The ranged coordinate for type u128"
318 );
319 make_numeric_coord!(
320     i128,
321     RangedCoordi128,
322     compute_i128_key_points,
323     "The ranged coordinate for type i128"
324 );
325 make_numeric_coord!(
326     usize,
327     RangedCoordusize,
328     compute_usize_key_points,
329     "The ranged coordinate for type usize"
330 );
331 make_numeric_coord!(
332     isize,
333     RangedCoordisize,
334     compute_isize_key_points,
335     "The ranged coordinate for type isize"
336 );
337 
338 impl_discrete_trait!(RangedCoordu32);
339 impl_discrete_trait!(RangedCoordi32);
340 impl_discrete_trait!(RangedCoordu64);
341 impl_discrete_trait!(RangedCoordi64);
342 impl_discrete_trait!(RangedCoordu128);
343 impl_discrete_trait!(RangedCoordi128);
344 impl_discrete_trait!(RangedCoordusize);
345 impl_discrete_trait!(RangedCoordisize);
346 
347 impl_ranged_type_trait!(f32, RangedCoordf32);
348 impl_ranged_type_trait!(f64, RangedCoordf64);
349 impl_ranged_type_trait!(i32, RangedCoordi32);
350 impl_ranged_type_trait!(u32, RangedCoordu32);
351 impl_ranged_type_trait!(i64, RangedCoordi64);
352 impl_ranged_type_trait!(u64, RangedCoordu64);
353 impl_ranged_type_trait!(i128, RangedCoordi128);
354 impl_ranged_type_trait!(u128, RangedCoordu128);
355 impl_ranged_type_trait!(isize, RangedCoordisize);
356 impl_ranged_type_trait!(usize, RangedCoordusize);
357 
358 #[cfg(test)]
359 mod test {
360     use super::*;
361     #[test]
test_key_points()362     fn test_key_points() {
363         let kp = compute_i32_key_points((0, 999), 28);
364 
365         assert!(!kp.is_empty());
366         assert!(kp.len() <= 28);
367 
368         let kp = compute_f64_key_points((-1.2, 1.2), 1);
369         assert!(kp.len() == 1);
370 
371         let kp = compute_f64_key_points((-1.2, 1.2), 0);
372         assert!(kp.is_empty());
373     }
374 
375     #[test]
test_linear_coord_map()376     fn test_linear_coord_map() {
377         let coord: RangedCoordu32 = (0..20).into();
378         assert_eq!(coord.key_points(11).len(), 11);
379         assert_eq!(coord.key_points(11)[0], 0);
380         assert_eq!(coord.key_points(11)[10], 20);
381         assert_eq!(coord.map(&5, (0, 100)), 25);
382 
383         let coord: RangedCoordf32 = (0f32..20f32).into();
384         assert_eq!(coord.map(&5.0, (0, 100)), 25);
385     }
386 
387     #[test]
test_linear_coord_system()388     fn test_linear_coord_system() {
389         let _coord =
390             crate::coord::ranged2d::cartesian::Cartesian2d::<RangedCoordu32, RangedCoordu32>::new(
391                 0..10,
392                 0..10,
393                 (0..1024, 0..768),
394             );
395     }
396 
397     #[test]
test_coord_unmap()398     fn test_coord_unmap() {
399         let coord: RangedCoordu32 = (0..20).into();
400         let pos = coord.map(&5, (1000, 2000));
401         let value = coord.unmap(pos, (1000, 2000));
402         assert_eq!(value, Some(5));
403     }
404 
405     #[test]
regression_test_issue_253_zero_sized_coord_not_hang()406     fn regression_test_issue_253_zero_sized_coord_not_hang() {
407         let coord: RangedCoordf32 = (0.0..0.0).into();
408         let _points = coord.key_points(10);
409     }
410 
411     #[test]
test_small_coord()412     fn test_small_coord() {
413         let coord: RangedCoordf64 = (0.0..1e-25).into();
414         let points = coord.key_points(10);
415         assert!(!points.is_empty());
416     }
417 
418     #[test]
regression_test_issue_255_reverse_f32_coord_no_hang()419     fn regression_test_issue_255_reverse_f32_coord_no_hang() {
420         let coord: RangedCoordf32 = (10.0..0.0).into();
421         let _points = coord.key_points(10);
422     }
423 
424     #[test]
regression_test_issue_358_key_points_no_hang()425     fn regression_test_issue_358_key_points_no_hang() {
426         let coord: RangedCoordf64 = (-200.0..801.0).into();
427         let points = coord.key_points(500);
428         assert!(points.len() <= 500);
429     }
430 
431     #[test]
regression_test_issue_358_key_points_no_hang_2()432     fn regression_test_issue_358_key_points_no_hang_2() {
433         let coord: RangedCoordf64 = (10000000000001f64..10000000000002f64).into();
434         let points = coord.key_points(500);
435         assert!(points.len() <= 500);
436     }
437 
438     #[test]
test_coord_follows_hint()439     fn test_coord_follows_hint() {
440         let coord: RangedCoordf64 = (1.0..2.0).into();
441         let points = coord.key_points(6);
442         assert_eq!(points.len(), 6);
443         assert_eq!(points[0], 1.0);
444         let coord: RangedCoordf64 = (1.0..125.0).into();
445         let points = coord.key_points(12);
446         assert_eq!(points.len(), 12);
447         let coord: RangedCoordf64 = (0.9995..1.0005).into();
448         let points = coord.key_points(11);
449         assert_eq!(points.len(), 11);
450         let coord: RangedCoordf64 = (0.9995..1.0005).into();
451         let points = coord.key_points(2);
452         assert!(points.len() <= 2);
453     }
454 
455     #[test]
regression_test_issue_304_intmax_keypoint_no_panic()456     fn regression_test_issue_304_intmax_keypoint_no_panic() {
457         let coord: RangedCoordu32 = (0..u32::MAX).into();
458         let p = coord.key_points(10);
459         assert!(!p.is_empty() && p.len() <= 10);
460     }
461 }
462