1pub mod traits;
4use std::ops::{Add, Shr};
5
6use beetle_nonzero::NonZero;
7use num::{Integer, One, PrimInt};
8pub use traits::*;
9
10impl<T> TwoThree for T where T: PrimInt + Integer + One + Add<Output = T> {}
11
12impl<T> Rules for NonZero<T>
13where
14 T: PrimInt + Integer + Shr<u32, Output = T>,
15{
16 fn odd_rule(self) -> Self {
17 unsafe { self.map_unchecked(|x| x * T::three() + T::one()) }
18 }
19
20 fn even_rule(self) -> Self {
21 unsafe { self.map_unchecked(|x| x / T::two()) }
22 }
23
24 fn rules(self) -> Self {
25 if self.is_odd() {
26 self.odd_rule()
27 } else {
28 self.even_rule()
29 }
30 }
31
32 fn rules_halve_odds(self) -> Self {
33 if self.is_odd() {
34 self.odd_rule().even_rule()
35 } else {
36 self.even_rule()
37 }
38 }
39
40 fn rules_remove_trailing_zeros(self) -> Self {
41 let x = if self.is_odd() { self.odd_rule() } else { self };
42 x.without_trailing_zeros()
43 }
44}
45
46impl<T> Steps for NonZero<T>
47where
48 T: PrimInt + Integer + Shr<u32, Output = T>,
49{
50 fn steps_to_one(self) -> u64 {
51 if self.is_even() {
52 self.steps_to_one_for_even_number()
53 } else {
54 self.steps_to_one_for_odd_number()
55 }
56 }
57
58 fn steps_to_one_for_even_number(self) -> u64 {
59 let steps_to_become_odd: u64 = self.trailing_zeros().into();
60 let without_zeros = self.without_trailing_zeros();
61 steps_to_become_odd + without_zeros.steps_to_one_for_odd_number()
62 }
63
64 fn steps_to_one_for_odd_number(self) -> u64 {
65 let mut steps: u64 = 0;
66 let mut n = self;
67 while !n.get().is_one() {
68 n = n.odd_rule();
71 steps += 1;
72
73 let tz: u64 = n.trailing_zeros().into();
77 n = n.without_trailing_zeros();
78 steps += tz;
79 }
80 steps
81 }
82
83 fn steps_to_decrease(self) -> u64 {
84 if self.is_even() {
85 1
86 } else {
87 self.steps_to_decrease_for_odd_number()
88 }
89 }
90
91 fn steps_to_decrease_for_odd_number(self) -> u64 {
92 let mut n = self;
93 let mut steps: u64 = 0;
94
95 n = n.odd_rule();
96 steps += u64::from(n.trailing_zeros()) + 1;
97 n = n.without_trailing_zeros();
98
99 let starting_value = self;
100 while n > starting_value {
101 n = n.odd_rule();
102 steps += u64::from(n.trailing_zeros()) + 1;
103 n = n.without_trailing_zeros();
104 }
105
106 steps
107 }
108}
109
110impl<T> Transformations for NonZero<T>
111where
112 T: Integer + PrimInt + Shr<u32, Output = T>,
113{
114 fn transformations_to_one(&self) -> Vec<Self> {
115 let mut v: Vec<Self> = Vec::new();
116 let mut n: Self = *self;
117 v.push(n);
118 while !n.get().is_one() {
119 n = n.rules();
120 v.push(n);
121 }
122 v
123 }
124}
125
126#[allow(clippy::unwrap_used, unused_imports)]
127mod tests {
128 use beetle_nonzero::NonZero;
129
130 use crate::{Steps, TwoThree};
131
132 #[allow(dead_code)]
135 const OEIS_STEPS: [u64; 72] = [
136 0, 1, 7, 2, 5, 8, 16, 3, 19, 6, 14, 9, 9, 17, 17, 4, 12, 20, 20, 7, 7, 15, 15, 10, 23, 10,
137 111, 18, 18, 18, 106, 5, 26, 13, 13, 21, 21, 21, 34, 8, 109, 8, 29, 16, 16, 16, 104, 11,
138 24, 24, 24, 11, 11, 112, 112, 19, 32, 19, 32, 19, 19, 107, 107, 6, 27, 27, 27, 14, 14, 14,
139 102, 22,
140 ];
141
142 #[test]
143 fn step_counts_for_integers_are_correct() {
144 use crate::traits::Steps;
145 use beetle_nonzero::NonZero;
146 use num::One;
147
148 let one: NonZero<u8> = unsafe { NonZero::new_unchecked(1) };
150 assert_eq!(one.steps_to_one(), 0);
151
152 let one: NonZero<u16> = unsafe { NonZero::new_unchecked(1) };
154 assert_eq!(one.steps_to_one(), 0);
155
156 let one: NonZero<u32> = unsafe { NonZero::new_unchecked(1) };
158 assert_eq!(one.steps_to_one(), 0);
159
160 let one: NonZero<u64> = unsafe { NonZero::new_unchecked(1) };
162 assert_eq!(one.steps_to_one(), 0);
163
164 let one: NonZero<u128> = unsafe { NonZero::new_unchecked(1) };
166 assert_eq!(one.steps_to_one(), 0);
167 }
168
169 #[test]
170 fn step_counts_for_ranges_are_correct() {
171 let start: u32 = 1;
172 let stop: u32 = 73;
173 let steps: Vec<u64> = (start..stop)
174 .map(|n| unsafe { NonZero::new_unchecked(n).steps_to_one() })
175 .collect();
176 assert_eq!(steps, OEIS_STEPS.to_vec());
177
178 let start: u32 = 1;
180 let stop: u32 = 73;
181
182 let steps: Vec<u64> = (start..stop)
183 .map(|n| unsafe { NonZero::new_unchecked(n).steps_to_one() })
184 .collect();
185 assert_eq!(steps, OEIS_STEPS.to_vec());
186 }
187
188 #[test]
189 fn transformations_for_numbers_are_correct() {
190 use crate::Transformations;
191 let n: NonZero<u32> = unsafe { NonZero::new_unchecked(4u32) };
192 let transforms = n.transformations_to_one();
193 let expected_transformations: Vec<NonZero<u32>> = [4, 2, 1]
194 .into_iter()
195 .map(|x: u32| unsafe { NonZero::new_unchecked(x) })
196 .collect();
197 assert_eq!(transforms, expected_transformations);
198 }
199}