nissy.c (8698B)
1 #include <stdlib.h> 2 #include <inttypes.h> 3 #include <limits.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 STATIC long long write_result(oriented_cube_t, char [static NISSY_SIZE_CUBE]); 16 STATIC long long nissy_dataid(const char *, char [static NISSY_SIZE_DATAID]); 17 STATIC long long nissy_gendata_unsafe( 18 const char *, unsigned long long, unsigned char *); 19 20 #define GETCUBE_OPTIONS(S, F) { .option = S, .fix = F } 21 struct { 22 char *option; 23 void (*fix)(long long *, long long *, 24 long long *, long long *, long long *); 25 } getcube_options[] = { 26 GETCUBE_OPTIONS("fix", getcube_fix), 27 GETCUBE_OPTIONS(NULL, NULL) 28 }; 29 30 STATIC long long 31 write_result(oriented_cube_t cube, char result[static NISSY_SIZE_CUBE]) 32 { 33 writecube(cube, NISSY_SIZE_CUBE, result); 34 35 if (!issolvable(cube)) { 36 LOG("Warning: resulting cube is not solvable\n"); 37 return NISSY_WARNING_UNSOLVABLE; 38 } 39 40 return NISSY_OK; 41 } 42 43 long long 44 nissy_inverse( 45 const char cube[static NISSY_SIZE_CUBE], 46 char result[static NISSY_SIZE_CUBE] 47 ) 48 { 49 oriented_cube_t c, res; 50 long long err; 51 52 c = readcube(cube); 53 54 if (iserror(c)) { 55 LOG("[inverse] Error: the given cube is invalid\n"); 56 err = NISSY_ERROR_INVALID_CUBE; 57 goto nissy_inverse_error; 58 } 59 60 res = (oriented_cube_t) { 61 .cube = inverse(c.cube), 62 .orientation = c.orientation 63 }; 64 65 if (!isconsistent(res)) { 66 LOG("[inverse] Unknown error: inverted cube is invalid\n"); 67 err = NISSY_ERROR_UNKNOWN; 68 goto nissy_inverse_error; 69 } 70 71 return write_result(res, result); 72 73 nissy_inverse_error: 74 writecube(ZERO_ORIENTED_CUBE, NISSY_SIZE_CUBE, result); 75 return err; 76 } 77 78 long long 79 nissy_applymoves( 80 const char cube[static NISSY_SIZE_CUBE], 81 const char *moves, 82 char result[static NISSY_SIZE_CUBE] 83 ) 84 { 85 oriented_cube_t c, res; 86 long long err; 87 88 if (moves == NULL) { 89 LOG("[applymoves] Error: 'moves' argument is NULL\n"); 90 err = NISSY_ERROR_NULL_POINTER; 91 goto nissy_applymoves_error; 92 } 93 94 c = readcube(cube); 95 96 if (!isconsistent(c)) { 97 LOG("[applymoves] Error: given cube is invalid\n"); 98 err = NISSY_ERROR_INVALID_CUBE; 99 goto nissy_applymoves_error; 100 } 101 102 res = applymoves(c, moves); 103 104 if (!isconsistent(res)) { 105 /* Assume we got a reasonable error message from applymoves */ 106 err = NISSY_ERROR_INVALID_MOVES; 107 goto nissy_applymoves_error; 108 } 109 110 return write_result(res, result); 111 112 nissy_applymoves_error: 113 writecube(ZERO_ORIENTED_CUBE, NISSY_SIZE_CUBE, result); 114 return err; 115 } 116 117 long long 118 nissy_applytrans( 119 const char cube[static NISSY_SIZE_CUBE], 120 const char transformation[static NISSY_SIZE_TRANSFORMATION], 121 char result[static NISSY_SIZE_CUBE] 122 ) 123 { 124 oriented_cube_t c, res; 125 long long err; 126 127 c = readcube(cube); 128 129 if (!isconsistent(c)) { 130 LOG("[applytrans] Error: given cube is invalid\n"); 131 err = NISSY_ERROR_INVALID_CUBE; 132 goto nissy_applytrans_error; 133 } 134 135 res = applytrans(c, transformation); 136 137 if (!isconsistent(res)) { 138 /* Assume we got a reasonable error message from applytrans */ 139 err = NISSY_ERROR_INVALID_TRANS; 140 goto nissy_applytrans_error; 141 } 142 143 return write_result(res, result); 144 145 nissy_applytrans_error: 146 writecube(ZERO_ORIENTED_CUBE, NISSY_SIZE_CUBE, result); 147 return err; 148 } 149 150 long long 151 nissy_variations( 152 const char *moves, 153 const char *variation, 154 unsigned long long result_size, 155 char *result 156 ) 157 { 158 if (moves == NULL) { 159 LOG("[variations] Error: 'moves' argument is NULL\n"); 160 return NISSY_ERROR_NULL_POINTER; 161 } 162 163 if (variation == NULL) { 164 LOG("[variations] Error: 'variation' argument is NULL\n"); 165 return NISSY_ERROR_NULL_POINTER; 166 } 167 168 if (result == NULL) { 169 LOG("[variations] Error: 'result' argument is NULL\n"); 170 return NISSY_ERROR_NULL_POINTER; 171 } 172 173 return move_variations(moves, variation, result_size, result); 174 } 175 176 long long 177 nissy_getcube( 178 long long ep, 179 long long eo, 180 long long cp, 181 long long co, 182 long long orient, 183 const char *options, 184 char result[static NISSY_SIZE_CUBE] 185 ) 186 { 187 int i; 188 oriented_cube_t oc; 189 190 if (options == NULL) { 191 LOG("[getcube] Error: 'options' argument is NULL\n"); 192 return NISSY_ERROR_NULL_POINTER; 193 } 194 195 for (i = 0; getcube_options[i].option != NULL; i++) 196 if (!strcmp(options, getcube_options[i].option)) 197 getcube_options[i].fix(&ep, &eo, &cp, &co, &orient); 198 199 oc.cube = getcube(ep, eo, cp, co); 200 oc.orientation = orient; 201 202 if (!isconsistent(oc)) { 203 LOG("[getcube] Error: could not get cube with ep=%lld, " 204 "eo=%lld, cp=%lld, co=%lld, orient=%lld.\n", 205 ep, eo, cp, co, orient); 206 return NISSY_ERROR_OPTIONS; 207 } 208 209 return write_result(oc, result); 210 } 211 212 STATIC long long 213 nissy_dataid(const char *solver, char dataid[static NISSY_SIZE_DATAID]) 214 { 215 solver_dispatch_t dispatch; 216 217 dispatch = match_solver(solver); 218 if (dispatch.prefix == NULL) { 219 LOG("[dataid] Unknown solver %s\n", solver); 220 return NISSY_ERROR_INVALID_SOLVER; 221 } 222 223 return dispatch.dataid(dispatch.solvername, dataid); 224 } 225 226 long long 227 nissy_solverinfo( 228 const char *solver, 229 char dataid[static NISSY_SIZE_DATAID] 230 ) 231 { 232 long long err; 233 if ((err = nissy_dataid(solver, dataid)) != NISSY_OK) 234 return err; 235 236 /* gendata() handles a NULL *data as a "dryrun" request */ 237 return nissy_gendata_unsafe(solver, 0, NULL); 238 } 239 240 long long 241 nissy_gendata( 242 const char *solver, 243 unsigned long long data_size, 244 unsigned char *data 245 ) 246 { 247 return nissy_gendata_unsafe(solver, data_size, data); 248 } 249 250 STATIC long long 251 nissy_gendata_unsafe( 252 const char *solver, 253 unsigned long long data_size, 254 unsigned char *data 255 ) 256 { 257 solver_dispatch_t dispatch; 258 259 if (solver == NULL) { 260 LOG("[gendata] Error: 'solver' argument is NULL\n"); 261 return NISSY_ERROR_NULL_POINTER; 262 } 263 264 if ((size_t)data % 8 != 0) { 265 LOG("[gendata] Error: buffer is not 8-byte aligned\n"); 266 return NISSY_ERROR_DATA; 267 } 268 269 dispatch = match_solver(solver); 270 if (dispatch.prefix == NULL) { 271 LOG("[gendata] Unknown solver %s\n", solver); 272 return NISSY_ERROR_INVALID_SOLVER; 273 } 274 275 return dispatch.gendata(dispatch.solvername, data_size, data); 276 } 277 278 long long 279 nissy_checkdata( 280 const char *solver, 281 unsigned long long data_size, 282 const unsigned char *data 283 ) 284 { 285 solver_dispatch_t dispatch; 286 287 dispatch = match_solver(solver); 288 if (dispatch.prefix == NULL) { 289 LOG("[checkdata] Unknown solver %s\n", solver); 290 return NISSY_ERROR_INVALID_SOLVER; 291 } 292 293 return dispatch.checkdata(dispatch.solvername, data_size, data); 294 } 295 296 long long 297 nissy_solve( 298 const char cube[static NISSY_SIZE_CUBE], 299 const char *solver, 300 unsigned nissflag, 301 unsigned minmoves, 302 unsigned maxmoves, 303 unsigned maxsols, 304 unsigned optimal, 305 unsigned threads, 306 unsigned long long data_size, 307 const unsigned char *data, 308 unsigned sols_size, 309 char *sols, 310 long long stats[static NISSY_SIZE_SOLVE_STATS], 311 int (*poll_status)(void *), 312 void *poll_status_data 313 ) 314 { 315 oriented_cube_t oc; 316 int t; 317 solver_dispatch_t dispatch; 318 319 if (solver == NULL) { 320 LOG("[solve] Error: 'solver' argument is NULL\n"); 321 return NISSY_ERROR_NULL_POINTER; 322 } 323 324 oc = readcube(cube); 325 326 if (!isconsistent(oc)) { 327 LOG("[solve] Error: cube is invalid\n"); 328 return NISSY_ERROR_INVALID_CUBE; 329 } 330 331 if (maxmoves > 20) { 332 LOG("[solve] 'maxmoves' larger than 20 not supported yet, " 333 "setting it to 20\n"); 334 maxmoves = 20; 335 } 336 337 if (minmoves > maxmoves) { 338 LOG("[solve] value provided for 'minmoves' (%u) is larger " 339 "than that provided for 'maxmoves' (%u), setting " 340 "'minmoves' to %u\n", minmoves, maxmoves, maxmoves); 341 minmoves = maxmoves; 342 } 343 344 if (maxsols == 0) { 345 LOG("[solve] 'maxsols' is 0, returning no solution\n"); 346 return 0; 347 } 348 349 if (threads > THREADS) 350 LOG("[solve] Selected number of threads (%u) is above the " 351 "maximum value (%d), using %d threads instead\n", 352 threads, THREADS, THREADS); 353 t = threads == 0 ? THREADS : MIN(THREADS, threads); 354 355 if ((size_t)data % 8 != 0) { 356 LOG("[solve] Error: data buffer is not 8-byte aligned\n"); 357 return NISSY_ERROR_DATA; 358 } 359 360 dispatch = match_solver(solver); 361 if (dispatch.prefix == NULL) { 362 LOG("[solve] Error: unknown solver '%s'\n", solver); 363 return NISSY_ERROR_INVALID_SOLVER; 364 } 365 return dispatch.solve(oc, dispatch.solvername, nissflag, minmoves, 366 maxmoves, maxsols, optimal, t, data_size, data, sols_size, sols, 367 stats, poll_status, poll_status_data); 368 } 369 370 long long 371 nissy_countmoves( 372 const char *moves 373 ) 374 { 375 if (moves == NULL) 376 return NISSY_ERROR_NULL_POINTER; 377 378 return countmoves(moves); 379 } 380 381 long long 382 nissy_comparemoves( 383 const char *moves1, 384 const char *moves2 385 ) 386 { 387 if (moves1 == NULL || moves2 == NULL) 388 return NISSY_ERROR_NULL_POINTER; 389 390 return comparemoves(moves1, moves2); 391 } 392 393 long long 394 nissy_setlogger( 395 void (*log)(const char *, void *), 396 void *user_data 397 ) 398 { 399 nissy_log = log; 400 nissy_log_data = user_data; 401 return NISSY_OK; 402 }