tile.rs
7.5 kB · rust · 316 lines
1use std::collections::BTreeSet;23#[derive(Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Debug)]4pub struct Tile {5 pub side: usize,6 pub cells: Vec<bool>,7}89impl Tile {10 pub fn new(side: usize) -> Tile {11 Tile {12 side,13 cells: vec![false; side * side],14 }15 }1617 pub fn at(&self, r: usize, c: usize) -> bool {18 self.cells[r * self.side + c]19 }2021 pub fn set(&mut self, r: usize, c: usize) {22 let side = self.side;23 self.cells[r * side + c] = true;24 }2526 pub fn fill(&self) -> usize {27 self.cells.iter().filter(|cell| **cell).count()28 }2930 pub fn empty(&self) -> bool {31 !self.cells.iter().any(|cell| *cell)32 }3334 pub fn pack(&self) -> u64 {35 let mut key = 0u64;36 for (index, cell) in self.cells.iter().enumerate() {37 if *cell {38 key |= 1u64 << index;39 }40 }41 key42 }4344 pub fn key(&self) -> [u64; 3] {45 let mut key = [0u64; 3];46 for (index, cell) in self.cells.iter().enumerate() {47 if *cell {48 key[index / 64] |= 1u64 << (index % 64);49 }50 }51 key52 }5354 pub fn support(&self) -> Vec<(usize, usize)> {55 let mut out = Vec::new();56 for r in 0..self.side {57 for c in 0..self.side {58 if self.at(r, c) {59 out.push((r, c));60 }61 }62 }63 out64 }6566 pub fn text(&self) -> String {67 let cells: Vec<String> = self68 .support()69 .iter()70 .map(|(r, c)| format!("({r},{c})"))71 .collect();72 format!("[{}]{{{}}}", self.side, cells.join(","))73 }74}7576pub fn mask_tile(mask: u64, side: usize) -> Tile {77 let mut tile = Tile::new(side);78 for index in 0..side * side {79 if (mask >> index) & 1 == 1 {80 tile.set(index / side, index % side);81 }82 }83 tile84}8586pub fn unpack(key: u64, side: usize) -> Tile {87 mask_tile(key, side)88}8990pub fn kron(outer: &Tile, inner: &Tile) -> Tile {91 let side = outer.side * inner.side;92 let mut out = Tile::new(side);93 let n = inner.side;94 for i in 0..outer.side {95 for j in 0..outer.side {96 if !outer.at(i, j) {97 continue;98 }99 for p in 0..n {100 for q in 0..n {101 if inner.at(p, q) {102 out.set(i * n + p, j * n + q);103 }104 }105 }106 }107 }108 out109}110111pub fn divisors(n: usize) -> Vec<usize> {112 (1..=n).filter(|d| n % d == 0).collect()113}114115pub fn split(tile: &Tile, d: usize) -> Option<(Tile, Tile)> {116 if tile.side % d != 0 {117 return None;118 }119 let n = tile.side / d;120 let mut outer = Tile::new(d);121 let mut inner: Option<Tile> = None;122 for i in 0..d {123 for j in 0..d {124 let mut block = Tile::new(n);125 let mut live = false;126 for p in 0..n {127 for q in 0..n {128 if tile.at(i * n + p, j * n + q) {129 block.set(p, q);130 live = true;131 }132 }133 }134 if !live {135 continue;136 }137 outer.set(i, j);138 match &inner {139 None => inner = Some(block),140 Some(first) => {141 if *first != block {142 return None;143 }144 }145 }146 }147 }148 inner.map(|block| (outer, block))149}150151pub fn cuts(tile: &Tile) -> Vec<usize> {152 divisors(tile.side)153 .into_iter()154 .filter(|d| split(tile, *d).is_some())155 .collect()156}157158pub fn irreducible(tile: &Tile) -> bool {159 if tile.side < 2 || tile.empty() {160 return false;161 }162 !divisors(tile.side)163 .into_iter()164 .any(|d| d > 1 && d < tile.side && split(tile, d).is_some())165}166167pub fn separable(tile: &Tile) -> bool {168 let mut rows = BTreeSet::new();169 let mut cols = BTreeSet::new();170 for (r, c) in tile.support() {171 rows.insert(r);172 cols.insert(c);173 }174 rows.len() * cols.len() == tile.fill()175}176177pub fn factorisations(tile: &Tile) -> Vec<Vec<Tile>> {178 let mut out: Vec<Vec<Tile>> = Vec::new();179 let mut found = false;180 for d in divisors(tile.side) {181 if d == 1 || d == tile.side {182 continue;183 }184 if let Some((outer, inner)) = split(tile, d) {185 found = true;186 for left in factorisations(&outer) {187 for right in factorisations(&inner) {188 let mut whole = left.clone();189 whole.extend(right.iter().cloned());190 if !out.contains(&whole) {191 out.push(whole);192 }193 }194 }195 }196 }197 if !found {198 out.push(vec![tile.clone()]);199 }200 out201}202203pub fn profile(word: &[Tile]) -> Vec<usize> {204 word.iter().map(|tile| tile.side).collect()205}206207pub fn chain(word: &[Tile]) -> Vec<usize> {208 let mut out = vec![1usize];209 let mut run = 1usize;210 for tile in word {211 run *= tile.side;212 out.push(run);213 }214 out215}216217pub fn totally_ordered(set: &BTreeSet<usize>) -> bool {218 let list: Vec<usize> = set.iter().copied().collect();219 for i in 0..list.len() {220 for j in i + 1..list.len() {221 if list[j] % list[i] != 0 {222 return false;223 }224 }225 }226 true227}228229pub fn incomparable(list: &[usize]) -> bool {230 for i in 0..list.len() {231 for j in i + 1..list.len() {232 if list[j] % list[i] != 0 && list[i] % list[j] != 0 {233 return true;234 }235 }236 }237 false238}239240pub fn gcd(a: usize, b: usize) -> usize {241 if b == 0 {242 a243 } else {244 gcd(b, a % b)245 }246}247248pub fn lcm(a: usize, b: usize) -> usize {249 a / gcd(a, b) * b250}251252pub fn line_kron(outer: u128, outer_side: usize, inner: u128, inner_side: usize) -> u128 {253 let mut out = 0u128;254 for i in 0..outer_side {255 if (outer >> i) & 1 == 0 {256 continue;257 }258 for p in 0..inner_side {259 if (inner >> p) & 1 == 1 {260 out |= 1u128 << (i * inner_side + p);261 }262 }263 }264 out265}266267pub fn line_split(mask: u128, side: usize, d: usize) -> Option<(u128, u128)> {268 if side % d != 0 {269 return None;270 }271 let n = side / d;272 let full = if n >= 128 {273 u128::MAX274 } else {275 (1u128 << n) - 1276 };277 let mut outer = 0u128;278 let mut inner: Option<u128> = None;279 for i in 0..d {280 let block = (mask >> (i * n)) & full;281 if block == 0 {282 continue;283 }284 outer |= 1u128 << i;285 match inner {286 None => inner = Some(block),287 Some(first) => {288 if first != block {289 return None;290 }291 }292 }293 }294 inner.map(|block| (outer, block))295}296297pub fn line_cuts(mask: u128, side: usize) -> Vec<usize> {298 divisors(side)299 .into_iter()300 .filter(|d| line_split(mask, side, *d).is_some())301 .collect()302}303304pub fn line_reducible(mask: u128, side: usize) -> bool {305 divisors(side)306 .into_iter()307 .any(|d| d > 1 && d < side && line_split(mask, side, d).is_some())308}309310pub fn line_text(mask: u128, side: usize) -> String {311 let cells: Vec<String> = (0..side)312 .filter(|i| (mask >> i) & 1 == 1)313 .map(|i| format!("{i}"))314 .collect();315 format!("{{{}}}", cells.join(","))316}