aboutsummaryrefslogtreecommitdiff
path: root/src/coord/ranged1d/types/numeric.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/coord/ranged1d/types/numeric.rs')
-rw-r--r--src/coord/ranged1d/types/numeric.rs361
1 files changed, 361 insertions, 0 deletions
diff --git a/src/coord/ranged1d/types/numeric.rs b/src/coord/ranged1d/types/numeric.rs
new file mode 100644
index 0000000..6de2bdf
--- /dev/null
+++ b/src/coord/ranged1d/types/numeric.rs
@@ -0,0 +1,361 @@
+use std::convert::TryFrom;
+use std::ops::Range;
+
+use crate::coord::ranged1d::{
+ AsRangedCoord, DefaultFormatting, DiscreteRanged, KeyPointHint, NoDefaultFormatting, Ranged,
+ ReversibleRanged, ValueFormatter,
+};
+
+macro_rules! impl_discrete_trait {
+ ($name:ident) => {
+ impl DiscreteRanged for $name {
+ fn size(&self) -> usize {
+ if &self.1 < &self.0 {
+ return 0;
+ }
+ let values = self.1 - self.0;
+ (values + 1) as usize
+ }
+
+ fn index_of(&self, value: &Self::ValueType) -> Option<usize> {
+ if value < &self.0 {
+ return None;
+ }
+ let ret = value - self.0;
+ Some(ret as usize)
+ }
+
+ fn from_index(&self, index: usize) -> Option<Self::ValueType> {
+ if let Ok(index) = Self::ValueType::try_from(index) {
+ return Some(self.0 + index);
+ }
+ None
+ }
+ }
+ };
+}
+
+macro_rules! impl_ranged_type_trait {
+ ($value:ty, $coord:ident) => {
+ impl AsRangedCoord for Range<$value> {
+ type CoordDescType = $coord;
+ type Value = $value;
+ }
+ };
+}
+macro_rules! impl_reverse_mapping_trait {
+ ($type:ty, $name: ident) => {
+ impl ReversibleRanged for $name {
+ fn unmap(&self, p: i32, (min, max): (i32, i32)) -> Option<$type> {
+ if p < min.min(max) || p > max.max(min) || min == max {
+ return None;
+ }
+
+ let logical_offset = f64::from(p - min) / f64::from(max - min);
+
+ return Some(((self.1 - self.0) as f64 * logical_offset + self.0 as f64) as $type);
+ }
+ }
+ };
+}
+macro_rules! make_numeric_coord {
+ ($type:ty, $name:ident, $key_points:ident, $doc: expr, $fmt: ident) => {
+ #[doc = $doc]
+ #[derive(Clone)]
+ pub struct $name($type, $type);
+ impl From<Range<$type>> for $name {
+ fn from(range: Range<$type>) -> Self {
+ return $name(range.start, range.end);
+ }
+ }
+ impl Ranged for $name {
+ type FormatOption = $fmt;
+ type ValueType = $type;
+ #[allow(clippy::float_cmp)]
+ fn map(&self, v: &$type, limit: (i32, i32)) -> i32 {
+ // Corner case: If we have a range that have only one value,
+ // then we just assign everything to the only point
+ if self.1 == self.0 {
+ return (limit.1 - limit.0) / 2;
+ }
+
+ let logic_length = (*v - self.0) as f64 / (self.1 - self.0) as f64;
+
+ let actual_length = limit.1 - limit.0;
+
+ if actual_length == 0 {
+ return limit.1;
+ }
+
+ return limit.0 + (actual_length as f64 * logic_length + 1e-3).floor() as i32;
+ }
+ fn key_points<Hint: KeyPointHint>(&self, hint: Hint) -> Vec<$type> {
+ $key_points((self.0, self.1), hint.max_num_points())
+ }
+ fn range(&self) -> Range<$type> {
+ return self.0..self.1;
+ }
+ }
+ };
+ ($type:ty, $name:ident, $key_points:ident, $doc: expr) => {
+ make_numeric_coord!($type, $name, $key_points, $doc, DefaultFormatting);
+ }
+}
+
+macro_rules! gen_key_points_comp {
+ (float, $name:ident, $type:ty) => {
+ fn $name(range: ($type, $type), max_points: usize) -> Vec<$type> {
+ if max_points == 0 {
+ return vec![];
+ }
+
+ let range = (range.0 as f64, range.1 as f64);
+ let mut scale = (10f64).powf((range.1 - range.0).log(10.0).floor());
+ let mut digits = -(range.1 - range.0).log(10.0).floor() as i32 + 1;
+ fn rem_euclid(a: f64, b: f64) -> f64 {
+ if b > 0.0 {
+ a - (a / b).floor() * b
+ } else {
+ a - (a / b).ceil() * b
+ }
+ }
+
+ // At this point we need to make sure that the loop invariant:
+ // The scale must yield number of points than requested
+ if 1 + ((range.1 - range.0) / scale).floor() as usize > max_points {
+ scale *= 10.0;
+ }
+
+ 'outer: loop {
+ let old_scale = scale;
+ for nxt in [2.0, 5.0, 10.0].iter() {
+ let new_left = range.0 + scale / nxt - rem_euclid(range.0, scale / nxt);
+ let new_right = range.1 - rem_euclid(range.1, scale / nxt);
+
+ let npoints = 1 + ((new_right - new_left) / old_scale * nxt) as usize;
+
+ if npoints > max_points {
+ break 'outer;
+ }
+
+ scale = old_scale / nxt;
+ }
+ scale = old_scale / 10.0;
+ if scale < 1.0 {
+ digits += 1;
+ }
+ }
+
+ let mut ret = vec![];
+ let mut left = range.0 + scale - rem_euclid(range.0, scale);
+ let right = range.1 - rem_euclid(range.1, scale);
+ while left <= right {
+ let size = (10f64).powf(digits as f64 + 1.0);
+ let new_left = (left * size).abs() + 1e-3;
+ if left < 0.0 {
+ left = -new_left.round() / size;
+ } else {
+ left = new_left.round() / size;
+ }
+ ret.push(left as $type);
+ left += scale;
+ }
+ return ret;
+ }
+ };
+ (integer, $name:ident, $type:ty) => {
+ fn $name(range: ($type, $type), max_points: usize) -> Vec<$type> {
+ let mut scale: $type = 1;
+ let range = (range.0.min(range.1), range.0.max(range.1));
+ 'outer: while (range.1 - range.0 + scale - 1) as usize / (scale as usize) > max_points {
+ let next_scale = scale * 10;
+ for new_scale in [scale * 2, scale * 5, scale * 10].iter() {
+ scale = *new_scale;
+ if (range.1 - range.0 + *new_scale - 1) as usize / (*new_scale as usize)
+ < max_points
+ {
+ break 'outer;
+ }
+ }
+ scale = next_scale;
+ }
+
+ let (mut left, right) = (
+ range.0 + (scale - range.0 % scale) % scale,
+ range.1 - range.1 % scale,
+ );
+
+ let mut ret = vec![];
+ while left <= right {
+ ret.push(left as $type);
+ left += scale;
+ }
+
+ return ret;
+ }
+ };
+}
+
+gen_key_points_comp!(float, compute_f32_key_points, f32);
+gen_key_points_comp!(float, compute_f64_key_points, f64);
+gen_key_points_comp!(integer, compute_i32_key_points, i32);
+gen_key_points_comp!(integer, compute_u32_key_points, u32);
+gen_key_points_comp!(integer, compute_i64_key_points, i64);
+gen_key_points_comp!(integer, compute_u64_key_points, u64);
+gen_key_points_comp!(integer, compute_i128_key_points, i128);
+gen_key_points_comp!(integer, compute_u128_key_points, u128);
+gen_key_points_comp!(integer, compute_isize_key_points, isize);
+gen_key_points_comp!(integer, compute_usize_key_points, usize);
+
+make_numeric_coord!(
+ f32,
+ RangedCoordf32,
+ compute_f32_key_points,
+ "The ranged coordinate for type f32",
+ NoDefaultFormatting
+);
+impl_reverse_mapping_trait!(f32, RangedCoordf32);
+impl ValueFormatter<f32> for RangedCoordf32 {
+ fn format(value: &f32) -> String {
+ crate::data::float::FloatPrettyPrinter {
+ allow_scientific: false,
+ min_decimal: 1,
+ max_decimal: 5,
+ }
+ .print(*value as f64)
+ }
+}
+make_numeric_coord!(
+ f64,
+ RangedCoordf64,
+ compute_f64_key_points,
+ "The ranged coordinate for type f64",
+ NoDefaultFormatting
+);
+impl_reverse_mapping_trait!(f64, RangedCoordf64);
+impl ValueFormatter<f64> for RangedCoordf64 {
+ fn format(value: &f64) -> String {
+ crate::data::float::FloatPrettyPrinter {
+ allow_scientific: false,
+ min_decimal: 1,
+ max_decimal: 5,
+ }
+ .print(*value)
+ }
+}
+make_numeric_coord!(
+ u32,
+ RangedCoordu32,
+ compute_u32_key_points,
+ "The ranged coordinate for type u32"
+);
+make_numeric_coord!(
+ i32,
+ RangedCoordi32,
+ compute_i32_key_points,
+ "The ranged coordinate for type i32"
+);
+make_numeric_coord!(
+ u64,
+ RangedCoordu64,
+ compute_u64_key_points,
+ "The ranged coordinate for type u64"
+);
+make_numeric_coord!(
+ i64,
+ RangedCoordi64,
+ compute_i64_key_points,
+ "The ranged coordinate for type i64"
+);
+make_numeric_coord!(
+ u128,
+ RangedCoordu128,
+ compute_u128_key_points,
+ "The ranged coordinate for type u128"
+);
+make_numeric_coord!(
+ i128,
+ RangedCoordi128,
+ compute_i128_key_points,
+ "The ranged coordinate for type i128"
+);
+make_numeric_coord!(
+ usize,
+ RangedCoordusize,
+ compute_usize_key_points,
+ "The ranged coordinate for type usize"
+);
+make_numeric_coord!(
+ isize,
+ RangedCoordisize,
+ compute_isize_key_points,
+ "The ranged coordinate for type isize"
+);
+
+impl_discrete_trait!(RangedCoordu32);
+impl_discrete_trait!(RangedCoordi32);
+impl_discrete_trait!(RangedCoordu64);
+impl_discrete_trait!(RangedCoordi64);
+impl_discrete_trait!(RangedCoordu128);
+impl_discrete_trait!(RangedCoordi128);
+impl_discrete_trait!(RangedCoordusize);
+impl_discrete_trait!(RangedCoordisize);
+
+impl_ranged_type_trait!(f32, RangedCoordf32);
+impl_ranged_type_trait!(f64, RangedCoordf64);
+impl_ranged_type_trait!(i32, RangedCoordi32);
+impl_ranged_type_trait!(u32, RangedCoordu32);
+impl_ranged_type_trait!(i64, RangedCoordi64);
+impl_ranged_type_trait!(u64, RangedCoordu64);
+impl_ranged_type_trait!(i128, RangedCoordi128);
+impl_ranged_type_trait!(u128, RangedCoordu128);
+impl_ranged_type_trait!(isize, RangedCoordisize);
+impl_ranged_type_trait!(usize, RangedCoordusize);
+
+#[cfg(test)]
+mod test {
+ use super::*;
+ #[test]
+ fn test_key_points() {
+ let kp = compute_i32_key_points((0, 999), 28);
+
+ assert!(kp.len() > 0);
+ assert!(kp.len() <= 28);
+
+ let kp = compute_f64_key_points((-1.2, 1.2), 1);
+ assert!(kp.len() == 1);
+
+ let kp = compute_f64_key_points((-1.2, 1.2), 0);
+ assert!(kp.len() == 0);
+ }
+
+ #[test]
+ fn test_linear_coord_map() {
+ let coord: RangedCoordu32 = (0..20).into();
+ assert_eq!(coord.key_points(11).len(), 11);
+ assert_eq!(coord.key_points(11)[0], 0);
+ assert_eq!(coord.key_points(11)[10], 20);
+ assert_eq!(coord.map(&5, (0, 100)), 25);
+
+ let coord: RangedCoordf32 = (0f32..20f32).into();
+ assert_eq!(coord.map(&5.0, (0, 100)), 25);
+ }
+
+ #[test]
+ fn test_linear_coord_system() {
+ let _coord =
+ crate::coord::ranged2d::cartesian::Cartesian2d::<RangedCoordu32, RangedCoordu32>::new(
+ 0..10,
+ 0..10,
+ (0..1024, 0..768),
+ );
+ }
+
+ #[test]
+ fn test_coord_unmap() {
+ let coord: RangedCoordu32 = (0..20).into();
+ let pos = coord.map(&5, (1000, 2000));
+ let value = coord.unmap(pos, (1000, 2000));
+ assert_eq!(value, Some(5));
+ }
+}