Skip to main content

gol_engines/
pattern.rs

1use crate::VERSION;
2use ahash::AHashMap as HashMap;
3use anyhow::{anyhow, Context, Result};
4use flate2::{
5    read::{GzDecoder, GzEncoder},
6    Compression,
7};
8use num_bigint::BigInt;
9use rand::{Rng, SeedableRng};
10use std::{fs, io::Read, path::Path};
11
12/// Nodes typically have size of about 32 bytes, so 2^32 (128 GiB) nodes
13/// is generally enough.
14type NodeIdx = u32;
15/// A type for measuring the depth of the quadtree.
16type SizeLog2 = u32;
17
18/// Represents a Game of Life pattern using a memory-efficient quadtree structure.
19///
20/// # Overview
21///
22/// This struct implements a quadtree where each node represents a square region
23/// of the pattern. Leaf nodes represent 8x8 cell blocks ([`PatternNode::Leaf`]),
24/// while internal nodes ([`PatternNode::Node`]) subdivide their region into four
25/// smaller quadrants (nw, ne, sw, se).
26///
27/// This structure allows for significant memory savings for large or sparse patterns
28/// through node deduplication (via `KIVMap`), making it ideal for compact
29/// checkpoints in Game of Life engines.
30///
31/// # Features
32///
33/// *   **Quadtree Representation:** Efficiently stores patterns with regularities.
34///     Hovewer, even randomly-filled patterns are stored with insignificant overhead
35///     compared to a flat packed array.
36/// *   **Power-of-2 Sizes:** Supports square patterns with side lengths that are
37///     powers of 2 (e.g., 1x1, 2x2, 8x8, 16x16...).
38/// *   **Serialization:** Can be serialized to and deserialized from various
39///     standard formats (see [`PatternFormat`]):
40///     *   `PackedCells`
41///     *   `Macrocell`
42///     *   `CompressedMacrocell`
43///     *   `RLE`
44/// *   **Precise Population Count:** Accurately computes the total number of
45///     live cells in the pattern using arbitrary-precision arithmetic. This method
46///     guarantees precise results even for extremely large patterns where cell counts
47///     might exceed the limits of fixed-size integers.
48/// *   **Metafy Expansion:** Recursively expands the pattern by replacing each cell
49///     with the corresponding base metacell from the provided `metacells`. This approach
50///     delivers higher performance than Golly scripts and enables efficient creation
51///     of multi-level metapatterns.
52///
53/// # Format Recommendations
54///
55/// For best performance and memory efficiency, prefer [`PatternFormat::Macrocell`] or
56/// [`PatternFormat::CompressedMacrocell`] over other formats. These formats:
57///
58/// * Best align with the internal quadtree representation used by `Pattern`
59/// * Provide faster loading and saving for large patterns
60/// * Handle sparse patterns more efficiently
61/// * Support patterns of virtually unlimited size
62///
63/// If you encounter issues loading patterns that work in Golly and should work here,
64/// the file might not conform to the format specifications detailed at
65/// https://golly.sourceforge.io/Help/formats.html.
66/// Try opening the problematic pattern in a modern version of Golly and saving it again
67/// to ensure it meets the expected format requirements.
68///
69/// # Limitations
70///
71/// *   **Two-State Only:** Currently supports only standard two-state
72///     (dead/alive) cells.
73/// *   **B3/S23 Rule:** Assumes Conway's standard B3/S23 rule for format conversions
74///     (like RLE and Macrocell) and engine operations.
75/// *   **Size Limitations:** While the theoretical maximum dimension is about
76///     `2^(2^32) x 2^(2^32)` (limited by `SizeLog2`), the practical size is
77///     constrained by the `NodeIdx` limit of `2^32` unique nodes.
78///     Dense or random patterns may exhaust node indices much sooner;
79///     the maximum size guaranteed to fit regardless of pattern content is
80///     `2^18 x 2^18`. Highly repetitive patterns can achieve much larger sizes.
81/// *   **Memory Allocation:** Relies on Rust's default allocator. If memory allocation
82///     fails (e.g., due to insufficient system memory), functions will typically
83///     panic and terminate rather than return an error.
84#[derive(Clone)]
85pub struct Pattern {
86    /// The root node of the quadtree, has size of
87    /// `(1 << size_log2) x (1 << size_log2)`.
88    root: NodeIdx,
89    /// `size_log2` is the log2(side length of the square).
90    /// For example, a 16x16 pattern has `size_log2` of 4.
91    size_log2: SizeLog2,
92    /// Storage for the quadtree nodes.
93    kiv: KIVMap,
94    /// Caches blank nodes for faster search.
95    blank_nodes: Vec<NodeIdx>,
96}
97
98impl Pattern {
99    /// Returns the root node index of the pattern.
100    ///
101    /// The root node represents the top-level quadtree node covering the entire pattern.
102    pub fn get_root(&self) -> NodeIdx {
103        self.root
104    }
105
106    /// Changes the root node of the pattern to a new node with a specified size.
107    ///
108    /// # Safety
109    ///
110    /// This function is unsafe because it requires the caller to ensure:
111    /// - `root` is a valid node index that exists in the pattern's KIVMap
112    /// - `size_log2` correctly represents the size of the new root node
113    ///
114    /// # Arguments
115    ///
116    /// * `root` - The index of the new root node
117    /// * `size_log2` - The log base 2 of the new root's side length
118    pub unsafe fn change_root(&mut self, root: NodeIdx, size_log2: SizeLog2) {
119        self.root = root;
120        self.size_log2 = size_log2;
121    }
122
123    /// Finds an existing node with the given content or creates a new one.
124    ///
125    /// This method delegates to the internal KIVMap to find or create a node,
126    /// maintaining the pattern's node deduplication property.
127    ///
128    /// # Arguments
129    ///
130    /// * `node` - The pattern node content to find or create
131    ///
132    /// # Returns
133    ///
134    /// A `NodeIdx` that uniquely identifies the node with the specified content
135    pub fn find_or_create_node(&mut self, node: PatternNode) -> NodeIdx {
136        self.kiv.find_or_create_node(node)
137    }
138
139    /// Returns the log base 2 of the pattern's side length.
140    ///
141    /// The pattern is always square with side length of `2^size_log2`.
142    /// For example, a pattern with `size_log2 = 3` is 8x8 cells.
143    pub fn get_size_log2(&self) -> SizeLog2 {
144        self.size_log2
145    }
146
147    /// Retrieves a node from the pattern by its index.
148    ///
149    /// # Arguments
150    ///
151    /// * `idx` - The index of the node to retrieve.
152    ///
153    /// # Returns
154    ///
155    /// A reference to the requested `PatternNode`.
156    pub fn get_node(&self, idx: NodeIdx) -> &PatternNode {
157        self.kiv.get_node(idx)
158    }
159
160    /// Computes a 64-bit hash of the entire pattern. Intended for fast
161    /// probabilistic pattern comparison.
162    ///
163    /// This method recursively traverses the quadtree and combines hashes
164    /// of each node using a non-cryptographic hash function. The hash is
165    /// consistent for identical patterns regardless of their internal
166    /// representation.
167    ///
168    /// # Returns
169    ///
170    /// A 64-bit hash value for the pattern.
171    pub fn hash(&self) -> u64 {
172        fn inner(idx: NodeIdx, cache: &mut HashMap<NodeIdx, u64>, kiv: &KIVMap) -> u64 {
173            if let Some(&val) = cache.get(&idx) {
174                return val;
175            }
176
177            let combine = |x: u64, y: u64| -> u64 {
178                x ^ y
179                    .wrapping_add(0x9e3779b9)
180                    .wrapping_add(x << 6)
181                    .wrapping_add(x >> 2)
182            };
183
184            match kiv.get_node(idx) {
185                PatternNode::Leaf(cells) => *cells,
186                PatternNode::Node { nw, ne, sw, se } => {
187                    let mut result = 0;
188                    for x in [*nw, *ne, *sw, *se] {
189                        result = combine(result, inner(x, cache, kiv));
190                    }
191                    cache.insert(idx, result);
192                    result
193                }
194            }
195        }
196
197        let mut cache = HashMap::new();
198        inner(self.root, &mut cache, &self.kiv)
199    }
200
201    /// Counts the total number of alive cells in the pattern.
202    ///
203    /// This method efficiently traverses the quadtree structure and
204    /// memoizes intermediate results to avoid redundant calculations.
205    ///
206    /// # Returns
207    ///
208    /// A `BigInt` representing the total number of alive cells, which can
209    /// be arbitrarily large for very large patterns.
210    pub fn population(&self) -> BigInt {
211        fn inner<'a>(idx: NodeIdx, cache: &'a mut HashMap<NodeIdx, BigInt>, kiv: &'a KIVMap) {
212            let result = match kiv.get_node(idx) {
213                PatternNode::Leaf(cells) => BigInt::from(cells.count_ones()),
214                PatternNode::Node { nw, ne, sw, se } => {
215                    let mut result = BigInt::ZERO;
216                    for x in [*nw, *ne, *sw, *se] {
217                        if !cache.contains_key(&x) {
218                            inner(x, cache, kiv);
219                        }
220                        result += cache.get(&x).unwrap();
221                    }
222                    result
223                }
224            };
225            cache.insert(idx, result);
226        }
227
228        let mut cache = HashMap::new();
229        inner(self.root, &mut cache, &self.kiv);
230        cache.remove(&self.root).unwrap()
231    }
232
233    /// Expands the pattern to a larger size by adding dead cells to the right and bottom.
234    ///
235    /// This function increases the `size_log2` of the pattern to the requested value, effectively
236    /// making the pattern's dimensions larger. The original pattern content remains in the top-left
237    /// corner, with dead cells filling the newly added space.
238    ///
239    /// # Arguments
240    ///
241    /// * `size_log2` - The desired log base 2 of the new pattern size.
242    ///
243    /// # Examples
244    ///
245    /// ```rust
246    /// use gol_engines::{Pattern, PatternFormat};
247    ///
248    /// let mut pattern = gol_engines::Pattern::random(2, None).unwrap();
249    /// assert_eq!(pattern.get_size_log2(), 2); // 4x4 pattern
250    ///
251    /// // Expand to 256x256
252    /// pattern.expand(8);
253    /// assert_eq!(pattern.get_size_log2(), 8);
254    /// ```
255    ///
256    /// # Note
257    ///
258    /// If `size_log2` is less than or equal to the current size, this function does nothing.
259    pub fn expand(&mut self, size_log2: SizeLog2) {
260        if self.size_log2 >= size_log2 {
261            return;
262        }
263        if size_log2 <= 3 {
264            self.size_log2 = size_log2;
265            return;
266        }
267        self.size_log2 = self.size_log2.max(3);
268        while self.size_log2 < size_log2 {
269            let blank = Self::find_or_create_blank_node(
270                self.size_log2,
271                &mut self.kiv,
272                &mut self.blank_nodes,
273            );
274
275            self.root = self.kiv.find_or_create_node(PatternNode::Node {
276                nw: self.root,
277                ne: blank,
278                sw: blank,
279                se: blank,
280            });
281            self.size_log2 += 1;
282        }
283    }
284
285    /// Creates a random pattern of the specified size.
286    ///
287    /// # Arguments
288    ///
289    /// * `size_log2` - Log base 2 of the pattern's side length.
290    /// * `seed` - Optional seed for the random number generator.
291    ///   If None, seeds from the OS.
292    ///
293    /// # Returns
294    ///
295    /// A `Result` containing either the created random `Pattern` or an error.
296    ///
297    /// # Errors
298    ///
299    /// Returns an error if `size_log2` is too large (would cause overflow).
300    pub fn random(size_log2: SizeLog2, seed: Option<u64>) -> Result<Self> {
301        if 1usize.checked_shl(size_log2 * 2).is_none() {
302            return Err(anyhow!("size_log2 {} is too large", size_log2));
303        }
304        let n = 1usize << size_log2;
305        let mut cells = vec![0u8; (n * n).div_ceil(8).max(n)];
306        if let Some(x) = seed {
307            rand_chacha::ChaCha8Rng::seed_from_u64(x)
308        } else {
309            rand_chacha::ChaCha8Rng::from_os_rng()
310        }
311        .fill(&mut cells[..]);
312        if size_log2 < 3 {
313            // clear the upper bits
314            for x in cells.iter_mut() {
315                *x &= ((1u32 << n) - 1) as u8;
316            }
317        }
318        Self::from_packed_cells(&cells)
319    }
320
321    /// Recursively expands a pattern by replacing cells with `metacells` multiple times.
322    /// Optimized for building huge multi-level metacells.
323    ///
324    /// This function treats the current pattern (`self`) as a high-level layout.
325    /// It performs `level` steps of expansion. In each step, every 'alive' cell
326    /// is replaced by the `metacells[1]` pattern (ON state), and every 'dead' cell
327    /// is replaced by the `metacells[0]` pattern (OFF state).
328    ///
329    /// For example, if `level = 1`, each cell in `self` is directly replaced by the
330    /// corresponding pattern from `metacells`. If `level = 2`, each cell in `self` is
331    /// replaced by a meta-pattern, which itself is constructed by replacing *its*
332    /// cells with the patterns from `metacells`.
333    ///
334    /// # Arguments
335    ///
336    /// * `self` - The pattern representing the initial high-level layout.
337    /// * `metacells` - An array containing two `Pattern`s defining the base unit cells:
338    ///   `metacells[0]` for the OFF state and `metacells[1]` for the ON state.
339    ///   These patterns must have the same `size_log2`.
340    /// * `level` - The number of recursive expansion level to perform.
341    ///   Zero means doing nothing.
342    ///
343    /// # Returns
344    ///
345    /// A `Result` containing the expanded `Pattern`. The final size exponent will be
346    /// `self.size_log2 + level * base_size_log2`, where `base_size_log2` is the
347    /// `size_log2` of the patterns in `metacells`.
348    ///
349    /// # Errors
350    ///
351    /// Returns an error if:
352    /// - `metacells[0].size_log2` does not equal `metacells[1].size_log2`
353    /// - metacells are smaller than 8x8
354    pub fn metafy(&self, metacells: [&Pattern; 2], level: u32) -> Result<Self> {
355        if metacells[0].size_log2 != metacells[1].size_log2 {
356            return Err(anyhow!(
357                "Metacells must have the same size_log2: {} != {}",
358                metacells[0].size_log2,
359                metacells[1].size_log2
360            ));
361        }
362        if metacells[0].size_log2 < 3 {
363            return Err(anyhow!(
364                "Metacell size_log2 must be at least 3: {}",
365                metacells[0].size_log2
366            ));
367        }
368
369        if level == 0 {
370            return Ok(self.clone());
371        }
372
373        fn copy_from_pattern(
374            idx: NodeIdx,
375            pattern: &Pattern,
376            kiv: &mut KIVMap,
377            cache: &mut HashMap<NodeIdx, NodeIdx>,
378        ) -> NodeIdx {
379            if let Some(&x) = cache.get(&idx) {
380                return x;
381            }
382            let result = match pattern.get_node(idx) {
383                PatternNode::Leaf(cells) => kiv.find_or_create_node(PatternNode::Leaf(*cells)),
384                PatternNode::Node { nw, ne, sw, se } => {
385                    let nw = copy_from_pattern(*nw, pattern, kiv, cache);
386                    let ne = copy_from_pattern(*ne, pattern, kiv, cache);
387                    let sw = copy_from_pattern(*sw, pattern, kiv, cache);
388                    let se = copy_from_pattern(*se, pattern, kiv, cache);
389                    kiv.find_or_create_node(PatternNode::Node { nw, ne, sw, se })
390                }
391            };
392            cache.insert(idx, result);
393            result
394        }
395
396        let mut kiv = KIVMap::new();
397        let mut cache = HashMap::new();
398        let mut metacell_idx = [0; 2];
399        for (src, dst) in metacells.iter().zip(metacell_idx.iter_mut()) {
400            *dst = copy_from_pattern(src.root, src, &mut kiv, &mut cache);
401            cache.clear();
402        }
403
404        fn build_pattern_from_metacells(
405            pattern: &Pattern,
406            metacells: [NodeIdx; 2],
407            idx: NodeIdx,
408            kiv: &mut KIVMap,
409            cache: &mut HashMap<NodeIdx, NodeIdx>,
410        ) -> NodeIdx {
411            if let Some(&x) = cache.get(&idx) {
412                return x;
413            }
414            let result = match pattern.get_node(idx) {
415                PatternNode::Leaf(cells) => {
416                    let mut a = [[0; 8]; 8];
417                    for y in 0..8 {
418                        for x in 0..8 {
419                            a[y][x] = metacells[(cells >> (x + y * 8)) as usize & 1];
420                        }
421                    }
422                    if pattern.size_log2 == 0 {
423                        return a[0][0];
424                    }
425                    let mut b = [[0; 4]; 4];
426                    for y in 0..4 {
427                        for x in 0..4 {
428                            b[y][x] = kiv.find_or_create_node(PatternNode::Node {
429                                nw: a[y * 2][x * 2],
430                                ne: a[y * 2][x * 2 + 1],
431                                sw: a[y * 2 + 1][x * 2],
432                                se: a[y * 2 + 1][x * 2 + 1],
433                            });
434                        }
435                    }
436                    if pattern.size_log2 == 1 {
437                        return b[0][0];
438                    }
439                    let mut c = [[0; 2]; 2];
440                    for y in 0..2 {
441                        for x in 0..2 {
442                            c[y][x] = kiv.find_or_create_node(PatternNode::Node {
443                                nw: b[y * 2][x * 2],
444                                ne: b[y * 2][x * 2 + 1],
445                                sw: b[y * 2 + 1][x * 2],
446                                se: b[y * 2 + 1][x * 2 + 1],
447                            });
448                        }
449                    }
450                    if pattern.size_log2 == 2 {
451                        return c[0][0];
452                    }
453                    kiv.find_or_create_node(PatternNode::Node {
454                        nw: c[0][0],
455                        ne: c[0][1],
456                        sw: c[1][0],
457                        se: c[1][1],
458                    })
459                }
460                PatternNode::Node { nw, ne, sw, se } => {
461                    let nw = build_pattern_from_metacells(pattern, metacells, *nw, kiv, cache);
462                    let ne = build_pattern_from_metacells(pattern, metacells, *ne, kiv, cache);
463                    let sw = build_pattern_from_metacells(pattern, metacells, *sw, kiv, cache);
464                    let se = build_pattern_from_metacells(pattern, metacells, *se, kiv, cache);
465                    kiv.find_or_create_node(PatternNode::Node { nw, ne, sw, se })
466                }
467            };
468            cache.insert(idx, result);
469            result
470        }
471
472        for _ in 0..level - 1 {
473            metacell_idx = [0, 1].map(|i| {
474                let result = build_pattern_from_metacells(
475                    metacells[i],
476                    metacell_idx,
477                    metacells[i].root,
478                    &mut kiv,
479                    &mut cache,
480                );
481                cache.clear();
482                result
483            });
484        }
485
486        let root =
487            build_pattern_from_metacells(self, metacell_idx, self.root, &mut kiv, &mut cache);
488        Ok(Self {
489            root,
490            size_log2: self.size_log2 + level * metacells[0].size_log2,
491            kiv,
492            blank_nodes: vec![],
493        })
494    }
495
496    /// Creates a pattern from data in the specified format.
497    ///
498    /// # Arguments
499    ///
500    /// * `format` - The format of the provided data.
501    /// * `data` - The bytes containing the pattern data.
502    ///
503    /// # Returns
504    ///
505    /// A `Result` containing either the created `Pattern` or an error.
506    ///
507    /// # Errors
508    ///
509    /// Returns format-specific errors:
510    /// - `PackedCells`: If the data does not represent a square with a side that is a power of 2
511    /// - `Macrocell` or `RLE`: If data is invalid, uses more than two states, or specifies
512    ///   non-B3/S23 rules
513    /// - `CompressedMacrocell`: If decompression fails or macrocell parsing errors occur
514    ///
515    /// # Performance Considerations
516    ///
517    /// For large or highly non-square patterns, the tree-based formats (`Macrocell` and
518    /// `CompressedMacrocell`) generally provide better performance and memory efficiency than
519    /// the flat array-based formats (`PackedCells` and `RLE`).
520    pub fn from_format(format: PatternFormat, data: &[u8]) -> Result<Self> {
521        match format {
522            PatternFormat::PackedCells => Self::from_packed_cells(data),
523            PatternFormat::Macrocell => Self::from_macrocell(data),
524            PatternFormat::CompressedMacrocell => Self::from_compressed_macrocell(data),
525            PatternFormat::RLE => Self::from_rle(data),
526        }
527    }
528
529    /// Converts the pattern to the specified format.
530    ///
531    /// # Arguments
532    ///
533    /// * `format` - The desired output format.
534    ///
535    /// # Returns
536    ///
537    /// A `Result` containing either the bytes of the pattern in the requested format
538    /// or an error.
539    ///
540    /// # Errors
541    ///
542    /// Returns format-specific errors:
543    /// - `PackedCells` or `RLE`: If `size_log2` is too large (would cause overflow)
544    /// - `Macrocell` or `CompressedMacrocell`: If the pattern is blank (contains no alive cells)
545    pub fn to_format(&self, format: PatternFormat) -> Result<Vec<u8>> {
546        match format {
547            PatternFormat::PackedCells => self.to_packed_cells(),
548            PatternFormat::Macrocell => self.to_macrocell(),
549            PatternFormat::CompressedMacrocell => self.to_compressed_macrocell(),
550            PatternFormat::RLE => self.to_rle(),
551        }
552    }
553
554    /// Creates a pattern from a file.
555    ///
556    /// The format will be inferred from the file extension:
557    /// - `.mc` → `PatternFormat::Macrocell`
558    /// - `.mc.gz` → `PatternFormat::CompressedMacrocell`
559    /// - `.rle` → `PatternFormat::RLE`
560    ///
561    /// # Arguments
562    ///
563    /// * `path` - Path to the file containing the pattern data.
564    ///
565    /// # Returns
566    ///
567    /// A `Result` containing either the created `Pattern` or an error.
568    ///
569    /// # Errors
570    ///
571    /// Returns an error if:
572    /// - File cannot be read
573    /// - File extension is not recognized
574    /// - Format-specific errors occur from `from_format`
575    pub fn from_file<P: AsRef<Path>>(path: P) -> Result<Self> {
576        let path = path.as_ref();
577        let data = fs::read(path).with_context(|| format!("Failed to read file: {:?}", path))?;
578
579        if path.extension() == Some("mc".as_ref()) {
580            return Self::from_format(PatternFormat::Macrocell, &data);
581        }
582
583        if path
584            .to_str()
585            .map(|s| s.ends_with(".mc.gz"))
586            .unwrap_or(false)
587        {
588            return Self::from_format(PatternFormat::CompressedMacrocell, &data);
589        }
590
591        if path.extension() == Some("rle".as_ref()) {
592            return Self::from_format(PatternFormat::RLE, &data);
593        }
594
595        Err(anyhow!("Cannot infer format from file path: {:?}", path))
596    }
597
598    /// Saves the pattern to a file.
599    ///
600    /// The format will be inferred from the file extension:
601    /// - `.rle` → `PatternFormat::RLE`
602    /// - `.mc` → `PatternFormat::Macrocell`
603    /// - `.mc.gz` → `PatternFormat::CompressedMacrocell`
604    ///
605    /// The file will be created or overwritten.
606    ///
607    /// # Arguments
608    ///
609    /// * `path` - Path where the pattern will be saved.
610    ///
611    /// # Returns
612    ///
613    /// A `Result<()>` indicating success or containing an error.
614    ///
615    /// # Errors
616    ///
617    /// Returns an error if:
618    /// - File cannot be written
619    /// - File extension is not recognized
620    /// - Format-specific errors from `to_format`
621    pub fn to_file<P: AsRef<Path>>(&self, path: P) -> Result<()> {
622        let path = path.as_ref();
623
624        let format = if path.extension() == Some("mc".as_ref()) {
625            PatternFormat::Macrocell
626        } else if path
627            .to_str()
628            .map(|s| s.ends_with(".mc.gz"))
629            .unwrap_or(false)
630        {
631            PatternFormat::CompressedMacrocell
632        } else if path.extension() == Some("rle".as_ref()) {
633            PatternFormat::RLE
634        } else {
635            return Err(anyhow!("Cannot infer format from file path: {:?}", path));
636        };
637
638        let data = self.to_format(format)?;
639        fs::write(path, data).with_context(|| format!("Failed to write file: {:?}", path))
640    }
641
642    /// Creates a pattern from packed cell data. See [`PatternFormat::PackedCells`].
643    ///
644    /// # Arguments
645    ///
646    /// * `data` - Bytes containing the packed cell data.
647    ///
648    /// # Returns
649    ///
650    /// A `Result` containing either the created `Pattern` or an error.
651    ///
652    /// # Errors
653    ///
654    /// Returns an error if the data does not represent a square with a side that is a power of 2.
655    fn from_packed_cells(data: &[u8]) -> Result<Self> {
656        if data.is_empty() {
657            return Err(anyhow!("Packed cells are empty"));
658        }
659        let size_log2 = data.len().ilog2().min((data.len().ilog2() + 3) / 2);
660        let n = 1usize << size_log2;
661        if data.len() != (n * n).div_ceil(8).max(n) {
662            return Err(anyhow!(
663                "Packed cells don't represent a square with a power of 2 side"
664            ));
665        }
666
667        if size_log2 < 3 {
668            for x in data.iter() {
669                if *x & !((1u32 << n) - 1) as u8 != 0 {
670                    return Err(anyhow!("Found cells out of bounds"));
671                }
672            }
673        }
674
675        let mut kiv = KIVMap::new();
676        let (mut nodes_curr, mut nodes_next) = (vec![], vec![]);
677
678        let stride = n.max(8);
679        for y in (0..n).step_by(8) {
680            for x in (0..n).step_by(8) {
681                let mut leaf = [0; 8];
682                for dy in 0..8.min(n) {
683                    leaf[dy] = data[(x + (dy + y) * stride) / 8];
684                }
685                nodes_curr
686                    .push(kiv.find_or_create_node(PatternNode::Leaf(u64::from_le_bytes(leaf))));
687            }
688        }
689        let mut t = stride / 8;
690        while t != 1 {
691            for y in (0..t).step_by(2) {
692                for x in (0..t).step_by(2) {
693                    let nw = nodes_curr[x + y * t];
694                    let ne = nodes_curr[(x + 1) + y * t];
695                    let sw = nodes_curr[x + (y + 1) * t];
696                    let se = nodes_curr[(x + 1) + (y + 1) * t];
697                    nodes_next.push(kiv.find_or_create_node(PatternNode::Node { nw, ne, sw, se }));
698                }
699            }
700            std::mem::swap(&mut nodes_curr, &mut nodes_next);
701            nodes_next.clear();
702            t >>= 1;
703        }
704        assert_eq!(nodes_curr.len(), 1);
705        Ok(Self {
706            root: nodes_curr[0],
707            size_log2,
708            kiv,
709            blank_nodes: vec![],
710        })
711    }
712
713    /// Converts the pattern to packed cell data. See [`PatternFormat::PackedCells`].
714    ///
715    /// # Returns
716    ///
717    /// A `Result` containing either the bytes of the packed cells representation
718    /// or an error.
719    ///
720    /// # Errors
721    ///
722    /// Returns an error if `self.size_log2` is too large (would cause overflow).
723    fn to_packed_cells(&self) -> Result<Vec<u8>> {
724        fn inner(
725            this: &Pattern,
726            idx: NodeIdx,
727            size_log2: SizeLog2,
728            cells: &mut Vec<u8>,
729            stride: usize,
730            x: usize,
731            y: usize,
732        ) {
733            match this.get_node(idx) {
734                PatternNode::Leaf(cells_data) => {
735                    for dy in 0..1 << size_log2 {
736                        cells[(x + (dy + y) * stride) / 8] |= cells_data.to_le_bytes()[dy];
737                    }
738                }
739                PatternNode::Node { nw, ne, sw, se } => {
740                    let half = 1 << (size_log2 - 1);
741                    inner(this, *nw, size_log2 - 1, cells, stride, x, y);
742                    inner(this, *ne, size_log2 - 1, cells, stride, x + half, y);
743                    inner(this, *sw, size_log2 - 1, cells, stride, x, y + half);
744                    inner(this, *se, size_log2 - 1, cells, stride, x + half, y + half);
745                }
746            }
747        }
748
749        if 1usize.checked_shl(self.size_log2 * 2).is_none() {
750            return Err(anyhow!("size_log2 {} is too large", self.size_log2));
751        }
752        let n = 1usize << self.size_log2;
753        let mut cells = vec![0; (n * n).div_ceil(8).max(n)];
754        inner(self, self.root, self.size_log2, &mut cells, n.max(8), 0, 0);
755        Ok(cells)
756    }
757
758    /// Creates a pattern from data in the macrocell format. See [`PatternFormat::Macrocell`].
759    ///
760    /// # Arguments
761    ///
762    /// * `data` - The bytes containing the macrocell data.
763    ///
764    /// # Returns
765    ///
766    /// A `Result` containing either the created `Pattern` or an error.
767    ///
768    /// # Errors
769    ///
770    /// Returns an error if:
771    /// - Data is invalid
772    /// - Algorithm has more than two states
773    /// - A rule other than B3/S23 is specified
774    fn from_macrocell(data: &[u8]) -> Result<Self> {
775        let mut kiv = KIVMap::new();
776        let mut blank_nodes = vec![];
777        let mut codes_and_sizes = HashMap::<u32, (NodeIdx, SizeLog2)>::new();
778        let mut last_code_and_size = None;
779
780        let mut lines = data
781            .split(|&x| x == b'\n')
782            .map(|x| x.strip_suffix(b"\r").unwrap_or(x))
783            .filter(|x| !x.is_empty());
784        if !lines
785            .next()
786            .ok_or_else(|| anyhow!("Missing format identifier"))?
787            .starts_with(b"[M2]")
788        {
789            return Err(anyhow!("Invalid format identifier"));
790        }
791
792        for s in lines {
793            if s[0] == b'#' {
794                if s.starts_with(b"#R") && &s[2..] != b" B3/S23" {
795                    return Err(anyhow!("Only B3/S23 rule is supported"));
796                }
797                continue;
798            }
799
800            let idx = (codes_and_sizes.len() + 1).try_into().with_context(|| {
801                format!("Failed to convert {} to NodeIdx", codes_and_sizes.len() + 1)
802            })?;
803
804            let code_and_size = if s[0].is_ascii_digit() {
805                // non-leaf
806                let numbers: Vec<u32> = s
807                    .split(|&x| x == b' ')
808                    .map(|part| {
809                        std::str::from_utf8(part)
810                            .with_context(|| format!("Invalid UTF-8 sequence in {part:?}"))
811                            .and_then(|s| {
812                                s.parse::<u32>()
813                                    .with_context(|| format!("Failed to parse integer from '{s}'"))
814                            })
815                    })
816                    .collect::<Result<_>>()?;
817                if numbers.len() != 5 {
818                    return Err(anyhow!("Expected 5 numbers, got {}", numbers.len()));
819                }
820                if numbers[0] < 4 {
821                    return Err(anyhow!(
822                        "Node {} has size_log2 {}, expected >= 4",
823                        idx,
824                        numbers[0],
825                    ));
826                }
827
828                let mut resolve = |x: u32| -> Result<NodeIdx> {
829                    if x == 0 {
830                        Ok(Self::find_or_create_blank_node(
831                            numbers[0] - 1,
832                            &mut kiv,
833                            &mut blank_nodes,
834                        ))
835                    } else {
836                        let (code, size_log2) =
837                            codes_and_sizes.get(&x).copied().ok_or_else(|| {
838                                anyhow!("Reference to undeclared node with code {}", x)
839                            })?;
840                        if size_log2 != numbers[0] - 1 {
841                            return Err(anyhow!(
842                                "Node {} has size_log2 {}, expected {}",
843                                x,
844                                size_log2,
845                                numbers[0] - 1
846                            ));
847                        }
848                        Ok(code)
849                    }
850                };
851
852                let nw = resolve(numbers[1])?;
853                let ne = resolve(numbers[2])?;
854                let sw = resolve(numbers[3])?;
855                let se = resolve(numbers[4])?;
856                let code = kiv.find_or_create_node(PatternNode::Node { nw, ne, sw, se });
857                (code, numbers[0])
858            } else {
859                // is leaf
860                let mut cells = 0u64;
861                let (mut i, mut j) = (0, 0);
862                for &c in s {
863                    match c {
864                        b'$' => (i, j) = (i + 1, 0),
865                        b'*' => {
866                            if i >= 8 || j >= 8 {
867                                return Err(anyhow!(
868                                    "Leaf {} does not fit in 8x8",
869                                    String::from_utf8_lossy(s)
870                                ));
871                            }
872                            cells |= 1 << (i * 8 + j);
873                            j += 1;
874                        }
875                        b'.' => {
876                            j += 1;
877                        }
878                        _ => {
879                            return Err(anyhow!(
880                                "Invalid symbol '{}' in leaf {}",
881                                c as char,
882                                String::from_utf8_lossy(s)
883                            ))
884                        }
885                    }
886                }
887                let code = kiv.find_or_create_node(PatternNode::Leaf(cells));
888                (code, 3)
889            };
890            last_code_and_size = Some(code_and_size);
891            codes_and_sizes.insert(idx, code_and_size);
892        }
893        let (root, size_log2) =
894            last_code_and_size.ok_or_else(|| anyhow!("No nodes found in the pattern"))?;
895        Ok(Self {
896            root,
897            size_log2,
898            kiv,
899            blank_nodes,
900        })
901    }
902
903    /// Converts the pattern to the macrocell format. See [`PatternFormat::Macrocell`].
904    ///
905    /// # Returns
906    ///
907    /// A `Result` containing either the bytes of the macrocell representation
908    /// or an error.
909    ///
910    /// # Errors
911    ///
912    /// Returns an error if the pattern is blank (contains no alive cells).
913    fn to_macrocell(&self) -> Result<Vec<u8>> {
914        fn inner(
915            this: &Pattern,
916            idx: NodeIdx,
917            size_log2: SizeLog2,
918            codes: &mut HashMap<NodeIdx, usize>,
919            blank_nodes: &mut Vec<NodeIdx>,
920            result: &mut Vec<u8>,
921        ) -> usize {
922            // if already serialized
923            if let Some(&x) = codes.get(&idx) {
924                return x;
925            }
926            // if node is blank
927            if let Some(x) = Pattern::find_blank_node(size_log2, &this.kiv, blank_nodes) {
928                if x == idx {
929                    return 0;
930                }
931            }
932
933            match *this.get_node(idx) {
934                PatternNode::Leaf(x) => {
935                    for t in x.to_le_bytes() {
936                        for i in 0..8 {
937                            if (t >> i) & 1 != 0 {
938                                result.push(b'*');
939                            } else {
940                                result.push(b'.');
941                            }
942                        }
943                        while result.ends_with(b".") {
944                            result.pop();
945                        }
946                        result.push(b'$');
947                    }
948                    while result.ends_with(b"$$") {
949                        result.pop();
950                    }
951                    result.push(b'\n');
952                }
953                PatternNode::Node { nw, ne, sw, se } => {
954                    let new_line = format!(
955                        "{} {} {} {} {}\n",
956                        size_log2,
957                        inner(this, nw, size_log2 - 1, codes, blank_nodes, result),
958                        inner(this, ne, size_log2 - 1, codes, blank_nodes, result),
959                        inner(this, sw, size_log2 - 1, codes, blank_nodes, result),
960                        inner(this, se, size_log2 - 1, codes, blank_nodes, result),
961                    );
962                    result.extend_from_slice(new_line.as_bytes());
963                }
964            }
965            codes.insert(idx, codes.len() + 1);
966            codes.len()
967        }
968
969        let mut blank_nodes = self.blank_nodes.clone();
970        if let Some(x) = Self::find_blank_node(self.size_log2, &self.kiv, &mut blank_nodes) {
971            if x == self.root {
972                return Err(anyhow!("Cannot serialize blank pattern"));
973            }
974        }
975
976        let mut codes = HashMap::new();
977        let mut result = format!("[M2] (gol_engines {VERSION})\n#R B3/S23\n").into_bytes();
978        inner(
979            self,
980            self.root,
981            self.size_log2,
982            &mut codes,
983            &mut blank_nodes,
984            &mut result,
985        );
986
987        Ok(result)
988    }
989
990    /// Creates a pattern from gzip-compressed macrocell data.
991    /// See [`PatternFormat::CompressedMacrocell`].
992    ///
993    /// This method decompresses the input data and then parses it using
994    /// the standard macrocell format parser.
995    ///
996    /// # Arguments
997    ///
998    /// * `compressed_data` - The gzip-compressed bytes containing macrocell data.
999    ///
1000    /// # Returns
1001    ///
1002    /// A `Result` containing either the created `Pattern` or an error.
1003    ///
1004    /// # Errors
1005    ///
1006    /// Returns an error if:
1007    /// - The input data is not valid gzip-compressed data
1008    /// - Other errors from `from_macrocell` occur
1009    fn from_compressed_macrocell(compressed_data: &[u8]) -> Result<Self> {
1010        // Create a gzip decoder
1011        let mut decoder = GzDecoder::new(compressed_data);
1012        let mut decompressed_data = Vec::new();
1013        decoder
1014            .read_to_end(&mut decompressed_data)
1015            .context("Failed to decompress macrocell data")?;
1016        Self::from_macrocell(&decompressed_data)
1017    }
1018
1019    /// Converts the pattern to gzip-compressed macrocell format.
1020    /// See [`PatternFormat::CompressedMacrocell`].
1021    ///
1022    /// This method first converts the pattern to macrocell format using
1023    /// `to_macrocell`, then compresses the result using gzip.
1024    ///
1025    /// # Returns
1026    ///
1027    /// A `Result` containing either the gzip-compressed bytes of the
1028    /// macrocell representation or an error.
1029    ///
1030    /// # Errors
1031    ///
1032    /// Returns an error if:
1033    /// - The pattern is blank (contains no alive cells)
1034    /// - Compression fails
1035    fn to_compressed_macrocell(&self) -> Result<Vec<u8>> {
1036        let macrocell_data = self.to_macrocell()?;
1037        let mut encoder = GzEncoder::new(&macrocell_data[..], Compression::default());
1038        let mut compressed_data = Vec::new();
1039        encoder
1040            .read_to_end(&mut compressed_data)
1041            .context("Failed to compress macrocell data")?;
1042        Ok(compressed_data)
1043    }
1044
1045    /// Creates a pattern from data in the Extended RLE format. See [`PatternFormat::RLE`].
1046    ///
1047    /// # Arguments
1048    ///
1049    /// * `data` - The bytes containing the Extended RLE data.
1050    ///
1051    /// # Returns
1052    ///
1053    /// A `Result` containing either the created `Pattern` or an error.
1054    ///
1055    /// # Errors
1056    ///
1057    /// Returns an error if:
1058    /// - Data is invalid
1059    /// - Algorithm has more than two states
1060    /// - Rule is not B3/S23
1061    ///
1062    /// # Performance Considerations
1063    ///
1064    /// The current implementation struggles with highly non-square patterns
1065    /// (like very long and narrow ones), as it fills a square grid based on the
1066    /// maximum dimension. This can lead to excessive memory usage for patterns
1067    /// with disparate dimensions. Consider using [`PatternFormat::Macrocell`]
1068    /// or [`PatternFormat::CompressedMacrocell`] instead.
1069    fn from_rle(data: &[u8]) -> Result<Self> {
1070        let mut lines = data
1071            .split(|&b| b == b'\n')
1072            .map(|x| x.strip_suffix(b"\r").unwrap_or(x))
1073            .filter(|x| !x.is_empty() && x[0] != b'#');
1074        let width: usize;
1075        let height: usize;
1076
1077        // Parse header
1078        if let Some(line) = lines.next() {
1079            // Parse x, y and rule
1080            let mut parts = line.split(|&b| b == b',').map(|x| x.trim_ascii());
1081
1082            let extract_value = |part: &[u8], expected_key: &[u8]| {
1083                let mut items = part.split(|&b| b == b'=');
1084                let key = items.next().unwrap_or(&[]).trim_ascii_end();
1085                if key != expected_key {
1086                    return Err(anyhow!(
1087                        "Invalid header: expected {}, got {}",
1088                        String::from_utf8_lossy(expected_key),
1089                        String::from_utf8_lossy(key)
1090                    ));
1091                }
1092                let value = items.next().unwrap_or(&[]).trim_ascii_start();
1093                if items.next().is_some() {
1094                    return Err(anyhow!("Invalid header: missing ',' between '='"));
1095                }
1096                Ok(value.to_vec())
1097            };
1098
1099            let value = extract_value(
1100                parts
1101                    .next()
1102                    .ok_or_else(|| anyhow!("Invalid header: missing \"x\""))?,
1103                b"x",
1104            )?;
1105            width = std::str::from_utf8(&value)?.parse()?;
1106
1107            let value = extract_value(
1108                parts
1109                    .next()
1110                    .ok_or_else(|| anyhow!("Invalid header: missing \"y\""))?,
1111                b"y",
1112            )?;
1113            height = std::str::from_utf8(&value)?.parse()?;
1114
1115            // rule is optional
1116            if let Some(rule) = parts.next().map(|part| extract_value(part, b"rule")) {
1117                match rule {
1118                    Ok(rule) => {
1119                        if rule != b"B3/S23" {
1120                            return Err(anyhow!("Only B3/S23 rule is supported"));
1121                        }
1122                    }
1123                    Err(err) => return Err(err),
1124                }
1125            }
1126        } else {
1127            return Err(anyhow!("Missing header"));
1128        }
1129
1130        // Calculate size and prepare cells array
1131        let size_log2 = (width.max(height)).next_power_of_two().ilog2();
1132        let n = 1usize << size_log2;
1133        let stride = n.max(8);
1134        let mut cells = vec![0u8; (n * n).div_ceil(8).max(n)];
1135
1136        // Parse pattern data
1137        let mut x = 0;
1138        let mut y = 0;
1139        let mut count = 0;
1140
1141        for line in lines {
1142            for &b in line {
1143                match b {
1144                    b'0'..=b'9' => count = count * 10 + (b - b'0') as usize,
1145                    b'b' => {
1146                        x += if count == 0 { 1 } else { count };
1147                        count = 0;
1148                    }
1149                    b'o' => {
1150                        let c = if count == 0 { 1 } else { count };
1151                        for i in 0..c {
1152                            if x + i >= width || y >= height {
1153                                return Err(anyhow!(
1154                                    "Pattern data out of bounds: x = {}, y = {}",
1155                                    x + i,
1156                                    y
1157                                ));
1158                            }
1159                            let pos = x + i + y * stride;
1160                            cells[pos / 8] |= 1 << (pos % 8);
1161                        }
1162                        x += c;
1163                        count = 0;
1164                    }
1165                    b'$' => {
1166                        y += if count == 0 { 1 } else { count };
1167                        x = 0;
1168                        count = 0;
1169                    }
1170                    b'!' => break,
1171                    b' ' => continue,
1172                    _ => return Err(anyhow!("Invalid RLE character: '{}'", b as char)),
1173                }
1174                if x > width {
1175                    return Err(anyhow!("Pattern data out of bounds: x = {}, y = {}", x, y));
1176                }
1177            }
1178        }
1179
1180        Self::from_packed_cells(&cells)
1181    }
1182
1183    /// Converts the pattern to the RLE format. See [`PatternFormat::RLE`].
1184    ///
1185    /// # Returns
1186    ///
1187    /// A `Result` containing either the bytes of the RLE representation
1188    /// or an error.
1189    ///
1190    /// # Errors
1191    ///
1192    /// Returns an error if pattern is too large.
1193    ///
1194    /// # Performance Considerations
1195    ///
1196    /// The current implementation struggles with highly non-square patterns
1197    /// (like very long and narrow ones), as it fills a square grid based on the
1198    /// maximum dimension. This can lead to excessive memory usage for patterns
1199    /// with disparate dimensions. Consider using [`PatternFormat::Macrocell`]
1200    /// or [`PatternFormat::CompressedMacrocell`] instead.
1201    fn to_rle(&self) -> Result<Vec<u8>> {
1202        let cells_data = self.to_packed_cells()?;
1203        let n = 1usize << self.size_log2;
1204        let stride = n.max(8);
1205
1206        // Find the bounding box
1207        let mut min_x = n;
1208        let mut max_x = 0;
1209        let mut min_y = n;
1210        let mut max_y = 0;
1211        for y in 0..n {
1212            for x in 0..n {
1213                let pos = x + y * stride;
1214                if cells_data[pos / 8] & (1 << (pos % 8)) != 0 {
1215                    min_x = min_x.min(x);
1216                    max_x = max_x.max(x);
1217                    min_y = min_y.min(y);
1218                    max_y = max_y.max(y);
1219                }
1220            }
1221        }
1222
1223        // Empty pattern
1224        if min_x > max_x || min_y > max_y {
1225            return Ok(b"x = 0, y = 0, rule = B3/S23\n!".to_vec());
1226        }
1227
1228        let mut result = format!(
1229            "x = {}, y = {}, rule = B3/S23\n",
1230            max_x - min_x + 1,
1231            max_y - min_y + 1
1232        )
1233        .into_bytes();
1234        // Encode the pattern
1235        let mut line_length = 0;
1236
1237        for y in min_y..=max_y {
1238            let mut run_length = 0u32;
1239            let mut last_state = false;
1240
1241            for x in min_x..=max_x {
1242                let pos = x + y * stride;
1243                let current_state = (cells_data[pos / 8] & (1 << (pos % 8))) != 0;
1244
1245                if x == min_x || current_state != last_state {
1246                    if run_length > 0 {
1247                        let mut run = Vec::new();
1248                        if run_length > 1 {
1249                            run.extend_from_slice(run_length.to_string().as_bytes());
1250                        }
1251                        run.push(if last_state { b'o' } else { b'b' });
1252
1253                        if line_length + run.len() > 70 {
1254                            result.push(b'\n');
1255                            line_length = 0;
1256                        }
1257
1258                        result.extend_from_slice(&run);
1259                        line_length += run.len();
1260                    }
1261
1262                    run_length = 1;
1263                    last_state = current_state;
1264                } else {
1265                    run_length += 1;
1266                }
1267            }
1268
1269            if run_length > 0 {
1270                let mut run = Vec::new();
1271                if run_length > 1 {
1272                    run.extend_from_slice(run_length.to_string().as_bytes());
1273                }
1274                run.push(if last_state { b'o' } else { b'b' });
1275                if line_length + run.len() + 1 > 70 {
1276                    // +1 for the $ we'll add
1277                    result.push(b'\n');
1278                    line_length = 0;
1279                }
1280                result.extend_from_slice(&run);
1281                line_length += run.len();
1282            }
1283
1284            if y < max_y {
1285                result.push(b'$');
1286                line_length += 1;
1287            } else {
1288                result.push(b'!');
1289            }
1290        }
1291
1292        Ok(result)
1293    }
1294
1295    /// Finds or creates a blank node (containing all dead cells) of the
1296    /// specified size.
1297    ///
1298    /// This is an internal utility method used to efficiently represent empty
1299    /// regions in the quadtree.
1300    ///
1301    /// # Arguments
1302    ///
1303    /// * `size_log2` - Log base 2 of the node's side length.
1304    /// * `kiv` - The key-index-value map storing the pattern nodes.
1305    /// * `blank_nodes` - Cache of previously created blank nodes of different sizes.
1306    ///
1307    /// # Returns
1308    ///
1309    /// The index of the found or newly created blank node.
1310    fn find_or_create_blank_node(
1311        size_log2: SizeLog2,
1312        kiv: &mut KIVMap,
1313        blank_nodes: &mut Vec<NodeIdx>,
1314    ) -> NodeIdx {
1315        let i = (size_log2.max(3) - 3) as usize;
1316        while blank_nodes.len() <= i {
1317            let next = if let Some(&b) = blank_nodes.last() {
1318                PatternNode::Node {
1319                    nw: b,
1320                    ne: b,
1321                    sw: b,
1322                    se: b,
1323                }
1324            } else {
1325                PatternNode::Leaf(0)
1326            };
1327            blank_nodes.push(kiv.find_or_create_node(next));
1328        }
1329        blank_nodes[i]
1330    }
1331
1332    /// Finds a blank node (containing all dead cells) of the specified size.
1333    ///
1334    /// Similar to `find_or_create_blank_node`, but returns `None` if the node doesn't
1335    /// already exist in the provided `kiv`. This allows the function to take a
1336    /// const reference to `KIVMap` rather than requiring a mutable reference.
1337    ///
1338    /// # Arguments
1339    ///
1340    /// * `size_log2` - Log base 2 of the node's side length.
1341    /// * `kiv` - The key-index-value map storing the pattern nodes (immutable).
1342    /// * `blank_nodes` - Cache of previously created blank nodes of different sizes.
1343    ///
1344    /// # Returns
1345    ///
1346    /// An `Option` containing the index of the found blank node, or `None` if
1347    /// the requested blank node doesn't exist in the `kiv`.
1348    fn find_blank_node(
1349        size_log2: SizeLog2,
1350        kiv: &KIVMap,
1351        blank_nodes: &mut Vec<NodeIdx>,
1352    ) -> Option<NodeIdx> {
1353        let i = (size_log2.max(3) - 3) as usize;
1354        if let Some(&b) = blank_nodes.get(i) {
1355            return Some(b);
1356        }
1357        let i = (size_log2.max(3) - 3) as usize;
1358        while blank_nodes.len() <= i {
1359            let next = if let Some(&b) = blank_nodes.last() {
1360                PatternNode::Node {
1361                    nw: b,
1362                    ne: b,
1363                    sw: b,
1364                    se: b,
1365                }
1366            } else {
1367                PatternNode::Leaf(0)
1368            };
1369            if let Some(k) = kiv.find_node(next) {
1370                blank_nodes.push(k);
1371            } else {
1372                return None;
1373            }
1374        }
1375        Some(blank_nodes[i])
1376    }
1377}
1378
1379impl Default for Pattern {
1380    /// Creates a new empty pattern with the following properties:
1381    /// - An 8x8 empty grid (size_log2 = 3)
1382    fn default() -> Self {
1383        let mut kiv = KIVMap::new();
1384        Self {
1385            root: kiv.find_or_create_node(PatternNode::Leaf(0)),
1386            size_log2: 3,
1387            kiv: KIVMap::new(),
1388            blank_nodes: vec![],
1389        }
1390    }
1391}
1392
1393/// A node is either a leaf (8x8 cells) or a non-leaf (4x4 nodes).
1394/// Cells in the leaf are stored as a 64-bit integer in row-major order.
1395#[derive(Clone, Copy, PartialEq, Eq)]
1396pub enum PatternNode {
1397    Leaf(u64),
1398    Node {
1399        nw: NodeIdx,
1400        ne: NodeIdx,
1401        sw: NodeIdx,
1402        se: NodeIdx,
1403    },
1404}
1405
1406impl PatternNode {
1407    /// Fast yet effective hash function.
1408    /// It is used for indexing nodes in the KIVMap.
1409    fn hash(&self) -> u32 {
1410        let h = match self {
1411            PatternNode::Leaf(cells) => (cells ^ (cells >> 32)) as u32,
1412            PatternNode::Node { nw, ne, sw, se } => 0u32
1413                .wrapping_add((nw).wrapping_mul(5))
1414                .wrapping_add((ne).wrapping_mul(17))
1415                .wrapping_add((sw).wrapping_mul(257))
1416                .wrapping_add((se).wrapping_mul(65537)),
1417        };
1418        h.wrapping_add(h.rotate_right(11))
1419    }
1420}
1421
1422/// Key-Index-Value map: growable hashtable with linked list chains.
1423///
1424/// This data structure efficiently stores pattern nodes with two key access methods:
1425/// 1. By index: retrieve a node by its unique integer ID
1426/// 2. By content: find a node with specific content orcreate a new one if
1427///    it doesn't exist
1428///
1429/// KIVMap is an essential component of the Pattern's quadtree implementation,
1430/// providing node deduplication to reduce memory usage in large patterns.
1431///
1432/// # Implementation Details
1433///
1434/// - Uses open addressing with chained linked lists for collision resolution
1435/// - Stores nodes contiguously in a vector for cache-friendly access
1436/// - Maintains separate array of hash table chains for fast lookups
1437/// - Automatically grows the hash table when load factor exceeds 1.0
1438/// - Uses a non-cryptographic hash function optimized for pattern nodes
1439///
1440/// # Panics
1441///
1442/// Panics on running out of indexes (when the number of nodes exceeds
1443/// the maximum value of NodeIdx).
1444#[derive(Clone)]
1445struct KIVMap {
1446    hashmap_chains: Vec<NodeIdx>,
1447    storage: Vec<NodeAndNext>,
1448    capacity_log2: u32,
1449}
1450
1451#[derive(Clone, Copy)]
1452struct NodeAndNext {
1453    node: PatternNode,
1454    next: NodeIdx,
1455}
1456
1457impl KIVMap {
1458    const INITIAL_CAPACITY_LOG2: u32 = 4;
1459
1460    fn new() -> Self {
1461        let mut storage = Vec::with_capacity(1 << Self::INITIAL_CAPACITY_LOG2);
1462        // index 0 is reserved
1463        storage.push(NodeAndNext {
1464            node: PatternNode::Leaf(0),
1465            next: 0,
1466        });
1467        KIVMap {
1468            hashmap_chains: vec![0; 1 << Self::INITIAL_CAPACITY_LOG2],
1469            storage,
1470            capacity_log2: Self::INITIAL_CAPACITY_LOG2,
1471        }
1472    }
1473
1474    fn get_node(&self, idx: NodeIdx) -> &PatternNode {
1475        &self.storage[idx as usize].node
1476    }
1477
1478    fn find_node(&self, node: PatternNode) -> Option<NodeIdx> {
1479        let i = node.hash() as usize & (self.hashmap_chains.len() - 1);
1480        let mut curr = self.hashmap_chains[i];
1481        // search for the node in the linked list
1482        while curr != 0 {
1483            if self.storage[curr as usize].node == node {
1484                return Some(curr);
1485            }
1486            curr = self.storage[curr as usize].next;
1487        }
1488        None
1489    }
1490
1491    fn find_or_create_node(&mut self, node: PatternNode) -> NodeIdx {
1492        let i = node.hash() as usize & (self.hashmap_chains.len() - 1);
1493        let mut curr = self.hashmap_chains[i];
1494        // search for the node in the linked list
1495        while curr != 0 {
1496            if self.storage[curr as usize].node == node {
1497                return curr;
1498            }
1499            curr = self.storage[curr as usize].next;
1500        }
1501
1502        self.storage.push(NodeAndNext {
1503            node,
1504            next: self.hashmap_chains[i],
1505        });
1506        let idx = (self.storage.len() - 1).try_into().expect("Index overflow");
1507        self.hashmap_chains[i] = idx;
1508        if self.storage.len() > self.hashmap_chains.len() && self.capacity_log2 != u32::BITS {
1509            self.rehash();
1510        }
1511        idx
1512    }
1513
1514    fn rehash(&mut self) {
1515        self.capacity_log2 += 1;
1516        let new_size = self.hashmap_chains.len() * 2;
1517        let mut new_chains = vec![0; new_size];
1518        for &chain in &self.hashmap_chains {
1519            let mut curr = chain as usize;
1520            while curr != 0 {
1521                let hash = self.storage[curr].node.hash() as usize;
1522                let next = self.storage[curr].next as usize;
1523                let index = hash & (new_size - 1);
1524                self.storage[curr].next = new_chains[index];
1525                new_chains[index] = curr as NodeIdx;
1526                curr = next;
1527            }
1528        }
1529        self.hashmap_chains = new_chains;
1530    }
1531}
1532
1533/// Supported formats for pattern serialization and deserialization.
1534///
1535/// Notice that only two-state patterns and B3/S23 rules are supported.
1536#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1537pub enum PatternFormat {
1538    /// Packed cells format: a flat array of bytes where each bit represents one cell.
1539    /// Cells are stored in row-major order with minimal stride of 8 cells per row.
1540    PackedCells,
1541
1542    /// [Macrocell](https://golly.sourceforge.io/Help/formats.html#mc) format:
1543    /// a text-based format that efficiently represents large and sparse patterns.
1544    ///
1545    /// This format does not support blank patterns (patterns with no alive cells) and
1546    /// extends tiny patterns to 8x8 cells.
1547    ///
1548    /// Only two-state patterns and B3/S23 rules are supported.
1549    Macrocell,
1550
1551    /// Gzip-compressed macrocell format for more efficient storage.
1552    CompressedMacrocell,
1553
1554    /// [Extended RLE](https://golly.sourceforge.io/Help/formats.html#rle) format:
1555    /// a text-based format that efficiently encodes patterns using run-length encoding.
1556    ///
1557    /// This implementation supports the extended RLE format with header and comments,
1558    /// but only for two-state patterns with B3/S23 rules.
1559    RLE,
1560}
1561
1562#[cfg(test)]
1563mod tests {
1564    use super::*;
1565    const SEED: u64 = 42;
1566    // for simple patterns
1567    const HUGE_SIZE_LOG2_LIMIT: SizeLog2 = 300;
1568    // for conversions from/to packed cells
1569    const MEDIUM_SIZE_LOG2_LIMIT: SizeLog2 = 10;
1570
1571    fn repeat_leaf(leaf: u64, size_log2: SizeLog2) -> Pattern {
1572        let mut kiv = KIVMap::new();
1573        let cells = if size_log2 >= 3 {
1574            leaf
1575        } else {
1576            let mut x = leaf.to_le_bytes();
1577            for i in 0..1 << size_log2 {
1578                x[i] &= ((1u32 << (1 << size_log2)) - 1) as u8;
1579            }
1580            for i in (1 << size_log2)..8 {
1581                x[i] = 0;
1582            }
1583            u64::from_le_bytes(x)
1584        };
1585        let mut node = kiv.find_or_create_node(PatternNode::Leaf(cells));
1586        for _ in 3..size_log2 {
1587            node = kiv.find_or_create_node(PatternNode::Node {
1588                nw: node,
1589                ne: node,
1590                sw: node,
1591                se: node,
1592            });
1593        }
1594        Pattern {
1595            root: node,
1596            size_log2,
1597            kiv,
1598            blank_nodes: vec![],
1599        }
1600    }
1601
1602    #[test]
1603    fn test_population_blank() {
1604        for size_log2 in 0..HUGE_SIZE_LOG2_LIMIT {
1605            // all cells are dead
1606            let pattern = repeat_leaf(0, size_log2);
1607            assert_eq!(pattern.population(), BigInt::ZERO);
1608        }
1609    }
1610
1611    #[test]
1612    fn test_population_sparse() {
1613        for size_log2 in 0..HUGE_SIZE_LOG2_LIMIT {
1614            // one alive cell per leaf
1615            let pattern = repeat_leaf(1, size_log2);
1616            assert_eq!(
1617                pattern.population(),
1618                BigInt::from(1u64) << (size_log2.max(3) - 3) * 2
1619            );
1620        }
1621    }
1622
1623    #[test]
1624    fn test_population_full() {
1625        for size_log2 in 0..HUGE_SIZE_LOG2_LIMIT {
1626            // all cells are alive
1627            let pattern = repeat_leaf(u64::MAX, size_log2);
1628            assert_eq!(pattern.population(), BigInt::from(1u64) << (size_log2 * 2));
1629        }
1630    }
1631
1632    #[test]
1633    fn test_metafy_glider() {
1634        // Create a glider pattern (4x4)
1635        let glider = Pattern::from_rle(b"x = 3, y = 3, rule = B3/S23\nbo$2bo$3o!").unwrap();
1636
1637        // Create metacells where ON is a smaller glider and OFF is empty
1638        let off_metacell = repeat_leaf(0, 3); // All dead cells
1639        let on_metacell =
1640            Pattern::from_packed_cells(&[0b00100000, 0b00010000, 0b01110000, 0, 0, 0, 0, 0])
1641                .unwrap();
1642
1643        // Metafy with level 1
1644        let metafied = glider.metafy([&off_metacell, &on_metacell], 1).unwrap();
1645
1646        // The metafied pattern should have 5 gliders (one for each ON cell in the original glider)
1647        assert_eq!(metafied.size_log2, 5);
1648        assert_eq!(metafied.population(), BigInt::from(5 * 5)); // 5 gliders with 5 cells each
1649    }
1650
1651    #[test]
1652    fn test_metafy_simple() {
1653        // Create a simple pattern (2x2)
1654        let pattern = Pattern::from_packed_cells(&[0b11, 0b10]).unwrap();
1655
1656        // Create simple metacells (8x8)
1657        let off_metacell = repeat_leaf(1, 3); // 1 alive cell
1658        let on_metacell = repeat_leaf(u64::MAX, 3); // All alive cells
1659
1660        let mut off_population = BigInt::from(0);
1661        let mut on_population = BigInt::from(1);
1662        let mut level = 0;
1663        let size_log2 = |level: u32| 1 + level * 3;
1664        while size_log2(level) < HUGE_SIZE_LOG2_LIMIT {
1665            let metafied = pattern
1666                .metafy([&off_metacell, &on_metacell], level)
1667                .unwrap();
1668
1669            assert_eq!(metafied.size_log2, size_log2(level));
1670            assert_eq!(
1671                metafied.population(),
1672                off_population.clone() + on_population.clone() * 3
1673            );
1674            (off_population, on_population) = (
1675                off_population * 63 + on_population.clone(),
1676                on_population * 64,
1677            );
1678            level += 1;
1679        }
1680    }
1681
1682    #[test]
1683    fn test_metafy_random() {
1684        for pattern_size_log2 in 0..6 {
1685            let pattern_total_count = BigInt::from(1) << pattern_size_log2 * 2;
1686            let pattern = Pattern::random(pattern_size_log2, Some(SEED)).unwrap();
1687            for metacell_size_log2 in 3..6 {
1688                let metacell_total_count = BigInt::from(1) << metacell_size_log2 * 2;
1689                let off_metacell = Pattern::random(metacell_size_log2, Some(SEED + 1)).unwrap();
1690                let on_metacell = Pattern::random(metacell_size_log2, Some(SEED + 2)).unwrap();
1691                let metacells = [&off_metacell, &on_metacell];
1692
1693                let mut populations = [BigInt::from(0), BigInt::from(1)];
1694                let mut total_count = BigInt::from(1);
1695                for level in 0..10 {
1696                    let metafied = pattern.metafy(metacells, level).unwrap();
1697
1698                    assert_eq!(
1699                        metafied.size_log2,
1700                        pattern_size_log2 + metacell_size_log2 * level
1701                    );
1702                    assert_eq!(
1703                        metafied.population(),
1704                        pattern.population() * &populations[1]
1705                            + (&pattern_total_count - pattern.population()) * &populations[0]
1706                    );
1707                    populations = [
1708                        &populations[0] * metacells[1].population()
1709                            + (&total_count - &populations[0]) * &metacells[0].population(),
1710                        &populations[1] * metacells[1].population()
1711                            + (&total_count - &populations[1]) * &metacells[0].population(),
1712                    ];
1713                    total_count *= &metacell_total_count;
1714                }
1715            }
1716        }
1717    }
1718
1719    #[test]
1720    fn test_expand() {
1721        let mut pattern = repeat_leaf(1, 0);
1722        for size_log2 in 0..HUGE_SIZE_LOG2_LIMIT {
1723            pattern.expand(size_log2);
1724            assert_eq!(pattern.size_log2, size_log2);
1725            assert_eq!(pattern.population(), BigInt::from(1));
1726        }
1727    }
1728
1729    #[test]
1730    fn test_packed_cells_roundtrip_blank() {
1731        for size_log2 in 0..MEDIUM_SIZE_LOG2_LIMIT {
1732            let pattern = repeat_leaf(0, size_log2);
1733            let data = pattern.to_packed_cells().unwrap();
1734            let deserialized = Pattern::from_packed_cells(&data).unwrap();
1735
1736            assert_eq!(pattern.size_log2, deserialized.size_log2);
1737            assert_eq!(pattern.population(), deserialized.population());
1738            assert_eq!(pattern.hash(), deserialized.hash());
1739        }
1740    }
1741
1742    #[test]
1743    fn test_packed_cells_roundtrip_sparse() {
1744        for size_log2 in 0..MEDIUM_SIZE_LOG2_LIMIT {
1745            let pattern = repeat_leaf(1, size_log2);
1746            let data = pattern.to_packed_cells().unwrap();
1747            let deserialized = Pattern::from_packed_cells(&data).unwrap();
1748
1749            assert_eq!(pattern.size_log2, deserialized.size_log2);
1750            assert_eq!(pattern.population(), deserialized.population());
1751            assert_eq!(pattern.hash(), deserialized.hash());
1752        }
1753    }
1754
1755    #[test]
1756    fn test_packed_cells_roundtrip_full() {
1757        for size_log2 in 0..MEDIUM_SIZE_LOG2_LIMIT {
1758            let pattern = repeat_leaf(u64::MAX, size_log2);
1759            let data = pattern.to_packed_cells().unwrap();
1760            let deserialized = Pattern::from_packed_cells(&data).unwrap();
1761
1762            assert_eq!(pattern.size_log2, deserialized.size_log2);
1763            assert_eq!(pattern.population(), deserialized.population());
1764            assert_eq!(pattern.hash(), deserialized.hash());
1765        }
1766    }
1767
1768    #[test]
1769    fn test_packed_cells_roundtrip_random() {
1770        for size_log2 in 0..MEDIUM_SIZE_LOG2_LIMIT {
1771            let pattern = Pattern::random(size_log2, Some(SEED)).unwrap();
1772            let data = pattern.to_packed_cells().unwrap();
1773            let deserialized = Pattern::from_packed_cells(&data).unwrap();
1774
1775            assert_eq!(pattern.size_log2, deserialized.size_log2);
1776            assert_eq!(pattern.population(), deserialized.population());
1777            assert_eq!(pattern.hash(), deserialized.hash());
1778        }
1779    }
1780
1781    #[test]
1782    fn test_macrocell_roundtrip_sparse() {
1783        for size_log2 in 0..HUGE_SIZE_LOG2_LIMIT {
1784            let pattern = repeat_leaf(1, size_log2);
1785            let data = pattern.to_macrocell().unwrap();
1786            let deserialized = Pattern::from_macrocell(&data).unwrap();
1787
1788            assert_eq!(pattern.size_log2.max(3), deserialized.size_log2);
1789            assert_eq!(pattern.population(), deserialized.population());
1790            assert_eq!(pattern.hash(), deserialized.hash());
1791        }
1792    }
1793
1794    #[test]
1795    fn test_macrocell_roundtrip_full() {
1796        for size_log2 in 0..HUGE_SIZE_LOG2_LIMIT {
1797            let pattern = repeat_leaf(u64::MAX, size_log2);
1798            let data = pattern.to_macrocell().unwrap();
1799            let deserialized = Pattern::from_macrocell(&data).unwrap();
1800
1801            assert_eq!(pattern.size_log2.max(3), deserialized.size_log2);
1802            assert_eq!(pattern.population(), deserialized.population());
1803            assert_eq!(pattern.hash(), deserialized.hash());
1804        }
1805    }
1806
1807    #[test]
1808    fn test_macrocell_roundtrip_random() {
1809        for size_log2 in 0..MEDIUM_SIZE_LOG2_LIMIT {
1810            let pattern = Pattern::random(size_log2, Some(SEED)).unwrap();
1811            // Be careful: blank patterns are not supported
1812            let data = pattern.to_macrocell().unwrap();
1813            let deserialized = Pattern::from_macrocell(&data).unwrap();
1814
1815            assert_eq!(pattern.size_log2.max(3), deserialized.size_log2);
1816            assert_eq!(pattern.population(), deserialized.population());
1817            assert_eq!(pattern.hash(), deserialized.hash());
1818        }
1819    }
1820
1821    #[test]
1822    fn test_compressed_macrocell_roundtrip_random() {
1823        for size_log2 in 0..MEDIUM_SIZE_LOG2_LIMIT {
1824            let pattern = Pattern::random(size_log2, Some(SEED)).unwrap();
1825            // Be careful: blank patterns are not supported
1826            let data = pattern.to_compressed_macrocell().unwrap();
1827            let deserialized = Pattern::from_compressed_macrocell(&data).unwrap();
1828
1829            assert_eq!(pattern.size_log2.max(3), deserialized.size_log2);
1830            assert_eq!(pattern.population(), deserialized.population());
1831            assert_eq!(pattern.hash(), deserialized.hash());
1832        }
1833    }
1834
1835    #[test]
1836    fn test_rle_roundtrip_random() {
1837        for size_log2 in 0..MEDIUM_SIZE_LOG2_LIMIT {
1838            let pattern = Pattern::random(size_log2, Some(SEED)).unwrap();
1839            // Be careful: empty rows/columns at the top/left of the original pattern are trimmed
1840            let data = pattern.to_rle().unwrap();
1841            let deserialized = Pattern::from_rle(&data).unwrap();
1842
1843            assert_eq!(pattern.size_log2, deserialized.size_log2);
1844            assert_eq!(pattern.population(), deserialized.population());
1845            assert_eq!(pattern.hash(), deserialized.hash());
1846        }
1847    }
1848
1849    #[test]
1850    fn test_conversions_glider() {
1851        let packed = Pattern::from_packed_cells(&[0b010, 0b100, 0b111, 0]).unwrap();
1852        let rle = Pattern::from_rle(b"x = 3, y = 3, rule = B3/S23\nbo$2bo$3o!").unwrap();
1853        let macrocell = Pattern::from_macrocell(b"[M2]\n#R B3/S23\n.*.$..*$***$").unwrap();
1854
1855        assert_eq!(packed.hash(), rle.hash());
1856        assert_eq!(packed.hash(), macrocell.hash());
1857    }
1858}