nissy.c (7882B)
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 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_getcube( 152 long long ep, 153 long long eo, 154 long long cp, 155 long long co, 156 long long orient, 157 const char *options, 158 char result[static NISSY_SIZE_CUBE] 159 ) 160 { 161 int i; 162 oriented_cube_t oc; 163 164 if (options == NULL) { 165 LOG("[getcube] Error: 'options' argument is NULL\n"); 166 return NISSY_ERROR_NULL_POINTER; 167 } 168 169 for (i = 0; getcube_options[i].option != NULL; i++) 170 if (!strcmp(options, getcube_options[i].option)) 171 getcube_options[i].fix(&ep, &eo, &cp, &co, &orient); 172 173 oc.cube = getcube(ep, eo, cp, co); 174 oc.orientation = orient; 175 176 if (!isconsistent(oc)) { 177 LOG("[getcube] Error: could not get cube with ep=%lld, " 178 "eo=%lld, cp=%lld, co=%lld, orient=%lld.\n", 179 ep, eo, cp, co, orient); 180 return NISSY_ERROR_OPTIONS; 181 } 182 183 return write_result(oc, result); 184 } 185 186 STATIC long long 187 nissy_dataid(const char *solver, char dataid[static NISSY_SIZE_DATAID]) 188 { 189 solver_dispatch_t *dispatch; 190 191 dispatch = match_solver(solver); 192 if (dispatch == NULL) { 193 LOG("[dataid] Unknown solver %s\n", solver); 194 return NISSY_ERROR_INVALID_SOLVER; 195 } 196 197 return dispatch->dataid(solver, dataid); 198 } 199 200 long long 201 nissy_solverinfo( 202 const char *solver, 203 char dataid[static NISSY_SIZE_DATAID] 204 ) 205 { 206 long long err; 207 if ((err = nissy_dataid(solver, dataid)) != NISSY_OK) 208 return err; 209 210 /* gendata() handles a NULL *data as a "dryrun" request */ 211 return nissy_gendata_unsafe(solver, 0, NULL); 212 } 213 214 long long 215 nissy_gendata( 216 const char *solver, 217 unsigned long long data_size, 218 unsigned char *data 219 ) 220 { 221 return nissy_gendata_unsafe(solver, data_size, data); 222 } 223 224 STATIC long long 225 nissy_gendata_unsafe( 226 const char *solver, 227 unsigned long long data_size, 228 unsigned char *data 229 ) 230 { 231 solver_dispatch_t *dispatch; 232 233 if (solver == NULL) { 234 LOG("[gendata] Error: 'solver' argument is NULL\n"); 235 return NISSY_ERROR_NULL_POINTER; 236 } 237 238 if ((size_t)data % 8 != 0) { 239 LOG("[gendata] Error: buffer is not 8-byte aligned\n"); 240 return NISSY_ERROR_DATA; 241 } 242 243 dispatch = match_solver(solver); 244 if (dispatch == NULL) { 245 LOG("[gendata] Unknown solver %s\n", solver); 246 return NISSY_ERROR_INVALID_SOLVER; 247 } 248 249 return dispatch->gendata(solver, data_size, data); 250 } 251 252 long long 253 nissy_checkdata( 254 const char *solver, 255 unsigned long long data_size, 256 const unsigned char *data 257 ) 258 { 259 solver_dispatch_t *dispatch; 260 261 dispatch = match_solver(solver); 262 if (dispatch == NULL) { 263 LOG("[checkdata] Unknown solver %s\n", solver); 264 return NISSY_ERROR_INVALID_SOLVER; 265 } 266 267 return dispatch->checkdata(solver, data_size, data); 268 } 269 270 long long 271 nissy_solve( 272 const char cube[static NISSY_SIZE_CUBE], 273 const char *solver, 274 unsigned nissflag, 275 unsigned minmoves, 276 unsigned maxmoves, 277 unsigned maxsols, 278 unsigned optimal, 279 unsigned threads, 280 unsigned long long data_size, 281 const unsigned char *data, 282 unsigned sols_size, 283 char *sols, 284 long long stats[static NISSY_SIZE_SOLVE_STATS], 285 int (*poll_status)(void *), 286 void *poll_status_data 287 ) 288 { 289 oriented_cube_t oc; 290 int t; 291 solver_dispatch_t *dispatch; 292 293 if (solver == NULL) { 294 LOG("[solve] Error: 'solver' argument is NULL\n"); 295 return NISSY_ERROR_NULL_POINTER; 296 } 297 298 oc = readcube(cube); 299 300 if (!isconsistent(oc)) { 301 LOG("[solve] Error: cube is invalid\n"); 302 return NISSY_ERROR_INVALID_CUBE; 303 } 304 305 if (maxmoves > 20) { 306 LOG("[solve] 'maxmoves' larger than 20 not supported yet, " 307 "setting it to 20\n"); 308 maxmoves = 20; 309 } 310 311 if (minmoves > maxmoves) { 312 LOG("[solve] value provided for 'minmoves' (%u) is larger " 313 "than that provided for 'maxmoves' (%u), setting " 314 "'minmoves' to %u\n", minmoves, maxmoves, maxmoves); 315 minmoves = maxmoves; 316 } 317 318 if (maxsols == 0) { 319 LOG("[solve] 'maxsols' is 0, returning no solution\n"); 320 return 0; 321 } 322 323 if (threads > THREADS) 324 LOG("[solve] Selected number of threads (%u) is above the " 325 "maximum value (%d), using %d threads instead\n", 326 threads, THREADS, THREADS); 327 t = threads == 0 ? THREADS : MIN(THREADS, threads); 328 329 if ((size_t)data % 8 != 0) { 330 LOG("[solve] Error: data buffer is not 8-byte aligned\n"); 331 return NISSY_ERROR_DATA; 332 } 333 334 dispatch = match_solver(solver); 335 if (dispatch == NULL) { 336 LOG("[solve] Error: unknown solver '%s'\n", solver); 337 return NISSY_ERROR_INVALID_SOLVER; 338 } 339 return dispatch->solve(oc, solver, nissflag, minmoves, maxmoves, 340 maxsols, optimal, t, data_size, data, sols_size, sols, stats, 341 poll_status, poll_status_data); 342 } 343 344 long long 345 nissy_countmoves( 346 const char *moves 347 ) 348 { 349 if (moves == NULL) 350 return NISSY_ERROR_NULL_POINTER; 351 352 return countmoves(moves); 353 } 354 355 long long 356 nissy_setlogger( 357 void (*log)(const char *, void *), 358 void *user_data 359 ) 360 { 361 nissy_log = log; 362 nissy_log_data = user_data; 363 return NISSY_OK; 364 }