1036 lines
		
	
	
		
			25 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
		
		
			
		
	
	
			1036 lines
		
	
	
		
			25 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| 
								 | 
							
								/*
							 | 
						||
| 
								 | 
							
								 * Copyright (c) 2000 Matteo Frigo
							 | 
						||
| 
								 | 
							
								 * Copyright (c) 2000 Massachusetts Institute of Technology
							 | 
						||
| 
								 | 
							
								 *
							 | 
						||
| 
								 | 
							
								 * This program is free software; you can redistribute it and/or modify
							 | 
						||
| 
								 | 
							
								 * it under the terms of the GNU General Public License as published by
							 | 
						||
| 
								 | 
							
								 * the Free Software Foundation; either version 2 of the License, or
							 | 
						||
| 
								 | 
							
								 * (at your option) any later version.
							 | 
						||
| 
								 | 
							
								 *
							 | 
						||
| 
								 | 
							
								 * This program is distributed in the hope that it will be useful,
							 | 
						||
| 
								 | 
							
								 * but WITHOUT ANY WARRANTY; without even the implied warranty of
							 | 
						||
| 
								 | 
							
								 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
							 | 
						||
| 
								 | 
							
								 * GNU General Public License for more details.
							 | 
						||
| 
								 | 
							
								 *
							 | 
						||
| 
								 | 
							
								 * You should have received a copy of the GNU General Public License
							 | 
						||
| 
								 | 
							
								 * along with this program; if not, write to the Free Software
							 | 
						||
| 
								 | 
							
								 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
							 | 
						||
| 
								 | 
							
								 *
							 | 
						||
| 
								 | 
							
								 */
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								#include "kernel/ifftw.h"
							 | 
						||
| 
								 | 
							
								#include <string.h>
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								/* GNU Coding Standards, Sec. 5.2: "Please write the comments in a GNU
							 | 
						||
| 
								 | 
							
								   program in English, because English is the one language that nearly
							 | 
						||
| 
								 | 
							
								   all programmers in all countries can read."
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								                    ingemisco tanquam reus
							 | 
						||
| 
								 | 
							
										    culpa rubet vultus meus
							 | 
						||
| 
								 | 
							
										    supplicanti parce [rms]
							 | 
						||
| 
								 | 
							
								*/
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								#define VALIDP(solution) ((solution)->flags.hash_info & H_VALID)
							 | 
						||
| 
								 | 
							
								#define LIVEP(solution) ((solution)->flags.hash_info & H_LIVE)
							 | 
						||
| 
								 | 
							
								#define SLVNDX(solution) ((solution)->flags.slvndx)
							 | 
						||
| 
								 | 
							
								#define BLISS(flags) (((flags).hash_info) & BLESSING)
							 | 
						||
| 
								 | 
							
								#define INFEASIBLE_SLVNDX ((1U<<BITS_FOR_SLVNDX)-1)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								#define MAXNAM 64  /* maximum length of registrar's name.
							 | 
						||
| 
								 | 
							
										      Used for reading wisdom.  There is no point
							 | 
						||
| 
								 | 
							
										      in doing this right */
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								#ifdef FFTW_DEBUG
							 | 
						||
| 
								 | 
							
								static void check(hashtab *ht);
							 | 
						||
| 
								 | 
							
								#endif
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								/* x <= y */
							 | 
						||
| 
								 | 
							
								#define LEQ(x, y) (((x) & (y)) == (x))
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								/* A subsumes B */
							 | 
						||
| 
								 | 
							
								static int subsumes(const flags_t *a, unsigned slvndx_a, const flags_t *b)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								     if (slvndx_a != INFEASIBLE_SLVNDX) {
							 | 
						||
| 
								 | 
							
									  A(a->timelimit_impatience == 0);
							 | 
						||
| 
								 | 
							
									  return (LEQ(a->u, b->u) && LEQ(b->l, a->l));
							 | 
						||
| 
								 | 
							
								     } else {
							 | 
						||
| 
								 | 
							
									  return (LEQ(a->l, b->l) 
							 | 
						||
| 
								 | 
							
										  && a->timelimit_impatience <= b->timelimit_impatience);
							 | 
						||
| 
								 | 
							
								     }
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								static unsigned addmod(unsigned a, unsigned b, unsigned p)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								     /* gcc-2.95/sparc produces incorrect code for the fast version below. */
							 | 
						||
| 
								 | 
							
								#if defined(__sparc__) && defined(__GNUC__)
							 | 
						||
| 
								 | 
							
								     /* slow version  */
							 | 
						||
| 
								 | 
							
								     return (a + b) % p;
							 | 
						||
| 
								 | 
							
								#else
							 | 
						||
| 
								 | 
							
								     /* faster version */
							 | 
						||
| 
								 | 
							
								     unsigned c = a + b;
							 | 
						||
| 
								 | 
							
								     return c >= p ? c - p : c;
							 | 
						||
| 
								 | 
							
								#endif
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								/*
							 | 
						||
| 
								 | 
							
								  slvdesc management:
							 | 
						||
| 
								 | 
							
								*/
							 | 
						||
| 
								 | 
							
								static void sgrow(planner *ego)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								     unsigned osiz = ego->slvdescsiz, nsiz = 1 + osiz + osiz / 4;
							 | 
						||
| 
								 | 
							
								     slvdesc *ntab = (slvdesc *)MALLOC(nsiz * sizeof(slvdesc), SLVDESCS);
							 | 
						||
| 
								 | 
							
								     slvdesc *otab = ego->slvdescs;
							 | 
						||
| 
								 | 
							
								     unsigned i;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								     ego->slvdescs = ntab;
							 | 
						||
| 
								 | 
							
								     ego->slvdescsiz = nsiz;
							 | 
						||
| 
								 | 
							
								     for (i = 0; i < osiz; ++i)
							 | 
						||
| 
								 | 
							
									  ntab[i] = otab[i];
							 | 
						||
| 
								 | 
							
								     X(ifree0)(otab);
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								static void register_solver(planner *ego, solver *s)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								     slvdesc *n;
							 | 
						||
| 
								 | 
							
								     int kind;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								     if (s) { /* add s to solver list */
							 | 
						||
| 
								 | 
							
									  X(solver_use)(s);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									  A(ego->nslvdesc < INFEASIBLE_SLVNDX);
							 | 
						||
| 
								 | 
							
									  if (ego->nslvdesc >= ego->slvdescsiz)
							 | 
						||
| 
								 | 
							
									       sgrow(ego);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									  n = ego->slvdescs + ego->nslvdesc;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									  n->slv = s;
							 | 
						||
| 
								 | 
							
									  n->reg_nam = ego->cur_reg_nam;
							 | 
						||
| 
								 | 
							
									  n->reg_id = ego->cur_reg_id++;
							 | 
						||
| 
								 | 
							
									  
							 | 
						||
| 
								 | 
							
									  A(strlen(n->reg_nam) < MAXNAM);
							 | 
						||
| 
								 | 
							
									  n->nam_hash = X(hash)(n->reg_nam);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									  kind = s->adt->problem_kind;
							 | 
						||
| 
								 | 
							
									  n->next_for_same_problem_kind = ego->slvdescs_for_problem_kind[kind];
							 | 
						||
| 
								 | 
							
									  ego->slvdescs_for_problem_kind[kind] = (int)/*from unsigned*/ego->nslvdesc;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									  ego->nslvdesc++;
							 | 
						||
| 
								 | 
							
								     }
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								static unsigned slookup(planner *ego, char *nam, int id)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								     unsigned h = X(hash)(nam); /* used to avoid strcmp in the common case */
							 | 
						||
| 
								 | 
							
								     FORALL_SOLVERS(ego, s, sp, {
							 | 
						||
| 
								 | 
							
									  UNUSED(s);
							 | 
						||
| 
								 | 
							
									  if (sp->reg_id == id && sp->nam_hash == h
							 | 
						||
| 
								 | 
							
									      && !strcmp(sp->reg_nam, nam))
							 | 
						||
| 
								 | 
							
									       return (unsigned)/*from ptrdiff_t*/(sp - ego->slvdescs);
							 | 
						||
| 
								 | 
							
								     });
							 | 
						||
| 
								 | 
							
								     return INFEASIBLE_SLVNDX;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								/* Compute a MD5 hash of the configuration of the planner.
							 | 
						||
| 
								 | 
							
								   We store it into the wisdom file to make absolutely sure that
							 | 
						||
| 
								 | 
							
								   we are reading wisdom that is applicable */
							 | 
						||
| 
								 | 
							
								static void signature_of_configuration(md5 *m, planner *ego)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								     X(md5begin)(m);
							 | 
						||
| 
								 | 
							
								     X(md5unsigned)(m, sizeof(R)); /* so we don't mix different precisions */
							 | 
						||
| 
								 | 
							
								     FORALL_SOLVERS(ego, s, sp, {
							 | 
						||
| 
								 | 
							
									  UNUSED(s);
							 | 
						||
| 
								 | 
							
									  X(md5int)(m, sp->reg_id);
							 | 
						||
| 
								 | 
							
									  X(md5puts)(m, sp->reg_nam);
							 | 
						||
| 
								 | 
							
								     });
							 | 
						||
| 
								 | 
							
								     X(md5end)(m);
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								/*
							 | 
						||
| 
								 | 
							
								  md5-related stuff:
							 | 
						||
| 
								 | 
							
								*/
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								/* first hash function */
							 | 
						||
| 
								 | 
							
								static unsigned h1(const hashtab *ht, const md5sig s)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								     unsigned h = s[0] % ht->hashsiz;
							 | 
						||
| 
								 | 
							
								     A(h == (s[0] % ht->hashsiz));
							 | 
						||
| 
								 | 
							
								     return h;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								/* second hash function (for double hashing) */
							 | 
						||
| 
								 | 
							
								static unsigned h2(const hashtab *ht, const md5sig s)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								     unsigned h = 1U + s[1] % (ht->hashsiz - 1);
							 | 
						||
| 
								 | 
							
								     A(h == (1U + s[1] % (ht->hashsiz - 1)));
							 | 
						||
| 
								 | 
							
								     return h;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								static void md5hash(md5 *m, const problem *p, const planner *plnr)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								     X(md5begin)(m);
							 | 
						||
| 
								 | 
							
								     X(md5unsigned)(m, sizeof(R)); /* so we don't mix different precisions */
							 | 
						||
| 
								 | 
							
								     X(md5int)(m, plnr->nthr);
							 | 
						||
| 
								 | 
							
								     p->adt->hash(p, m);
							 | 
						||
| 
								 | 
							
								     X(md5end)(m);
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								static int md5eq(const md5sig a, const md5sig b)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								     return a[0] == b[0] && a[1] == b[1] && a[2] == b[2] && a[3] == b[3];
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								static void sigcpy(const md5sig a, md5sig b)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								     b[0] = a[0]; b[1] = a[1]; b[2] = a[2]; b[3] = a[3];
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								/*
							 | 
						||
| 
								 | 
							
								  memoization routines :
							 | 
						||
| 
								 | 
							
								*/
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								/*
							 | 
						||
| 
								 | 
							
								   liber scriptus proferetur
							 | 
						||
| 
								 | 
							
								   in quo totum continetur
							 | 
						||
| 
								 | 
							
								   unde mundus iudicetur
							 | 
						||
| 
								 | 
							
								*/
							 | 
						||
| 
								 | 
							
								struct solution_s {
							 | 
						||
| 
								 | 
							
								     md5sig s;
							 | 
						||
| 
								 | 
							
								     flags_t flags;
							 | 
						||
| 
								 | 
							
								};
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								static solution *htab_lookup(hashtab *ht, const md5sig s, 
							 | 
						||
| 
								 | 
							
											     const flags_t *flagsp)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								     unsigned g, h = h1(ht, s), d = h2(ht, s);
							 | 
						||
| 
								 | 
							
								     solution *best = 0;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								     ++ht->lookup;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								     /* search all entries that match; select the one with
							 | 
						||
| 
								 | 
							
									the lowest flags.u */
							 | 
						||
| 
								 | 
							
								     /* This loop may potentially traverse the whole table, since at
							 | 
						||
| 
								 | 
							
									least one element is guaranteed to be !LIVEP, but all elements
							 | 
						||
| 
								 | 
							
									may be VALIDP.  Hence, we stop after at the first invalid
							 | 
						||
| 
								 | 
							
									element or after traversing the whole table. */
							 | 
						||
| 
								 | 
							
								     g = h;
							 | 
						||
| 
								 | 
							
								     do {
							 | 
						||
| 
								 | 
							
									  solution *l = ht->solutions + g;
							 | 
						||
| 
								 | 
							
									  ++ht->lookup_iter;
							 | 
						||
| 
								 | 
							
									  if (VALIDP(l)) {
							 | 
						||
| 
								 | 
							
									       if (LIVEP(l)
							 | 
						||
| 
								 | 
							
										   && md5eq(s, l->s)
							 | 
						||
| 
								 | 
							
										   && subsumes(&l->flags, SLVNDX(l), flagsp) ) { 
							 | 
						||
| 
								 | 
							
										    if (!best || LEQ(l->flags.u, best->flags.u))
							 | 
						||
| 
								 | 
							
											 best = l;
							 | 
						||
| 
								 | 
							
									       }
							 | 
						||
| 
								 | 
							
									  } else 
							 | 
						||
| 
								 | 
							
									       break;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									  g = addmod(g, d, ht->hashsiz);
							 | 
						||
| 
								 | 
							
								     } while (g != h);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								     if (best) 
							 | 
						||
| 
								 | 
							
									  ++ht->succ_lookup;
							 | 
						||
| 
								 | 
							
								     return best;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								static solution *hlookup(planner *ego, const md5sig s, 
							 | 
						||
| 
								 | 
							
											 const flags_t *flagsp)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								     solution *sol = htab_lookup(&ego->htab_blessed, s, flagsp);
							 | 
						||
| 
								 | 
							
								     if (!sol) sol = htab_lookup(&ego->htab_unblessed, s, flagsp);
							 | 
						||
| 
								 | 
							
								     return sol;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								static void fill_slot(hashtab *ht, const md5sig s, const flags_t *flagsp,
							 | 
						||
| 
								 | 
							
										      unsigned slvndx, solution *slot)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								     ++ht->insert;
							 | 
						||
| 
								 | 
							
								     ++ht->nelem;
							 | 
						||
| 
								 | 
							
								     A(!LIVEP(slot));
							 | 
						||
| 
								 | 
							
								     slot->flags.u = flagsp->u;
							 | 
						||
| 
								 | 
							
								     slot->flags.l = flagsp->l;
							 | 
						||
| 
								 | 
							
								     slot->flags.timelimit_impatience = flagsp->timelimit_impatience;
							 | 
						||
| 
								 | 
							
								     slot->flags.hash_info |= H_VALID | H_LIVE;
							 | 
						||
| 
								 | 
							
								     SLVNDX(slot) = slvndx;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								     /* keep this check enabled in case we add so many solvers
							 | 
						||
| 
								 | 
							
									that the bitfield overflows */
							 | 
						||
| 
								 | 
							
								     CK(SLVNDX(slot) == slvndx);     
							 | 
						||
| 
								 | 
							
								     sigcpy(s, slot->s);
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								static void kill_slot(hashtab *ht, solution *slot)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								     A(LIVEP(slot)); /* ==> */ A(VALIDP(slot));
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								     --ht->nelem;
							 | 
						||
| 
								 | 
							
								     slot->flags.hash_info = H_VALID;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								static void hinsert0(hashtab *ht, const md5sig s, const flags_t *flagsp, 
							 | 
						||
| 
								 | 
							
										     unsigned slvndx)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								     solution *l;
							 | 
						||
| 
								 | 
							
								     unsigned g, h = h1(ht, s), d = h2(ht, s); 
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								     ++ht->insert_unknown;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								     /* search for nonfull slot */
							 | 
						||
| 
								 | 
							
								     for (g = h; ; g = addmod(g, d, ht->hashsiz)) {
							 | 
						||
| 
								 | 
							
									  ++ht->insert_iter;
							 | 
						||
| 
								 | 
							
									  l = ht->solutions + g;
							 | 
						||
| 
								 | 
							
									  if (!LIVEP(l)) break;
							 | 
						||
| 
								 | 
							
									  A((g + d) % ht->hashsiz != h);
							 | 
						||
| 
								 | 
							
								     }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								     fill_slot(ht, s, flagsp, slvndx, l);
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								static void rehash(hashtab *ht, unsigned nsiz)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								     unsigned osiz = ht->hashsiz, h;
							 | 
						||
| 
								 | 
							
								     solution *osol = ht->solutions, *nsol;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								     nsiz = (unsigned)X(next_prime)((INT)nsiz);
							 | 
						||
| 
								 | 
							
								     nsol = (solution *)MALLOC(nsiz * sizeof(solution), HASHT);
							 | 
						||
| 
								 | 
							
								     ++ht->nrehash;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								     /* init new table */
							 | 
						||
| 
								 | 
							
								     for (h = 0; h < nsiz; ++h) 
							 | 
						||
| 
								 | 
							
									  nsol[h].flags.hash_info = 0;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								     /* install new table */
							 | 
						||
| 
								 | 
							
								     ht->hashsiz = nsiz;
							 | 
						||
| 
								 | 
							
								     ht->solutions = nsol;
							 | 
						||
| 
								 | 
							
								     ht->nelem = 0;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								     /* copy table */
							 | 
						||
| 
								 | 
							
								     for (h = 0; h < osiz; ++h) {
							 | 
						||
| 
								 | 
							
									  solution *l = osol + h;
							 | 
						||
| 
								 | 
							
									  if (LIVEP(l))
							 | 
						||
| 
								 | 
							
									       hinsert0(ht, l->s, &l->flags, SLVNDX(l));
							 | 
						||
| 
								 | 
							
								     }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								     X(ifree0)(osol);
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								static unsigned minsz(unsigned nelem)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								     return 1U + nelem + nelem / 8U;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								static unsigned nextsz(unsigned nelem)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								     return minsz(minsz(nelem));
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								static void hgrow(hashtab *ht)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								     unsigned nelem = ht->nelem;
							 | 
						||
| 
								 | 
							
								     if (minsz(nelem) >= ht->hashsiz)
							 | 
						||
| 
								 | 
							
									  rehash(ht, nextsz(nelem));
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								#if 0
							 | 
						||
| 
								 | 
							
								/* shrink the hash table, never used */
							 | 
						||
| 
								 | 
							
								static void hshrink(hashtab *ht)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								     unsigned nelem = ht->nelem;
							 | 
						||
| 
								 | 
							
								     /* always rehash after deletions */
							 | 
						||
| 
								 | 
							
								     rehash(ht, nextsz(nelem));
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								#endif
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								static void htab_insert(hashtab *ht, const md5sig s, const flags_t *flagsp,
							 | 
						||
| 
								 | 
							
											unsigned slvndx)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								     unsigned g, h = h1(ht, s), d = h2(ht, s);
							 | 
						||
| 
								 | 
							
								     solution *first = 0;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								     /* Remove all entries that are subsumed by the new one.  */
							 | 
						||
| 
								 | 
							
								     /* This loop may potentially traverse the whole table, since at
							 | 
						||
| 
								 | 
							
									least one element is guaranteed to be !LIVEP, but all elements
							 | 
						||
| 
								 | 
							
									may be VALIDP.  Hence, we stop after at the first invalid
							 | 
						||
| 
								 | 
							
									element or after traversing the whole table. */
							 | 
						||
| 
								 | 
							
								     g = h;
							 | 
						||
| 
								 | 
							
								     do {
							 | 
						||
| 
								 | 
							
									  solution *l = ht->solutions + g;
							 | 
						||
| 
								 | 
							
									  ++ht->insert_iter;
							 | 
						||
| 
								 | 
							
									  if (VALIDP(l)) {
							 | 
						||
| 
								 | 
							
									       if (LIVEP(l) && md5eq(s, l->s)) {
							 | 
						||
| 
								 | 
							
										    if (subsumes(flagsp, slvndx, &l->flags)) {
							 | 
						||
| 
								 | 
							
											 if (!first) first = l;
							 | 
						||
| 
								 | 
							
											 kill_slot(ht, l);
							 | 
						||
| 
								 | 
							
										    } else {
							 | 
						||
| 
								 | 
							
											 /* It is an error to insert an element that
							 | 
						||
| 
								 | 
							
											    is subsumed by an existing entry. */
							 | 
						||
| 
								 | 
							
											 A(!subsumes(&l->flags, SLVNDX(l), flagsp));
							 | 
						||
| 
								 | 
							
										    }
							 | 
						||
| 
								 | 
							
									       }
							 | 
						||
| 
								 | 
							
									  } else 
							 | 
						||
| 
								 | 
							
									       break;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									  g = addmod(g, d, ht->hashsiz);
							 | 
						||
| 
								 | 
							
								     } while (g != h);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								     if (first) {
							 | 
						||
| 
								 | 
							
									  /* overwrite FIRST */
							 | 
						||
| 
								 | 
							
									  fill_slot(ht, s, flagsp, slvndx, first);
							 | 
						||
| 
								 | 
							
								     } else {
							 | 
						||
| 
								 | 
							
									  /* create a new entry */
							 | 
						||
| 
								 | 
							
								 	  hgrow(ht);
							 | 
						||
| 
								 | 
							
									  hinsert0(ht, s, flagsp, slvndx);
							 | 
						||
| 
								 | 
							
								     }
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								static void hinsert(planner *ego, const md5sig s, const flags_t *flagsp, 
							 | 
						||
| 
								 | 
							
										    unsigned slvndx)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								     htab_insert(BLISS(*flagsp) ? &ego->htab_blessed : &ego->htab_unblessed,
							 | 
						||
| 
								 | 
							
										 s, flagsp, slvndx );
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								static void invoke_hook(planner *ego, plan *pln, const problem *p, 
							 | 
						||
| 
								 | 
							
											int optimalp)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								     if (ego->hook)
							 | 
						||
| 
								 | 
							
									  ego->hook(ego, pln, p, optimalp);
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								#ifdef FFTW_RANDOM_ESTIMATOR
							 | 
						||
| 
								 | 
							
								/* a "random" estimate, used for debugging to generate "random"
							 | 
						||
| 
								 | 
							
								   plans, albeit from a deterministic seed. */
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								unsigned X(random_estimate_seed) = 0;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								static double random_estimate(const planner *ego, const plan *pln, 
							 | 
						||
| 
								 | 
							
											      const problem *p)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								     md5 m;
							 | 
						||
| 
								 | 
							
								     X(md5begin)(&m);
							 | 
						||
| 
								 | 
							
								     X(md5unsigned)(&m, X(random_estimate_seed));
							 | 
						||
| 
								 | 
							
								     X(md5int)(&m, ego->nthr);
							 | 
						||
| 
								 | 
							
								     p->adt->hash(p, &m);
							 | 
						||
| 
								 | 
							
								     X(md5putb)(&m, &pln->ops, sizeof(pln->ops));
							 | 
						||
| 
								 | 
							
								     X(md5putb)(&m, &pln->adt, sizeof(pln->adt));
							 | 
						||
| 
								 | 
							
								     X(md5end)(&m);
							 | 
						||
| 
								 | 
							
								     return ego->cost_hook ? ego->cost_hook(p, m.s[0], COST_MAX) : m.s[0];
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								#endif
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								double X(iestimate_cost)(const planner *ego, const plan *pln, const problem *p)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								     double cost =
							 | 
						||
| 
								 | 
							
									  + pln->ops.add
							 | 
						||
| 
								 | 
							
									  + pln->ops.mul
							 | 
						||
| 
								 | 
							
									  
							 | 
						||
| 
								 | 
							
								#if HAVE_FMA
							 | 
						||
| 
								 | 
							
									  + pln->ops.fma
							 | 
						||
| 
								 | 
							
								#else
							 | 
						||
| 
								 | 
							
									  + 2 * pln->ops.fma
							 | 
						||
| 
								 | 
							
								#endif
							 | 
						||
| 
								 | 
							
									  
							 | 
						||
| 
								 | 
							
									  + pln->ops.other;
							 | 
						||
| 
								 | 
							
								     if (ego->cost_hook)
							 | 
						||
| 
								 | 
							
									  cost = ego->cost_hook(p, cost, COST_MAX);
							 | 
						||
| 
								 | 
							
								     return cost;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								static void evaluate_plan(planner *ego, plan *pln, const problem *p)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								     if (ESTIMATEP(ego) || !BELIEVE_PCOSTP(ego) || pln->pcost == 0.0) {
							 | 
						||
| 
								 | 
							
									  ego->nplan++;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									  if (ESTIMATEP(ego)) {
							 | 
						||
| 
								 | 
							
									  estimate:
							 | 
						||
| 
								 | 
							
									       /* heuristic */
							 | 
						||
| 
								 | 
							
								#ifdef FFTW_RANDOM_ESTIMATOR
							 | 
						||
| 
								 | 
							
									       pln->pcost = random_estimate(ego, pln, p);
							 | 
						||
| 
								 | 
							
									       ego->epcost += X(iestimate_cost)(ego, pln, p);
							 | 
						||
| 
								 | 
							
								#else
							 | 
						||
| 
								 | 
							
									       pln->pcost = X(iestimate_cost)(ego, pln, p);
							 | 
						||
| 
								 | 
							
									       ego->epcost += pln->pcost;
							 | 
						||
| 
								 | 
							
								#endif
							 | 
						||
| 
								 | 
							
									  } else {
							 | 
						||
| 
								 | 
							
									       double t = X(measure_execution_time)(ego, pln, p);
							 | 
						||
| 
								 | 
							
									       
							 | 
						||
| 
								 | 
							
									       if (t < 0) {  /* unavailable cycle counter */
							 | 
						||
| 
								 | 
							
										    /* Real programmers can write FORTRAN in any language */
							 | 
						||
| 
								 | 
							
										    goto estimate;
							 | 
						||
| 
								 | 
							
									       }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									       pln->pcost = t;
							 | 
						||
| 
								 | 
							
									       ego->pcost += t;
							 | 
						||
| 
								 | 
							
									       ego->need_timeout_check = 1;
							 | 
						||
| 
								 | 
							
									  }
							 | 
						||
| 
								 | 
							
								     }
							 | 
						||
| 
								 | 
							
								     
							 | 
						||
| 
								 | 
							
								     invoke_hook(ego, pln, p, 0);
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								/* maintain dynamic scoping of flags, nthr: */
							 | 
						||
| 
								 | 
							
								static plan *invoke_solver(planner *ego, const problem *p, solver *s, 
							 | 
						||
| 
								 | 
							
											   const flags_t *nflags)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								     flags_t flags = ego->flags;
							 | 
						||
| 
								 | 
							
								     int nthr = ego->nthr;
							 | 
						||
| 
								 | 
							
								     plan *pln;
							 | 
						||
| 
								 | 
							
								     ego->flags = *nflags;
							 | 
						||
| 
								 | 
							
								     PLNR_TIMELIMIT_IMPATIENCE(ego) = 0;
							 | 
						||
| 
								 | 
							
								     A(p->adt->problem_kind == s->adt->problem_kind);
							 | 
						||
| 
								 | 
							
								     pln = s->adt->mkplan(s, p, ego);
							 | 
						||
| 
								 | 
							
								     ego->nthr = nthr;
							 | 
						||
| 
								 | 
							
								     ego->flags = flags;
							 | 
						||
| 
								 | 
							
								     return pln;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								/* maintain the invariant TIMED_OUT ==> NEED_TIMEOUT_CHECK */
							 | 
						||
| 
								 | 
							
								static int timeout_p(planner *ego, const problem *p)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								     /* do not timeout when estimating.  First, the estimator is the
							 | 
						||
| 
								 | 
							
									planner of last resort.  Second, calling X(elapsed_since)() is
							 | 
						||
| 
								 | 
							
									slower than estimating */
							 | 
						||
| 
								 | 
							
								     if (!ESTIMATEP(ego)) {
							 | 
						||
| 
								 | 
							
									  /* do not assume that X(elapsed_since)() is monotonic */
							 | 
						||
| 
								 | 
							
									  if (ego->timed_out) {
							 | 
						||
| 
								 | 
							
									       A(ego->need_timeout_check);
							 | 
						||
| 
								 | 
							
									       return 1;
							 | 
						||
| 
								 | 
							
									  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									  if (ego->timelimit >= 0 &&
							 | 
						||
| 
								 | 
							
									      X(elapsed_since)(ego, p, ego->start_time) >= ego->timelimit) {
							 | 
						||
| 
								 | 
							
									       ego->timed_out = 1;
							 | 
						||
| 
								 | 
							
									       ego->need_timeout_check = 1;
							 | 
						||
| 
								 | 
							
									       return 1;
							 | 
						||
| 
								 | 
							
									  }
							 | 
						||
| 
								 | 
							
								     }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								     A(!ego->timed_out);
							 | 
						||
| 
								 | 
							
								     ego->need_timeout_check = 0;
							 | 
						||
| 
								 | 
							
								     return 0;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								static plan *search0(planner *ego, const problem *p, unsigned *slvndx, 
							 | 
						||
| 
								 | 
							
										     const flags_t *flagsp)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								     plan *best = 0;
							 | 
						||
| 
								 | 
							
								     int best_not_yet_timed = 1;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								     /* Do not start a search if the planner timed out. This check is
							 | 
						||
| 
								 | 
							
									necessary, lest the relaxation mechanism kick in */
							 | 
						||
| 
								 | 
							
								     if (timeout_p(ego, p))
							 | 
						||
| 
								 | 
							
									  return 0;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								     FORALL_SOLVERS_OF_KIND(p->adt->problem_kind, ego, s, sp, {
							 | 
						||
| 
								 | 
							
									  plan *pln;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									  pln = invoke_solver(ego, p, s, flagsp);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									  if (ego->need_timeout_check) 
							 | 
						||
| 
								 | 
							
									       if (timeout_p(ego, p)) {
							 | 
						||
| 
								 | 
							
										    X(plan_destroy_internal)(pln);
							 | 
						||
| 
								 | 
							
										    X(plan_destroy_internal)(best);
							 | 
						||
| 
								 | 
							
										    return 0;
							 | 
						||
| 
								 | 
							
									       }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									  if (pln) {
							 | 
						||
| 
								 | 
							
									       /* read COULD_PRUNE_NOW_P because PLN may be destroyed
							 | 
						||
| 
								 | 
							
										  before we use COULD_PRUNE_NOW_P */
							 | 
						||
| 
								 | 
							
									       int could_prune_now_p = pln->could_prune_now_p;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									       if (best) {
							 | 
						||
| 
								 | 
							
										    if (best_not_yet_timed) {
							 | 
						||
| 
								 | 
							
											 evaluate_plan(ego, best, p);
							 | 
						||
| 
								 | 
							
											 best_not_yet_timed = 0;
							 | 
						||
| 
								 | 
							
										    }
							 | 
						||
| 
								 | 
							
										    evaluate_plan(ego, pln, p);
							 | 
						||
| 
								 | 
							
										    if (pln->pcost < best->pcost) {
							 | 
						||
| 
								 | 
							
											 X(plan_destroy_internal)(best);
							 | 
						||
| 
								 | 
							
											 best = pln;
							 | 
						||
| 
								 | 
							
								                         *slvndx = (unsigned)/*from ptrdiff_t*/(sp - ego->slvdescs);
							 | 
						||
| 
								 | 
							
										    } else {
							 | 
						||
| 
								 | 
							
											 X(plan_destroy_internal)(pln);
							 | 
						||
| 
								 | 
							
										    }
							 | 
						||
| 
								 | 
							
									       } else {
							 | 
						||
| 
								 | 
							
										    best = pln;
							 | 
						||
| 
								 | 
							
								                    *slvndx = (unsigned)/*from ptrdiff_t*/(sp - ego->slvdescs);                    
							 | 
						||
| 
								 | 
							
									       }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									       if (ALLOW_PRUNINGP(ego) && could_prune_now_p) 
							 | 
						||
| 
								 | 
							
										    break;
							 | 
						||
| 
								 | 
							
									  }
							 | 
						||
| 
								 | 
							
								     });
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								     return best;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								static plan *search(planner *ego, const problem *p, unsigned *slvndx, 
							 | 
						||
| 
								 | 
							
										    flags_t *flagsp)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								     plan *pln = 0;
							 | 
						||
| 
								 | 
							
								     unsigned i;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								     /* relax impatience in this order: */
							 | 
						||
| 
								 | 
							
								     static const unsigned relax_tab[] = {
							 | 
						||
| 
								 | 
							
									  0, /* relax nothing */
							 | 
						||
| 
								 | 
							
									  NO_VRECURSE,
							 | 
						||
| 
								 | 
							
									  NO_FIXED_RADIX_LARGE_N,
							 | 
						||
| 
								 | 
							
									  NO_SLOW,
							 | 
						||
| 
								 | 
							
									  NO_UGLY
							 | 
						||
| 
								 | 
							
								     };
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								     unsigned l_orig = flagsp->l;
							 | 
						||
| 
								 | 
							
								     unsigned x = flagsp->u;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								     /* guaranteed to be different from X */
							 | 
						||
| 
								 | 
							
								     unsigned last_x = ~x; 
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								     for (i = 0; i < sizeof(relax_tab) / sizeof(relax_tab[0]); ++i) {
							 | 
						||
| 
								 | 
							
									  if (LEQ(l_orig, x & ~relax_tab[i]))
							 | 
						||
| 
								 | 
							
									       x = x & ~relax_tab[i];
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									  if (x != last_x) {
							 | 
						||
| 
								 | 
							
									       last_x = x;
							 | 
						||
| 
								 | 
							
									       flagsp->l = x;
							 | 
						||
| 
								 | 
							
									       pln = search0(ego, p, slvndx, flagsp);
							 | 
						||
| 
								 | 
							
									       if (pln) break;
							 | 
						||
| 
								 | 
							
									  }
							 | 
						||
| 
								 | 
							
								     }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								     if (!pln) {
							 | 
						||
| 
								 | 
							
									  /* search [L_ORIG, U] */
							 | 
						||
| 
								 | 
							
									  if (l_orig != last_x) {
							 | 
						||
| 
								 | 
							
									       last_x = l_orig;
							 | 
						||
| 
								 | 
							
									       flagsp->l = l_orig;
							 | 
						||
| 
								 | 
							
									       pln = search0(ego, p, slvndx, flagsp);
							 | 
						||
| 
								 | 
							
									  }
							 | 
						||
| 
								 | 
							
								     }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								     return pln;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								#define CHECK_FOR_BOGOSITY						\
							 | 
						||
| 
								 | 
							
								     if ((ego->bogosity_hook ?						\
							 | 
						||
| 
								 | 
							
									  (ego->wisdom_state = ego->bogosity_hook(ego->wisdom_state, p)) \
							 | 
						||
| 
								 | 
							
									  : ego->wisdom_state) == WISDOM_IS_BOGUS)			\
							 | 
						||
| 
								 | 
							
									  goto wisdom_is_bogus;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								static plan *mkplan(planner *ego, const problem *p)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								     plan *pln;
							 | 
						||
| 
								 | 
							
								     md5 m;
							 | 
						||
| 
								 | 
							
								     unsigned slvndx;
							 | 
						||
| 
								 | 
							
								     flags_t flags_of_solution;
							 | 
						||
| 
								 | 
							
								     solution *sol;
							 | 
						||
| 
								 | 
							
								     solver *s;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								     ASSERT_ALIGNED_DOUBLE;
							 | 
						||
| 
								 | 
							
								     A(LEQ(PLNR_L(ego), PLNR_U(ego)));
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								     if (ESTIMATEP(ego))
							 | 
						||
| 
								 | 
							
									  PLNR_TIMELIMIT_IMPATIENCE(ego) = 0; /* canonical form */
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								#ifdef FFTW_DEBUG
							 | 
						||
| 
								 | 
							
								     check(&ego->htab_blessed);
							 | 
						||
| 
								 | 
							
								     check(&ego->htab_unblessed);
							 | 
						||
| 
								 | 
							
								#endif
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								     pln = 0;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								     CHECK_FOR_BOGOSITY;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								     ego->timed_out = 0;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								     ++ego->nprob;
							 | 
						||
| 
								 | 
							
								     md5hash(&m, p, ego);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								     flags_of_solution = ego->flags;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								     if (ego->wisdom_state != WISDOM_IGNORE_ALL) {
							 | 
						||
| 
								 | 
							
									  if ((sol = hlookup(ego, m.s, &flags_of_solution))) { 
							 | 
						||
| 
								 | 
							
									       /* wisdom is acceptable */
							 | 
						||
| 
								 | 
							
									       wisdom_state_t owisdom_state = ego->wisdom_state;
							 | 
						||
| 
								 | 
							
									       
							 | 
						||
| 
								 | 
							
									       /* this hook is mainly for MPI, to make sure that
							 | 
						||
| 
								 | 
							
										  wisdom is in sync across all processes for MPI problems */
							 | 
						||
| 
								 | 
							
									       if (ego->wisdom_ok_hook && !ego->wisdom_ok_hook(p, sol->flags))
							 | 
						||
| 
								 | 
							
										    goto do_search; /* ignore not-ok wisdom */
							 | 
						||
| 
								 | 
							
									       
							 | 
						||
| 
								 | 
							
									       slvndx = SLVNDX(sol);
							 | 
						||
| 
								 | 
							
									       
							 | 
						||
| 
								 | 
							
									       if (slvndx == INFEASIBLE_SLVNDX) {
							 | 
						||
| 
								 | 
							
										    if (ego->wisdom_state == WISDOM_IGNORE_INFEASIBLE)
							 | 
						||
| 
								 | 
							
											 goto do_search;
							 | 
						||
| 
								 | 
							
										    else
							 | 
						||
| 
								 | 
							
											 return 0;   /* known to be infeasible */
							 | 
						||
| 
								 | 
							
									       }
							 | 
						||
| 
								 | 
							
									       
							 | 
						||
| 
								 | 
							
									       flags_of_solution = sol->flags;
							 | 
						||
| 
								 | 
							
									       
							 | 
						||
| 
								 | 
							
									       /* inherit blessing either from wisdom
							 | 
						||
| 
								 | 
							
										  or from the planner */
							 | 
						||
| 
								 | 
							
									       flags_of_solution.hash_info |= BLISS(ego->flags);
							 | 
						||
| 
								 | 
							
									       
							 | 
						||
| 
								 | 
							
									       ego->wisdom_state = WISDOM_ONLY;
							 | 
						||
| 
								 | 
							
									       
							 | 
						||
| 
								 | 
							
									       s = ego->slvdescs[slvndx].slv;
							 | 
						||
| 
								 | 
							
									       if (p->adt->problem_kind != s->adt->problem_kind)
							 | 
						||
| 
								 | 
							
										    goto wisdom_is_bogus;
							 | 
						||
| 
								 | 
							
									       
							 | 
						||
| 
								 | 
							
									       pln = invoke_solver(ego, p, s, &flags_of_solution);
							 | 
						||
| 
								 | 
							
									       
							 | 
						||
| 
								 | 
							
									       CHECK_FOR_BOGOSITY; 	  /* catch error in child solvers */
							 | 
						||
| 
								 | 
							
									       
							 | 
						||
| 
								 | 
							
									       sol = 0; /* Paranoia: SOL may be dangling after
							 | 
						||
| 
								 | 
							
											   invoke_solver(); make sure we don't accidentally
							 | 
						||
| 
								 | 
							
											   reuse it. */
							 | 
						||
| 
								 | 
							
									       
							 | 
						||
| 
								 | 
							
									       if (!pln)
							 | 
						||
| 
								 | 
							
										    goto wisdom_is_bogus;
							 | 
						||
| 
								 | 
							
									       
							 | 
						||
| 
								 | 
							
									       ego->wisdom_state = owisdom_state;
							 | 
						||
| 
								 | 
							
									       
							 | 
						||
| 
								 | 
							
									       goto skip_search;
							 | 
						||
| 
								 | 
							
									  }
							 | 
						||
| 
								 | 
							
									  else if (ego->nowisdom_hook) /* for MPI, make sure lack of wisdom */
							 | 
						||
| 
								 | 
							
									       ego->nowisdom_hook(p);  /*   is in sync across all processes */
							 | 
						||
| 
								 | 
							
								     }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								 do_search:
							 | 
						||
| 
								 | 
							
								     /* cannot search in WISDOM_ONLY mode */
							 | 
						||
| 
								 | 
							
								     if (ego->wisdom_state == WISDOM_ONLY)
							 | 
						||
| 
								 | 
							
									  goto wisdom_is_bogus;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								     flags_of_solution = ego->flags;
							 | 
						||
| 
								 | 
							
								     pln = search(ego, p, &slvndx, &flags_of_solution);
							 | 
						||
| 
								 | 
							
								     CHECK_FOR_BOGOSITY; 	  /* catch error in child solvers */
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								     if (ego->timed_out) {
							 | 
						||
| 
								 | 
							
									  A(!pln);
							 | 
						||
| 
								 | 
							
									  if (PLNR_TIMELIMIT_IMPATIENCE(ego) != 0) {
							 | 
						||
| 
								 | 
							
									       /* record (below) that this plan has failed because of
							 | 
						||
| 
								 | 
							
										  timeout */
							 | 
						||
| 
								 | 
							
									       flags_of_solution.hash_info |= BLESSING;
							 | 
						||
| 
								 | 
							
									  } else {
							 | 
						||
| 
								 | 
							
									       /* this is not the top-level problem or timeout is not
							 | 
						||
| 
								 | 
							
										  active: record no wisdom. */
							 | 
						||
| 
								 | 
							
									       return 0;
							 | 
						||
| 
								 | 
							
									  }
							 | 
						||
| 
								 | 
							
								     } else {
							 | 
						||
| 
								 | 
							
									  /* canonicalize to infinite timeout */
							 | 
						||
| 
								 | 
							
									  flags_of_solution.timelimit_impatience = 0;
							 | 
						||
| 
								 | 
							
								     }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								 skip_search:
							 | 
						||
| 
								 | 
							
								     if (ego->wisdom_state == WISDOM_NORMAL ||
							 | 
						||
| 
								 | 
							
									 ego->wisdom_state == WISDOM_ONLY) {
							 | 
						||
| 
								 | 
							
									  if (pln) {
							 | 
						||
| 
								 | 
							
									       hinsert(ego, m.s, &flags_of_solution, slvndx);
							 | 
						||
| 
								 | 
							
									       invoke_hook(ego, pln, p, 1);
							 | 
						||
| 
								 | 
							
									  } else {
							 | 
						||
| 
								 | 
							
									       hinsert(ego, m.s, &flags_of_solution, INFEASIBLE_SLVNDX);
							 | 
						||
| 
								 | 
							
									  }
							 | 
						||
| 
								 | 
							
								     }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								     return pln;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								 wisdom_is_bogus:
							 | 
						||
| 
								 | 
							
								     X(plan_destroy_internal)(pln);
							 | 
						||
| 
								 | 
							
								     ego->wisdom_state = WISDOM_IS_BOGUS;
							 | 
						||
| 
								 | 
							
								     return 0;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								static void htab_destroy(hashtab *ht)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								     X(ifree)(ht->solutions);
							 | 
						||
| 
								 | 
							
								     ht->solutions = 0;
							 | 
						||
| 
								 | 
							
								     ht->nelem = 0U;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								static void mkhashtab(hashtab *ht)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								     ht->nrehash = 0;
							 | 
						||
| 
								 | 
							
								     ht->succ_lookup = ht->lookup = ht->lookup_iter = 0;
							 | 
						||
| 
								 | 
							
								     ht->insert = ht->insert_iter = ht->insert_unknown = 0;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								     ht->solutions = 0;
							 | 
						||
| 
								 | 
							
								     ht->hashsiz = ht->nelem = 0U;
							 | 
						||
| 
								 | 
							
								     hgrow(ht);			/* so that hashsiz > 0 */
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								/* destroy hash table entries.  If FORGET_EVERYTHING, destroy the whole
							 | 
						||
| 
								 | 
							
								   table.  If FORGET_ACCURSED, then destroy entries that are not blessed. */
							 | 
						||
| 
								 | 
							
								static void forget(planner *ego, amnesia a)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								     switch (a) {
							 | 
						||
| 
								 | 
							
									 case FORGET_EVERYTHING:
							 | 
						||
| 
								 | 
							
									      htab_destroy(&ego->htab_blessed);
							 | 
						||
| 
								 | 
							
									      mkhashtab(&ego->htab_blessed);
							 | 
						||
| 
								 | 
							
									      /* fall through */
							 | 
						||
| 
								 | 
							
									 case FORGET_ACCURSED:
							 | 
						||
| 
								 | 
							
									      htab_destroy(&ego->htab_unblessed);
							 | 
						||
| 
								 | 
							
									      mkhashtab(&ego->htab_unblessed);
							 | 
						||
| 
								 | 
							
									      break;
							 | 
						||
| 
								 | 
							
									 default:
							 | 
						||
| 
								 | 
							
									      break;
							 | 
						||
| 
								 | 
							
								     }
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								/* FIXME: what sort of version information should we write? */
							 | 
						||
| 
								 | 
							
								#define WISDOM_PREAMBLE PACKAGE "-" VERSION " " STRINGIZE(X(wisdom))
							 | 
						||
| 
								 | 
							
								static const char stimeout[] = "TIMEOUT";
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								/* tantus labor non sit cassus */
							 | 
						||
| 
								 | 
							
								static void exprt(planner *ego, printer *p)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								     unsigned h;
							 | 
						||
| 
								 | 
							
								     hashtab *ht = &ego->htab_blessed;
							 | 
						||
| 
								 | 
							
								     md5 m;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								     signature_of_configuration(&m, ego);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								     p->print(p, 
							 | 
						||
| 
								 | 
							
									      "(" WISDOM_PREAMBLE " #x%M #x%M #x%M #x%M\n",
							 | 
						||
| 
								 | 
							
									      m.s[0], m.s[1], m.s[2], m.s[3]);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								     for (h = 0; h < ht->hashsiz; ++h) {
							 | 
						||
| 
								 | 
							
									  solution *l = ht->solutions + h;
							 | 
						||
| 
								 | 
							
									  if (LIVEP(l)) {
							 | 
						||
| 
								 | 
							
									       const char *reg_nam;
							 | 
						||
| 
								 | 
							
									       int reg_id;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									       if (SLVNDX(l) == INFEASIBLE_SLVNDX) {
							 | 
						||
| 
								 | 
							
										    reg_nam = stimeout;
							 | 
						||
| 
								 | 
							
										    reg_id = 0;
							 | 
						||
| 
								 | 
							
									       } else {
							 | 
						||
| 
								 | 
							
										    slvdesc *sp = ego->slvdescs + SLVNDX(l);
							 | 
						||
| 
								 | 
							
										    reg_nam = sp->reg_nam;
							 | 
						||
| 
								 | 
							
										    reg_id = sp->reg_id;
							 | 
						||
| 
								 | 
							
									       }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									       /* qui salvandos salvas gratis
							 | 
						||
| 
								 | 
							
										  salva me fons pietatis */
							 | 
						||
| 
								 | 
							
									       p->print(p, "  (%s %d #x%x #x%x #x%x #x%M #x%M #x%M #x%M)\n",
							 | 
						||
| 
								 | 
							
											reg_nam, reg_id, 
							 | 
						||
| 
								 | 
							
											l->flags.l, l->flags.u, l->flags.timelimit_impatience, 
							 | 
						||
| 
								 | 
							
											l->s[0], l->s[1], l->s[2], l->s[3]);
							 | 
						||
| 
								 | 
							
									  }
							 | 
						||
| 
								 | 
							
								     }
							 | 
						||
| 
								 | 
							
								     p->print(p, ")\n");
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								/* mors stupebit et natura
							 | 
						||
| 
								 | 
							
								   cum resurget creatura */
							 | 
						||
| 
								 | 
							
								static int imprt(planner *ego, scanner *sc)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								     char buf[MAXNAM + 1];
							 | 
						||
| 
								 | 
							
								     md5uint sig[4];
							 | 
						||
| 
								 | 
							
								     unsigned l, u, timelimit_impatience;
							 | 
						||
| 
								 | 
							
								     flags_t flags;
							 | 
						||
| 
								 | 
							
								     int reg_id;
							 | 
						||
| 
								 | 
							
								     unsigned slvndx;
							 | 
						||
| 
								 | 
							
								     hashtab *ht = &ego->htab_blessed;
							 | 
						||
| 
								 | 
							
								     hashtab old;
							 | 
						||
| 
								 | 
							
								     md5 m;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								     if (!sc->scan(sc, 
							 | 
						||
| 
								 | 
							
										   "(" WISDOM_PREAMBLE " #x%M #x%M #x%M #x%M\n",
							 | 
						||
| 
								 | 
							
										   sig + 0, sig + 1, sig + 2, sig + 3))
							 | 
						||
| 
								 | 
							
									  return 0; /* don't need to restore hashtable */
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								     signature_of_configuration(&m, ego);
							 | 
						||
| 
								 | 
							
								     if (m.s[0] != sig[0] || m.s[1] != sig[1] ||
							 | 
						||
| 
								 | 
							
									 m.s[2] != sig[2] || m.s[3] != sig[3]) {
							 | 
						||
| 
								 | 
							
									  /* invalid configuration */
							 | 
						||
| 
								 | 
							
									  return 0;
							 | 
						||
| 
								 | 
							
								     }
							 | 
						||
| 
								 | 
							
								     
							 | 
						||
| 
								 | 
							
								     /* make a backup copy of the hash table (cache the hash) */
							 | 
						||
| 
								 | 
							
								     {
							 | 
						||
| 
								 | 
							
									  unsigned h, hsiz = ht->hashsiz;
							 | 
						||
| 
								 | 
							
									  old = *ht;
							 | 
						||
| 
								 | 
							
									  old.solutions = (solution *)MALLOC(hsiz * sizeof(solution), HASHT);
							 | 
						||
| 
								 | 
							
									  for (h = 0; h < hsiz; ++h)
							 | 
						||
| 
								 | 
							
									       old.solutions[h] = ht->solutions[h];
							 | 
						||
| 
								 | 
							
								     }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								     while (1) {
							 | 
						||
| 
								 | 
							
									  if (sc->scan(sc, ")"))
							 | 
						||
| 
								 | 
							
									       break;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									  /* qua resurget ex favilla */
							 | 
						||
| 
								 | 
							
									  if (!sc->scan(sc, "(%*s %d #x%x #x%x #x%x #x%M #x%M #x%M #x%M)",
							 | 
						||
| 
								 | 
							
											MAXNAM, buf, ®_id, &l, &u, &timelimit_impatience,
							 | 
						||
| 
								 | 
							
											sig + 0, sig + 1, sig + 2, sig + 3))
							 | 
						||
| 
								 | 
							
									       goto bad;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									  if (!strcmp(buf, stimeout) && reg_id == 0) {
							 | 
						||
| 
								 | 
							
									       slvndx = INFEASIBLE_SLVNDX;
							 | 
						||
| 
								 | 
							
									  } else {
							 | 
						||
| 
								 | 
							
									       if (timelimit_impatience != 0)
							 | 
						||
| 
								 | 
							
										    goto bad;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									       slvndx = slookup(ego, buf, reg_id);
							 | 
						||
| 
								 | 
							
									       if (slvndx == INFEASIBLE_SLVNDX)
							 | 
						||
| 
								 | 
							
										    goto bad;
							 | 
						||
| 
								 | 
							
									  }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									  /* inter oves locum praesta */
							 | 
						||
| 
								 | 
							
									  flags.l = l;
							 | 
						||
| 
								 | 
							
									  flags.u = u;
							 | 
						||
| 
								 | 
							
									  flags.timelimit_impatience = timelimit_impatience;
							 | 
						||
| 
								 | 
							
									  flags.hash_info = BLESSING;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									  CK(flags.l == l);
							 | 
						||
| 
								 | 
							
									  CK(flags.u == u);
							 | 
						||
| 
								 | 
							
									  CK(flags.timelimit_impatience == timelimit_impatience);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									  if (!hlookup(ego, sig, &flags))
							 | 
						||
| 
								 | 
							
									       hinsert(ego, sig, &flags, slvndx);
							 | 
						||
| 
								 | 
							
								     }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								     X(ifree0)(old.solutions);
							 | 
						||
| 
								 | 
							
								     return 1;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								 bad:
							 | 
						||
| 
								 | 
							
								     /* ``The wisdom of FFTW must be above suspicion.'' */
							 | 
						||
| 
								 | 
							
								     X(ifree0)(ht->solutions);
							 | 
						||
| 
								 | 
							
								     *ht = old;
							 | 
						||
| 
								 | 
							
								     return 0;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								/*
							 | 
						||
| 
								 | 
							
								 * create a planner
							 | 
						||
| 
								 | 
							
								 */
							 | 
						||
| 
								 | 
							
								planner *X(mkplanner)(void)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								     int i;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								     static const planner_adt padt = {
							 | 
						||
| 
								 | 
							
									  register_solver, mkplan, forget, exprt, imprt
							 | 
						||
| 
								 | 
							
								     };
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								     planner *p = (planner *) MALLOC(sizeof(planner), PLANNERS);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								     p->adt = &padt;
							 | 
						||
| 
								 | 
							
								     p->nplan = p->nprob = 0;
							 | 
						||
| 
								 | 
							
								     p->pcost = p->epcost = 0.0;
							 | 
						||
| 
								 | 
							
								     p->hook = 0;
							 | 
						||
| 
								 | 
							
								     p->cost_hook = 0;
							 | 
						||
| 
								 | 
							
								     p->wisdom_ok_hook = 0;
							 | 
						||
| 
								 | 
							
								     p->nowisdom_hook = 0;
							 | 
						||
| 
								 | 
							
								     p->bogosity_hook = 0;
							 | 
						||
| 
								 | 
							
								     p->cur_reg_nam = 0;
							 | 
						||
| 
								 | 
							
								     p->wisdom_state = WISDOM_NORMAL;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								     p->slvdescs = 0;
							 | 
						||
| 
								 | 
							
								     p->nslvdesc = p->slvdescsiz = 0;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								     p->flags.l = 0;
							 | 
						||
| 
								 | 
							
								     p->flags.u = 0;
							 | 
						||
| 
								 | 
							
								     p->flags.timelimit_impatience = 0;
							 | 
						||
| 
								 | 
							
								     p->flags.hash_info = 0;
							 | 
						||
| 
								 | 
							
								     p->nthr = 1;
							 | 
						||
| 
								 | 
							
								     p->need_timeout_check = 1;
							 | 
						||
| 
								 | 
							
								     p->timelimit = -1;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								     mkhashtab(&p->htab_blessed);
							 | 
						||
| 
								 | 
							
								     mkhashtab(&p->htab_unblessed);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								     for (i = 0; i < PROBLEM_LAST; ++i)
							 | 
						||
| 
								 | 
							
									  p->slvdescs_for_problem_kind[i] = -1;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								     return p;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								void X(planner_destroy)(planner *ego)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								     /* destroy hash table */
							 | 
						||
| 
								 | 
							
								     htab_destroy(&ego->htab_blessed);
							 | 
						||
| 
								 | 
							
								     htab_destroy(&ego->htab_unblessed);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								     /* destroy solvdesc table */
							 | 
						||
| 
								 | 
							
								     FORALL_SOLVERS(ego, s, sp, {
							 | 
						||
| 
								 | 
							
									  UNUSED(sp);
							 | 
						||
| 
								 | 
							
									  X(solver_destroy)(s);
							 | 
						||
| 
								 | 
							
								     });
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								     X(ifree0)(ego->slvdescs);
							 | 
						||
| 
								 | 
							
								     X(ifree)(ego); /* dona eis requiem */
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								plan *X(mkplan_d)(planner *ego, problem *p)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								     plan *pln = ego->adt->mkplan(ego, p);
							 | 
						||
| 
								 | 
							
								     X(problem_destroy)(p);
							 | 
						||
| 
								 | 
							
								     return pln;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								/* like X(mkplan_d), but sets/resets flags as well */
							 | 
						||
| 
								 | 
							
								plan *X(mkplan_f_d)(planner *ego, problem *p, 
							 | 
						||
| 
								 | 
							
										    unsigned l_set, unsigned u_set, unsigned u_reset)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								     flags_t oflags = ego->flags;
							 | 
						||
| 
								 | 
							
								     plan *pln;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								     PLNR_U(ego) &= ~u_reset;
							 | 
						||
| 
								 | 
							
								     PLNR_L(ego) &= ~u_reset;
							 | 
						||
| 
								 | 
							
								     PLNR_L(ego) |= l_set;
							 | 
						||
| 
								 | 
							
								     PLNR_U(ego) |= u_set | l_set;
							 | 
						||
| 
								 | 
							
								     pln = X(mkplan_d)(ego, p);
							 | 
						||
| 
								 | 
							
								     ego->flags = oflags;
							 | 
						||
| 
								 | 
							
								     return pln;
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								/*
							 | 
						||
| 
								 | 
							
								 * Debugging code:
							 | 
						||
| 
								 | 
							
								 */
							 | 
						||
| 
								 | 
							
								#ifdef FFTW_DEBUG
							 | 
						||
| 
								 | 
							
								static void check(hashtab *ht)
							 | 
						||
| 
								 | 
							
								{
							 | 
						||
| 
								 | 
							
								     unsigned live = 0;
							 | 
						||
| 
								 | 
							
								     unsigned i;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								     A(ht->nelem < ht->hashsiz);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								     for (i = 0; i < ht->hashsiz; ++i) {
							 | 
						||
| 
								 | 
							
									  solution *l = ht->solutions + i; 
							 | 
						||
| 
								 | 
							
									  if (LIVEP(l)) 
							 | 
						||
| 
								 | 
							
									       ++live; 
							 | 
						||
| 
								 | 
							
								     }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								     A(ht->nelem == live);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								     for (i = 0; i < ht->hashsiz; ++i) {
							 | 
						||
| 
								 | 
							
									  solution *l1 = ht->solutions + i; 
							 | 
						||
| 
								 | 
							
									  int foundit = 0;
							 | 
						||
| 
								 | 
							
									  if (LIVEP(l1)) {
							 | 
						||
| 
								 | 
							
									       unsigned g, h = h1(ht, l1->s), d = h2(ht, l1->s);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									       g = h;
							 | 
						||
| 
								 | 
							
									       do {
							 | 
						||
| 
								 | 
							
										    solution *l = ht->solutions + g;
							 | 
						||
| 
								 | 
							
										    if (VALIDP(l)) {
							 | 
						||
| 
								 | 
							
											 if (l1 == l)
							 | 
						||
| 
								 | 
							
											      foundit = 1;
							 | 
						||
| 
								 | 
							
											 else if (LIVEP(l) && md5eq(l1->s, l->s)) {
							 | 
						||
| 
								 | 
							
											      A(!subsumes(&l->flags, SLVNDX(l), &l1->flags));
							 | 
						||
| 
								 | 
							
											      A(!subsumes(&l1->flags, SLVNDX(l1), &l->flags));
							 | 
						||
| 
								 | 
							
											 }
							 | 
						||
| 
								 | 
							
										    } else 
							 | 
						||
| 
								 | 
							
											 break;
							 | 
						||
| 
								 | 
							
										    g = addmod(g, d, ht->hashsiz);
							 | 
						||
| 
								 | 
							
									       } while (g != h);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									       A(foundit);
							 | 
						||
| 
								 | 
							
									  }
							 | 
						||
| 
								 | 
							
								     }
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								#endif
							 |