spiral.rs

24.0 kB · rust · 682 lines

1use crate::factor::mobius_sieve;2use crate::prime::{is_prime, Sieve};34const HEX: [(i64, i64); 6] = [(1, 0), (1, -1), (0, -1), (-1, 0), (-1, 1), (0, 1)];56/// The two lattices a spiral of the whole numbers is wound on, one at the centre and two to its right.7#[derive(Clone, Copy, Debug, PartialEq, Eq)]8pub enum Lattice {9    /// Unit squares turning anticlockwise with y up: ring k holds 8k cells and ends at the odd square (2k + 1)^2 on the diagonal below right.10    Square,11    /// Hexagons in axial coordinates q and r, r growing downward: ring r holds 6r cells and ends at the centered hexagonal number 3r^2 + 3r + 1 below right of the centre.12    Hex,13}1415impl Lattice {16    /// Reads a lattice from its name.17    pub fn named(name: &str) -> Option<Lattice> {18        match name {19            "square" => Some(Lattice::Square),20            "hex" => Some(Lattice::Hex),21            _ => None,22        }23    }24    /// Returns the outermost ring of a sheet the odd side wide, half the side rounded down.25    pub fn radius(self, side: usize) -> usize {26        side.saturating_sub(1) / 227    }28    /// Returns the count of numbers a sheet the odd side wide holds: the side squared, or the hexagon of that many cells across.29    pub fn count(self, side: usize) -> usize {30        let r = self.radius(side);31        match self {32            Lattice::Square => (2 * r + 1) * (2 * r + 1),33            Lattice::Hex => 3 * r * r + 3 * r + 1,34        }35    }36    /// Returns the ring a number sits on, zero for one.37    pub fn ring(self, n: u64) -> u64 {38        if n < 2 {39            return 0;40        }41        match self {42            Lattice::Square => (n - 1).isqrt().div_ceil(2),43            Lattice::Hex => {44                let mut r = ((12 * n - 3).isqrt() - 3) / 6;45                while 3 * r * r + 3 * r + 1 < n {46                    r += 1;47                }48                r49            }50        }51    }52    /// Returns the ring of a cell: the larger of the coordinates on the square, the hex distance on the hexagon.53    pub fn ring_of(self, x: i64, y: i64) -> u64 {54        match self {55            Lattice::Square => x.abs().max(y.abs()) as u64,56            Lattice::Hex => x.abs().max(y.abs()).max((x + y).abs()) as u64,57        }58    }59    /// Returns the cell of a number: x right and y up on the square, axial q and r on the hexagon.60    ///61    /// ```62    /// use mrlynum::spiral::Lattice;63    /// assert_eq!(Lattice::Square.xy(10), (2, -1));64    /// assert_eq!(Lattice::Hex.xy(8), (1, 1));65    /// ```66    pub fn xy(self, n: u64) -> (i64, i64) {67        let k = self.ring(n) as i64;68        if k == 0 {69            return (0, 0);70        }71        let n = n as i64;72        match self {73            Lattice::Square => {74                let m = (2 * k + 1) * (2 * k + 1);75                if n >= m - 2 * k {76                    (k - (m - n), -k)77                } else if n >= m - 4 * k {78                    (-k, -k + (m - 2 * k - n))79                } else if n >= m - 6 * k {80                    (-k + (m - 4 * k - n), k)81                } else {82                    (k, k - (m - 6 * k - n))83                }84            }85            Lattice::Hex => {86                let i = n - (3 * k * k - 3 * k + 1) - 1;87                let (side, step) = ((i / k) as usize, i % k + 1);88                let (cq, cr) = HEX[(side + 5) % 6];89                let (dq, dr) = HEX[(side + 1) % 6];90                (k * cq + step * dq, k * cr + step * dr)91            }92        }93    }94    /// Returns the number at a cell, one at the origin.95    ///96    /// ```97    /// use mrlynum::spiral::Lattice;98    /// assert_eq!(Lattice::Square.n(2, -2), 25);99    /// assert_eq!(Lattice::Hex.n(0, 2), 19);100    /// ```101    pub fn n(self, x: i64, y: i64) -> u64 {102        let k = self.ring_of(x, y) as i64;103        if k == 0 {104            return 1;105        }106        let n = match self {107            Lattice::Square => {108                let m = (2 * k + 1) * (2 * k + 1);109                if y == -k {110                    m - k + x111                } else if x == -k {112                    m - 3 * k - y113                } else if y == k {114                    m - 5 * k - x115                } else {116                    m - 7 * k + y117                }118            }119            Lattice::Hex => {120                let base = 3 * k * k - 3 * k + 1;121                let (side, step) = if x > 0 && y >= 0 && x + y == k {122                    (0, x)123                } else if x == k {124                    (1, -y)125                } else if y == -k && x >= 0 {126                    (2, k - x)127                } else if x + y == -k && x < 0 {128                    (3, -x)129                } else if x == -k {130                    (4, y)131                } else {132                    (5, x + k)133                };134                base + side * k + step135            }136        };137        n as u64138    }139}140141/// What a cell is painted for.142#[derive(Clone, Copy, Debug, PartialEq, Eq)]143pub enum Mark {144    /// The primes.145    Prime,146    /// The primes with a prime two away.147    Twin,148    /// The numbers no prime squares into.149    Squarefree,150    /// The Mobius value: one, minus one, or zero for a squared factor.151    Mobius,152}153154impl Mark {155    /// Reads a mark from its name.156    pub fn named(name: &str) -> Option<Mark> {157        match name {158            "prime" => Some(Mark::Prime),159            "twin" => Some(Mark::Twin),160            "squarefree" => Some(Mark::Squarefree),161            "mobius" => Some(Mark::Mobius),162            _ => None,163        }164    }165}166167/// Returns whether every number from zero through the limit is prime, by one sieve.168pub fn flags(limit: usize) -> Vec<bool> {169    let mut sieve = Sieve::new(limit);170    sieve.finish();171    sieve.types().iter().map(|&t| t == 1).collect()172}173174/// Marks every number from zero through the limit: one when marked, minus one for a Mobius value of minus one, else zero.175///176/// ```177/// assert_eq!(mrlynum::spiral::marks(mrlynum::spiral::Mark::Mobius, 6), vec![0, 1, -1, -1, 0, -1, 1]);178/// ```179pub fn marks(mark: Mark, limit: usize) -> Vec<i8> {180    match mark {181        Mark::Prime => flags(limit).iter().map(|&p| i8::from(p)).collect(),182        Mark::Twin => {183            let prime = flags(limit);184            (0..=limit)185                .map(|n| {186                    let twin = (n >= 2 && prime[n - 2]) || is_prime(n + 2);187                    i8::from(prime[n] && twin)188                })189                .collect()190        }191        Mark::Squarefree => mobius_sieve(limit)192            .iter()193            .map(|&m| i8::from(m != 0))194            .collect(),195        Mark::Mobius => mobius_sieve(limit),196    }197}198199/// The readout of one quadratic a k^2 + b k + c across a sheet: where it lands and how often on a prime.200#[derive(Clone, Debug, PartialEq)]201pub struct Diagonal {202    /// The count of numbers the sheet holds.203    pub top: usize,204    /// The count of primes among them.205    pub primes: usize,206    /// The primes as a share of the numbers.207    pub density: f64,208    /// The values of the quadratic inside the sheet, k counting up from zero.209    pub values: Vec<u64>,210    /// The cell of each value.211    pub cells: Vec<(i64, i64)>,212    /// Whether each value is prime.213    pub hit: Vec<bool>,214    /// The count of values that are prime.215    pub hits: usize,216    /// The count of primes before the first composite.217    pub streak: usize,218    /// The hits as a share of the values, zero when the quadratic misses the sheet.219    pub share: f64,220}221222/// Reads the quadratic a k^2 + b k + c, a at least one, over the sheet the odd side wide: every value from one through the top, its cell, the prime hits and the opening streak.223///224/// ```225/// let read = mrlynum::spiral::diagonal(mrlynum::spiral::Lattice::Square, 201, 4, -2, 41);226/// assert_eq!((read.top, read.streak), (40401, 21));227/// ```228pub fn diagonal(lattice: Lattice, side: usize, a: i64, b: i64, c: i64) -> Diagonal {229    let top = lattice.count(side);230    let prime = flags(top);231    let mut values = Vec::new();232    let mut k = 0i64;233    while k <= b.abs() + side as i64 + 2 {234        let v = a * k * k + b * k + c;235        if v >= 1 && v <= top as i64 {236            values.push(v as u64);237        }238        if v > top as i64 && 2 * a * k + b >= 0 {239            break;240        }241        k += 1;242    }243    let cells = values.iter().map(|&v| lattice.xy(v)).collect();244    let hit: Vec<bool> = values.iter().map(|&v| prime[v as usize]).collect();245    let primes = prime.iter().filter(|&&p| p).count();246    let hits = hit.iter().filter(|&&h| h).count();247    Diagonal {248        top,249        primes,250        density: primes as f64 / top.max(1) as f64,251        share: if hit.is_empty() {252            0.0253        } else {254            hits as f64 / hit.len() as f64255        },256        hits,257        streak: hit.iter().take_while(|&&h| h).count(),258        values,259        cells,260        hit,261    }262}263264/// Which cells of the square winding grow into a tile.265#[derive(Clone, Copy, Debug, PartialEq, Eq)]266pub enum Growth {267    /// Only the primes grow; one and every composite stay unit cells.268    Prime,269    /// Every number grows.270    Every,271}272273impl Growth {274    /// Reads a growth from its name.275    pub fn named(name: &str) -> Option<Growth> {276        match name {277            "prime" => Some(Growth::Prime),278            "every" => Some(Growth::Every),279            _ => None,280        }281    }282}283284/// One tile of the snail: the number it stands for, its level, the side of its square, whether the number is prime, and the lower-left corner it is laid at.285#[derive(Clone, Copy, Debug, PartialEq, Eq)]286pub struct Tile {287    /// The number the tile stands for.288    pub n: u64,289    /// The level the design is grown to.290    pub level: u32,291    /// The side of the tile, the base raised to the level.292    pub side: u64,293    /// Whether the number is prime.294    pub prime: bool,295    /// The x of the lower-left corner.296    pub x: i64,297    /// The y of the lower-left corner.298    pub y: i64,299}300301/// The snail: every tile of the winding, the tallies, the area drawn and the box filled.302#[derive(Clone, Debug, PartialEq)]303pub struct Snail {304    /// The base every tile side is a power of.305    pub base: u64,306    /// Every tile, in the order one, two, three and on.307    pub tiles: Vec<Tile>,308    /// The count of primes at or below the top.309    pub primes: usize,310    /// The count of tiles at each level, from zero up.311    pub levels: Vec<usize>,312    /// The sum of the tile areas, a tile counted once wherever it overlaps another.313    pub area: u128,314    /// The lower-left corner of the box the tiles fill.315    pub low: (i64, i64),316    /// The upper-right corner of the box the tiles fill.317    pub high: (i64, i64),318}319320/// Returns the level of a number in a base, the count of its digits less one, so zero below the base and one at the base itself.321///322/// ```323/// use mrlynum::spiral::level_of;324/// assert_eq!((level_of(1, 3), level_of(2, 3), level_of(3, 3), level_of(8, 3), level_of(9, 3)), (0, 0, 1, 1, 2));325/// ```326pub fn level_of(n: u64, base: u64) -> u32 {327    let base = base.max(2);328    let (mut level, mut reach) = (0, base);329    while reach <= n {330        reach *= base;331        level += 1;332    }333    level334}335336/// Winds one to the top on the square spiral and lays a square tile on every cell, the snail.337///338/// The tile of n has side base to the level of n when n grows and side one when it does not, and its lower-left corner is the corner of the tile before it plus one unit step of the winding scaled by the side of that earlier tile. Tiles overlap wherever the growth outruns the winding; a base below two is read as two and a top below one as one.339///340/// ```341/// use mrlynum::spiral::{snail, Growth};342/// let shell = snail(3, 9, Growth::Every);343/// assert_eq!(shell.tiles[0].side, 1);344/// assert_eq!(shell.tiles[8].side, 9);345/// ```346pub fn snail(base: u64, top: u64, growth: Growth) -> Snail {347    let base = base.max(2);348    let top = top.max(1);349    let prime = flags(top as usize);350    let mut tiles = Vec::with_capacity(top as usize);351    let mut levels = vec![0usize; level_of(top, base) as usize + 1];352    let (mut x, mut y) = (0i64, 0i64);353    let (mut low, mut high) = ((0i64, 0i64), (0i64, 0i64));354    let mut area = 0u128;355    let mut cell = (0i64, 0i64);356    for n in 1..=top {357        let is_prime = prime[n as usize];358        let level = if growth == Growth::Every || is_prime {359            level_of(n, base)360        } else {361            0362        };363        let side = base.pow(level);364        levels[level as usize] += 1;365        area += u128::from(side) * u128::from(side);366        low = (low.0.min(x), low.1.min(y));367        high = (high.0.max(x + side as i64), high.1.max(y + side as i64));368        tiles.push(Tile {369            n,370            level,371            side,372            prime: is_prime,373            x,374            y,375        });376        let next = Lattice::Square.xy(n + 1);377        x += (next.0 - cell.0) * side as i64;378        y += (next.1 - cell.1) * side as i64;379        cell = next;380    }381    Snail {382        base,383        tiles,384        primes: prime.iter().filter(|&&p| p).count(),385        levels,386        area,387        low,388        high,389    }390}391392#[cfg(test)]393mod tests {394    use super::*;395    use crate::classics::primes;396    use crate::factor::{mobius, squarefree};397398    #[test]399    fn the_square_spiral_pins_the_first_rings() {400        let first: Vec<(i64, i64)> = (1..=10).map(|n| Lattice::Square.xy(n)).collect();401        assert_eq!(402            first,403            vec![404                (0, 0),405                (1, 0),406                (1, 1),407                (0, 1),408                (-1, 1),409                (-1, 0),410                (-1, -1),411                (0, -1),412                (1, -1),413                (2, -1)414            ]415        );416        assert_eq!(Lattice::Square.xy(25), (2, -2));417        assert_eq!(Lattice::Square.xy(0), (0, 0));418        for k in 1..=30u64 {419            let odd = (2 * k + 1) * (2 * k + 1);420            assert_eq!(Lattice::Square.xy(odd), (k as i64, -(k as i64)));421            assert_eq!(Lattice::Square.ring(odd), k);422            assert_eq!(Lattice::Square.ring(odd + 1), k + 1);423        }424    }425426    #[test]427    fn the_hex_spiral_pins_the_first_rings() {428        let first: Vec<(i64, i64)> = (1..=8).map(|n| Lattice::Hex.xy(n)).collect();429        assert_eq!(430            first,431            vec![432                (0, 0),433                (1, 0),434                (1, -1),435                (0, -1),436                (-1, 0),437                (-1, 1),438                (0, 1),439                (1, 1)440            ]441        );442        assert_eq!(Lattice::Hex.ring(8), 2);443        assert_eq!((Lattice::Hex.xy(19), Lattice::Hex.ring(19)), ((0, 2), 2));444        assert_eq!((Lattice::Hex.xy(20), Lattice::Hex.ring(20)), ((1, 2), 3));445        for r in 1..=30u64 {446            let last = 3 * r * r + 3 * r + 1;447            assert_eq!(Lattice::Hex.xy(last), (0, r as i64));448            assert_eq!(Lattice::Hex.ring(last), r);449            assert_eq!(Lattice::Hex.ring(last + 1), r + 1);450        }451    }452453    #[test]454    fn both_spirals_walk_neighbours_and_map_back() {455        for lattice in [Lattice::Square, Lattice::Hex] {456            let mut last = (0, 0);457            let mut on_ring = vec![0u64; 200];458            for n in 1..=100_000u64 {459                let (x, y) = lattice.xy(n);460                assert_eq!(lattice.n(x, y), n, "{lattice:?} {n}");461                let ring = lattice.ring(n);462                assert_eq!(lattice.ring_of(x, y), ring, "{lattice:?} {n}");463                on_ring[ring as usize] += 1;464                if n > 1 {465                    let step = (x - last.0, y - last.1);466                    let near = match lattice {467                        Lattice::Square => step.0.abs() + step.1.abs() == 1,468                        Lattice::Hex => HEX.contains(&step),469                    };470                    assert!(near, "{lattice:?} {n}");471                }472                last = (x, y);473            }474            let per = match lattice {475                Lattice::Square => 8,476                Lattice::Hex => 6,477            };478            for (r, &count) in on_ring.iter().enumerate().take(51).skip(1) {479                assert_eq!(count, per * r as u64, "{lattice:?} ring {r}");480            }481        }482    }483484    #[test]485    fn the_sheet_counts_agree_with_the_rings() {486        assert_eq!(Lattice::Square.count(201), 40401);487        assert_eq!(Lattice::Square.count(401), 160801);488        assert_eq!(Lattice::Hex.count(401), 120601);489        assert_eq!(Lattice::Hex.radius(401), 200);490        assert_eq!((Lattice::Hex.count(1), Lattice::Hex.count(0)), (1, 1));491        for side in (1..=101).step_by(2) {492            for lattice in [Lattice::Square, Lattice::Hex] {493                let top = lattice.count(side) as u64;494                assert_eq!(lattice.ring(top), lattice.radius(side) as u64);495                assert_eq!(lattice.ring(top + 1), lattice.radius(side) as u64 + 1);496            }497        }498        assert_eq!(Lattice::named("hex"), Some(Lattice::Hex));499        assert_eq!(Lattice::named("cube"), None);500        assert_eq!(Mark::named("twin"), Some(Mark::Twin));501        assert_eq!(Mark::named("odd"), None);502    }503504    #[test]505    fn the_marks_agree_with_the_single_tests() {506        let limit = 3_000;507        let prime = marks(Mark::Prime, limit);508        let twin = marks(Mark::Twin, limit);509        let free = marks(Mark::Squarefree, limit);510        let mu = marks(Mark::Mobius, limit);511        for n in 0..=limit {512            assert_eq!(prime[n] == 1, is_prime(n), "{n}");513            let pair = is_prime(n) && (is_prime(n + 2) || (n >= 2 && is_prime(n - 2)));514            assert_eq!(twin[n] == 1, pair, "{n}");515            assert_eq!(free[n] == 1, squarefree(n), "{n}");516            assert_eq!(mu[n], mobius(n), "{n}");517        }518        assert_eq!(519            marks(Mark::Prime, 40_000)520                .iter()521                .filter(|&&m| m == 1)522                .count(),523            4203524        );525        assert_eq!(526            marks(Mark::Prime, 40_401)527                .iter()528                .filter(|&&m| m == 1)529                .count(),530            primes(40_401).len()531        );532        assert_eq!(marks(Mark::Twin, 30)[19], 1);533        assert_eq!(marks(Mark::Twin, 30)[23], 0);534    }535536    #[test]537    fn eulers_quadratic_opens_with_twenty_one_primes_on_one_line() {538        let read = diagonal(Lattice::Square, 201, 4, -2, 41);539        assert_eq!((read.top, read.primes), (40401, primes(40401).len()));540        assert_eq!(read.values.len(), 101);541        assert_eq!(read.streak, 21);542        for (k, &v) in read.values.iter().enumerate() {543            let m = 2 * k as u64;544            assert_eq!(v, m * m - m + 41);545        }546        assert_eq!(read.values[21], 1763);547        assert!(!is_prime(1763));548        let direct = read549            .values550            .iter()551            .filter(|&&v| is_prime(v as usize))552            .count();553        assert_eq!(read.hits, direct);554        assert_eq!(read.hits, 80);555        assert_eq!(read.hit.iter().filter(|&&h| h).count(), 80);556        assert!(!read.hit[21]);557        assert!((read.density - read.primes as f64 / 40401.0).abs() < 1e-12);558        assert!((read.share - 80.0 / 101.0).abs() < 1e-12);559        assert_eq!(diagonal(Lattice::Square, 21, 1, 0, 500).share, 0.0);560        for k in 20..=100 {561            assert_eq!(read.cells[k], (k as i64 - 40, k as i64), "{k}");562        }563        let spoke = diagonal(Lattice::Hex, 41, 3, 3, 1);564        assert_eq!(spoke.values.len(), 21);565        for (k, &cell) in spoke.cells.iter().enumerate() {566            assert_eq!(cell, (0, k as i64));567        }568        let dip = diagonal(Lattice::Square, 21, 1, -30, 2);569        assert_eq!(570            dip.values,571            vec![2, 2, 33, 66, 101, 138, 177, 218, 261, 306, 353, 402]572        );573        assert!(diagonal(Lattice::Square, 21, 1, 0, 500).values.is_empty());574    }575    #[test]576    fn the_snail_lays_its_tiles_along_the_winding() {577        let placed = |shell: &Snail| -> Vec<(u64, u32, u64, bool, i64, i64)> {578            shell579                .tiles580                .iter()581                .take(12)582                .map(|t| (t.n, t.level, t.side, t.prime, t.x, t.y))583                .collect()584        };585        let every = snail(3, 100, Growth::Every);586        assert_eq!(587            placed(&every),588            vec![589                (1, 0, 1, false, 0, 0),590                (2, 0, 1, true, 1, 0),591                (3, 1, 3, true, 1, 1),592                (4, 1, 3, false, -2, 1),593                (5, 1, 3, true, -5, 1),594                (6, 1, 3, false, -5, -2),595                (7, 1, 3, true, -5, -5),596                (8, 1, 3, false, -2, -5),597                (9, 2, 9, false, 1, -5),598                (10, 2, 9, false, 10, -5),599                (11, 2, 9, true, 10, 4),600                (12, 2, 9, false, 10, 13),601            ]602        );603        let prime = snail(3, 100, Growth::Prime);604        assert_eq!(605            placed(&prime),606            vec![607                (1, 0, 1, false, 0, 0),608                (2, 0, 1, true, 1, 0),609                (3, 1, 3, true, 1, 1),610                (4, 0, 1, false, -2, 1),611                (5, 1, 3, true, -3, 1),612                (6, 0, 1, false, -3, -2),613                (7, 1, 3, true, -3, -3),614                (8, 0, 1, false, 0, -3),615                (9, 0, 1, false, 1, -3),616                (10, 0, 1, false, 2, -3),617                (11, 2, 9, true, 2, -2),618                (12, 0, 1, false, 2, 7),619            ]620        );621        assert_eq!((every.area, prime.area), (172_100, 29_668));622        assert_eq!(every.levels, vec![2, 6, 18, 54, 20]);623        assert_eq!(prime.levels, vec![76, 3, 5, 13, 3]);624        assert_eq!((every.primes, prime.primes), (25, 25));625        assert_eq!((every.low, every.high), ((-602, -86), (208, 724)));626        assert_eq!((prime.low, prime.high), ((-58, -66), (112, 184)));627    }628629    #[test]630    fn the_snail_grows_by_the_digit_law_and_never_leaves_its_box() {631        for base in [2u64, 3, 5, 7] {632            for growth in [Growth::Every, Growth::Prime] {633                let shell = snail(base, 400, growth);634                assert_eq!(shell.base, base);635                assert_eq!(shell.tiles.len(), 400);636                assert_eq!(shell.levels.iter().sum::<usize>(), 400);637                assert_eq!(shell.levels.len(), level_of(400, base) as usize + 1);638                let mut area = 0u128;639                for (at, tile) in shell.tiles.iter().enumerate() {640                    let n = at as u64 + 1;641                    assert_eq!(tile.n, n);642                    assert_eq!(tile.prime, is_prime(n as usize), "{base} {n}");643                    let level = if growth == Growth::Every || tile.prime {644                        level_of(n, base)645                    } else {646                        0647                    };648                    assert_eq!(649                        (tile.level, tile.side),650                        (level, base.pow(level)),651                        "{base} {n}"652                    );653                    assert!(tile.x >= shell.low.0 && tile.y >= shell.low.1);654                    assert!(tile.x + tile.side as i64 <= shell.high.0);655                    assert!(tile.y + tile.side as i64 <= shell.high.1);656                    area += u128::from(tile.side) * u128::from(tile.side);657                    if at == 0 {658                        assert_eq!((tile.x, tile.y), (0, 0));659                        continue;660                    }661                    let last = shell.tiles[at - 1];662                    let step = (663                        Lattice::Square.xy(n).0 - Lattice::Square.xy(n - 1).0,664                        Lattice::Square.xy(n).1 - Lattice::Square.xy(n - 1).1,665                    );666                    assert_eq!(step.0.abs() + step.1.abs(), 1, "{base} {n}");667                    assert_eq!(tile.x, last.x + step.0 * last.side as i64, "{base} {n}");668                    assert_eq!(tile.y, last.y + step.1 * last.side as i64, "{base} {n}");669                }670                assert_eq!(shell.area, area);671            }672        }673        assert_eq!(level_of(1, 10), 0);674        assert_eq!(level_of(999, 10), 2);675        assert_eq!(level_of(1000, 10), 3);676        assert_eq!(level_of(5, 1), level_of(5, 2));677        assert_eq!(snail(1, 0, Growth::Every).tiles.len(), 1);678        assert_eq!(Growth::named("prime"), Some(Growth::Prime));679        assert_eq!(Growth::named("every"), Some(Growth::Every));680        assert_eq!(Growth::named("some"), None);681    }682}