1use std::{mem::swap};
2use rand::{random_range, random_bool};
3use rayon::prelude::*;
4
5use crate::strats::{conway_next};
6
7pub struct CellularAutomaton {
8 pub x:usize,
9 pub y:usize,
10 pub cells: Vec<Vec<i32>>,
12 next_cells: Vec<Vec<i32>>,
14 pub cell_processor: fn(&Self, usize, usize) -> i32,
16}
17
18impl CellularAutomaton
20{
21 pub fn new(x:usize, y:usize) -> CellularAutomaton{
24 let mut this = CellularAutomaton{
25 cells: vec![vec![0; y]; x],
26 next_cells: vec![vec![0; y]; x],
27 x:x, y:y,
28 cell_processor:conway_next
29 };
30 this.set_xy(x, y, 0);
31 this
32 }
33
34 pub fn new_with_processor(
37 x:usize,
38 y:usize,
39 processor: fn(c:&CellularAutomaton, x:usize, y:usize) -> i32
40 ) -> CellularAutomaton {
41 let mut this = CellularAutomaton::new(x, y);
42 this.set_processor(processor);
43 this
44 }
45 pub fn set_processor(&mut self, processor: fn(c:&CellularAutomaton, x:usize, y:usize) -> i32) -> &mut Self
54 {
55 self.cell_processor = processor;
56 self
57 }
58}
59impl CellularAutomaton
61{
62 #[inline]
64 pub fn next(&self, x:usize, y:usize) -> i32
65 {
66 (&self.cell_processor)(self, x, y)
67 }
68 pub fn step(&mut self) -> &mut Self {
70 self.next_cells = (0..self.x)
71 .into_par_iter()
72 .map(|i| {
73 (0..self.y)
74 .map(|j| self.next(i, j))
75 .collect()
76 })
77 .collect();
78 swap(&mut self.cells, &mut self.next_cells);
79 self
80 }
81 pub fn steps(&mut self, steps:u64) -> &mut Self {
85 for _ in 0..steps {
86 self.step();
87 }
88 self
89 }
90}
91impl CellularAutomaton
93{
94 pub fn randomize(&mut self) -> &mut Self {
96 for x in 0..self.x
97 {
98 for y in 0..self.y
99 {
100 self.cells[x][y] = random_range(0..2);
101 }
102 }
103 self
104 }
105 pub fn randomize_prob(&mut self, alive_probability:f64) -> &mut Self {
106 for x in 0..self.x
107 {
108 for y in 0..self.y
109 {
110 let p = random_bool(alive_probability);
111 self.cells[x][y] = p as i32;
112 }
113 }
114 self
115 }
116 pub fn set_xy(&mut self, x:usize, y:usize, init_state: i32) -> &mut Self {
119 self.x = x;
120 self.y = y;
121 self.cells = Vec::new();
122 for _ in 0..x {
123 let mut row = Vec::new();
124 for _ in 0..y {
125 row.push(init_state);
126 }
127 self.cells.push(row);
128 }
129 self
130 }
131 }
142
143#[cfg(test)]
144mod tests {
145 use std::time;
146 #[cfg(feature = "cli")]
147 use colored::{Colorize, ColoredString};
148 use crate::automaton::CellularAutomaton;
149
150 fn bechmark(steps_count: u32, threshold: u128, x: usize, y: usize) -> u128{
151 let mut c:CellularAutomaton = CellularAutomaton::new(x, y);
152 c.set_processor(|automaton, x, y|{
153 let mut living = 0;
154 if x+1 < automaton.x {living += automaton.cells[x+1][y];}
155 if x > 0 {living += automaton.cells[x-1][y];}
156 if y+1 < automaton.y {living += automaton.cells[x][y+1];}
157 if y > 0 {living += automaton.cells[x][y-1];}
158 match living > 2 {
159 true => 0,
160 false => 1,
161 }
162 });
163 c.randomize();
164 let prev = time::Instant::now();
165 for _ in 0..steps_count{
166 c.step();
167 }
168 let elapsed:u128 = prev.elapsed().as_millis();
169 if elapsed > threshold
170 {
171 let message = "Benchmark failed";
172 #[cfg(feature = "cli")]
173 let msg = message.red();
174 #[cfg(not(feature = "cli"))]
175 let msg = message;
176 println!("{}: {} > {}.", msg, elapsed, threshold);
177 assert!(false);
178 }
179 elapsed
181 }
182
183 fn testing(threshold: u128, steps_count: u32, x: usize, y: usize, tests_count:u32) -> u128{
184 println!("{} {} {} {}", steps_count, threshold, x, y);
185 let mut results = 0;
186 let mut mean = 0;
187 for i in 0..tests_count{
188 let result:u128 = bechmark(steps_count, threshold, x, y);
189 mean = (mean * (results) + result) / (results + 1);
190 results += 1;
191 #[cfg(feature = "cli")]
192 let status:ColoredString;
193 #[cfg(feature = "cli")]
194 {
195 if result < threshold{
196 status = "OK".green();
197 } else {
198 status = "FAIL".red();
199 }
200 }
201 #[cfg(not(feature = "cli"))]
202 let status:&'static str;
203 #[cfg(not(feature = "cli"))]
204 {
205 if result < threshold{
206 status = "OK";
207 } else {
208 status = "FAIL";
209 }
210 }
211 println!("Test {}: {:.3}s ({:.3}s): {}",
212 i + 1,
213 result as f64 / 1000.0,
214 threshold as f64 / 1000.0,
215 status);
216 }
217 println!("Mean: {:.3}s", mean as f64 / 1000.0);
218 mean
219 }
220
221 pub const TESTS_AMT:u32 = 3;
222
223 #[test]
224 fn test11_100(){
225 testing(20, 100, 100, 100, TESTS_AMT);
227 }
228 #[test]
229 fn test12_100_200x200(){
230 testing(40, 100, 200, 200, TESTS_AMT);
231 }
232 #[test]
233 fn test13_100_300x300(){
234 testing(50, 100, 300, 300, TESTS_AMT);
235 }
236 #[test]
237 fn test14_100_400x400(){
238 testing(100, 100, 400, 400, TESTS_AMT);
239 }
240 #[test]
241 fn test15_100_1000x1000(){
242 testing(500, 100, 1000, 1000, TESTS_AMT);
243 }
244 #[test]
245 fn test16_1000_1000x1000(){
246 testing(3000, 1000, 1000, 1000, TESTS_AMT);
247 }
248 #[test]
249 fn test21_1000(){
250 testing(200, 1000, 100, 100, TESTS_AMT);
251 }
252 #[test]
253 fn test22_1000_200x200(){
254 testing(500, 1000, 200, 200, TESTS_AMT);
255 }
256 #[test]
257 fn test31_50_100x100(){
258 testing(10, 50, 100, 100, TESTS_AMT);
259 }
260 #[test]
261 fn test32_50_200x200(){
262 testing(20, 50, 200, 200, TESTS_AMT);
263 }
264 #[test]
265 fn test33_50_1000x1000(){
266 testing(200, 50, 1000, 1000, TESTS_AMT);
267 }
268 fn ns2s(n:i128) -> f64{
269 (n as f64) / (10 as f64).powf(9.0)
270 }
271 #[test]
272 fn switching_test(){
273 println!("Switching Test");
274 for _ in 0..TESTS_AMT{
275 switching();
276 }
277 }
278 fn switching(){
279 use crate::strats::conway_next;
281 let mut c1 = CellularAutomaton::new(100, 100);
282 let start_time = time::Instant::now();
283 c1.steps(100);
284 let elapsed_time = start_time.elapsed().as_nanos();
285 println!("Elapsed time for c1: {}ns", elapsed_time);
286
287 let mut c2 = CellularAutomaton::new(100, 100);
288
289 let p1_start = time::Instant::now();
290 c2.steps(50);
291 let p1_elapsed = p1_start.elapsed().as_nanos();
292 println!("Elapsed time for p1: {}ns", p1_elapsed);
293
294 c2.set_processor(conway_next);
295
296 let p2_start = time::Instant::now();
297 c2.steps(50);
298 let p2_elapsed = p2_start.elapsed().as_nanos();
299 println!("Elapsed time for p2: {}ns", p2_elapsed);
301
302 println!("Difference between ca1 ({}ns) and ca2 ({}ns): {}ns, {}",
303 ns2s(elapsed_time as i128),
304 ns2s((p2_elapsed + p1_elapsed) as i128),
305 ns2s(((p2_elapsed + p1_elapsed) - (elapsed_time)) as i128),
306 ((p2_elapsed+p1_elapsed) as f64)/(elapsed_time as f64)
307 );
308 }
309}