diff options
| author | Sebastiano Tronto <sebastiano@tronto.net> | 2024-08-18 14:26:45 +0200 |
|---|---|---|
| committer | Sebastiano Tronto <sebastiano@tronto.net> | 2024-08-18 14:26:45 +0200 |
| commit | 18c9a8b8905304cf5f8fc15825769046a3144866 (patch) | |
| tree | a7807bb32b0a5d9ded7d3cedccc598f64a9b00fe /src/solve_h48.h | |
| parent | f25a10e19eca294c4e6a99e4f80ce5cfd11a0e5f (diff) | |
| download | nissy-core-18c9a8b8905304cf5f8fc15825769046a3144866.tar.gz nissy-core-18c9a8b8905304cf5f8fc15825769046a3144866.zip | |
Reorganized folder structure
Diffstat (limited to 'src/solve_h48.h')
| -rw-r--r-- | src/solve_h48.h | 839 |
1 files changed, 0 insertions, 839 deletions
diff --git a/src/solve_h48.h b/src/solve_h48.h deleted file mode 100644 index 1701268..0000000 --- a/src/solve_h48.h +++ /dev/null | |||
| @@ -1,839 +0,0 @@ | |||
| 1 | #define MAP_UNSET UINT64_C(0xFFFFFFFFFFFFFFFF) | ||
| 2 | #define MAP_KEYMASK UINT64_C(0xFFFFFFFFFF) | ||
| 3 | #define MAP_KEYSHIFT UINT64_C(40) | ||
| 4 | |||
| 5 | #define COCSEP_CLASSES ((size_t)3393) | ||
| 6 | #define COCSEP_TABLESIZE ((size_t)_3p7 << (size_t)7) | ||
| 7 | #define COCSEP_VISITEDSIZE ((COCSEP_TABLESIZE + (size_t)7) / (size_t)8) | ||
| 8 | #define COCSEP_FULLSIZE ((size_t)4 * (COCSEP_TABLESIZE + (size_t)12)) | ||
| 9 | |||
| 10 | #define ESEP_NOEO (COCSEP_CLASSES * (size_t)_12c4 * (size_t)_8c4) | ||
| 11 | #define ESEP_MAX(h) (ESEP_NOEO << (size_t)(h)) | ||
| 12 | #define ESEP_TABLESIZE(h, k) (ESEP_MAX((h)) / ((size_t)8 / (size_t)(k))) | ||
| 13 | |||
| 14 | #define COCLASS_MASK (UINT32_C(0xFFFF) << UINT32_C(16)) | ||
| 15 | #define COCLASS(x) (((x) & COCLASS_MASK) >> UINT32_C(16)) | ||
| 16 | #define TTREP_MASK (UINT32_C(0xFF) << UINT32_C(8)) | ||
| 17 | #define TTREP(x) (((x) & TTREP_MASK) >> UINT32_C(8)) | ||
| 18 | #define CBOUND_MASK UINT32_C(0xFF) | ||
| 19 | #define CBOUND(x) ((x) & CBOUND_MASK) | ||
| 20 | #define H48_ESIZE(h) ((_12c4 * _8c4) << (int64_t)(h)) | ||
| 21 | |||
| 22 | #define ESEP_IND(i) ((uint32_t)(i) / UINT32_C(8)) | ||
| 23 | #define ESEP_SHIFT(i) (UINT32_C(4) * ((uint32_t)(i) % UINT32_C(8))) | ||
| 24 | #define ESEP_MASK(i) ((_bit_u32(4) - (uint32_t)(1)) << ESEP_SHIFT(i)) | ||
| 25 | #define VISITED_IND(i) ((uint32_t)(i) / UINT32_C(8)) | ||
| 26 | #define VISITED_MASK(i) (UINT32_C(1) << ((uint32_t)(i) % UINT32_C(8))) | ||
| 27 | |||
| 28 | #define MAX_SOLUTION_LENGTH 20 | ||
| 29 | |||
| 30 | /* | ||
| 31 | TODO: This loop other similar h48 coordinates can be improved by only | ||
| 32 | transforming edges, but we need to compose transformations (i.e. conjugate | ||
| 33 | _t by _ttrep). | ||
| 34 | */ | ||
| 35 | #define _foreach_h48sim(_cube, _cocsepdata, _selfsim, _h, _action) \ | ||
| 36 | int64_t _cocsep = coord_cocsep(_cube); \ | ||
| 37 | uint8_t _ttrep = TTREP(_cocsepdata[_cocsep]); \ | ||
| 38 | uint8_t _inverse_ttrep = inverse_trans(_ttrep); \ | ||
| 39 | int64_t _coclass = COCLASS(_cocsepdata[_cocsep]); \ | ||
| 40 | cube_t _rep = transform(_cube, _ttrep); \ | ||
| 41 | uint64_t _sim = _selfsim[_coclass]; \ | ||
| 42 | for (uint8_t _t = 0; _t < 48 && _sim; _t++, _sim >>= 1) { \ | ||
| 43 | if (!(_sim & 1)) continue; \ | ||
| 44 | _cube = transform(_rep, _t); \ | ||
| 45 | _cube = transform(_cube, _inverse_ttrep); \ | ||
| 46 | _action \ | ||
| 47 | } | ||
| 48 | |||
| 49 | typedef struct { | ||
| 50 | uint64_t n; | ||
| 51 | uint64_t capacity; | ||
| 52 | uint64_t randomizer; | ||
| 53 | uint64_t *table; | ||
| 54 | } h48map_t; | ||
| 55 | |||
| 56 | typedef struct { | ||
| 57 | uint64_t key; | ||
| 58 | uint64_t val; | ||
| 59 | } kvpair_t; | ||
| 60 | |||
| 61 | typedef struct { | ||
| 62 | cube_t cube; | ||
| 63 | uint8_t depth; | ||
| 64 | uint8_t maxdepth; | ||
| 65 | uint16_t *n; | ||
| 66 | uint32_t *buf32; | ||
| 67 | uint8_t *visited; | ||
| 68 | uint64_t *selfsim; | ||
| 69 | cube_t *rep; | ||
| 70 | } dfsarg_cocsep_t; | ||
| 71 | |||
| 72 | /* TODO keep or not? */ | ||
| 73 | typedef struct { | ||
| 74 | cube_t cube; | ||
| 75 | int8_t nmoves; | ||
| 76 | int8_t depth; | ||
| 77 | uint8_t moves[MAX_SOLUTION_LENGTH]; | ||
| 78 | uint32_t *cocsepdata; | ||
| 79 | h48map_t *visited; | ||
| 80 | } dfsarg_genh48set_t; | ||
| 81 | |||
| 82 | typedef struct { | ||
| 83 | uint8_t depth; | ||
| 84 | uint32_t *cocsepdata; | ||
| 85 | uint32_t *buf32; | ||
| 86 | uint64_t *selfsim; | ||
| 87 | int64_t done; | ||
| 88 | cube_t *crep; | ||
| 89 | } bfsarg_esep_t; | ||
| 90 | |||
| 91 | typedef struct { | ||
| 92 | cube_t cube; | ||
| 93 | cube_t inverse; | ||
| 94 | int8_t nmoves; | ||
| 95 | int8_t depth; | ||
| 96 | uint8_t moves[MAX_SOLUTION_LENGTH]; | ||
| 97 | int64_t *nsols; | ||
| 98 | int64_t maxsolutions; | ||
| 99 | uint8_t h; | ||
| 100 | uint32_t *cocsepdata; | ||
| 101 | uint32_t *h48data; | ||
| 102 | char **nextsol; | ||
| 103 | } dfsarg_solveh48_t; | ||
| 104 | |||
| 105 | typedef struct { | ||
| 106 | cube_t cube; | ||
| 107 | int8_t nmoves; | ||
| 108 | int8_t depth; | ||
| 109 | uint8_t moves[MAX_SOLUTION_LENGTH]; | ||
| 110 | uint32_t *cocsepdata; | ||
| 111 | uint32_t *h48data; | ||
| 112 | char *s; | ||
| 113 | } dfsarg_solveh48stats_t; | ||
| 114 | |||
| 115 | _static void h48map_create(h48map_t *, uint64_t, uint64_t); | ||
| 116 | _static void h48map_clear(h48map_t *); | ||
| 117 | _static void h48map_destroy(h48map_t *); | ||
| 118 | _static uint64_t h48map_lookup(h48map_t *, uint64_t); | ||
| 119 | _static void h48map_insertmin(h48map_t *, uint64_t, uint64_t); | ||
| 120 | _static uint64_t h48map_value(h48map_t *, uint64_t); | ||
| 121 | _static kvpair_t h48map_nextkvpair(h48map_t *, uint64_t *); | ||
| 122 | |||
| 123 | _static_inline int64_t coord_h48(cube_t, const uint32_t *, uint8_t); | ||
| 124 | _static_inline int64_t coord_h48_edges(cube_t, int64_t, uint8_t, uint8_t); | ||
| 125 | _static_inline cube_t invcoord_h48(int64_t, const cube_t *, uint8_t); | ||
| 126 | |||
| 127 | _static_inline bool get_visited(const uint8_t *, int64_t); | ||
| 128 | _static_inline void set_visited(uint8_t *, int64_t); | ||
| 129 | _static_inline uint8_t get_esep_pval(const uint32_t *, int64_t); | ||
| 130 | _static_inline void set_esep_pval(uint32_t *, int64_t, uint8_t); | ||
| 131 | |||
| 132 | _static size_t gendata_cocsep(void *, uint64_t *, cube_t *); | ||
| 133 | _static uint32_t gendata_cocsep_dfs(dfsarg_cocsep_t *); | ||
| 134 | _static uint64_t gen_h48short( | ||
| 135 | uint8_t, const uint32_t *, const cube_t *, const uint64_t *, h48map_t *); | ||
| 136 | _static size_t gendata_h48h0k4(void *, uint8_t); | ||
| 137 | _static int64_t gendata_h48h0k4_bfs(bfsarg_esep_t *); | ||
| 138 | _static int64_t gendata_h48h0k4_bfs_fromdone(bfsarg_esep_t *); | ||
| 139 | _static int64_t gendata_h48h0k4_bfs_fromnew(bfsarg_esep_t *); | ||
| 140 | |||
| 141 | _static void solve_h48_appendsolution(dfsarg_solveh48_t *); | ||
| 142 | _static_inline int8_t get_h48_cdata(cube_t, uint32_t *, uint32_t *); | ||
| 143 | _static_inline int8_t get_h48_bound(cube_t, uint32_t, uint8_t, uint32_t *); | ||
| 144 | _static_inline bool solve_h48_stop(dfsarg_solveh48_t *); | ||
| 145 | _static int64_t solve_h48_dfs(dfsarg_solveh48_t *); | ||
| 146 | _static int64_t solve_h48( | ||
| 147 | cube_t, int8_t, int8_t, int8_t, uint8_t, const void *, char *); | ||
| 148 | |||
| 149 | _static int64_t solve_h48stats_dfs(dfsarg_solveh48stats_t *); | ||
| 150 | _static int64_t solve_h48stats(cube_t, int8_t, const void *, char [static 12]); | ||
| 151 | |||
| 152 | _static void | ||
| 153 | h48map_create(h48map_t *map, uint64_t capacity, uint64_t randomizer) | ||
| 154 | { | ||
| 155 | map->capacity = capacity; | ||
| 156 | map->randomizer = randomizer; | ||
| 157 | |||
| 158 | map->table = malloc(map->capacity * sizeof(int64_t)); | ||
| 159 | h48map_clear(map); | ||
| 160 | } | ||
| 161 | |||
| 162 | _static void | ||
| 163 | h48map_clear(h48map_t *map) | ||
| 164 | { | ||
| 165 | memset(map->table, 0xFF, map->capacity * sizeof(uint64_t)); | ||
| 166 | map->n = 0; | ||
| 167 | } | ||
| 168 | |||
| 169 | _static void | ||
| 170 | h48map_destroy(h48map_t *map) | ||
| 171 | { | ||
| 172 | free(map->table); | ||
| 173 | } | ||
| 174 | |||
| 175 | _static_inline uint64_t | ||
| 176 | h48map_lookup(h48map_t *map, uint64_t x) | ||
| 177 | { | ||
| 178 | uint64_t hash, i; | ||
| 179 | |||
| 180 | hash = ((x % map->capacity) * map->randomizer) % map->capacity; | ||
| 181 | for (i = hash; | ||
| 182 | map->table[i] != MAP_UNSET && (map->table[i] & MAP_KEYMASK) != x; | ||
| 183 | i = (i+1) % map->capacity | ||
| 184 | ) ; | ||
| 185 | |||
| 186 | return i; | ||
| 187 | } | ||
| 188 | |||
| 189 | _static_inline void | ||
| 190 | h48map_insertmin(h48map_t *map, uint64_t key, uint64_t val) | ||
| 191 | { | ||
| 192 | uint64_t i, oldval, min; | ||
| 193 | |||
| 194 | i = h48map_lookup(map, key); | ||
| 195 | oldval = map->table[i] >> MAP_KEYSHIFT; | ||
| 196 | min = _min(val, oldval); | ||
| 197 | |||
| 198 | map->n += map->table[i] == MAP_UNSET; | ||
| 199 | map->table[i] = (key & MAP_KEYMASK) | (min << MAP_KEYSHIFT); | ||
| 200 | } | ||
| 201 | |||
| 202 | _static_inline uint64_t | ||
| 203 | h48map_value(h48map_t *map, uint64_t key) | ||
| 204 | { | ||
| 205 | return map->table[h48map_lookup(map, key)] >> MAP_KEYSHIFT; | ||
| 206 | } | ||
| 207 | |||
| 208 | _static kvpair_t | ||
| 209 | h48map_nextkvpair(h48map_t *map, uint64_t *p) | ||
| 210 | { | ||
| 211 | kvpair_t kv; | ||
| 212 | uint64_t pair; | ||
| 213 | |||
| 214 | kv.key = MAP_UNSET; | ||
| 215 | kv.val = MAP_UNSET; | ||
| 216 | |||
| 217 | DBG_ASSERT(*p < map->capacity, kv, | ||
| 218 | "Error looping over map: given index %" PRIu64 " is out of " | ||
| 219 | "range [0,%" PRIu64 "]", *p, map->capacity); | ||
| 220 | |||
| 221 | for ( ; *p < map->capacity; (*p)++) { | ||
| 222 | if (map->table[*p] != MAP_UNSET) { | ||
| 223 | pair = map->table[(*p)++]; | ||
| 224 | kv.key = pair & MAP_KEYMASK; | ||
| 225 | kv.val = pair >> MAP_KEYSHIFT; | ||
| 226 | return kv; | ||
| 227 | } | ||
| 228 | } | ||
| 229 | |||
| 230 | return kv; | ||
| 231 | } | ||
| 232 | |||
| 233 | _static_inline int64_t | ||
| 234 | coord_h48(cube_t c, const uint32_t *cocsepdata, uint8_t h) | ||
| 235 | { | ||
| 236 | int64_t cocsep, coclass; | ||
| 237 | uint32_t data; | ||
| 238 | uint8_t ttrep; | ||
| 239 | |||
| 240 | DBG_ASSERT(h <= 11, -1, "coord_h48: h must be between 0 and 11\n"); | ||
| 241 | |||
| 242 | cocsep = coord_cocsep(c); | ||
| 243 | data = cocsepdata[cocsep]; | ||
| 244 | coclass = (int64_t)COCLASS(data); | ||
| 245 | ttrep = (int64_t)TTREP(data); | ||
| 246 | |||
| 247 | return coord_h48_edges(c, coclass, ttrep, h); | ||
| 248 | } | ||
| 249 | |||
| 250 | _static_inline int64_t | ||
| 251 | coord_h48_edges(cube_t c, int64_t coclass, uint8_t ttrep, uint8_t h) | ||
| 252 | { | ||
| 253 | cube_t d; | ||
| 254 | int64_t esep, eo, edges; | ||
| 255 | |||
| 256 | d = transform_edges(c, ttrep); | ||
| 257 | esep = coord_esep(d); | ||
| 258 | eo = coord_eo(d); | ||
| 259 | edges = (esep << 11) + eo; | ||
| 260 | |||
| 261 | return (coclass * H48_ESIZE(11) + edges) >> (11 - (int64_t)h); | ||
| 262 | } | ||
| 263 | |||
| 264 | /* | ||
| 265 | This function does not necessarily return a cube whose coordinate is | ||
| 266 | the given value, because it works up to symmetry. This means that the | ||
| 267 | returned cube is a transformed cube of one that gives the correct value. | ||
| 268 | */ | ||
| 269 | _static_inline cube_t | ||
| 270 | invcoord_h48(int64_t i, const cube_t *crep, uint8_t h) | ||
| 271 | { | ||
| 272 | cube_t ret; | ||
| 273 | int64_t hh, coclass, ee, esep, eo; | ||
| 274 | |||
| 275 | DBG_ASSERT(h <= 11, zero, | ||
| 276 | "invcoord_h48: h must be between 0 and 11\n"); | ||
| 277 | |||
| 278 | hh = (int64_t)h; | ||
| 279 | coclass = i / H48_ESIZE(h); | ||
| 280 | ee = i % H48_ESIZE(h); | ||
| 281 | esep = ee >> hh; | ||
| 282 | eo = (ee & ((1 << hh) - 1)) << (11 - hh); | ||
| 283 | |||
| 284 | ret = invcoord_esep(esep); | ||
| 285 | copy_corners(&ret, crep[coclass]); | ||
| 286 | set_eo(&ret, eo); | ||
| 287 | |||
| 288 | return ret; | ||
| 289 | } | ||
| 290 | |||
| 291 | /* | ||
| 292 | Each element of the cocsep table is a uint32_t used as follows: | ||
| 293 | - Lowest 8-bit block: pruning value | ||
| 294 | - Second-lowest 8-bit block: "ttrep" (transformation to representative) | ||
| 295 | - Top 16-bit block: symcoord value | ||
| 296 | After the data as described above, more auxiliary information is appended: | ||
| 297 | - A uint32_t representing the number of symmetry classes | ||
| 298 | - A uint32_t representing the highest value of the pruning table | ||
| 299 | - One uint32_t for each "line" of the pruning table, representing the number | ||
| 300 | of positions having that pruning value. | ||
| 301 | */ | ||
| 302 | _static size_t | ||
| 303 | gendata_cocsep(void *buf, uint64_t *selfsim, cube_t *rep) | ||
| 304 | { | ||
| 305 | uint32_t *buf32, *info, cc; | ||
| 306 | uint16_t n; | ||
| 307 | uint8_t i, j, visited[COCSEP_VISITEDSIZE]; | ||
| 308 | dfsarg_cocsep_t arg; | ||
| 309 | |||
| 310 | if (buf == NULL) | ||
| 311 | goto gendata_cocsep_return_size; | ||
| 312 | |||
| 313 | buf32 = (uint32_t *)buf; | ||
| 314 | info = buf32 + COCSEP_TABLESIZE; | ||
| 315 | memset(buf32, 0xFF, sizeof(uint32_t) * COCSEP_TABLESIZE); | ||
| 316 | if (selfsim != NULL) | ||
| 317 | memset(selfsim, 0, sizeof(uint64_t) * COCSEP_CLASSES); | ||
| 318 | |||
| 319 | arg = (dfsarg_cocsep_t) { | ||
| 320 | .cube = solved, | ||
| 321 | .n = &n, | ||
| 322 | .buf32 = buf32, | ||
| 323 | .visited = visited, | ||
| 324 | .selfsim = selfsim, | ||
| 325 | .rep = rep | ||
| 326 | }; | ||
| 327 | for (i = 0, n = 0, cc = 0; i < 10; i++) { | ||
| 328 | LOG("cocsep: generating depth %" PRIu8 "\n", i); | ||
| 329 | memset(visited, 0, COCSEP_VISITEDSIZE); | ||
| 330 | arg.depth = 0; | ||
| 331 | arg.maxdepth = i; | ||
| 332 | cc = gendata_cocsep_dfs(&arg); | ||
| 333 | info[i+2] = cc; | ||
| 334 | LOG("found %" PRIu32 "\n", cc); | ||
| 335 | } | ||
| 336 | |||
| 337 | info[0] = (uint32_t)n; | ||
| 338 | info[1] = 9; /* Known max pruning value */ | ||
| 339 | DBG_ASSERT(n == COCSEP_CLASSES, 0, | ||
| 340 | "cocsep: computed %" PRIu16 " symmetry classes, " | ||
| 341 | "expected %zu\n", n, COCSEP_CLASSES); | ||
| 342 | |||
| 343 | LOG("cocsep data computed\n"); | ||
| 344 | LOG("Symmetry classes: %" PRIu32 "\n", info[0]); | ||
| 345 | LOG("Maximum pruning value: %" PRIu32 "\n", info[1]); | ||
| 346 | LOG("Pruning value distribution:\n"); | ||
| 347 | for (j = 0; j < 10; j++) | ||
| 348 | LOG("%" PRIu8 ":\t%" PRIu32 "\n", j, info[j+2]); | ||
| 349 | |||
| 350 | gendata_cocsep_return_size: | ||
| 351 | return COCSEP_FULLSIZE; | ||
| 352 | } | ||
| 353 | |||
| 354 | _static uint32_t | ||
| 355 | gendata_cocsep_dfs(dfsarg_cocsep_t *arg) | ||
| 356 | { | ||
| 357 | uint8_t m; | ||
| 358 | uint32_t cc, class, ttrep, depth, olddepth, tinv; | ||
| 359 | uint64_t t; | ||
| 360 | int64_t i, j; | ||
| 361 | cube_t d; | ||
| 362 | dfsarg_cocsep_t nextarg; | ||
| 363 | |||
| 364 | i = coord_cocsep(arg->cube); | ||
| 365 | olddepth = (uint8_t)(arg->buf32[i] & 0xFF); | ||
| 366 | if (olddepth < arg->depth || get_visited(arg->visited, i)) | ||
| 367 | return 0; | ||
| 368 | set_visited(arg->visited, i); | ||
| 369 | |||
| 370 | if (arg->depth == arg->maxdepth) { | ||
| 371 | if ((arg->buf32[i] & 0xFF) != 0xFF) | ||
| 372 | return 0; | ||
| 373 | |||
| 374 | if (arg->rep != NULL) | ||
| 375 | arg->rep[*arg->n] = arg->cube; | ||
| 376 | for (t = 0, cc = 0; t < 48; t++) { | ||
| 377 | d = transform_corners(arg->cube, t); | ||
| 378 | j = coord_cocsep(d); | ||
| 379 | if (i == j && arg->selfsim != NULL) | ||
| 380 | arg->selfsim[*arg->n] |= UINT64_C(1) << t; | ||
| 381 | if (COCLASS(arg->buf32[j]) != UINT32_C(0xFFFF)) | ||
| 382 | continue; | ||
| 383 | set_visited(arg->visited, j); | ||
| 384 | tinv = inverse_trans(t); | ||
| 385 | olddepth = arg->buf32[j] & 0xFF; | ||
| 386 | cc += olddepth == 0xFF; | ||
| 387 | |||
| 388 | class = (uint32_t)(*arg->n) << UINT32_C(16); | ||
| 389 | ttrep = (uint32_t)tinv << UINT32_C(8); | ||
| 390 | depth = (uint32_t)arg->depth; | ||
| 391 | arg->buf32[j] = class | ttrep | depth; | ||
| 392 | } | ||
| 393 | (*arg->n)++; | ||
| 394 | |||
| 395 | return cc; | ||
| 396 | } | ||
| 397 | |||
| 398 | memcpy(&nextarg, arg, sizeof(dfsarg_cocsep_t)); | ||
| 399 | nextarg.depth++; | ||
| 400 | for (m = 0, cc = 0; m < 18; m++) { | ||
| 401 | nextarg.cube = move(arg->cube, m); | ||
| 402 | cc += gendata_cocsep_dfs(&nextarg); | ||
| 403 | } | ||
| 404 | |||
| 405 | return cc; | ||
| 406 | } | ||
| 407 | |||
| 408 | _static uint64_t | ||
| 409 | gen_h48short( | ||
| 410 | uint8_t n, | ||
| 411 | const uint32_t *cocsepdata, | ||
| 412 | const cube_t *crep, | ||
| 413 | const uint64_t *selfsim, | ||
| 414 | h48map_t *map | ||
| 415 | ) { | ||
| 416 | uint8_t i, m; | ||
| 417 | int64_t coord; | ||
| 418 | uint64_t j, oldn; | ||
| 419 | kvpair_t kv; | ||
| 420 | cube_t cube, d; | ||
| 421 | |||
| 422 | cube = solvedcube(); | ||
| 423 | coord = coord_h48(cube, cocsepdata, 11); | ||
| 424 | h48map_insertmin(map, coord, 0); | ||
| 425 | oldn = 0; | ||
| 426 | LOG("Short h48: generating depth 0\nfound %" PRIu8 "\n", map->n-oldn); | ||
| 427 | for (i = 0; i < n; i++) { | ||
| 428 | LOG("Short h48: generating depth %" PRIu8 "\n", i+1); | ||
| 429 | j = 0; | ||
| 430 | oldn = map->n; | ||
| 431 | for (kv = h48map_nextkvpair(map, &j); | ||
| 432 | j != map->capacity; | ||
| 433 | kv = h48map_nextkvpair(map, &j) | ||
| 434 | ) { | ||
| 435 | if (kv.val != i) | ||
| 436 | continue; | ||
| 437 | cube = invcoord_h48(kv.key, crep, 11); | ||
| 438 | for (m = 0; m < 18; m++) { | ||
| 439 | d = move(cube, m); | ||
| 440 | _foreach_h48sim(d, cocsepdata, selfsim, 11, | ||
| 441 | coord = coord_h48(d, cocsepdata, 11); | ||
| 442 | h48map_insertmin(map, coord, i+1); | ||
| 443 | ) | ||
| 444 | } | ||
| 445 | } | ||
| 446 | LOG("found %" PRIu8 "\n", map->n-oldn); | ||
| 447 | } | ||
| 448 | |||
| 449 | return map->n; | ||
| 450 | } | ||
| 451 | |||
| 452 | /* | ||
| 453 | TODO description | ||
| 454 | generating fixed table with h=0, k=4 | ||
| 455 | */ | ||
| 456 | _static size_t | ||
| 457 | gendata_h48h0k4(void *buf, uint8_t maxdepth) | ||
| 458 | { | ||
| 459 | uint32_t j, *buf32, *info, *cocsepdata; | ||
| 460 | bfsarg_esep_t arg; | ||
| 461 | int64_t sc, cc, esep_max; | ||
| 462 | uint64_t selfsim[COCSEP_CLASSES]; | ||
| 463 | cube_t crep[COCSEP_CLASSES]; | ||
| 464 | size_t cocsepsize, infosize; | ||
| 465 | |||
| 466 | /* TODO: move info at start of tables (all tables!) */ | ||
| 467 | infosize = 4 * maxdepth; | ||
| 468 | cocsepsize = gendata_cocsep(buf, selfsim, crep); | ||
| 469 | infosize = 88; | ||
| 470 | |||
| 471 | if (buf == NULL) | ||
| 472 | goto gendata_h48h0k4_return_size; | ||
| 473 | |||
| 474 | esep_max = (int64_t)ESEP_MAX(0); | ||
| 475 | cocsepdata = (uint32_t *)buf; | ||
| 476 | buf32 = cocsepdata + cocsepsize / 4; | ||
| 477 | info = buf32 + (ESEP_TABLESIZE(0, 4) / sizeof(uint32_t)); | ||
| 478 | memset(buf32, 0xFF, ESEP_TABLESIZE(0, 4)); | ||
| 479 | |||
| 480 | sc = coord_h48(solved, cocsepdata, 0); | ||
| 481 | set_esep_pval(buf32, sc, 0); | ||
| 482 | info[1] = 1; | ||
| 483 | arg = (bfsarg_esep_t) { | ||
| 484 | .cocsepdata = cocsepdata, | ||
| 485 | .buf32 = buf32, | ||
| 486 | .selfsim = selfsim, | ||
| 487 | .crep = crep | ||
| 488 | }; | ||
| 489 | for ( | ||
| 490 | arg.done = 1, arg.depth = 1, cc = 0; | ||
| 491 | arg.done < esep_max && arg.depth <= maxdepth; | ||
| 492 | arg.depth++ | ||
| 493 | ) { | ||
| 494 | LOG("esep: generating depth %" PRIu8 "\n", arg.depth); | ||
| 495 | cc = gendata_h48h0k4_bfs(&arg); | ||
| 496 | arg.done += cc; | ||
| 497 | info[arg.depth+1] = cc; | ||
| 498 | LOG("found %" PRId64 "\n", cc); | ||
| 499 | } | ||
| 500 | |||
| 501 | info[0] = arg.depth-1; | ||
| 502 | |||
| 503 | LOG("h48 pruning table computed\n"); | ||
| 504 | LOG("Maximum pruning value: %" PRIu32 "\n", info[0]); | ||
| 505 | LOG("Pruning value distribution:\n"); | ||
| 506 | for (j = 0; j <= info[0]; j++) | ||
| 507 | LOG("%" PRIu8 ":\t%" PRIu32 "\n", j, info[j+1]); | ||
| 508 | |||
| 509 | gendata_h48h0k4_return_size: | ||
| 510 | return cocsepsize + ESEP_TABLESIZE(0, 4) + infosize; | ||
| 511 | } | ||
| 512 | |||
| 513 | _static int64_t | ||
| 514 | gendata_h48h0k4_bfs(bfsarg_esep_t *arg) | ||
| 515 | { | ||
| 516 | const uint8_t breakpoint = 10; /* Hand-picked optimal */ | ||
| 517 | |||
| 518 | if (arg->depth < breakpoint) | ||
| 519 | return gendata_h48h0k4_bfs_fromdone(arg); | ||
| 520 | else | ||
| 521 | return gendata_h48h0k4_bfs_fromnew(arg); | ||
| 522 | } | ||
| 523 | |||
| 524 | _static int64_t | ||
| 525 | gendata_h48h0k4_bfs_fromdone(bfsarg_esep_t *arg) | ||
| 526 | { | ||
| 527 | uint8_t c, m, x; | ||
| 528 | uint32_t cc; | ||
| 529 | int64_t i, j, k; | ||
| 530 | cube_t cube, moved; | ||
| 531 | |||
| 532 | for (i = 0, cc = 0; i < (int64_t)ESEP_MAX(0); i++) { | ||
| 533 | c = get_esep_pval(arg->buf32, i); | ||
| 534 | if (c != arg->depth - 1) | ||
| 535 | continue; | ||
| 536 | cube = invcoord_h48(i, arg->crep, 0); | ||
| 537 | for (m = 0; m < 18; m++) { | ||
| 538 | moved = move(cube, m); | ||
| 539 | j = coord_h48(moved, arg->cocsepdata, 0); | ||
| 540 | if (get_esep_pval(arg->buf32, j) <= arg->depth) | ||
| 541 | continue; | ||
| 542 | _foreach_h48sim(moved, arg->cocsepdata, arg->selfsim, 0, | ||
| 543 | k = coord_h48(moved, arg->cocsepdata, 0); | ||
| 544 | x = get_esep_pval(arg->buf32, k); | ||
| 545 | set_esep_pval(arg->buf32, k, arg->depth); | ||
| 546 | cc += x != arg->depth; | ||
| 547 | ) | ||
| 548 | } | ||
| 549 | } | ||
| 550 | |||
| 551 | return cc; | ||
| 552 | } | ||
| 553 | |||
| 554 | _static int64_t | ||
| 555 | gendata_h48h0k4_bfs_fromnew(bfsarg_esep_t *arg) | ||
| 556 | { | ||
| 557 | uint8_t c, m, x; | ||
| 558 | uint32_t cc; | ||
| 559 | int64_t i, j; | ||
| 560 | cube_t cube, moved; | ||
| 561 | |||
| 562 | for (i = 0, cc = 0; i < (int64_t)ESEP_MAX(0); i++) { | ||
| 563 | c = get_esep_pval(arg->buf32, i); | ||
| 564 | if (c != 0xF) | ||
| 565 | continue; | ||
| 566 | cube = invcoord_h48(i, arg->crep, 0); | ||
| 567 | for (m = 0; m < 18; m++) { | ||
| 568 | moved = move(cube, m); | ||
| 569 | j = coord_h48(moved, arg->cocsepdata, 0); | ||
| 570 | x = get_esep_pval(arg->buf32, j); | ||
| 571 | if (x >= arg->depth) | ||
| 572 | continue; | ||
| 573 | _foreach_h48sim(cube, arg->cocsepdata, arg->selfsim, 0, | ||
| 574 | j = coord_h48(cube, arg->cocsepdata, 0); | ||
| 575 | x = get_esep_pval(arg->buf32, j); | ||
| 576 | set_esep_pval(arg->buf32, j, arg->depth); | ||
| 577 | cc += x == 0xF; | ||
| 578 | ) | ||
| 579 | break; /* Enough to find one, skip the rest */ | ||
| 580 | } | ||
| 581 | } | ||
| 582 | |||
| 583 | return cc; | ||
| 584 | } | ||
| 585 | |||
| 586 | _static_inline bool | ||
| 587 | get_visited(const uint8_t *a, int64_t i) | ||
| 588 | { | ||
| 589 | return a[VISITED_IND(i)] & VISITED_MASK(i); | ||
| 590 | } | ||
| 591 | |||
| 592 | _static_inline void | ||
| 593 | set_visited(uint8_t *a, int64_t i) | ||
| 594 | { | ||
| 595 | a[VISITED_IND(i)] |= VISITED_MASK(i); | ||
| 596 | } | ||
| 597 | |||
| 598 | _static_inline uint8_t | ||
| 599 | get_esep_pval(const uint32_t *buf32, int64_t i) | ||
| 600 | { | ||
| 601 | return (buf32[ESEP_IND(i)] & ESEP_MASK(i)) >> ESEP_SHIFT(i); | ||
| 602 | } | ||
| 603 | |||
| 604 | _static_inline void | ||
| 605 | set_esep_pval(uint32_t *buf32, int64_t i, uint8_t val) | ||
| 606 | { | ||
| 607 | buf32[ESEP_IND(i)] = | ||
| 608 | (buf32[ESEP_IND(i)] & (~ESEP_MASK(i))) | (val << ESEP_SHIFT(i)); | ||
| 609 | } | ||
| 610 | |||
| 611 | _static void | ||
| 612 | solve_h48_appendsolution(dfsarg_solveh48_t *arg) | ||
| 613 | { | ||
| 614 | int strl; | ||
| 615 | |||
| 616 | strl = writemoves(arg->moves, arg->nmoves, *arg->nextsol); | ||
| 617 | LOG("Solution found: %s\n", *arg->nextsol); | ||
| 618 | *arg->nextsol += strl; | ||
| 619 | **arg->nextsol = '\n'; | ||
| 620 | (*arg->nextsol)++; | ||
| 621 | (*arg->nsols)++; | ||
| 622 | } | ||
| 623 | |||
| 624 | _static_inline int8_t | ||
| 625 | get_h48_cdata(cube_t cube, uint32_t *cocsepdata, uint32_t *cdata) | ||
| 626 | { | ||
| 627 | int64_t coord; | ||
| 628 | |||
| 629 | coord = coord_cocsep(cube); | ||
| 630 | *cdata = cocsepdata[coord]; | ||
| 631 | |||
| 632 | return CBOUND(*cdata); | ||
| 633 | } | ||
| 634 | |||
| 635 | _static_inline int8_t | ||
| 636 | get_h48_bound(cube_t cube, uint32_t cdata, uint8_t h, uint32_t *h48data) | ||
| 637 | { | ||
| 638 | int64_t coord; | ||
| 639 | |||
| 640 | coord = coord_h48_edges(cube, COCLASS(cdata), TTREP(cdata), h); | ||
| 641 | return get_esep_pval(h48data, coord); | ||
| 642 | } | ||
| 643 | |||
| 644 | _static_inline bool | ||
| 645 | solve_h48_stop(dfsarg_solveh48_t *arg) | ||
| 646 | { | ||
| 647 | uint32_t data, data_inv; | ||
| 648 | int8_t bound; | ||
| 649 | |||
| 650 | bound = get_h48_cdata(arg->cube, arg->cocsepdata, &data); | ||
| 651 | if (bound + arg->nmoves > arg->depth) | ||
| 652 | return true; | ||
| 653 | |||
| 654 | bound = get_h48_cdata(arg->inverse, arg->cocsepdata, &data_inv); | ||
| 655 | if (bound + arg->nmoves > arg->depth) | ||
| 656 | return true; | ||
| 657 | |||
| 658 | /* | ||
| 659 | bound = get_h48_bound(arg->cube, data, arg->h, arg->h48data); | ||
| 660 | LOG("Using pval %" PRId8 "\n", bound); | ||
| 661 | if (bound + arg->nmoves > arg->depth) | ||
| 662 | return true; | ||
| 663 | |||
| 664 | bound = get_h48_bound(arg->inverse, data_inv, arg->h, arg->h48data); | ||
| 665 | if (bound + arg->nmoves > arg->depth) | ||
| 666 | return true; | ||
| 667 | */ | ||
| 668 | |||
| 669 | return false; | ||
| 670 | } | ||
| 671 | |||
| 672 | _static int64_t | ||
| 673 | solve_h48_dfs(dfsarg_solveh48_t *arg) | ||
| 674 | { | ||
| 675 | dfsarg_solveh48_t nextarg; | ||
| 676 | int64_t ret; | ||
| 677 | uint8_t m; | ||
| 678 | |||
| 679 | if (*arg->nsols == arg->maxsolutions) | ||
| 680 | return 0; | ||
| 681 | |||
| 682 | if (solve_h48_stop(arg)) | ||
| 683 | return 0; | ||
| 684 | |||
| 685 | if (issolved(arg->cube)) { | ||
| 686 | if (arg->nmoves != arg->depth) | ||
| 687 | return 0; | ||
| 688 | solve_h48_appendsolution(arg); | ||
| 689 | return 1; | ||
| 690 | } | ||
| 691 | |||
| 692 | /* TODO: avoid copy, change arg and undo changes after recursion */ | ||
| 693 | nextarg = *arg; | ||
| 694 | nextarg.nmoves = arg->nmoves + 1; | ||
| 695 | ret = 0; | ||
| 696 | for (m = 0; m < 18; m++) { | ||
| 697 | nextarg.moves[arg->nmoves] = m; | ||
| 698 | if (!allowednextmove(nextarg.moves, nextarg.nmoves)) { | ||
| 699 | /* If a move is not allowed, neither are its 180 | ||
| 700 | * and 270 degree variations */ | ||
| 701 | m += 2; | ||
| 702 | continue; | ||
| 703 | } | ||
| 704 | nextarg.cube = move(arg->cube, m); | ||
| 705 | nextarg.inverse = inverse(nextarg.cube); /* TODO: use premove */ | ||
| 706 | ret += solve_h48_dfs(&nextarg); | ||
| 707 | } | ||
| 708 | |||
| 709 | return ret; | ||
| 710 | } | ||
| 711 | |||
| 712 | _static int64_t | ||
| 713 | solve_h48( | ||
| 714 | cube_t cube, | ||
| 715 | int8_t minmoves, | ||
| 716 | int8_t maxmoves, | ||
| 717 | int8_t maxsolutions, | ||
| 718 | uint8_t h, | ||
| 719 | const void *data, | ||
| 720 | char *solutions | ||
| 721 | ) | ||
| 722 | { | ||
| 723 | int64_t nsols; | ||
| 724 | dfsarg_solveh48_t arg; | ||
| 725 | |||
| 726 | arg = (dfsarg_solveh48_t) { | ||
| 727 | .cube = cube, | ||
| 728 | .inverse = inverse(cube), | ||
| 729 | .nsols = &nsols, | ||
| 730 | .maxsolutions = maxsolutions, | ||
| 731 | .h = h, | ||
| 732 | .cocsepdata = (uint32_t *)data, | ||
| 733 | .h48data = ((uint32_t *)data) + COCSEP_FULLSIZE / 4, | ||
| 734 | .nextsol = &solutions | ||
| 735 | }; | ||
| 736 | |||
| 737 | nsols = 0; | ||
| 738 | for (arg.depth = minmoves; | ||
| 739 | arg.depth <= maxmoves && nsols < maxsolutions; | ||
| 740 | arg.depth++) | ||
| 741 | { | ||
| 742 | LOG("Found %" PRId64 " solutions, searching at depth %" | ||
| 743 | PRId8 "\n", nsols, arg.depth); | ||
| 744 | arg.nmoves = 0; | ||
| 745 | solve_h48_dfs(&arg); | ||
| 746 | } | ||
| 747 | |||
| 748 | return nsols; | ||
| 749 | } | ||
| 750 | |||
| 751 | /* | ||
| 752 | The h48stats solver computes how many moves it takes to solve to | ||
| 753 | each of the 12 h48 coordinates, one for each value of h from 0 to 11. | ||
| 754 | The solutions array is filled with the length of the solutions. The | ||
| 755 | solution array is therefore not a printable string. | ||
| 756 | */ | ||
| 757 | _static int64_t | ||
| 758 | solve_h48stats_dfs(dfsarg_solveh48stats_t *arg) | ||
| 759 | { | ||
| 760 | const int64_t limit = 11; | ||
| 761 | |||
| 762 | int8_t bound, u; | ||
| 763 | uint8_t m; | ||
| 764 | uint32_t d; | ||
| 765 | int64_t coord, h; | ||
| 766 | dfsarg_solveh48stats_t nextarg; | ||
| 767 | |||
| 768 | /* Check cocsep lower bound (corners only) */ | ||
| 769 | bound = get_h48_cdata(arg->cube, arg->cocsepdata, &d); | ||
| 770 | if (bound + arg->nmoves > arg->depth) | ||
| 771 | return 0; | ||
| 772 | |||
| 773 | /* Check h48 lower bound for h=0 (esep, but no eo) */ | ||
| 774 | coord = coord_h48_edges(arg->cube, COCLASS(d), TTREP(d), 0); | ||
| 775 | bound = get_esep_pval(arg->h48data, coord); | ||
| 776 | if (bound + arg->nmoves > arg->depth) | ||
| 777 | return 0; | ||
| 778 | |||
| 779 | /* Update all other values, if solved */ | ||
| 780 | coord = coord_h48_edges(arg->cube, COCLASS(d), TTREP(d), 11); | ||
| 781 | for (h = 0; h <= limit; h++) { | ||
| 782 | u = coord >> (11-h) == 0 && arg->s[h] == 99; | ||
| 783 | arg->s[h] = u * arg->nmoves + (1-u) * arg->s[h]; | ||
| 784 | } | ||
| 785 | |||
| 786 | if (arg->s[limit] != 99) | ||
| 787 | return 0; | ||
| 788 | |||
| 789 | nextarg = *arg; | ||
| 790 | nextarg.nmoves = arg->nmoves + 1; | ||
| 791 | for (m = 0; m < 18; m++) { | ||
| 792 | nextarg.moves[arg->nmoves] = m; | ||
| 793 | if (!allowednextmove(nextarg.moves, nextarg.nmoves)) { | ||
| 794 | /* If a move is not allowed, neither are its 180 | ||
| 795 | * and 270 degree variations */ | ||
| 796 | m += 2; | ||
| 797 | continue; | ||
| 798 | } | ||
| 799 | nextarg.cube = move(arg->cube, m); | ||
| 800 | solve_h48stats_dfs(&nextarg); | ||
| 801 | } | ||
| 802 | |||
| 803 | return 0; | ||
| 804 | } | ||
| 805 | |||
| 806 | _static int64_t | ||
| 807 | solve_h48stats( | ||
| 808 | cube_t cube, | ||
| 809 | int8_t maxmoves, | ||
| 810 | const void *data, | ||
| 811 | char solutions[static 12] | ||
| 812 | ) | ||
| 813 | { | ||
| 814 | int i; | ||
| 815 | size_t cocsepsize; | ||
| 816 | dfsarg_solveh48stats_t arg; | ||
| 817 | |||
| 818 | cocsepsize = gendata_cocsep(NULL, NULL, NULL); | ||
| 819 | |||
| 820 | arg = (dfsarg_solveh48stats_t) { | ||
| 821 | .cube = cube, | ||
| 822 | .cocsepdata = (uint32_t *)data, | ||
| 823 | .h48data = ((uint32_t *)data) + (cocsepsize/4), | ||
| 824 | .s = solutions | ||
| 825 | }; | ||
| 826 | |||
| 827 | for (i = 0; i < 12; i++) | ||
| 828 | solutions[i] = (char)99; | ||
| 829 | |||
| 830 | for (arg.depth = 0; | ||
| 831 | arg.depth <= maxmoves && solutions[11] == 99; | ||
| 832 | arg.depth++) | ||
| 833 | { | ||
| 834 | arg.nmoves = 0; | ||
| 835 | solve_h48stats_dfs(&arg); | ||
| 836 | } | ||
| 837 | |||
| 838 | return 0; | ||
| 839 | } | ||
