proof_engine/number_theory/
collatz.rs1use glam::{Vec3, Vec4};
4
5pub fn collatz_sequence(n: u64) -> Vec<u64> {
7 if n == 0 {
8 return vec![0];
9 }
10 let mut seq = vec![n];
11 let mut current = n;
12 while current != 1 {
13 current = if current % 2 == 0 {
14 current / 2
15 } else {
16 3 * current + 1
17 };
18 seq.push(current);
19 }
20 seq
21}
22
23pub fn collatz_stopping_time(n: u64) -> u32 {
25 if n <= 1 {
26 return 0;
27 }
28 let mut current = n;
29 let mut steps = 0u32;
30 while current != 1 {
31 current = if current % 2 == 0 {
32 current / 2
33 } else {
34 3 * current + 1
35 };
36 steps += 1;
37 }
38 steps
39}
40
41#[derive(Debug, Clone)]
43pub struct TreeNode {
44 pub value: u64,
45 pub children: Vec<TreeNode>,
46}
47
48pub struct CollatzTree {
51 pub root: TreeNode,
52 pub limit: u64,
53}
54
55impl CollatzTree {
56 pub fn build(limit: u64) -> Self {
58 let root = Self::build_node(1, limit, 0, 40);
59 CollatzTree { root, limit }
60 }
61
62 fn build_node(value: u64, limit: u64, depth: usize, max_depth: usize) -> TreeNode {
63 if depth >= max_depth {
64 return TreeNode { value, children: vec![] };
65 }
66 let mut children = Vec::new();
67
68 let child_even = 2 * value;
70 if child_even <= limit {
71 children.push(Self::build_node(child_even, limit, depth + 1, max_depth));
72 }
73
74 if value > 4 && (value - 1) % 3 == 0 {
78 let k = (value - 1) / 3;
79 if k > 0 && k % 2 == 1 && k != value {
80 if k <= limit {
82 children.push(Self::build_node(k, limit, depth + 1, max_depth));
83 }
84 }
85 }
86
87 TreeNode { value, children }
88 }
89
90 pub fn flatten(&self) -> Vec<(u64, usize)> {
92 let mut result = Vec::new();
93 Self::flatten_node(&self.root, 0, &mut result);
94 result
95 }
96
97 fn flatten_node(node: &TreeNode, depth: usize, result: &mut Vec<(u64, usize)>) {
98 result.push((node.value, depth));
99 for child in &node.children {
100 Self::flatten_node(child, depth + 1, result);
101 }
102 }
103
104 pub fn node_count(&self) -> usize {
106 Self::count_nodes(&self.root)
107 }
108
109 fn count_nodes(node: &TreeNode) -> usize {
110 1 + node.children.iter().map(|c| Self::count_nodes(c)).sum::<usize>()
111 }
112}
113
114pub struct CollatzTreeRenderer {
118 pub origin: Vec3,
119 pub scale: f32,
120 pub horizontal_spread: f32,
121}
122
123pub struct CollatzGlyph {
124 pub value: u64,
125 pub depth: usize,
126 pub position: Vec3,
127 pub color: Vec4,
128 pub character: char,
129}
130
131impl CollatzTreeRenderer {
132 pub fn new(origin: Vec3, scale: f32, horizontal_spread: f32) -> Self {
133 Self { origin, scale, horizontal_spread }
134 }
135
136 pub fn render(&self, tree: &CollatzTree) -> Vec<CollatzGlyph> {
138 let mut glyphs = Vec::new();
139 let mut counter = 0usize;
140 self.render_node(&tree.root, 0, &mut counter, &mut glyphs);
141 glyphs
142 }
143
144 fn render_node(
145 &self,
146 node: &TreeNode,
147 depth: usize,
148 counter: &mut usize,
149 glyphs: &mut Vec<CollatzGlyph>,
150 ) {
151 let x = *counter as f32 * self.horizontal_spread;
152 let y = -(depth as f32) * self.scale;
153 let stopping = collatz_stopping_time(node.value);
154 let t = (stopping as f32 / 50.0).min(1.0);
155 glyphs.push(CollatzGlyph {
156 value: node.value,
157 depth,
158 position: self.origin + Vec3::new(x, y, 0.0),
159 color: Vec4::new(t, 0.5, 1.0 - t, 1.0),
160 character: if node.value % 2 == 0 { 'E' } else { 'O' },
161 });
162 *counter += 1;
163 for child in &node.children {
164 self.render_node(child, depth + 1, counter, glyphs);
165 }
166 }
167
168 pub fn render_sequence(&self, n: u64) -> Vec<CollatzGlyph> {
170 let seq = collatz_sequence(n);
171 let max_val = *seq.iter().max().unwrap_or(&1) as f32;
172 seq.iter()
173 .enumerate()
174 .map(|(i, &v)| {
175 let t = i as f32 / seq.len().max(1) as f32;
176 let height = v as f32 / max_val;
177 CollatzGlyph {
178 value: v,
179 depth: i,
180 position: self.origin + Vec3::new(i as f32 * self.scale, height * self.scale * 5.0, 0.0),
181 color: Vec4::new(t, height, 1.0 - t, 1.0),
182 character: if v % 2 == 0 { 'v' } else { '^' },
183 }
184 })
185 .collect()
186 }
187}
188
189#[cfg(test)]
192mod tests {
193 use super::*;
194
195 #[test]
196 fn sequence_basic() {
197 let seq = collatz_sequence(6);
198 assert_eq!(seq, vec![6, 3, 10, 5, 16, 8, 4, 2, 1]);
199 }
200
201 #[test]
202 fn sequence_one() {
203 assert_eq!(collatz_sequence(1), vec![1]);
204 }
205
206 #[test]
207 fn sequence_27() {
208 let seq = collatz_sequence(27);
209 assert_eq!(*seq.last().unwrap(), 1);
210 assert_eq!(seq.len(), 112); }
213
214 #[test]
215 fn stopping_time() {
216 assert_eq!(collatz_stopping_time(1), 0);
217 assert_eq!(collatz_stopping_time(2), 1);
218 assert_eq!(collatz_stopping_time(6), 8);
219 assert_eq!(collatz_stopping_time(27), 111);
220 }
221
222 #[test]
223 fn tree_build() {
224 let tree = CollatzTree::build(32);
225 assert_eq!(tree.root.value, 1);
227 assert!(tree.root.children.iter().any(|c| c.value == 2));
229 }
230
231 #[test]
232 fn tree_contains_powers_of_2() {
233 let tree = CollatzTree::build(64);
234 let flat = tree.flatten();
235 let values: Vec<u64> = flat.iter().map(|&(v, _)| v).collect();
236 for &p in &[1, 2, 4, 8, 16, 32, 64] {
238 assert!(values.contains(&p), "tree should contain {}", p);
239 }
240 }
241
242 #[test]
243 fn tree_node_count() {
244 let tree = CollatzTree::build(16);
245 assert!(tree.node_count() >= 5); }
247
248 #[test]
249 fn renderer_sequence() {
250 let r = CollatzTreeRenderer::new(Vec3::ZERO, 1.0, 2.0);
251 let glyphs = r.render_sequence(27);
252 assert_eq!(glyphs.len(), 112);
253 assert_eq!(glyphs[0].value, 27);
254 assert_eq!(glyphs.last().unwrap().value, 1);
255 }
256
257 #[test]
258 fn renderer_tree() {
259 let tree = CollatzTree::build(32);
260 let r = CollatzTreeRenderer::new(Vec3::ZERO, 1.0, 2.0);
261 let glyphs = r.render(&tree);
262 assert!(!glyphs.is_empty());
263 assert_eq!(glyphs[0].value, 1); }
265
266 #[test]
267 fn all_reach_one() {
268 for n in 1..=1000 {
270 let seq = collatz_sequence(n);
271 assert_eq!(*seq.last().unwrap(), 1, "Collatz failed for {}", n);
272 }
273 }
274}