1 use csl::PageRangeFormat;
2 
3 /// Returns the second number with the page range format applied.
truncate_prf(prf: PageRangeFormat, first: u32, mut second: u32) -> u324 pub fn truncate_prf(prf: PageRangeFormat, first: u32, mut second: u32) -> u32 {
5     second = expand(first, second);
6     match prf {
7         PageRangeFormat::Chicago => {
8             let mod100 = first % 100;
9             let delta = second - first;
10             if first < 100 || mod100 == 0 {
11                 second
12             } else if mod100 < 10 && delta < 90 {
13                 truncate_diff(first, second, 1)
14             } else if closest_smaller_power_of_10(first) == 1000 {
15                 let chopped = truncate_diff(first, second, 2);
16                 if closest_smaller_power_of_10(chopped) == 100 {
17                     // force 4 digits if 3 are different
18                     return truncate_diff(first, second, 4);
19                 }
20                 chopped
21             } else {
22                 truncate_diff(first, second, 2)
23             }
24         }
25         PageRangeFormat::Minimal => truncate_diff(first, second, 1),
26         PageRangeFormat::MinimalTwo => truncate_diff(first, second, 2),
27         PageRangeFormat::Expanded => second,
28     }
29 }
30 
31 #[test]
page_range_chicago()32 fn page_range_chicago() {
33     fn go(a: u32, b: u32) -> u32 {
34         truncate_prf(PageRangeFormat::Chicago, a, b)
35     }
36     // https://docs.citationstyles.org/en/stable/specification.html#appendix-v-page-range-formats
37     // 1
38     assert_eq!(go(3, 10), 10);
39     assert_eq!(go(71, 72), 72);
40     // 2
41     assert_eq!(go(100, 104), 104);
42     assert_eq!(go(600, 613), 613);
43     assert_eq!(go(1100, 1123), 1123);
44     // 3
45     assert_eq!(go(101, 108), 8);
46     assert_eq!(go(107, 108), 8);
47     assert_eq!(go(505, 517), 17);
48     assert_eq!(go(1002, 1006), 6);
49     // 4
50     assert_eq!(go(321, 325), 25);
51     assert_eq!(go(415, 532), 532);
52     assert_eq!(go(11564, 11568), 68);
53     assert_eq!(go(13792, 13803), 803);
54     // 5 (force 4 digits where 3 are different)
55     assert_eq!(go(1496, 1504), 1504);
56     assert_eq!(go(2787, 2816), 2816);
57     // but if only two digits different, don't
58     assert_eq!(go(1486, 1496), 96);
59 }
60 
61 #[test]
test_truncate_diff()62 fn test_truncate_diff() {
63     assert_eq!(truncate_diff(101, 105, 1), 5);
64     assert_eq!(truncate_diff(121, 125, 1), 5);
65     assert_eq!(truncate_diff(121, 125, 2), 25);
66     assert_eq!(truncate_diff(121, 125, 3), 125);
67 }
68 
truncate_diff(a: u32, b: u32, min: u32) -> u3269 fn truncate_diff(a: u32, b: u32, min: u32) -> u32 {
70     if b < a {
71         return b;
72     }
73     let mut diff_started = false;
74     let mut acc = 0u32;
75     let mut iter_a = DigitsBase10::new(a);
76     let mut iter_b = DigitsBase10::new(b);
77     // fast forward iter_a until they have the same mask i.e. same remaining digit length
78     while iter_a.mask > iter_b.mask {
79         iter_a.next();
80     }
81     while iter_b.mask > iter_a.mask {
82         if let Some(b_dig) = iter_b.next() {
83             diff_started = true;
84             acc *= 10;
85             acc += b_dig as u32;
86         }
87     }
88     let min_mask = 10_u32.pow(min);
89     if iter_a.mask * 10 == min_mask {
90         diff_started = true;
91     }
92     // Primitive zip so we can keep access to iter_a
93     while let (Some(a_dig), Some(b_dig)) = (iter_a.next(), iter_b.next()) {
94         if diff_started || a_dig != b_dig {
95             diff_started = true;
96             acc *= 10;
97             acc += b_dig as u32;
98         }
99         if iter_a.mask * 10 == min_mask {
100             diff_started = true;
101         }
102     }
103     acc
104 }
105 
106 #[test]
test_expand()107 fn test_expand() {
108     assert_eq!(expand(103, 4), 104);
109     assert_eq!(expand(133, 4), 134);
110     assert_eq!(expand(133, 54), 154);
111 }
112 
expand(a: u32, b: u32) -> u32113 fn expand(a: u32, b: u32) -> u32 {
114     let mask = closest_smaller_power_of_10(b) * 10;
115     (a - (a % mask)) + (b % mask)
116 }
117 
118 // Thanks to timotree3 on the Rust users forum for writing this already
119 // https://users.rust-lang.org/t/iterate-through-digits-of-a-number/34465/9
120 
121 pub struct DigitsBase10 {
122     mask: u32,
123     num: u32,
124 }
125 
126 impl Iterator for DigitsBase10 {
127     type Item = u8;
128 
next(&mut self) -> Option<Self::Item>129     fn next(&mut self) -> Option<Self::Item> {
130         if self.mask == 0 {
131             return None;
132         }
133 
134         let digit = self.num / self.mask % 10;
135         self.mask /= 10;
136 
137         Some(digit as u8)
138     }
139 }
140 
closest_smaller_power_of_10(num: u32) -> u32141 fn closest_smaller_power_of_10(num: u32) -> u32 {
142     let answer = 10_f64.powf((num as f64).log10().floor()) as u32;
143 
144     // these properties need to hold. I think they do, but the float conversions
145     // might mess things up...
146     debug_assert!(answer <= num);
147     debug_assert!(answer > num / 10);
148     answer
149 }
150 
151 impl DigitsBase10 {
new(num: u32) -> Self152     pub fn new(num: u32) -> Self {
153         let mask = if num == 0 {
154             1
155         } else {
156             closest_smaller_power_of_10(num)
157         };
158 
159         DigitsBase10 { mask, num }
160     }
161 }
162