Skip to main content

dcre_native/
lib.rs

1use wasm_bindgen::prelude::*;
2
3// When the `wee_alloc` feature is enabled, use `wee_alloc` as the global
4// allocator.
5#[cfg(feature = "wee_alloc")]
6#[global_allocator]
7static ALLOC: wee_alloc::WeeAlloc = wee_alloc::WeeAlloc::INIT;
8
9#[wasm_bindgen]
10pub struct Lattice {
11    width: u32,
12    height: u32,
13    cells: Vec<u8>,
14    modulus: u8,
15}
16
17#[wasm_bindgen]
18impl Lattice {
19    #[wasm_bindgen(constructor)]
20    pub fn new(width: u32, height: u32, modulus: u8) -> Lattice {
21        // Initialize with zeros
22        let cells = vec![0; (width * height) as usize];
23        Lattice {
24            width,
25            height,
26            cells,
27            modulus,
28        }
29    }
30
31    pub fn width(&self) -> u32 {
32        self.width
33    }
34
35    pub fn height(&self) -> u32 {
36        self.height
37    }
38
39    pub fn modulus(&self) -> u8 {
40        self.modulus
41    }
42
43    /// Access memory directly from JS for zero-copy
44    pub fn cells_ptr(&self) -> *const u8 {
45        self.cells.as_ptr()
46    }
47
48    pub fn get(&self, row: u32, col: u32) -> u8 {
49        self.cells[(row * self.width + col) as usize]
50    }
51
52    pub fn set(&mut self, row: u32, col: u32, value: u8) {
53        self.cells[(row * self.width + col) as usize] = value % self.modulus;
54    }
55
56    /// Optimized Lattice Evolution
57    /// 
58    /// Applies the DCRE matrix [[2, 1], [1, 1]] to 2x2 blocks in a checkerboard pattern.
59    /// This implementation is single-threaded O(N^2) but allocation-free.
60    pub fn evolve(&mut self) {
61        let width = self.width;
62        let modulus = self.modulus as u16; // Use u16 to prevent overflow during calc
63
64        // Phase 1: Even blocks (0,0), (0,2)...
65        for r in (0..width).step_by(2) {
66            for c in (0..width).step_by(2) {
67                self.apply_block(r, c, modulus);
68            }
69        }
70
71        // Phase 2: Odd blocks (1,1), (1,3)...
72        for r in (1..width).step_by(2) {
73            for c in (1..width).step_by(2) {
74                self.apply_block(r, c, modulus);
75            }
76        }
77    }
78
79    #[inline(always)]
80    fn apply_block(&mut self, r: u32, c: u32, modulus: u16) {
81        let w = self.width;
82        
83        // Wrap coordinates (Toroidal topology)
84        let r1 = (r + 1) % w;
85        let c1 = (c + 1) % w;
86
87        // Indices
88        let i_a = (r * w + c) as usize;
89        let i_b = (r * w + c1) as usize;
90        let i_c = (r1 * w + c) as usize;
91        let i_d = (r1 * w + c1) as usize;
92
93        // Load values
94        let a = self.cells[i_a] as u16;
95        let b = self.cells[i_b] as u16;
96        let cc = self.cells[i_c] as u16; // 'cc' because 'c' is column
97        let d = self.cells[i_d] as u16;
98
99        // Matrix Multiply:
100        // [new_a, new_b]   [2 1] [a b]   [2a+b   a+b]
101        // [new_c, new_d] = [1 1] [c d] = [2c+d   c+d]
102        
103        self.cells[i_a] = ((2 * a + b) % modulus) as u8;
104        self.cells[i_b] = ((a + b) % modulus) as u8;
105        self.cells[i_c] = ((2 * cc + d) % modulus) as u8;
106        self.cells[i_d] = ((cc + d) % modulus) as u8;
107    }
108}