| cannam@95 | 1 /* | 
| cannam@95 | 2  * Copyright (c) 2000 Matteo Frigo | 
| cannam@95 | 3  * Copyright (c) 2000 Massachusetts Institute of Technology | 
| cannam@95 | 4  * | 
| cannam@95 | 5  * This program is free software; you can redistribute it and/or modify | 
| cannam@95 | 6  * it under the terms of the GNU General Public License as published by | 
| cannam@95 | 7  * the Free Software Foundation; either version 2 of the License, or | 
| cannam@95 | 8  * (at your option) any later version. | 
| cannam@95 | 9  * | 
| cannam@95 | 10  * This program is distributed in the hope that it will be useful, | 
| cannam@95 | 11  * but WITHOUT ANY WARRANTY; without even the implied warranty of | 
| cannam@95 | 12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | 
| cannam@95 | 13  * GNU General Public License for more details. | 
| cannam@95 | 14  * | 
| cannam@95 | 15  * You should have received a copy of the GNU General Public License | 
| cannam@95 | 16  * along with this program; if not, write to the Free Software | 
| cannam@95 | 17  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA | 
| cannam@95 | 18  * | 
| cannam@95 | 19  */ | 
| cannam@95 | 20 | 
| cannam@95 | 21 #include "ifftw.h" | 
| cannam@95 | 22 #include <string.h> | 
| cannam@95 | 23 | 
| cannam@95 | 24 /* GNU Coding Standards, Sec. 5.2: "Please write the comments in a GNU | 
| cannam@95 | 25    program in English, because English is the one language that nearly | 
| cannam@95 | 26    all programmers in all countries can read." | 
| cannam@95 | 27 | 
| cannam@95 | 28                     ingemisco tanquam reus | 
| cannam@95 | 29 		    culpa rubet vultus meus | 
| cannam@95 | 30 		    supplicanti parce [rms] | 
| cannam@95 | 31 */ | 
| cannam@95 | 32 | 
| cannam@95 | 33 #define VALIDP(solution) ((solution)->flags.hash_info & H_VALID) | 
| cannam@95 | 34 #define LIVEP(solution) ((solution)->flags.hash_info & H_LIVE) | 
| cannam@95 | 35 #define SLVNDX(solution) ((solution)->flags.slvndx) | 
| cannam@95 | 36 #define BLISS(flags) (((flags).hash_info) & BLESSING) | 
| cannam@95 | 37 #define INFEASIBLE_SLVNDX ((1U<<BITS_FOR_SLVNDX)-1) | 
| cannam@95 | 38 | 
| cannam@95 | 39 | 
| cannam@95 | 40 #define MAXNAM 64  /* maximum length of registrar's name. | 
| cannam@95 | 41 		      Used for reading wisdom.  There is no point | 
| cannam@95 | 42 		      in doing this right */ | 
| cannam@95 | 43 | 
| cannam@95 | 44 | 
| cannam@95 | 45 #ifdef FFTW_DEBUG | 
| cannam@95 | 46 static void check(hashtab *ht); | 
| cannam@95 | 47 #endif | 
| cannam@95 | 48 | 
| cannam@95 | 49 /* x <= y */ | 
| cannam@95 | 50 #define LEQ(x, y) (((x) & (y)) == (x)) | 
| cannam@95 | 51 | 
| cannam@95 | 52 /* A subsumes B */ | 
| cannam@95 | 53 static int subsumes(const flags_t *a, unsigned slvndx_a, const flags_t *b) | 
| cannam@95 | 54 { | 
| cannam@95 | 55      if (slvndx_a != INFEASIBLE_SLVNDX) { | 
| cannam@95 | 56 	  A(a->timelimit_impatience == 0); | 
| cannam@95 | 57 	  return (LEQ(a->u, b->u) && LEQ(b->l, a->l)); | 
| cannam@95 | 58      } else { | 
| cannam@95 | 59 	  return (LEQ(a->l, b->l) | 
| cannam@95 | 60 		  && a->timelimit_impatience <= b->timelimit_impatience); | 
| cannam@95 | 61      } | 
| cannam@95 | 62 } | 
| cannam@95 | 63 | 
| cannam@95 | 64 static unsigned addmod(unsigned a, unsigned b, unsigned p) | 
| cannam@95 | 65 { | 
| cannam@95 | 66      /* gcc-2.95/sparc produces incorrect code for the fast version below. */ | 
| cannam@95 | 67 #if defined(__sparc__) && defined(__GNUC__) | 
| cannam@95 | 68      /* slow version  */ | 
| cannam@95 | 69      return (a + b) % p; | 
| cannam@95 | 70 #else | 
| cannam@95 | 71      /* faster version */ | 
| cannam@95 | 72      unsigned c = a + b; | 
| cannam@95 | 73      return c >= p ? c - p : c; | 
| cannam@95 | 74 #endif | 
| cannam@95 | 75 } | 
| cannam@95 | 76 | 
| cannam@95 | 77 /* | 
| cannam@95 | 78   slvdesc management: | 
| cannam@95 | 79 */ | 
| cannam@95 | 80 static void sgrow(planner *ego) | 
| cannam@95 | 81 { | 
| cannam@95 | 82      unsigned osiz = ego->slvdescsiz, nsiz = 1 + osiz + osiz / 4; | 
| cannam@95 | 83      slvdesc *ntab = (slvdesc *)MALLOC(nsiz * sizeof(slvdesc), SLVDESCS); | 
| cannam@95 | 84      slvdesc *otab = ego->slvdescs; | 
| cannam@95 | 85      unsigned i; | 
| cannam@95 | 86 | 
| cannam@95 | 87      ego->slvdescs = ntab; | 
| cannam@95 | 88      ego->slvdescsiz = nsiz; | 
| cannam@95 | 89      for (i = 0; i < osiz; ++i) | 
| cannam@95 | 90 	  ntab[i] = otab[i]; | 
| cannam@95 | 91      X(ifree0)(otab); | 
| cannam@95 | 92 } | 
| cannam@95 | 93 | 
| cannam@95 | 94 static void register_solver(planner *ego, solver *s) | 
| cannam@95 | 95 { | 
| cannam@95 | 96      slvdesc *n; | 
| cannam@95 | 97      int kind; | 
| cannam@95 | 98 | 
| cannam@95 | 99      if (s) { /* add s to solver list */ | 
| cannam@95 | 100 	  X(solver_use)(s); | 
| cannam@95 | 101 | 
| cannam@95 | 102 	  A(ego->nslvdesc < INFEASIBLE_SLVNDX); | 
| cannam@95 | 103 	  if (ego->nslvdesc >= ego->slvdescsiz) | 
| cannam@95 | 104 	       sgrow(ego); | 
| cannam@95 | 105 | 
| cannam@95 | 106 	  n = ego->slvdescs + ego->nslvdesc; | 
| cannam@95 | 107 | 
| cannam@95 | 108 	  n->slv = s; | 
| cannam@95 | 109 	  n->reg_nam = ego->cur_reg_nam; | 
| cannam@95 | 110 	  n->reg_id = ego->cur_reg_id++; | 
| cannam@95 | 111 | 
| cannam@95 | 112 	  A(strlen(n->reg_nam) < MAXNAM); | 
| cannam@95 | 113 	  n->nam_hash = X(hash)(n->reg_nam); | 
| cannam@95 | 114 | 
| cannam@95 | 115 	  kind = s->adt->problem_kind; | 
| cannam@95 | 116 	  n->next_for_same_problem_kind = ego->slvdescs_for_problem_kind[kind]; | 
| cannam@95 | 117 	  ego->slvdescs_for_problem_kind[kind] = ego->nslvdesc; | 
| cannam@95 | 118 | 
| cannam@95 | 119 	  ego->nslvdesc++; | 
| cannam@95 | 120      } | 
| cannam@95 | 121 } | 
| cannam@95 | 122 | 
| cannam@95 | 123 static unsigned slookup(planner *ego, char *nam, int id) | 
| cannam@95 | 124 { | 
| cannam@95 | 125      unsigned h = X(hash)(nam); /* used to avoid strcmp in the common case */ | 
| cannam@95 | 126      FORALL_SOLVERS(ego, s, sp, { | 
| cannam@95 | 127 	  UNUSED(s); | 
| cannam@95 | 128 	  if (sp->reg_id == id && sp->nam_hash == h | 
| cannam@95 | 129 	      && !strcmp(sp->reg_nam, nam)) | 
| cannam@95 | 130 	       return sp - ego->slvdescs; | 
| cannam@95 | 131      }); | 
| cannam@95 | 132      return INFEASIBLE_SLVNDX; | 
| cannam@95 | 133 } | 
| cannam@95 | 134 | 
| cannam@95 | 135 /* Compute a MD5 hash of the configuration of the planner. | 
| cannam@95 | 136    We store it into the wisdom file to make absolutely sure that | 
| cannam@95 | 137    we are reading wisdom that is applicable */ | 
| cannam@95 | 138 static void signature_of_configuration(md5 *m, planner *ego) | 
| cannam@95 | 139 { | 
| cannam@95 | 140      X(md5begin)(m); | 
| cannam@95 | 141      X(md5unsigned)(m, sizeof(R)); /* so we don't mix different precisions */ | 
| cannam@95 | 142      FORALL_SOLVERS(ego, s, sp, { | 
| cannam@95 | 143 	  UNUSED(s); | 
| cannam@95 | 144 	  X(md5int)(m, sp->reg_id); | 
| cannam@95 | 145 	  X(md5puts)(m, sp->reg_nam); | 
| cannam@95 | 146      }); | 
| cannam@95 | 147      X(md5end)(m); | 
| cannam@95 | 148 } | 
| cannam@95 | 149 | 
| cannam@95 | 150 /* | 
| cannam@95 | 151   md5-related stuff: | 
| cannam@95 | 152 */ | 
| cannam@95 | 153 | 
| cannam@95 | 154 /* first hash function */ | 
| cannam@95 | 155 static unsigned h1(const hashtab *ht, const md5sig s) | 
| cannam@95 | 156 { | 
| cannam@95 | 157      unsigned h = s[0] % ht->hashsiz; | 
| cannam@95 | 158      A(h == (s[0] % ht->hashsiz)); | 
| cannam@95 | 159      return h; | 
| cannam@95 | 160 } | 
| cannam@95 | 161 | 
| cannam@95 | 162 /* second hash function (for double hashing) */ | 
| cannam@95 | 163 static unsigned h2(const hashtab *ht, const md5sig s) | 
| cannam@95 | 164 { | 
| cannam@95 | 165      unsigned h = 1U + s[1] % (ht->hashsiz - 1); | 
| cannam@95 | 166      A(h == (1U + s[1] % (ht->hashsiz - 1))); | 
| cannam@95 | 167      return h; | 
| cannam@95 | 168 } | 
| cannam@95 | 169 | 
| cannam@95 | 170 static void md5hash(md5 *m, const problem *p, const planner *plnr) | 
| cannam@95 | 171 { | 
| cannam@95 | 172      X(md5begin)(m); | 
| cannam@95 | 173      X(md5unsigned)(m, sizeof(R)); /* so we don't mix different precisions */ | 
| cannam@95 | 174      X(md5int)(m, plnr->nthr); | 
| cannam@95 | 175      p->adt->hash(p, m); | 
| cannam@95 | 176      X(md5end)(m); | 
| cannam@95 | 177 } | 
| cannam@95 | 178 | 
| cannam@95 | 179 static int md5eq(const md5sig a, const md5sig b) | 
| cannam@95 | 180 { | 
| cannam@95 | 181      return a[0] == b[0] && a[1] == b[1] && a[2] == b[2] && a[3] == b[3]; | 
| cannam@95 | 182 } | 
| cannam@95 | 183 | 
| cannam@95 | 184 static void sigcpy(const md5sig a, md5sig b) | 
| cannam@95 | 185 { | 
| cannam@95 | 186      b[0] = a[0]; b[1] = a[1]; b[2] = a[2]; b[3] = a[3]; | 
| cannam@95 | 187 } | 
| cannam@95 | 188 | 
| cannam@95 | 189 /* | 
| cannam@95 | 190   memoization routines : | 
| cannam@95 | 191 */ | 
| cannam@95 | 192 | 
| cannam@95 | 193 /* | 
| cannam@95 | 194    liber scriptus proferetur | 
| cannam@95 | 195    in quo totum continetur | 
| cannam@95 | 196    unde mundus iudicetur | 
| cannam@95 | 197 */ | 
| cannam@95 | 198 struct solution_s { | 
| cannam@95 | 199      md5sig s; | 
| cannam@95 | 200      flags_t flags; | 
| cannam@95 | 201 }; | 
| cannam@95 | 202 | 
| cannam@95 | 203 static solution *htab_lookup(hashtab *ht, const md5sig s, | 
| cannam@95 | 204 			     const flags_t *flagsp) | 
| cannam@95 | 205 { | 
| cannam@95 | 206      unsigned g, h = h1(ht, s), d = h2(ht, s); | 
| cannam@95 | 207      solution *best = 0; | 
| cannam@95 | 208 | 
| cannam@95 | 209      ++ht->lookup; | 
| cannam@95 | 210 | 
| cannam@95 | 211      /* search all entries that match; select the one with | 
| cannam@95 | 212 	the lowest flags.u */ | 
| cannam@95 | 213      /* This loop may potentially traverse the whole table, since at | 
| cannam@95 | 214 	least one element is guaranteed to be !LIVEP, but all elements | 
| cannam@95 | 215 	may be VALIDP.  Hence, we stop after at the first invalid | 
| cannam@95 | 216 	element or after traversing the whole table. */ | 
| cannam@95 | 217      g = h; | 
| cannam@95 | 218      do { | 
| cannam@95 | 219 	  solution *l = ht->solutions + g; | 
| cannam@95 | 220 	  ++ht->lookup_iter; | 
| cannam@95 | 221 	  if (VALIDP(l)) { | 
| cannam@95 | 222 	       if (LIVEP(l) | 
| cannam@95 | 223 		   && md5eq(s, l->s) | 
| cannam@95 | 224 		   && subsumes(&l->flags, SLVNDX(l), flagsp) ) { | 
| cannam@95 | 225 		    if (!best || LEQ(l->flags.u, best->flags.u)) | 
| cannam@95 | 226 			 best = l; | 
| cannam@95 | 227 	       } | 
| cannam@95 | 228 	  } else | 
| cannam@95 | 229 	       break; | 
| cannam@95 | 230 | 
| cannam@95 | 231 	  g = addmod(g, d, ht->hashsiz); | 
| cannam@95 | 232      } while (g != h); | 
| cannam@95 | 233 | 
| cannam@95 | 234      if (best) | 
| cannam@95 | 235 	  ++ht->succ_lookup; | 
| cannam@95 | 236      return best; | 
| cannam@95 | 237 } | 
| cannam@95 | 238 | 
| cannam@95 | 239 static solution *hlookup(planner *ego, const md5sig s, | 
| cannam@95 | 240 			 const flags_t *flagsp) | 
| cannam@95 | 241 { | 
| cannam@95 | 242      solution *sol = htab_lookup(&ego->htab_blessed, s, flagsp); | 
| cannam@95 | 243      if (!sol) sol = htab_lookup(&ego->htab_unblessed, s, flagsp); | 
| cannam@95 | 244      return sol; | 
| cannam@95 | 245 } | 
| cannam@95 | 246 | 
| cannam@95 | 247 static void fill_slot(hashtab *ht, const md5sig s, const flags_t *flagsp, | 
| cannam@95 | 248 		      unsigned slvndx, solution *slot) | 
| cannam@95 | 249 { | 
| cannam@95 | 250      ++ht->insert; | 
| cannam@95 | 251      ++ht->nelem; | 
| cannam@95 | 252      A(!LIVEP(slot)); | 
| cannam@95 | 253      slot->flags.u = flagsp->u; | 
| cannam@95 | 254      slot->flags.l = flagsp->l; | 
| cannam@95 | 255      slot->flags.timelimit_impatience = flagsp->timelimit_impatience; | 
| cannam@95 | 256      slot->flags.hash_info |= H_VALID | H_LIVE; | 
| cannam@95 | 257      SLVNDX(slot) = slvndx; | 
| cannam@95 | 258 | 
| cannam@95 | 259      /* keep this check enabled in case we add so many solvers | 
| cannam@95 | 260 	that the bitfield overflows */ | 
| cannam@95 | 261      CK(SLVNDX(slot) == slvndx); | 
| cannam@95 | 262      sigcpy(s, slot->s); | 
| cannam@95 | 263 } | 
| cannam@95 | 264 | 
| cannam@95 | 265 static void kill_slot(hashtab *ht, solution *slot) | 
| cannam@95 | 266 { | 
| cannam@95 | 267      A(LIVEP(slot)); /* ==> */ A(VALIDP(slot)); | 
| cannam@95 | 268 | 
| cannam@95 | 269      --ht->nelem; | 
| cannam@95 | 270      slot->flags.hash_info = H_VALID; | 
| cannam@95 | 271 } | 
| cannam@95 | 272 | 
| cannam@95 | 273 static void hinsert0(hashtab *ht, const md5sig s, const flags_t *flagsp, | 
| cannam@95 | 274 		     unsigned slvndx) | 
| cannam@95 | 275 { | 
| cannam@95 | 276      solution *l; | 
| cannam@95 | 277      unsigned g, h = h1(ht, s), d = h2(ht, s); | 
| cannam@95 | 278 | 
| cannam@95 | 279      ++ht->insert_unknown; | 
| cannam@95 | 280 | 
| cannam@95 | 281      /* search for nonfull slot */ | 
| cannam@95 | 282      for (g = h; ; g = addmod(g, d, ht->hashsiz)) { | 
| cannam@95 | 283 	  ++ht->insert_iter; | 
| cannam@95 | 284 	  l = ht->solutions + g; | 
| cannam@95 | 285 	  if (!LIVEP(l)) break; | 
| cannam@95 | 286 	  A((g + d) % ht->hashsiz != h); | 
| cannam@95 | 287      } | 
| cannam@95 | 288 | 
| cannam@95 | 289      fill_slot(ht, s, flagsp, slvndx, l); | 
| cannam@95 | 290 } | 
| cannam@95 | 291 | 
| cannam@95 | 292 static void rehash(hashtab *ht, unsigned nsiz) | 
| cannam@95 | 293 { | 
| cannam@95 | 294      unsigned osiz = ht->hashsiz, h; | 
| cannam@95 | 295      solution *osol = ht->solutions, *nsol; | 
| cannam@95 | 296 | 
| cannam@95 | 297      nsiz = (unsigned)X(next_prime)((INT)nsiz); | 
| cannam@95 | 298      nsol = (solution *)MALLOC(nsiz * sizeof(solution), HASHT); | 
| cannam@95 | 299      ++ht->nrehash; | 
| cannam@95 | 300 | 
| cannam@95 | 301      /* init new table */ | 
| cannam@95 | 302      for (h = 0; h < nsiz; ++h) | 
| cannam@95 | 303 	  nsol[h].flags.hash_info = 0; | 
| cannam@95 | 304 | 
| cannam@95 | 305      /* install new table */ | 
| cannam@95 | 306      ht->hashsiz = nsiz; | 
| cannam@95 | 307      ht->solutions = nsol; | 
| cannam@95 | 308      ht->nelem = 0; | 
| cannam@95 | 309 | 
| cannam@95 | 310      /* copy table */ | 
| cannam@95 | 311      for (h = 0; h < osiz; ++h) { | 
| cannam@95 | 312 	  solution *l = osol + h; | 
| cannam@95 | 313 	  if (LIVEP(l)) | 
| cannam@95 | 314 	       hinsert0(ht, l->s, &l->flags, SLVNDX(l)); | 
| cannam@95 | 315      } | 
| cannam@95 | 316 | 
| cannam@95 | 317      X(ifree0)(osol); | 
| cannam@95 | 318 } | 
| cannam@95 | 319 | 
| cannam@95 | 320 static unsigned minsz(unsigned nelem) | 
| cannam@95 | 321 { | 
| cannam@95 | 322      return 1U + nelem + nelem / 8U; | 
| cannam@95 | 323 } | 
| cannam@95 | 324 | 
| cannam@95 | 325 static unsigned nextsz(unsigned nelem) | 
| cannam@95 | 326 { | 
| cannam@95 | 327      return minsz(minsz(nelem)); | 
| cannam@95 | 328 } | 
| cannam@95 | 329 | 
| cannam@95 | 330 static void hgrow(hashtab *ht) | 
| cannam@95 | 331 { | 
| cannam@95 | 332      unsigned nelem = ht->nelem; | 
| cannam@95 | 333      if (minsz(nelem) >= ht->hashsiz) | 
| cannam@95 | 334 	  rehash(ht, nextsz(nelem)); | 
| cannam@95 | 335 } | 
| cannam@95 | 336 | 
| cannam@95 | 337 #if 0 | 
| cannam@95 | 338 /* shrink the hash table, never used */ | 
| cannam@95 | 339 static void hshrink(hashtab *ht) | 
| cannam@95 | 340 { | 
| cannam@95 | 341      unsigned nelem = ht->nelem; | 
| cannam@95 | 342      /* always rehash after deletions */ | 
| cannam@95 | 343      rehash(ht, nextsz(nelem)); | 
| cannam@95 | 344 } | 
| cannam@95 | 345 #endif | 
| cannam@95 | 346 | 
| cannam@95 | 347 static void htab_insert(hashtab *ht, const md5sig s, const flags_t *flagsp, | 
| cannam@95 | 348 			unsigned slvndx) | 
| cannam@95 | 349 { | 
| cannam@95 | 350      unsigned g, h = h1(ht, s), d = h2(ht, s); | 
| cannam@95 | 351      solution *first = 0; | 
| cannam@95 | 352 | 
| cannam@95 | 353      /* Remove all entries that are subsumed by the new one.  */ | 
| cannam@95 | 354      /* This loop may potentially traverse the whole table, since at | 
| cannam@95 | 355 	least one element is guaranteed to be !LIVEP, but all elements | 
| cannam@95 | 356 	may be VALIDP.  Hence, we stop after at the first invalid | 
| cannam@95 | 357 	element or after traversing the whole table. */ | 
| cannam@95 | 358      g = h; | 
| cannam@95 | 359      do { | 
| cannam@95 | 360 	  solution *l = ht->solutions + g; | 
| cannam@95 | 361 	  ++ht->insert_iter; | 
| cannam@95 | 362 	  if (VALIDP(l)) { | 
| cannam@95 | 363 	       if (LIVEP(l) && md5eq(s, l->s)) { | 
| cannam@95 | 364 		    if (subsumes(flagsp, slvndx, &l->flags)) { | 
| cannam@95 | 365 			 if (!first) first = l; | 
| cannam@95 | 366 			 kill_slot(ht, l); | 
| cannam@95 | 367 		    } else { | 
| cannam@95 | 368 			 /* It is an error to insert an element that | 
| cannam@95 | 369 			    is subsumed by an existing entry. */ | 
| cannam@95 | 370 			 A(!subsumes(&l->flags, SLVNDX(l), flagsp)); | 
| cannam@95 | 371 		    } | 
| cannam@95 | 372 	       } | 
| cannam@95 | 373 	  } else | 
| cannam@95 | 374 	       break; | 
| cannam@95 | 375 | 
| cannam@95 | 376 	  g = addmod(g, d, ht->hashsiz); | 
| cannam@95 | 377      } while (g != h); | 
| cannam@95 | 378 | 
| cannam@95 | 379      if (first) { | 
| cannam@95 | 380 	  /* overwrite FIRST */ | 
| cannam@95 | 381 	  fill_slot(ht, s, flagsp, slvndx, first); | 
| cannam@95 | 382      } else { | 
| cannam@95 | 383 	  /* create a new entry */ | 
| cannam@95 | 384  	  hgrow(ht); | 
| cannam@95 | 385 	  hinsert0(ht, s, flagsp, slvndx); | 
| cannam@95 | 386      } | 
| cannam@95 | 387 } | 
| cannam@95 | 388 | 
| cannam@95 | 389 static void hinsert(planner *ego, const md5sig s, const flags_t *flagsp, | 
| cannam@95 | 390 		    unsigned slvndx) | 
| cannam@95 | 391 { | 
| cannam@95 | 392      htab_insert(BLISS(*flagsp) ? &ego->htab_blessed : &ego->htab_unblessed, | 
| cannam@95 | 393 		 s, flagsp, slvndx ); | 
| cannam@95 | 394 } | 
| cannam@95 | 395 | 
| cannam@95 | 396 | 
| cannam@95 | 397 static void invoke_hook(planner *ego, plan *pln, const problem *p, | 
| cannam@95 | 398 			int optimalp) | 
| cannam@95 | 399 { | 
| cannam@95 | 400      if (ego->hook) | 
| cannam@95 | 401 	  ego->hook(ego, pln, p, optimalp); | 
| cannam@95 | 402 } | 
| cannam@95 | 403 | 
| cannam@95 | 404 #ifdef FFTW_RANDOM_ESTIMATOR | 
| cannam@95 | 405 /* a "random" estimate, used for debugging to generate "random" | 
| cannam@95 | 406    plans, albeit from a deterministic seed. */ | 
| cannam@95 | 407 | 
| cannam@95 | 408 unsigned X(random_estimate_seed) = 0; | 
| cannam@95 | 409 | 
| cannam@95 | 410 static double random_estimate(const planner *ego, const plan *pln, | 
| cannam@95 | 411 			      const problem *p) | 
| cannam@95 | 412 { | 
| cannam@95 | 413      md5 m; | 
| cannam@95 | 414      X(md5begin)(&m); | 
| cannam@95 | 415      X(md5unsigned)(&m, X(random_estimate_seed)); | 
| cannam@95 | 416      X(md5int)(&m, ego->nthr); | 
| cannam@95 | 417      p->adt->hash(p, &m); | 
| cannam@95 | 418      X(md5putb)(&m, &pln->ops, sizeof(pln->ops)); | 
| cannam@95 | 419      X(md5putb)(&m, &pln->adt, sizeof(pln->adt)); | 
| cannam@95 | 420      X(md5end)(&m); | 
| cannam@95 | 421      return ego->cost_hook ? ego->cost_hook(p, m.s[0], COST_MAX) : m.s[0]; | 
| cannam@95 | 422 } | 
| cannam@95 | 423 | 
| cannam@95 | 424 #endif | 
| cannam@95 | 425 | 
| cannam@95 | 426 double X(iestimate_cost)(const planner *ego, const plan *pln, const problem *p) | 
| cannam@95 | 427 { | 
| cannam@95 | 428      double cost = | 
| cannam@95 | 429 	  + pln->ops.add | 
| cannam@95 | 430 	  + pln->ops.mul | 
| cannam@95 | 431 | 
| cannam@95 | 432 #if HAVE_FMA | 
| cannam@95 | 433 	  + pln->ops.fma | 
| cannam@95 | 434 #else | 
| cannam@95 | 435 	  + 2 * pln->ops.fma | 
| cannam@95 | 436 #endif | 
| cannam@95 | 437 | 
| cannam@95 | 438 	  + pln->ops.other; | 
| cannam@95 | 439      if (ego->cost_hook) | 
| cannam@95 | 440 	  cost = ego->cost_hook(p, cost, COST_MAX); | 
| cannam@95 | 441      return cost; | 
| cannam@95 | 442 } | 
| cannam@95 | 443 | 
| cannam@95 | 444 static void evaluate_plan(planner *ego, plan *pln, const problem *p) | 
| cannam@95 | 445 { | 
| cannam@95 | 446      if (ESTIMATEP(ego) || !BELIEVE_PCOSTP(ego) || pln->pcost == 0.0) { | 
| cannam@95 | 447 	  ego->nplan++; | 
| cannam@95 | 448 | 
| cannam@95 | 449 	  if (ESTIMATEP(ego)) { | 
| cannam@95 | 450 	  estimate: | 
| cannam@95 | 451 	       /* heuristic */ | 
| cannam@95 | 452 #ifdef FFTW_RANDOM_ESTIMATOR | 
| cannam@95 | 453 	       pln->pcost = random_estimate(ego, pln, p); | 
| cannam@95 | 454 	       ego->epcost += X(iestimate_cost)(ego, pln, p); | 
| cannam@95 | 455 #else | 
| cannam@95 | 456 	       pln->pcost = X(iestimate_cost)(ego, pln, p); | 
| cannam@95 | 457 	       ego->epcost += pln->pcost; | 
| cannam@95 | 458 #endif | 
| cannam@95 | 459 	  } else { | 
| cannam@95 | 460 	       double t = X(measure_execution_time)(ego, pln, p); | 
| cannam@95 | 461 | 
| cannam@95 | 462 	       if (t < 0) {  /* unavailable cycle counter */ | 
| cannam@95 | 463 		    /* Real programmers can write FORTRAN in any language */ | 
| cannam@95 | 464 		    goto estimate; | 
| cannam@95 | 465 	       } | 
| cannam@95 | 466 | 
| cannam@95 | 467 	       pln->pcost = t; | 
| cannam@95 | 468 	       ego->pcost += t; | 
| cannam@95 | 469 	       ego->need_timeout_check = 1; | 
| cannam@95 | 470 	  } | 
| cannam@95 | 471      } | 
| cannam@95 | 472 | 
| cannam@95 | 473      invoke_hook(ego, pln, p, 0); | 
| cannam@95 | 474 } | 
| cannam@95 | 475 | 
| cannam@95 | 476 /* maintain dynamic scoping of flags, nthr: */ | 
| cannam@95 | 477 static plan *invoke_solver(planner *ego, const problem *p, solver *s, | 
| cannam@95 | 478 			   const flags_t *nflags) | 
| cannam@95 | 479 { | 
| cannam@95 | 480      flags_t flags = ego->flags; | 
| cannam@95 | 481      int nthr = ego->nthr; | 
| cannam@95 | 482      plan *pln; | 
| cannam@95 | 483      ego->flags = *nflags; | 
| cannam@95 | 484      PLNR_TIMELIMIT_IMPATIENCE(ego) = 0; | 
| cannam@95 | 485      A(p->adt->problem_kind == s->adt->problem_kind); | 
| cannam@95 | 486      pln = s->adt->mkplan(s, p, ego); | 
| cannam@95 | 487      ego->nthr = nthr; | 
| cannam@95 | 488      ego->flags = flags; | 
| cannam@95 | 489      return pln; | 
| cannam@95 | 490 } | 
| cannam@95 | 491 | 
| cannam@95 | 492 /* maintain the invariant TIMED_OUT ==> NEED_TIMEOUT_CHECK */ | 
| cannam@95 | 493 static int timeout_p(planner *ego, const problem *p) | 
| cannam@95 | 494 { | 
| cannam@95 | 495      /* do not timeout when estimating.  First, the estimator is the | 
| cannam@95 | 496 	planner of last resort.  Second, calling X(elapsed_since)() is | 
| cannam@95 | 497 	slower than estimating */ | 
| cannam@95 | 498      if (!ESTIMATEP(ego)) { | 
| cannam@95 | 499 	  /* do not assume that X(elapsed_since)() is monotonic */ | 
| cannam@95 | 500 	  if (ego->timed_out) { | 
| cannam@95 | 501 	       A(ego->need_timeout_check); | 
| cannam@95 | 502 	       return 1; | 
| cannam@95 | 503 	  } | 
| cannam@95 | 504 | 
| cannam@95 | 505 	  if (ego->timelimit >= 0 && | 
| cannam@95 | 506 	      X(elapsed_since)(ego, p, ego->start_time) >= ego->timelimit) { | 
| cannam@95 | 507 	       ego->timed_out = 1; | 
| cannam@95 | 508 	       ego->need_timeout_check = 1; | 
| cannam@95 | 509 	       return 1; | 
| cannam@95 | 510 	  } | 
| cannam@95 | 511      } | 
| cannam@95 | 512 | 
| cannam@95 | 513      A(!ego->timed_out); | 
| cannam@95 | 514      ego->need_timeout_check = 0; | 
| cannam@95 | 515      return 0; | 
| cannam@95 | 516 } | 
| cannam@95 | 517 | 
| cannam@95 | 518 static plan *search0(planner *ego, const problem *p, unsigned *slvndx, | 
| cannam@95 | 519 		     const flags_t *flagsp) | 
| cannam@95 | 520 { | 
| cannam@95 | 521      plan *best = 0; | 
| cannam@95 | 522      int best_not_yet_timed = 1; | 
| cannam@95 | 523 | 
| cannam@95 | 524      /* Do not start a search if the planner timed out. This check is | 
| cannam@95 | 525 	necessary, lest the relaxation mechanism kick in */ | 
| cannam@95 | 526      if (timeout_p(ego, p)) | 
| cannam@95 | 527 	  return 0; | 
| cannam@95 | 528 | 
| cannam@95 | 529      FORALL_SOLVERS_OF_KIND(p->adt->problem_kind, ego, s, sp, { | 
| cannam@95 | 530 	  plan *pln; | 
| cannam@95 | 531 | 
| cannam@95 | 532 	  pln = invoke_solver(ego, p, s, flagsp); | 
| cannam@95 | 533 | 
| cannam@95 | 534 	  if (ego->need_timeout_check) | 
| cannam@95 | 535 	       if (timeout_p(ego, p)) { | 
| cannam@95 | 536 		    X(plan_destroy_internal)(pln); | 
| cannam@95 | 537 		    X(plan_destroy_internal)(best); | 
| cannam@95 | 538 		    return 0; | 
| cannam@95 | 539 	       } | 
| cannam@95 | 540 | 
| cannam@95 | 541 	  if (pln) { | 
| cannam@95 | 542 	       /* read COULD_PRUNE_NOW_P because PLN may be destroyed | 
| cannam@95 | 543 		  before we use COULD_PRUNE_NOW_P */ | 
| cannam@95 | 544 	       int could_prune_now_p = pln->could_prune_now_p; | 
| cannam@95 | 545 | 
| cannam@95 | 546 	       if (best) { | 
| cannam@95 | 547 		    if (best_not_yet_timed) { | 
| cannam@95 | 548 			 evaluate_plan(ego, best, p); | 
| cannam@95 | 549 			 best_not_yet_timed = 0; | 
| cannam@95 | 550 		    } | 
| cannam@95 | 551 		    evaluate_plan(ego, pln, p); | 
| cannam@95 | 552 		    if (pln->pcost < best->pcost) { | 
| cannam@95 | 553 			 X(plan_destroy_internal)(best); | 
| cannam@95 | 554 			 best = pln; | 
| cannam@95 | 555 			 *slvndx = sp - ego->slvdescs; | 
| cannam@95 | 556 		    } else { | 
| cannam@95 | 557 			 X(plan_destroy_internal)(pln); | 
| cannam@95 | 558 		    } | 
| cannam@95 | 559 	       } else { | 
| cannam@95 | 560 		    best = pln; | 
| cannam@95 | 561 		    *slvndx = sp - ego->slvdescs; | 
| cannam@95 | 562 	       } | 
| cannam@95 | 563 | 
| cannam@95 | 564 	       if (ALLOW_PRUNINGP(ego) && could_prune_now_p) | 
| cannam@95 | 565 		    break; | 
| cannam@95 | 566 	  } | 
| cannam@95 | 567      }); | 
| cannam@95 | 568 | 
| cannam@95 | 569      return best; | 
| cannam@95 | 570 } | 
| cannam@95 | 571 | 
| cannam@95 | 572 static plan *search(planner *ego, const problem *p, unsigned *slvndx, | 
| cannam@95 | 573 		    flags_t *flagsp) | 
| cannam@95 | 574 { | 
| cannam@95 | 575      plan *pln = 0; | 
| cannam@95 | 576      unsigned i; | 
| cannam@95 | 577 | 
| cannam@95 | 578      /* relax impatience in this order: */ | 
| cannam@95 | 579      static const unsigned relax_tab[] = { | 
| cannam@95 | 580 	  0, /* relax nothing */ | 
| cannam@95 | 581 	  NO_VRECURSE, | 
| cannam@95 | 582 	  NO_FIXED_RADIX_LARGE_N, | 
| cannam@95 | 583 	  NO_SLOW, | 
| cannam@95 | 584 	  NO_UGLY | 
| cannam@95 | 585      }; | 
| cannam@95 | 586 | 
| cannam@95 | 587      unsigned l_orig = flagsp->l; | 
| cannam@95 | 588      unsigned x = flagsp->u; | 
| cannam@95 | 589 | 
| cannam@95 | 590      /* guaranteed to be different from X */ | 
| cannam@95 | 591      unsigned last_x = ~x; | 
| cannam@95 | 592 | 
| cannam@95 | 593      for (i = 0; i < sizeof(relax_tab) / sizeof(relax_tab[0]); ++i) { | 
| cannam@95 | 594 	  if (LEQ(l_orig, x & ~relax_tab[i])) | 
| cannam@95 | 595 	       x = x & ~relax_tab[i]; | 
| cannam@95 | 596 | 
| cannam@95 | 597 	  if (x != last_x) { | 
| cannam@95 | 598 	       last_x = x; | 
| cannam@95 | 599 	       flagsp->l = x; | 
| cannam@95 | 600 	       pln = search0(ego, p, slvndx, flagsp); | 
| cannam@95 | 601 	       if (pln) break; | 
| cannam@95 | 602 	  } | 
| cannam@95 | 603      } | 
| cannam@95 | 604 | 
| cannam@95 | 605      if (!pln) { | 
| cannam@95 | 606 	  /* search [L_ORIG, U] */ | 
| cannam@95 | 607 	  if (l_orig != last_x) { | 
| cannam@95 | 608 	       last_x = l_orig; | 
| cannam@95 | 609 	       flagsp->l = l_orig; | 
| cannam@95 | 610 	       pln = search0(ego, p, slvndx, flagsp); | 
| cannam@95 | 611 	  } | 
| cannam@95 | 612      } | 
| cannam@95 | 613 | 
| cannam@95 | 614      return pln; | 
| cannam@95 | 615 } | 
| cannam@95 | 616 | 
| cannam@95 | 617 #define CHECK_FOR_BOGOSITY						\ | 
| cannam@95 | 618      if ((ego->bogosity_hook ?						\ | 
| cannam@95 | 619 	  (ego->wisdom_state = ego->bogosity_hook(ego->wisdom_state, p)) \ | 
| cannam@95 | 620 	  : ego->wisdom_state) == WISDOM_IS_BOGUS)			\ | 
| cannam@95 | 621 	  goto wisdom_is_bogus; | 
| cannam@95 | 622 | 
| cannam@95 | 623 static plan *mkplan(planner *ego, const problem *p) | 
| cannam@95 | 624 { | 
| cannam@95 | 625      plan *pln; | 
| cannam@95 | 626      md5 m; | 
| cannam@95 | 627      unsigned slvndx; | 
| cannam@95 | 628      flags_t flags_of_solution; | 
| cannam@95 | 629      solution *sol; | 
| cannam@95 | 630      solver *s; | 
| cannam@95 | 631 | 
| cannam@95 | 632      ASSERT_ALIGNED_DOUBLE; | 
| cannam@95 | 633      A(LEQ(PLNR_L(ego), PLNR_U(ego))); | 
| cannam@95 | 634 | 
| cannam@95 | 635      if (ESTIMATEP(ego)) | 
| cannam@95 | 636 	  PLNR_TIMELIMIT_IMPATIENCE(ego) = 0; /* canonical form */ | 
| cannam@95 | 637 | 
| cannam@95 | 638 | 
| cannam@95 | 639 #ifdef FFTW_DEBUG | 
| cannam@95 | 640      check(&ego->htab_blessed); | 
| cannam@95 | 641      check(&ego->htab_unblessed); | 
| cannam@95 | 642 #endif | 
| cannam@95 | 643 | 
| cannam@95 | 644      pln = 0; | 
| cannam@95 | 645 | 
| cannam@95 | 646      CHECK_FOR_BOGOSITY; | 
| cannam@95 | 647 | 
| cannam@95 | 648      ego->timed_out = 0; | 
| cannam@95 | 649 | 
| cannam@95 | 650      ++ego->nprob; | 
| cannam@95 | 651      md5hash(&m, p, ego); | 
| cannam@95 | 652 | 
| cannam@95 | 653      flags_of_solution = ego->flags; | 
| cannam@95 | 654 | 
| cannam@95 | 655      if (ego->wisdom_state != WISDOM_IGNORE_ALL) { | 
| cannam@95 | 656 	  if ((sol = hlookup(ego, m.s, &flags_of_solution))) { | 
| cannam@95 | 657 	       /* wisdom is acceptable */ | 
| cannam@95 | 658 	       wisdom_state_t owisdom_state = ego->wisdom_state; | 
| cannam@95 | 659 | 
| cannam@95 | 660 	       /* this hook is mainly for MPI, to make sure that | 
| cannam@95 | 661 		  wisdom is in sync across all processes for MPI problems */ | 
| cannam@95 | 662 	       if (ego->wisdom_ok_hook && !ego->wisdom_ok_hook(p, sol->flags)) | 
| cannam@95 | 663 		    goto do_search; /* ignore not-ok wisdom */ | 
| cannam@95 | 664 | 
| cannam@95 | 665 	       slvndx = SLVNDX(sol); | 
| cannam@95 | 666 | 
| cannam@95 | 667 	       if (slvndx == INFEASIBLE_SLVNDX) { | 
| cannam@95 | 668 		    if (ego->wisdom_state == WISDOM_IGNORE_INFEASIBLE) | 
| cannam@95 | 669 			 goto do_search; | 
| cannam@95 | 670 		    else | 
| cannam@95 | 671 			 return 0;   /* known to be infeasible */ | 
| cannam@95 | 672 	       } | 
| cannam@95 | 673 | 
| cannam@95 | 674 	       flags_of_solution = sol->flags; | 
| cannam@95 | 675 | 
| cannam@95 | 676 	       /* inherit blessing either from wisdom | 
| cannam@95 | 677 		  or from the planner */ | 
| cannam@95 | 678 	       flags_of_solution.hash_info |= BLISS(ego->flags); | 
| cannam@95 | 679 | 
| cannam@95 | 680 	       ego->wisdom_state = WISDOM_ONLY; | 
| cannam@95 | 681 | 
| cannam@95 | 682 	       s = ego->slvdescs[slvndx].slv; | 
| cannam@95 | 683 	       if (p->adt->problem_kind != s->adt->problem_kind) | 
| cannam@95 | 684 		    goto wisdom_is_bogus; | 
| cannam@95 | 685 | 
| cannam@95 | 686 	       pln = invoke_solver(ego, p, s, &flags_of_solution); | 
| cannam@95 | 687 | 
| cannam@95 | 688 	       CHECK_FOR_BOGOSITY; 	  /* catch error in child solvers */ | 
| cannam@95 | 689 | 
| cannam@95 | 690 	       sol = 0; /* Paranoia: SOL may be dangling after | 
| cannam@95 | 691 			   invoke_solver(); make sure we don't accidentally | 
| cannam@95 | 692 			   reuse it. */ | 
| cannam@95 | 693 | 
| cannam@95 | 694 	       if (!pln) | 
| cannam@95 | 695 		    goto wisdom_is_bogus; | 
| cannam@95 | 696 | 
| cannam@95 | 697 	       ego->wisdom_state = owisdom_state; | 
| cannam@95 | 698 | 
| cannam@95 | 699 	       goto skip_search; | 
| cannam@95 | 700 	  } | 
| cannam@95 | 701 	  else if (ego->nowisdom_hook) /* for MPI, make sure lack of wisdom */ | 
| cannam@95 | 702 	       ego->nowisdom_hook(p);  /*   is in sync across all processes */ | 
| cannam@95 | 703      } | 
| cannam@95 | 704 | 
| cannam@95 | 705  do_search: | 
| cannam@95 | 706      /* cannot search in WISDOM_ONLY mode */ | 
| cannam@95 | 707      if (ego->wisdom_state == WISDOM_ONLY) | 
| cannam@95 | 708 	  goto wisdom_is_bogus; | 
| cannam@95 | 709 | 
| cannam@95 | 710      flags_of_solution = ego->flags; | 
| cannam@95 | 711      pln = search(ego, p, &slvndx, &flags_of_solution); | 
| cannam@95 | 712      CHECK_FOR_BOGOSITY; 	  /* catch error in child solvers */ | 
| cannam@95 | 713 | 
| cannam@95 | 714      if (ego->timed_out) { | 
| cannam@95 | 715 	  A(!pln); | 
| cannam@95 | 716 	  if (PLNR_TIMELIMIT_IMPATIENCE(ego) != 0) { | 
| cannam@95 | 717 	       /* record (below) that this plan has failed because of | 
| cannam@95 | 718 		  timeout */ | 
| cannam@95 | 719 	       flags_of_solution.hash_info |= BLESSING; | 
| cannam@95 | 720 	  } else { | 
| cannam@95 | 721 	       /* this is not the top-level problem or timeout is not | 
| cannam@95 | 722 		  active: record no wisdom. */ | 
| cannam@95 | 723 	       return 0; | 
| cannam@95 | 724 	  } | 
| cannam@95 | 725      } else { | 
| cannam@95 | 726 	  /* canonicalize to infinite timeout */ | 
| cannam@95 | 727 	  flags_of_solution.timelimit_impatience = 0; | 
| cannam@95 | 728      } | 
| cannam@95 | 729 | 
| cannam@95 | 730  skip_search: | 
| cannam@95 | 731      if (ego->wisdom_state == WISDOM_NORMAL || | 
| cannam@95 | 732 	 ego->wisdom_state == WISDOM_ONLY) { | 
| cannam@95 | 733 	  if (pln) { | 
| cannam@95 | 734 	       hinsert(ego, m.s, &flags_of_solution, slvndx); | 
| cannam@95 | 735 	       invoke_hook(ego, pln, p, 1); | 
| cannam@95 | 736 	  } else { | 
| cannam@95 | 737 	       hinsert(ego, m.s, &flags_of_solution, INFEASIBLE_SLVNDX); | 
| cannam@95 | 738 	  } | 
| cannam@95 | 739      } | 
| cannam@95 | 740 | 
| cannam@95 | 741      return pln; | 
| cannam@95 | 742 | 
| cannam@95 | 743  wisdom_is_bogus: | 
| cannam@95 | 744      X(plan_destroy_internal)(pln); | 
| cannam@95 | 745      ego->wisdom_state = WISDOM_IS_BOGUS; | 
| cannam@95 | 746      return 0; | 
| cannam@95 | 747 } | 
| cannam@95 | 748 | 
| cannam@95 | 749 static void htab_destroy(hashtab *ht) | 
| cannam@95 | 750 { | 
| cannam@95 | 751      X(ifree)(ht->solutions); | 
| cannam@95 | 752      ht->solutions = 0; | 
| cannam@95 | 753      ht->nelem = 0U; | 
| cannam@95 | 754 } | 
| cannam@95 | 755 | 
| cannam@95 | 756 static void mkhashtab(hashtab *ht) | 
| cannam@95 | 757 { | 
| cannam@95 | 758      ht->nrehash = 0; | 
| cannam@95 | 759      ht->succ_lookup = ht->lookup = ht->lookup_iter = 0; | 
| cannam@95 | 760      ht->insert = ht->insert_iter = ht->insert_unknown = 0; | 
| cannam@95 | 761 | 
| cannam@95 | 762      ht->solutions = 0; | 
| cannam@95 | 763      ht->hashsiz = ht->nelem = 0U; | 
| cannam@95 | 764      hgrow(ht);			/* so that hashsiz > 0 */ | 
| cannam@95 | 765 } | 
| cannam@95 | 766 | 
| cannam@95 | 767 /* destroy hash table entries.  If FORGET_EVERYTHING, destroy the whole | 
| cannam@95 | 768    table.  If FORGET_ACCURSED, then destroy entries that are not blessed. */ | 
| cannam@95 | 769 static void forget(planner *ego, amnesia a) | 
| cannam@95 | 770 { | 
| cannam@95 | 771      switch (a) { | 
| cannam@95 | 772 	 case FORGET_EVERYTHING: | 
| cannam@95 | 773 	      htab_destroy(&ego->htab_blessed); | 
| cannam@95 | 774 	      mkhashtab(&ego->htab_blessed); | 
| cannam@95 | 775 	      /* fall through */ | 
| cannam@95 | 776 	 case FORGET_ACCURSED: | 
| cannam@95 | 777 	      htab_destroy(&ego->htab_unblessed); | 
| cannam@95 | 778 	      mkhashtab(&ego->htab_unblessed); | 
| cannam@95 | 779 	      break; | 
| cannam@95 | 780 	 default: | 
| cannam@95 | 781 	      break; | 
| cannam@95 | 782      } | 
| cannam@95 | 783 } | 
| cannam@95 | 784 | 
| cannam@95 | 785 /* FIXME: what sort of version information should we write? */ | 
| cannam@95 | 786 #define WISDOM_PREAMBLE PACKAGE "-" VERSION " " STRINGIZE(X(wisdom)) | 
| cannam@95 | 787 static const char stimeout[] = "TIMEOUT"; | 
| cannam@95 | 788 | 
| cannam@95 | 789 /* tantus labor non sit cassus */ | 
| cannam@95 | 790 static void exprt(planner *ego, printer *p) | 
| cannam@95 | 791 { | 
| cannam@95 | 792      unsigned h; | 
| cannam@95 | 793      hashtab *ht = &ego->htab_blessed; | 
| cannam@95 | 794      md5 m; | 
| cannam@95 | 795 | 
| cannam@95 | 796      signature_of_configuration(&m, ego); | 
| cannam@95 | 797 | 
| cannam@95 | 798      p->print(p, | 
| cannam@95 | 799 	      "(" WISDOM_PREAMBLE " #x%M #x%M #x%M #x%M\n", | 
| cannam@95 | 800 	      m.s[0], m.s[1], m.s[2], m.s[3]); | 
| cannam@95 | 801 | 
| cannam@95 | 802      for (h = 0; h < ht->hashsiz; ++h) { | 
| cannam@95 | 803 	  solution *l = ht->solutions + h; | 
| cannam@95 | 804 	  if (LIVEP(l)) { | 
| cannam@95 | 805 	       const char *reg_nam; | 
| cannam@95 | 806 	       int reg_id; | 
| cannam@95 | 807 | 
| cannam@95 | 808 	       if (SLVNDX(l) == INFEASIBLE_SLVNDX) { | 
| cannam@95 | 809 		    reg_nam = stimeout; | 
| cannam@95 | 810 		    reg_id = 0; | 
| cannam@95 | 811 	       } else { | 
| cannam@95 | 812 		    slvdesc *sp = ego->slvdescs + SLVNDX(l); | 
| cannam@95 | 813 		    reg_nam = sp->reg_nam; | 
| cannam@95 | 814 		    reg_id = sp->reg_id; | 
| cannam@95 | 815 	       } | 
| cannam@95 | 816 | 
| cannam@95 | 817 	       /* qui salvandos salvas gratis | 
| cannam@95 | 818 		  salva me fons pietatis */ | 
| cannam@95 | 819 	       p->print(p, "  (%s %d #x%x #x%x #x%x #x%M #x%M #x%M #x%M)\n", | 
| cannam@95 | 820 			reg_nam, reg_id, | 
| cannam@95 | 821 			l->flags.l, l->flags.u, l->flags.timelimit_impatience, | 
| cannam@95 | 822 			l->s[0], l->s[1], l->s[2], l->s[3]); | 
| cannam@95 | 823 	  } | 
| cannam@95 | 824      } | 
| cannam@95 | 825      p->print(p, ")\n"); | 
| cannam@95 | 826 } | 
| cannam@95 | 827 | 
| cannam@95 | 828 /* mors stupebit et natura | 
| cannam@95 | 829    cum resurget creatura */ | 
| cannam@95 | 830 static int imprt(planner *ego, scanner *sc) | 
| cannam@95 | 831 { | 
| cannam@95 | 832      char buf[MAXNAM + 1]; | 
| cannam@95 | 833      md5uint sig[4]; | 
| cannam@95 | 834      unsigned l, u, timelimit_impatience; | 
| cannam@95 | 835      flags_t flags; | 
| cannam@95 | 836      int reg_id; | 
| cannam@95 | 837      unsigned slvndx; | 
| cannam@95 | 838      hashtab *ht = &ego->htab_blessed; | 
| cannam@95 | 839      hashtab old; | 
| cannam@95 | 840      md5 m; | 
| cannam@95 | 841 | 
| cannam@95 | 842      if (!sc->scan(sc, | 
| cannam@95 | 843 		   "(" WISDOM_PREAMBLE " #x%M #x%M #x%M #x%M\n", | 
| cannam@95 | 844 		   sig + 0, sig + 1, sig + 2, sig + 3)) | 
| cannam@95 | 845 	  return 0; /* don't need to restore hashtable */ | 
| cannam@95 | 846 | 
| cannam@95 | 847      signature_of_configuration(&m, ego); | 
| cannam@95 | 848      if (m.s[0] != sig[0] || m.s[1] != sig[1] || | 
| cannam@95 | 849 	 m.s[2] != sig[2] || m.s[3] != sig[3]) { | 
| cannam@95 | 850 	  /* invalid configuration */ | 
| cannam@95 | 851 	  return 0; | 
| cannam@95 | 852      } | 
| cannam@95 | 853 | 
| cannam@95 | 854      /* make a backup copy of the hash table (cache the hash) */ | 
| cannam@95 | 855      { | 
| cannam@95 | 856 	  unsigned h, hsiz = ht->hashsiz; | 
| cannam@95 | 857 	  old = *ht; | 
| cannam@95 | 858 	  old.solutions = (solution *)MALLOC(hsiz * sizeof(solution), HASHT); | 
| cannam@95 | 859 	  for (h = 0; h < hsiz; ++h) | 
| cannam@95 | 860 	       old.solutions[h] = ht->solutions[h]; | 
| cannam@95 | 861      } | 
| cannam@95 | 862 | 
| cannam@95 | 863      while (1) { | 
| cannam@95 | 864 	  if (sc->scan(sc, ")")) | 
| cannam@95 | 865 	       break; | 
| cannam@95 | 866 | 
| cannam@95 | 867 	  /* qua resurget ex favilla */ | 
| cannam@95 | 868 	  if (!sc->scan(sc, "(%*s %d #x%x #x%x #x%x #x%M #x%M #x%M #x%M)", | 
| cannam@95 | 869 			MAXNAM, buf, ®_id, &l, &u, &timelimit_impatience, | 
| cannam@95 | 870 			sig + 0, sig + 1, sig + 2, sig + 3)) | 
| cannam@95 | 871 	       goto bad; | 
| cannam@95 | 872 | 
| cannam@95 | 873 	  if (!strcmp(buf, stimeout) && reg_id == 0) { | 
| cannam@95 | 874 	       slvndx = INFEASIBLE_SLVNDX; | 
| cannam@95 | 875 	  } else { | 
| cannam@95 | 876 	       if (timelimit_impatience != 0) | 
| cannam@95 | 877 		    goto bad; | 
| cannam@95 | 878 | 
| cannam@95 | 879 	       slvndx = slookup(ego, buf, reg_id); | 
| cannam@95 | 880 	       if (slvndx == INFEASIBLE_SLVNDX) | 
| cannam@95 | 881 		    goto bad; | 
| cannam@95 | 882 	  } | 
| cannam@95 | 883 | 
| cannam@95 | 884 	  /* inter oves locum praesta */ | 
| cannam@95 | 885 	  flags.l = l; | 
| cannam@95 | 886 	  flags.u = u; | 
| cannam@95 | 887 	  flags.timelimit_impatience = timelimit_impatience; | 
| cannam@95 | 888 	  flags.hash_info = BLESSING; | 
| cannam@95 | 889 | 
| cannam@95 | 890 	  CK(flags.l == l); | 
| cannam@95 | 891 	  CK(flags.u == u); | 
| cannam@95 | 892 	  CK(flags.timelimit_impatience == timelimit_impatience); | 
| cannam@95 | 893 | 
| cannam@95 | 894 	  if (!hlookup(ego, sig, &flags)) | 
| cannam@95 | 895 	       hinsert(ego, sig, &flags, slvndx); | 
| cannam@95 | 896      } | 
| cannam@95 | 897 | 
| cannam@95 | 898      X(ifree0)(old.solutions); | 
| cannam@95 | 899      return 1; | 
| cannam@95 | 900 | 
| cannam@95 | 901  bad: | 
| cannam@95 | 902      /* ``The wisdom of FFTW must be above suspicion.'' */ | 
| cannam@95 | 903      X(ifree0)(ht->solutions); | 
| cannam@95 | 904      *ht = old; | 
| cannam@95 | 905      return 0; | 
| cannam@95 | 906 } | 
| cannam@95 | 907 | 
| cannam@95 | 908 /* | 
| cannam@95 | 909  * create a planner | 
| cannam@95 | 910  */ | 
| cannam@95 | 911 planner *X(mkplanner)(void) | 
| cannam@95 | 912 { | 
| cannam@95 | 913      int i; | 
| cannam@95 | 914 | 
| cannam@95 | 915      static const planner_adt padt = { | 
| cannam@95 | 916 	  register_solver, mkplan, forget, exprt, imprt | 
| cannam@95 | 917      }; | 
| cannam@95 | 918 | 
| cannam@95 | 919      planner *p = (planner *) MALLOC(sizeof(planner), PLANNERS); | 
| cannam@95 | 920 | 
| cannam@95 | 921      p->adt = &padt; | 
| cannam@95 | 922      p->nplan = p->nprob = 0; | 
| cannam@95 | 923      p->pcost = p->epcost = 0.0; | 
| cannam@95 | 924      p->hook = 0; | 
| cannam@95 | 925      p->cost_hook = 0; | 
| cannam@95 | 926      p->wisdom_ok_hook = 0; | 
| cannam@95 | 927      p->nowisdom_hook = 0; | 
| cannam@95 | 928      p->bogosity_hook = 0; | 
| cannam@95 | 929      p->cur_reg_nam = 0; | 
| cannam@95 | 930      p->wisdom_state = WISDOM_NORMAL; | 
| cannam@95 | 931 | 
| cannam@95 | 932      p->slvdescs = 0; | 
| cannam@95 | 933      p->nslvdesc = p->slvdescsiz = 0; | 
| cannam@95 | 934 | 
| cannam@95 | 935      p->flags.l = 0; | 
| cannam@95 | 936      p->flags.u = 0; | 
| cannam@95 | 937      p->flags.timelimit_impatience = 0; | 
| cannam@95 | 938      p->flags.hash_info = 0; | 
| cannam@95 | 939      p->nthr = 1; | 
| cannam@95 | 940      p->need_timeout_check = 1; | 
| cannam@95 | 941      p->timelimit = -1; | 
| cannam@95 | 942 | 
| cannam@95 | 943      mkhashtab(&p->htab_blessed); | 
| cannam@95 | 944      mkhashtab(&p->htab_unblessed); | 
| cannam@95 | 945 | 
| cannam@95 | 946      for (i = 0; i < PROBLEM_LAST; ++i) | 
| cannam@95 | 947 	  p->slvdescs_for_problem_kind[i] = -1; | 
| cannam@95 | 948 | 
| cannam@95 | 949      return p; | 
| cannam@95 | 950 } | 
| cannam@95 | 951 | 
| cannam@95 | 952 void X(planner_destroy)(planner *ego) | 
| cannam@95 | 953 { | 
| cannam@95 | 954      /* destroy hash table */ | 
| cannam@95 | 955      htab_destroy(&ego->htab_blessed); | 
| cannam@95 | 956      htab_destroy(&ego->htab_unblessed); | 
| cannam@95 | 957 | 
| cannam@95 | 958      /* destroy solvdesc table */ | 
| cannam@95 | 959      FORALL_SOLVERS(ego, s, sp, { | 
| cannam@95 | 960 	  UNUSED(sp); | 
| cannam@95 | 961 	  X(solver_destroy)(s); | 
| cannam@95 | 962      }); | 
| cannam@95 | 963 | 
| cannam@95 | 964      X(ifree0)(ego->slvdescs); | 
| cannam@95 | 965      X(ifree)(ego); /* dona eis requiem */ | 
| cannam@95 | 966 } | 
| cannam@95 | 967 | 
| cannam@95 | 968 plan *X(mkplan_d)(planner *ego, problem *p) | 
| cannam@95 | 969 { | 
| cannam@95 | 970      plan *pln = ego->adt->mkplan(ego, p); | 
| cannam@95 | 971      X(problem_destroy)(p); | 
| cannam@95 | 972      return pln; | 
| cannam@95 | 973 } | 
| cannam@95 | 974 | 
| cannam@95 | 975 /* like X(mkplan_d), but sets/resets flags as well */ | 
| cannam@95 | 976 plan *X(mkplan_f_d)(planner *ego, problem *p, | 
| cannam@95 | 977 		    unsigned l_set, unsigned u_set, unsigned u_reset) | 
| cannam@95 | 978 { | 
| cannam@95 | 979      flags_t oflags = ego->flags; | 
| cannam@95 | 980      plan *pln; | 
| cannam@95 | 981 | 
| cannam@95 | 982      PLNR_U(ego) &= ~u_reset; | 
| cannam@95 | 983      PLNR_L(ego) &= ~u_reset; | 
| cannam@95 | 984      PLNR_L(ego) |= l_set; | 
| cannam@95 | 985      PLNR_U(ego) |= u_set | l_set; | 
| cannam@95 | 986      pln = X(mkplan_d)(ego, p); | 
| cannam@95 | 987      ego->flags = oflags; | 
| cannam@95 | 988      return pln; | 
| cannam@95 | 989 } | 
| cannam@95 | 990 | 
| cannam@95 | 991 /* | 
| cannam@95 | 992  * Debugging code: | 
| cannam@95 | 993  */ | 
| cannam@95 | 994 #ifdef FFTW_DEBUG | 
| cannam@95 | 995 static void check(hashtab *ht) | 
| cannam@95 | 996 { | 
| cannam@95 | 997      unsigned live = 0; | 
| cannam@95 | 998      unsigned i; | 
| cannam@95 | 999 | 
| cannam@95 | 1000      A(ht->nelem < ht->hashsiz); | 
| cannam@95 | 1001 | 
| cannam@95 | 1002      for (i = 0; i < ht->hashsiz; ++i) { | 
| cannam@95 | 1003 	  solution *l = ht->solutions + i; | 
| cannam@95 | 1004 	  if (LIVEP(l)) | 
| cannam@95 | 1005 	       ++live; | 
| cannam@95 | 1006      } | 
| cannam@95 | 1007 | 
| cannam@95 | 1008      A(ht->nelem == live); | 
| cannam@95 | 1009 | 
| cannam@95 | 1010      for (i = 0; i < ht->hashsiz; ++i) { | 
| cannam@95 | 1011 	  solution *l1 = ht->solutions + i; | 
| cannam@95 | 1012 	  int foundit = 0; | 
| cannam@95 | 1013 	  if (LIVEP(l1)) { | 
| cannam@95 | 1014 	       unsigned g, h = h1(ht, l1->s), d = h2(ht, l1->s); | 
| cannam@95 | 1015 | 
| cannam@95 | 1016 	       g = h; | 
| cannam@95 | 1017 	       do { | 
| cannam@95 | 1018 		    solution *l = ht->solutions + g; | 
| cannam@95 | 1019 		    if (VALIDP(l)) { | 
| cannam@95 | 1020 			 if (l1 == l) | 
| cannam@95 | 1021 			      foundit = 1; | 
| cannam@95 | 1022 			 else if (LIVEP(l) && md5eq(l1->s, l->s)) { | 
| cannam@95 | 1023 			      A(!subsumes(&l->flags, SLVNDX(l), &l1->flags)); | 
| cannam@95 | 1024 			      A(!subsumes(&l1->flags, SLVNDX(l1), &l->flags)); | 
| cannam@95 | 1025 			 } | 
| cannam@95 | 1026 		    } else | 
| cannam@95 | 1027 			 break; | 
| cannam@95 | 1028 		    g = addmod(g, d, ht->hashsiz); | 
| cannam@95 | 1029 	       } while (g != h); | 
| cannam@95 | 1030 | 
| cannam@95 | 1031 	       A(foundit); | 
| cannam@95 | 1032 	  } | 
| cannam@95 | 1033      } | 
| cannam@95 | 1034 } | 
| cannam@95 | 1035 #endif |