nissy.c (11659B)
1 #include <inttypes.h> 2 #include <limits.h> 3 #include <pthread.h> 4 #include <stdatomic.h> 5 #include <stdarg.h> 6 #include <stdbool.h> 7 #include <string.h> 8 9 #include "nissy.h" 10 #include "utils/utils.h" 11 #include "arch/arch.h" 12 #include "core/core.h" 13 #include "solvers/solvers.h" 14 15 int parse_h48_solver(const char *, uint8_t [static 1], uint8_t [static 1]); 16 STATIC bool checkdata(const char *, const tableinfo_t *); 17 STATIC bool distribution_equal(const uint64_t [static INFO_DISTRIBUTION_LEN], 18 const uint64_t [static INFO_DISTRIBUTION_LEN], uint8_t); 19 STATIC long long write_result(cube_t, char [static NISSY_SIZE_B32]); 20 STATIC size_t my_strnlen(const char *, size_t); 21 22 #define GETCUBE_OPTIONS(S, F) { .option = S, .fix = F } 23 struct { 24 char *option; 25 void (*fix)(long long *, long long *, long long *, long long *); 26 } getcube_options[] = { 27 GETCUBE_OPTIONS("fix", getcube_fix), 28 GETCUBE_OPTIONS(NULL, NULL) 29 }; 30 31 int 32 parse_h48_solver(const char *buf, uint8_t h[static 1], uint8_t k[static 1]) 33 { 34 const char *fullbuf = buf; 35 36 buf += 3; 37 38 if (!strcmp(buf, "stats")) { 39 *h = 0; 40 *k = 4; 41 return 0; 42 } 43 44 if (*buf != 'h') 45 goto parse_h48_solver_error; 46 buf++; 47 48 *h = atoi(buf); 49 50 for ( ; *buf >= 0 + '0' && *buf <= 9 + '0'; buf++) 51 if (*buf == 0) 52 goto parse_h48_solver_error; 53 54 if (*buf != 'k') 55 goto parse_h48_solver_error; 56 buf++; 57 58 *k = atoi(buf); 59 60 return *h < 12 && (*k == 2 || (*k == 4 && *h == 0)) ? 0 : 1; 61 62 parse_h48_solver_error: 63 *h = 0; 64 *k = 0; 65 LOG("Error parsing solver: must be in \"h48h*k*\" format" 66 " or \"h48stats\", but got %s\n", fullbuf); 67 return -1; 68 } 69 70 STATIC bool 71 checkdata(const char *buf, const tableinfo_t *info) 72 { 73 uint64_t distr[INFO_DISTRIBUTION_LEN]; 74 75 if (info == NULL || my_strnlen(info->solver, INFO_SOLVER_STRLEN) 76 == INFO_SOLVER_STRLEN) { 77 LOG("checkdata: error reading table info\n"); 78 return false; 79 } else if (!strncmp(info->solver, "cocsep", 6)) { 80 getdistribution_cocsep( 81 (uint32_t *)((char *)buf + INFOSIZE), distr); 82 } else if (!strncmp(info->solver, "h48", 3)) { 83 getdistribution_h48((uint8_t *)buf + INFOSIZE, distr, 84 info->h48h, info->bits); 85 } else { 86 LOG("checkdata: unknown solver %s\n", info->solver); 87 return false; 88 } 89 90 return distribution_equal(info->distribution, distr, info->maxvalue); 91 } 92 93 STATIC bool 94 distribution_equal( 95 const uint64_t expected[static INFO_DISTRIBUTION_LEN], 96 const uint64_t actual[static INFO_DISTRIBUTION_LEN], 97 uint8_t maxvalue 98 ) 99 { 100 int wrong; 101 uint8_t i; 102 103 for (i = 0, wrong = 0; i <= MAX(maxvalue, 20); i++) { 104 if (expected[i] != actual[i]) { 105 wrong++; 106 LOG("Value %" PRIu8 ": expected %" PRIu64 ", found %" 107 PRIu64 "\n", i, expected[i], actual[i]); 108 } 109 } 110 111 return wrong == 0; 112 } 113 114 STATIC long long 115 write_result(cube_t cube, char result[static NISSY_SIZE_B32]) 116 { 117 writecube("B32", cube, NISSY_SIZE_B32, result); 118 119 if (!issolvable(cube)) { 120 LOG("Warning: resulting cube is not solvable\n"); 121 return NISSY_WARNING_UNSOLVABLE; 122 } 123 124 return NISSY_OK; 125 } 126 127 STATIC size_t 128 my_strnlen(const char *str, size_t maxlen) 129 { 130 size_t i; 131 132 for (i = 0; i < maxlen; i++) 133 if (str[i] == '\0') 134 return i; 135 136 return maxlen; 137 } 138 139 long long 140 nissy_compose( 141 const char cube[static NISSY_SIZE_B32], 142 const char permutation[static NISSY_SIZE_B32], 143 char result[static NISSY_SIZE_B32] 144 ) 145 { 146 cube_t c, p, res; 147 long long err; 148 149 c = readcube("B32", cube); 150 151 if (!isconsistent(c)) { 152 LOG("Error in nissy_compose: given cube is invalid\n"); 153 err = NISSY_ERROR_INVALID_CUBE; 154 goto nissy_compose_error; 155 } 156 157 p = readcube("B32", permutation); 158 159 if (!isconsistent(p)) { 160 LOG("Error in nissy_compose: given permutation is invalid\n"); 161 err = NISSY_ERROR_INVALID_CUBE; 162 goto nissy_compose_error; 163 } 164 165 res = compose(c, p); 166 167 if (!isconsistent(res)) { 168 LOG("Unknown error: resulting cube is invalid\n"); 169 err = NISSY_ERROR_UNKNOWN; 170 goto nissy_compose_error; 171 } 172 173 return write_result(res, result); 174 175 nissy_compose_error: 176 writecube("B32", ZERO_CUBE, NISSY_SIZE_B32, result); 177 return err; 178 } 179 180 long long 181 nissy_inverse( 182 const char cube[static NISSY_SIZE_B32], 183 char result[static NISSY_SIZE_B32] 184 ) 185 { 186 cube_t c, res; 187 long long err; 188 189 c = readcube("B32", cube); 190 191 if (iserror(c)) { 192 LOG("Error in nissy_inverse: given cube is invalid\n"); 193 err = NISSY_ERROR_INVALID_CUBE; 194 goto nissy_inverse_error; 195 } 196 197 res = inverse(c); 198 199 if (!isconsistent(res)) { 200 LOG("Unknown error: inverted cube is invalid\n"); 201 err = NISSY_ERROR_UNKNOWN; 202 goto nissy_inverse_error; 203 } 204 205 return write_result(res, result); 206 207 nissy_inverse_error: 208 writecube("B32", ZERO_CUBE, NISSY_SIZE_B32, result); 209 return err; 210 } 211 212 long long 213 nissy_applymoves( 214 const char cube[static NISSY_SIZE_B32], 215 const char *moves, 216 char result[static NISSY_SIZE_B32] 217 ) 218 { 219 cube_t c, res; 220 long long err; 221 222 if (moves == NULL) { 223 LOG("Error: 'moves' argument is NULL\n"); 224 err = NISSY_ERROR_NULL_POINTER; 225 goto nissy_applymoves_error; 226 } 227 228 c = readcube("B32", cube); 229 230 if (!isconsistent(c)) { 231 LOG("Error in nissy_applymoves: given cube is invalid\n"); 232 err = NISSY_ERROR_INVALID_CUBE; 233 goto nissy_applymoves_error; 234 } 235 236 res = applymoves(c, moves); 237 238 if (!isconsistent(res)) { 239 /* Assume we got a reasonable error message from applymoves */ 240 err = NISSY_ERROR_INVALID_MOVES; 241 goto nissy_applymoves_error; 242 } 243 244 return write_result(res, result); 245 246 nissy_applymoves_error: 247 writecube("B32", ZERO_CUBE, NISSY_SIZE_B32, result); 248 return err; 249 } 250 251 long long 252 nissy_applytrans( 253 const char cube[static NISSY_SIZE_B32], 254 const char transformation[static NISSY_SIZE_TRANSFORMATION], 255 char result[static NISSY_SIZE_B32] 256 ) 257 { 258 cube_t c, res; 259 long long err; 260 261 c = readcube("B32", cube); 262 263 if (!isconsistent(c)) { 264 LOG("Error in nissy_applytrans: given cube is invalid\n"); 265 err = NISSY_ERROR_INVALID_CUBE; 266 goto nissy_applytrans_error; 267 } 268 269 res = applytrans(c, transformation); 270 271 if (!isconsistent(res)) { 272 /* Assume we got a reasonable error message from applytrans */ 273 err = NISSY_ERROR_INVALID_TRANS; 274 goto nissy_applytrans_error; 275 } 276 277 return write_result(res, result); 278 279 nissy_applytrans_error: 280 writecube("B32", ZERO_CUBE, NISSY_SIZE_B32, result); 281 return err; 282 } 283 284 long long 285 nissy_convert( 286 const char *format_in, 287 const char *format_out, 288 const char *cube_string, 289 unsigned result_size, 290 char result[result_size] 291 ) 292 { 293 cube_t c; 294 long long err; 295 296 if (format_in == NULL) { 297 LOG("Error: 'format_in' argument is NULL\n"); 298 err = NISSY_ERROR_NULL_POINTER; 299 goto nissy_convert_error; 300 } 301 302 if (format_out == NULL) { 303 LOG("Error: 'format_out' argument is NULL\n"); 304 err = NISSY_ERROR_NULL_POINTER; 305 goto nissy_convert_error; 306 } 307 308 if (cube_string == NULL) { 309 LOG("Error: 'cube_string' argument is NULL\n"); 310 err = NISSY_ERROR_NULL_POINTER; 311 goto nissy_convert_error; 312 } 313 314 c = readcube(format_in, cube_string); 315 316 if (!isconsistent(c)) { 317 err = NISSY_ERROR_INVALID_CUBE; 318 goto nissy_convert_error; 319 } 320 321 return writecube(format_out, c, result_size, result); 322 323 nissy_convert_error: 324 result[0] = '\0'; 325 return err; 326 } 327 328 long long 329 nissy_getcube( 330 long long ep, 331 long long eo, 332 long long cp, 333 long long co, 334 const char *options, 335 char result[static NISSY_SIZE_B32] 336 ) 337 { 338 int i; 339 cube_t c; 340 341 if (options == NULL) { 342 LOG("Error: 'options' argument is NULL\n"); 343 return NISSY_ERROR_NULL_POINTER; 344 } 345 346 for (i = 0; getcube_options[i].option != NULL; i++) 347 if (!strcmp(options, getcube_options[i].option)) 348 getcube_options[i].fix(&ep, &eo, &cp, &co); 349 350 c = getcube(ep, eo, cp, co); 351 352 if (!isconsistent(c)) { 353 LOG("Error: could not get cube with ep=%" PRId64 ", eo=%" 354 PRId64 ", cp=%" PRId64 ", co=%" PRId64 ".\n", 355 ep, eo, cp, co); 356 return NISSY_ERROR_OPTIONS; 357 } 358 359 return write_result(c, result); 360 } 361 362 long long 363 nissy_datasize( 364 const char *solver 365 ) 366 { 367 if (solver == NULL) { 368 LOG("Error: 'solver' argument is NULL\n"); 369 return NISSY_ERROR_NULL_POINTER; 370 } 371 372 /* gendata() handles a NULL *data as a "dryrun" request */ 373 return nissy_gendata(solver, 0, NULL); 374 } 375 376 long long 377 nissy_datainfo( 378 uint64_t data_size, 379 const char data[data_size], 380 void (*write)(const char *, ...) 381 ) 382 { 383 uint8_t i; 384 tableinfo_t info; 385 long long ret; 386 387 ret = readtableinfo(data_size, data, &info); 388 if (ret != 0) 389 return ret; 390 391 write("\n---------\n\n"); 392 write("Table information for '%s'\n", info.solver); 393 write("\n"); 394 write("Size: %" PRIu64 " bytes\n", info.fullsize); 395 write("Entries: %" PRIu64 " (%" PRIu8 " bits per entry)", 396 info.entries, info.bits); 397 write("\n"); 398 399 switch (info.type) { 400 case TABLETYPE_PRUNING: 401 write("\n"); 402 write("Table distribution:\nValue\tPositions\n"); 403 for (i = 0; i <= info.maxvalue; i++) { 404 write("%" PRIu8 "\t%" PRIu64 "\n", 405 i + info.base, info.distribution[i]); 406 } 407 break; 408 case TABLETYPE_SPECIAL: 409 write("This is an ad-hoc table\n"); 410 break; 411 default: 412 LOG("datainfo: unknown table type\n"); 413 return NISSY_ERROR_DATA; 414 } 415 416 if (info.next != 0) 417 return nissy_datainfo( 418 data_size - info.next, (char *)data + info.next, write); 419 420 write("\n---------\n"); 421 422 return NISSY_OK; 423 } 424 425 long long 426 nissy_gendata( 427 const char *solver, 428 unsigned long long data_size, 429 char data[data_size] 430 ) 431 { 432 int p; 433 gendata_h48_arg_t arg; 434 435 if (solver == NULL) { 436 LOG("Error: 'solver' argument is NULL\n"); 437 return NISSY_ERROR_NULL_POINTER; 438 } 439 440 arg.buf_size = data_size; 441 arg.buf = data; 442 if (!strncmp(solver, "h48", 3)) { 443 p = parse_h48_solver(solver, &arg.h, &arg.k); 444 arg.maxdepth = 20; 445 if (p != 0) 446 return NISSY_ERROR_UNKNOWN; 447 return gendata_h48(&arg); 448 } else { 449 LOG("gendata: unknown solver %s\n", solver); 450 return NISSY_ERROR_INVALID_SOLVER; 451 } 452 } 453 454 long long 455 nissy_checkdata( 456 unsigned long long data_size, 457 const char data[data_size] 458 ) 459 { 460 char *buf; 461 tableinfo_t info; 462 int64_t err; 463 464 for (buf = (char *)data; 465 (err = readtableinfo(data_size, buf, &info)) == NISSY_OK; 466 buf += info.next, data_size -= info.next) 467 { 468 if (!checkdata(buf, &info)) { 469 LOG("Error: data for solver '%s' is corrupted!\n", 470 info.solver); 471 return NISSY_ERROR_DATA; 472 } 473 474 if (info.next == 0) 475 break; 476 } 477 478 return err; 479 } 480 481 long long 482 nissy_solve( 483 const char cube[static NISSY_SIZE_B32], 484 const char *solver, 485 unsigned nissflag, 486 unsigned minmoves, 487 unsigned maxmoves, 488 unsigned maxsols, 489 int optimal, 490 unsigned long long data_size, 491 const char data[data_size], 492 unsigned sols_size, 493 char sols[sols_size], 494 long long stats[static NISSY_SIZE_SOLVE_STATS] 495 ) 496 { 497 cube_t c; 498 int p; 499 uint8_t h, k; 500 501 if (solver == NULL) { 502 LOG("Error: 'solver' argument is NULL\n"); 503 return NISSY_ERROR_NULL_POINTER; 504 } 505 506 c = readcube_B32(cube); 507 508 if (!isconsistent(c)) { 509 LOG("solve: cube is invalid\n"); 510 return NISSY_ERROR_INVALID_CUBE; 511 } 512 513 if (!issolvable(c)) { 514 LOG("solve: cube is not solvable\n"); 515 return NISSY_ERROR_UNSOLVABLE_CUBE; 516 } 517 518 if (maxsols == 0) { 519 LOG("solve: 'maxsols' is 0, returning no solution\n"); 520 return 0; 521 } 522 523 if (!strncmp(solver, "h48", 3)) { 524 if (!strcmp(solver, "h48stats")) 525 return solve_h48stats(c, maxmoves, data, sols); 526 527 p = parse_h48_solver(solver, &h, &k); 528 if (p != 0) { 529 LOG("solve: unknown solver %s\n", solver); 530 return NISSY_ERROR_INVALID_SOLVER; 531 } else { 532 return THREADS > 1 ? 533 solve_h48_multithread(c, minmoves, maxmoves, 534 maxsols, data_size, data, sols_size, sols, stats) : 535 solve_h48(c, minmoves, maxmoves, maxsols, 536 data_size, data, sols_size, sols, stats); 537 } 538 } else { 539 LOG("solve: unknown solver '%s'\n", solver); 540 return NISSY_ERROR_INVALID_SOLVER; 541 } 542 } 543 544 long long 545 nissy_countmoves( 546 const char *moves 547 ) 548 { 549 if (moves == NULL) 550 return NISSY_ERROR_NULL_POINTER; 551 552 return readmoves(moves, INT_MAX, NULL); 553 } 554 555 long long 556 nissy_setlogger( 557 void (*log)(const char *, ...) 558 ) 559 { 560 nissy_log = log; 561 return NISSY_OK; 562 }