| 1 | use crate::coord::ranged1d::{ |
| 2 | AsRangedCoord, DiscreteRanged, KeyPointHint, NoDefaultFormatting, Ranged, ValueFormatter, |
| 3 | }; |
| 4 | use std::ops::Range; |
| 5 | |
| 6 | /// Describe a value for a nested coordinate |
| 7 | #[derive(PartialEq, Eq, Clone, Debug)] |
| 8 | pub enum NestedValue<C, V> { |
| 9 | /// Category value |
| 10 | Category(C), |
| 11 | /// One exact nested value |
| 12 | Value(C, V), |
| 13 | } |
| 14 | |
| 15 | impl<C, V> NestedValue<C, V> { |
| 16 | /// Get the category of current nest value |
| 17 | pub fn category(&self) -> &C { |
| 18 | match self { |
| 19 | NestedValue::Category(cat) => cat, |
| 20 | NestedValue::Value(cat, _) => cat, |
| 21 | } |
| 22 | } |
| 23 | /// Get the nested value from this value |
| 24 | pub fn nested_value(&self) -> Option<&V> { |
| 25 | match self { |
| 26 | NestedValue::Category(_) => None, |
| 27 | NestedValue::Value(_, val) => Some(val), |
| 28 | } |
| 29 | } |
| 30 | } |
| 31 | |
| 32 | impl<C, V> From<(C, V)> for NestedValue<C, V> { |
| 33 | fn from((cat, val): (C, V)) -> NestedValue<C, V> { |
| 34 | NestedValue::Value(cat, val) |
| 35 | } |
| 36 | } |
| 37 | |
| 38 | impl<C, V> From<C> for NestedValue<C, V> { |
| 39 | fn from(cat: C) -> NestedValue<C, V> { |
| 40 | NestedValue::Category(cat) |
| 41 | } |
| 42 | } |
| 43 | |
| 44 | /// A nested coordinate spec which is a discrete coordinate on the top level and |
| 45 | /// for each value in discrete value, there is a secondary coordinate system. |
| 46 | /// And the value is defined as a tuple of primary coordinate value and secondary |
| 47 | /// coordinate value |
| 48 | pub struct NestedRange<Primary: DiscreteRanged, Secondary: Ranged> { |
| 49 | primary: Primary, |
| 50 | secondary: Vec<Secondary>, |
| 51 | } |
| 52 | |
| 53 | impl<PT, ST, P, S> ValueFormatter<NestedValue<PT, ST>> for NestedRange<P, S> |
| 54 | where |
| 55 | P: Ranged<ValueType = PT> + DiscreteRanged, |
| 56 | S: Ranged<ValueType = ST>, |
| 57 | P: ValueFormatter<PT>, |
| 58 | S: ValueFormatter<ST>, |
| 59 | { |
| 60 | fn format(value: &NestedValue<PT, ST>) -> String { |
| 61 | match value { |
| 62 | NestedValue::Category(cat) => P::format(cat), |
| 63 | NestedValue::Value(_, val) => S::format(val), |
| 64 | } |
| 65 | } |
| 66 | } |
| 67 | |
| 68 | impl<P: DiscreteRanged, S: Ranged> Ranged for NestedRange<P, S> { |
| 69 | type FormatOption = NoDefaultFormatting; |
| 70 | type ValueType = NestedValue<P::ValueType, S::ValueType>; |
| 71 | |
| 72 | fn range(&self) -> Range<Self::ValueType> { |
| 73 | let primary_range = self.primary.range(); |
| 74 | |
| 75 | let secondary_left = self.secondary[0].range().start; |
| 76 | let secondary_right = self.secondary[self.primary.size() - 1].range().end; |
| 77 | |
| 78 | NestedValue::Value(primary_range.start, secondary_left) |
| 79 | ..NestedValue::Value(primary_range.end, secondary_right) |
| 80 | } |
| 81 | |
| 82 | fn map(&self, value: &Self::ValueType, limit: (i32, i32)) -> i32 { |
| 83 | let idx = self.primary.index_of(value.category()).unwrap_or(0); |
| 84 | let total = self.primary.size(); |
| 85 | |
| 86 | let bucket_size = (limit.1 - limit.0) / total as i32; |
| 87 | let mut residual = (limit.1 - limit.0) % total as i32; |
| 88 | |
| 89 | if residual < 0 { |
| 90 | residual += total as i32; |
| 91 | } |
| 92 | |
| 93 | let s_left = limit.0 + bucket_size * idx as i32 + residual.min(idx as i32); |
| 94 | let s_right = s_left + bucket_size + if (residual as usize) < idx { 1 } else { 0 }; |
| 95 | |
| 96 | if let Some(secondary_value) = value.nested_value() { |
| 97 | self.secondary[idx].map(secondary_value, (s_left, s_right)) |
| 98 | } else { |
| 99 | (s_left + s_right) / 2 |
| 100 | } |
| 101 | } |
| 102 | |
| 103 | fn key_points<Hint: KeyPointHint>(&self, hint: Hint) -> Vec<Self::ValueType> { |
| 104 | if !hint.weight().allow_light_points() || hint.max_num_points() < self.primary.size() * 2 { |
| 105 | self.primary |
| 106 | .key_points(hint) |
| 107 | .into_iter() |
| 108 | .map(NestedValue::Category) |
| 109 | .collect() |
| 110 | } else { |
| 111 | let secondary_size = |
| 112 | (hint.max_num_points() - self.primary.size()) / self.primary.size(); |
| 113 | self.primary |
| 114 | .values() |
| 115 | .enumerate() |
| 116 | .flat_map(|(idx, val)| { |
| 117 | std::iter::once(NestedValue::Category(val)).chain( |
| 118 | self.secondary[idx] |
| 119 | .key_points(secondary_size) |
| 120 | .into_iter() |
| 121 | .map(move |v| { |
| 122 | NestedValue::Value(self.primary.from_index(idx).unwrap(), v) |
| 123 | }), |
| 124 | ) |
| 125 | }) |
| 126 | .collect() |
| 127 | } |
| 128 | } |
| 129 | } |
| 130 | |
| 131 | impl<P: DiscreteRanged, S: DiscreteRanged> DiscreteRanged for NestedRange<P, S> { |
| 132 | fn size(&self) -> usize { |
| 133 | self.secondary.iter().map(|x| x.size()).sum::<usize>() |
| 134 | } |
| 135 | |
| 136 | fn index_of(&self, value: &Self::ValueType) -> Option<usize> { |
| 137 | let p_idx = self.primary.index_of(value.category())?; |
| 138 | let s_idx = self.secondary[p_idx].index_of(value.nested_value()?)?; |
| 139 | Some( |
| 140 | s_idx |
| 141 | + self.secondary[..p_idx] |
| 142 | .iter() |
| 143 | .map(|x| x.size()) |
| 144 | .sum::<usize>(), |
| 145 | ) |
| 146 | } |
| 147 | |
| 148 | fn from_index(&self, mut index: usize) -> Option<Self::ValueType> { |
| 149 | for (p_idx, snd) in self.secondary.iter().enumerate() { |
| 150 | if snd.size() > index { |
| 151 | return Some(NestedValue::Value( |
| 152 | self.primary.from_index(p_idx).unwrap(), |
| 153 | snd.from_index(index).unwrap(), |
| 154 | )); |
| 155 | } |
| 156 | index -= snd.size(); |
| 157 | } |
| 158 | None |
| 159 | } |
| 160 | } |
| 161 | |
| 162 | /// Used to build a nested coordinate system. |
| 163 | pub trait BuildNestedCoord: AsRangedCoord |
| 164 | where |
| 165 | Self::CoordDescType: DiscreteRanged, |
| 166 | { |
| 167 | /// Builds a nested coordinate system. |
| 168 | fn nested_coord<S: AsRangedCoord>( |
| 169 | self, |
| 170 | builder: impl Fn(<Self::CoordDescType as Ranged>::ValueType) -> S, |
| 171 | ) -> NestedRange<Self::CoordDescType, S::CoordDescType> { |
| 172 | let primary: Self::CoordDescType = self.into(); |
| 173 | assert!(primary.size() > 0); |
| 174 | |
| 175 | let secondary = primary |
| 176 | .values() |
| 177 | .map(|value| builder(value).into()) |
| 178 | .collect(); |
| 179 | |
| 180 | NestedRange { primary, secondary } |
| 181 | } |
| 182 | } |
| 183 | |
| 184 | impl<T: AsRangedCoord> BuildNestedCoord for T where T::CoordDescType: DiscreteRanged {} |
| 185 | |
| 186 | #[cfg (test)] |
| 187 | mod test { |
| 188 | use super::*; |
| 189 | |
| 190 | #[test] |
| 191 | fn test_nested_coord() { |
| 192 | let coord = (0..10).nested_coord(|x| 0..(x + 1)); |
| 193 | |
| 194 | let range = coord.range(); |
| 195 | |
| 196 | assert_eq!(NestedValue::Value(0, 0)..NestedValue::Value(10, 11), range); |
| 197 | assert_eq!(coord.map(&NestedValue::Category(0), (0, 1100)), 50); |
| 198 | assert_eq!(coord.map(&NestedValue::Value(0, 0), (0, 1100)), 0); |
| 199 | assert_eq!(coord.map(&NestedValue::Value(5, 4), (0, 1100)), 567); |
| 200 | |
| 201 | assert_eq!(coord.size(), (2 + 12) * 11 / 2); |
| 202 | assert_eq!(coord.index_of(&NestedValue::Value(5, 4)), Some(24)); |
| 203 | assert_eq!(coord.from_index(24), Some(NestedValue::Value(5, 4))); |
| 204 | } |
| 205 | } |
| 206 | |