- fix noprovide handling in findupdate
[platform/upstream/libsolv.git] / src / solver.c
index c030d46..e17205b 100644 (file)
 #include "bitmap.h"
 #include "pool.h"
 #include "util.h"
-#include "evr.h"
 #include "policy.h"
 #include "solverdebug.h"
 
 #define RULES_BLOCK 63
 
-static void disableupdaterules(Solver *solv, Queue *job, int jobidx);
-
 /********************************************************************
  *
  * dependency check helpers
@@ -97,443 +94,14 @@ solver_dep_installed(Solver *solv, Id dep)
 }
 
 
-/*-------------------------------------------------------------------
- * Check if dependenc is possible
- * 
- * this mirrors solver_dep_fulfilled
- * but uses map m instead of the decisionmap
- */
-
-static inline int
-dep_possible(Solver *solv, Id dep, Map *m)
-{
-  Pool *pool = solv->pool;
-  Id p, pp;
-
-  if (ISRELDEP(dep))
-    {
-      Reldep *rd = GETRELDEP(pool, dep);
-      if (rd->flags == REL_AND)
-       {
-         if (!dep_possible(solv, rd->name, m))
-           return 0;
-         return dep_possible(solv, rd->evr, m);
-       }
-      if (rd->flags == REL_NAMESPACE && rd->name == NAMESPACE_SPLITPROVIDES)
-       return solver_splitprovides(solv, rd->evr);
-      if (rd->flags == REL_NAMESPACE && rd->name == NAMESPACE_INSTALLED)
-       return solver_dep_installed(solv, rd->evr);
-    }
-  FOR_PROVIDES(p, pp, dep)
-    {
-      if (MAPTST(m, p))
-       return 1;
-    }
-  return 0;
-}
-
-/********************************************************************
- *
- * Rule handling
- *
- * - unify rules, remove duplicates
- */
-
-static Pool *unifyrules_sortcmp_data;
-
-/*-------------------------------------------------------------------
- *
- * compare rules for unification sort
- *
- */
-
-static int
-unifyrules_sortcmp(const void *ap, const void *bp)
-{
-  Pool *pool = unifyrules_sortcmp_data;
-  Rule *a = (Rule *)ap;
-  Rule *b = (Rule *)bp;
-  Id *ad, *bd;
-  int x;
-
-  x = a->p - b->p;
-  if (x)
-    return x;                         /* p differs */
-
-  /* identical p */
-  if (a->d == 0 && b->d == 0)
-    return a->w2 - b->w2;             /* assertion: return w2 diff */
-
-  if (a->d == 0)                      /* a is assertion, b not */
-    {
-      x = a->w2 - pool->whatprovidesdata[b->d];
-      return x ? x : -1;
-    }
-
-  if (b->d == 0)                      /* b is assertion, a not */
-    {
-      x = pool->whatprovidesdata[a->d] - b->w2;
-      return x ? x : 1;
-    }
-
-  /* compare whatprovidesdata */
-  ad = pool->whatprovidesdata + a->d;
-  bd = pool->whatprovidesdata + b->d;
-  while (*bd)
-    if ((x = *ad++ - *bd++) != 0)
-      return x;
-  return *ad;
-}
-
-
-/*-------------------------------------------------------------------
- *
- * unify rules
- * go over all rules and remove duplicates
- */
-
-static void
-unifyrules(Solver *solv)
-{
-  Pool *pool = solv->pool;
-  int i, j;
-  Rule *ir, *jr;
-
-  if (solv->nrules <= 1)              /* nothing to unify */
-    return;
-
-  POOL_DEBUG(SAT_DEBUG_SCHUBI, "----- unifyrules -----\n");
-
-  /* sort rules first */
-  unifyrules_sortcmp_data = solv->pool;
-  qsort(solv->rules + 1, solv->nrules - 1, sizeof(Rule), unifyrules_sortcmp);
-
-  /* prune rules
-   * i = unpruned
-   * j = pruned
-   */
-  jr = 0;
-  for (i = j = 1, ir = solv->rules + i; i < solv->nrules; i++, ir++)
-    {
-      if (jr && !unifyrules_sortcmp(ir, jr))
-       continue;                      /* prune! */
-      jr = solv->rules + j++;         /* keep! */
-      if (ir != jr)
-        *jr = *ir;
-    }
-
-  /* reduced count from nrules to j rules */
-  POOL_DEBUG(SAT_DEBUG_STATS, "pruned rules from %d to %d\n", solv->nrules, j);
-
-  /* adapt rule buffer */
-  solv->nrules = j;
-  solv->rules = sat_extend_resize(solv->rules, solv->nrules, sizeof(Rule), RULES_BLOCK);
-    /*
-     * debug: statistics
-     */
-  IF_POOLDEBUG (SAT_DEBUG_STATS)
-    {
-      int binr = 0;
-      int lits = 0;
-      Id *dp;
-      Rule *r;
-
-      for (i = 1; i < solv->nrules; i++)
-       {
-         r = solv->rules + i;
-         if (r->d == 0)
-           binr++;
-         else
-           {
-             dp = solv->pool->whatprovidesdata + r->d;
-             while (*dp++)
-               lits++;
-           }
-       }
-      POOL_DEBUG(SAT_DEBUG_STATS, "  binary: %d\n", binr);
-      POOL_DEBUG(SAT_DEBUG_STATS, "  normal: %d, %d literals\n", solv->nrules - 1 - binr, lits);
-    }
-  POOL_DEBUG(SAT_DEBUG_SCHUBI, "----- unifyrules end -----\n");
-}
-
-#if 0
-
-/*
- * hash rule
- */
-
-static Hashval
-hashrule(Solver *solv, Id p, Id d, int n)
-{
-  unsigned int x = (unsigned int)p;
-  int *dp;
-
-  if (n <= 1)
-    return (x * 37) ^ (unsigned int)d;
-  dp = solv->pool->whatprovidesdata + d;
-  while (*dp)
-    x = (x * 37) ^ (unsigned int)*dp++;
-  return x;
-}
-#endif
-
-
-/*-------------------------------------------------------------------
- * 
- */
-
-/*
- * add rule
- *  p = direct literal; always < 0 for installed rpm rules
- *  d, if < 0 direct literal, if > 0 offset into whatprovides, if == 0 rule is assertion (look at p only)
- *
- *
- * A requires b, b provided by B1,B2,B3 => (-A|B1|B2|B3)
- *
- * p < 0 : pkg id of A
- * d > 0 : Offset in whatprovidesdata (list of providers of b)
- *
- * A conflicts b, b provided by B1,B2,B3 => (-A|-B1), (-A|-B2), (-A|-B3)
- * p < 0 : pkg id of A
- * d < 0 : Id of solvable (e.g. B1)
- *
- * d == 0: unary rule, assertion => (A) or (-A)
- *
- *   Install:    p > 0, d = 0   (A)             user requested install
- *   Remove:     p < 0, d = 0   (-A)            user requested remove (also: uninstallable)
- *   Requires:   p < 0, d > 0   (-A|B1|B2|...)  d: <list of providers for requirement of p>
- *   Updates:    p > 0, d > 0   (A|B1|B2|...)   d: <list of updates for solvable p>
- *   Conflicts:  p < 0, d < 0   (-A|-B)         either p (conflict issuer) or d (conflict provider) (binary rule)
- *                                              also used for obsoletes
- *   ?:          p > 0, d < 0   (A|-B)          
- *   No-op ?:    p = 0, d = 0   (null)          (used as policy rule placeholder)
- *
- *   resulting watches:
- *   ------------------
- *   Direct assertion (no watch needed)( if d <0 ) --> d = 0, w1 = p, w2 = 0
- *   Binary rule: p = first literal, d = 0, w2 = second literal, w1 = p
- *   every other : w1 = p, w2 = whatprovidesdata[d];
- *   Disabled rule: w1 = 0
- *
- *   always returns a rule for non-rpm rules
- */
-
-static Rule *
-addrule(Solver *solv, Id p, Id d)
-{
-  Pool *pool = solv->pool;
-  Rule *r = 0;
-  Id *dp = 0;
-
-  int n = 0;                          /* number of literals in rule - 1
-                                         0 = direct assertion (single literal)
-                                         1 = binary rule
-                                         >1 = 
-                                       */
-
-  /* it often happenes that requires lead to adding the same rpm rule
-   * multiple times, so we prune those duplicates right away to make
-   * the work for unifyrules a bit easier */
-
-  if (solv->nrules                      /* we already have rules */
-      && !solv->rpmrules_end)           /* but are not done with rpm rules */
-    {
-      r = solv->rules + solv->nrules - 1;   /* get the last added rule */
-      if (r->p == p && r->d == d && d != 0)   /* identical and not user requested */
-       return r;
-    }
-
-    /*
-     * compute number of literals (n) in rule
-     */
-    
-  if (d < 0)
-    {
-      /* always a binary rule */
-      if (p == d)
-       return 0;                      /* ignore self conflict */
-      n = 1;
-    }
-  else if (d > 0)
-    {
-      for (dp = pool->whatprovidesdata + d; *dp; dp++, n++)
-       if (*dp == -p)
-         return 0;                     /* rule is self-fulfilling */
-       
-      if (n == 1)   /* have single provider */
-       d = dp[-1];                     /* take single literal */
-    }
-
-  if (n == 1 && p > d && !solv->rpmrules_end)
-    {
-      /* smallest literal first so we can find dups */
-      n = p; p = d; d = n;             /* p <-> d */
-      n = 1;                          /* re-set n, was used as temp var */
-    }
-
-    /*
-     * check for duplicate
-     */
-    
-  /* check if the last added rule (r) is exactly the same as what we're looking for. */
-  if (r && n == 1 && !r->d && r->p == p && r->w2 == d)
-    return r;  /* binary rule */
-
-    /* have n-ary rule with same first literal, check other literals */
-  if (r && n > 1 && r->d && r->p == p)
-    {
-      /* Rule where d is an offset in whatprovidesdata */
-      Id *dp2;
-      if (d == r->d)
-       return r;
-      dp2 = pool->whatprovidesdata + r->d;
-      for (dp = pool->whatprovidesdata + d; *dp; dp++, dp2++)
-       if (*dp != *dp2)
-         break;
-      if (*dp == *dp2)
-       return r;
-   }
-
-    /*
-     * allocate new rule
-     */
-
-  /* extend rule buffer */
-  solv->rules = sat_extend(solv->rules, solv->nrules, 1, sizeof(Rule), RULES_BLOCK);
-  r = solv->rules + solv->nrules++;    /* point to rule space */
-
-    /*
-     * r = new rule
-     */
-    
-  r->p = p;
-  if (n == 0)
-    {
-      /* direct assertion, no watch needed */
-      r->d = 0;
-      r->w1 = p;
-      r->w2 = 0;
-    }
-  else if (n == 1)
-    {
-      /* binary rule */
-      r->d = 0;
-      r->w1 = p;
-      r->w2 = d;
-    }
-  else
-    {
-      r->d = d;
-      r->w1 = p;
-      r->w2 = pool->whatprovidesdata[d];
-    }
-  r->n1 = 0;
-  r->n2 = 0;
-
-  IF_POOLDEBUG (SAT_DEBUG_RULE_CREATION)
-    {
-      POOL_DEBUG(SAT_DEBUG_RULE_CREATION, "  Add rule: ");
-      solver_printrule(solv, SAT_DEBUG_RULE_CREATION, r);
-    }
-
-  return r;
-}
-
-/*-------------------------------------------------------------------
- * disable rule
- */
-
-static inline void
-disablerule(Solver *solv, Rule *r)
-{
-  if (r->d >= 0)
-    r->d = -r->d - 1;
-}
-
-/*-------------------------------------------------------------------
- * enable rule
- */
-
-static inline void
-enablerule(Solver *solv, Rule *r)
-{
-  if (r->d < 0)
-    r->d = -r->d - 1;
-}
-
-
-/**********************************************************************************/
-
-/* a problem is an item on the solver's problem list. It can either be >0, in that
- * case it is a update rule, or it can be <0, which makes it refer to a job
- * consisting of multiple job rules.
- */
-
-static void
-disableproblem(Solver *solv, Id v)
-{
-  Rule *r;
-  int i;
-  Id *jp;
-
-  if (v > 0)
-    {
-      disablerule(solv, solv->rules + v);
-      return;
-    }
-  v = -(v + 1);
-  jp = solv->ruletojob.elements;
-  for (i = solv->jobrules, r = solv->rules + i; i < solv->jobrules_end; i++, r++, jp++)
-    if (*jp == v)
-      disablerule(solv, r);
-}
-
-/*-------------------------------------------------------------------
- * enableproblem
- */
-
-static void
-enableproblem(Solver *solv, Id v)
-{
-  Rule *r;
-  int i;
-  Id *jp;
-
-  if (v > 0)
-    {
-      if (v >= solv->featurerules && v < solv->featurerules_end)
-       {
-         /* do not enable feature rule if update rule is enabled */
-         r = solv->rules + (v - solv->featurerules + solv->updaterules);
-         if (r->d >= 0)
-           return;
-       }
-      enablerule(solv, solv->rules + v);
-      if (v >= solv->updaterules && v < solv->updaterules_end)
-       {
-         /* disable feature rule when enabling update rule */
-         r = solv->rules + (v - solv->updaterules + solv->featurerules);
-         if (r->p)
-           disablerule(solv, r);
-       }
-      return;
-    }
-  v = -(v + 1);
-  jp = solv->ruletojob.elements;
-  for (i = solv->jobrules, r = solv->rules + i; i < solv->jobrules_end; i++, r++, jp++)
-    if (*jp == v)
-      enablerule(solv, r);
-}
-
 
 /************************************************************************/
 
 /*
  * make assertion rules into decisions
  * 
- * go through update and job rules and add direct assertions
- * to the decisionqueue. If we find a conflict, disable rules and
- * add them to problem queue.
+ * Go through rules and add direct assertions to the decisionqueue.
+ * If we find a conflict, disable rules and add them to problem queue.
  */
 
 static void
@@ -547,6 +115,12 @@ makeruledecisions(Solver *solv)
 
   POOL_DEBUG(SAT_DEBUG_SCHUBI, "----- makeruledecisions ; size decisionq: %d -----\n",solv->decisionq.count);
 
+  /* The system solvable is always installed first */
+  assert(solv->decisionq.count == 0);
+  queue_push(&solv->decisionq, SYSTEMSOLVABLE);
+  queue_push(&solv->decisionq_why, 0);
+  solv->decisionmap[SYSTEMSOLVABLE] = 1;       /* installed at level '1' */
+
   decisionstart = solv->decisionq.count;
   for (ii = 0; ii < solv->ruleassertions.count; ii++)
     {
@@ -603,7 +177,7 @@ makeruledecisions(Solver *solv)
          /* can happen when packages cannot be installed for
            * multiple reasons. */
           /* we disable the learnt rule in this case */
-         disablerule(solv, r);
+         solver_disablerule(solv, r);
          continue;
        }
        
@@ -616,9 +190,9 @@ makeruledecisions(Solver *solv)
          break;
       assert(i < solv->decisionq.count);         /* assert that we found it */
        
-       /*
-        * conflict with system solvable ?
-        */
+      /*
+       * conflict with system solvable ?
+       */
        
       if (v == -SYSTEMSOLVABLE) {
        /* conflict with system solvable */
@@ -632,15 +206,15 @@ makeruledecisions(Solver *solv)
          v = ri;
        queue_push(&solv->problems, v);
        queue_push(&solv->problems, 0);
-       disableproblem(solv, v);
+       solver_disableproblem(solv, v);
        continue;
       }
 
       assert(solv->decisionq_why.elements[i] > 0);
        
-        /*
-        * conflict with an rpm rule ?
-        */
+      /*
+       * conflict with an rpm rule ?
+       */
        
       if (solv->decisionq_why.elements[i] < solv->rpmrules_end)
        {
@@ -657,13 +231,13 @@ makeruledecisions(Solver *solv)
            v = ri;
          queue_push(&solv->problems, v);
          queue_push(&solv->problems, 0);
-         disableproblem(solv, v);
+         solver_disableproblem(solv, v);
          continue;
        }
 
-        /*
-        * conflict with another job or update/feature rule
-        */
+      /*
+       * conflict with another job or update/feature rule
+       */
        
       /* record proof */
       queue_push(&solv->problems, solv->learnt_pool.count);
@@ -699,14 +273,14 @@ makeruledecisions(Solver *solv)
            v = -(solv->ruletojob.elements[i - solv->jobrules] + 1);
            
          queue_push(&solv->problems, v);
-         disableproblem(solv, v);
+         solver_disableproblem(solv, v);
        }
       queue_push(&solv->problems, 0);
 
-       /*
-       * start over
-       * (back up from decisions)
-       */
+      /*
+       * start over
+       * (back up from decisions)
+       */
       while (solv->decisionq.count > decisionstart)
        {
          v = solv->decisionq.elements[--solv->decisionq.count];
@@ -717,23 +291,23 @@ makeruledecisions(Solver *solv)
       ii = -1; /* restarts loop at 0 */
     }
 
-    /*
-     * phase 2: now do the weak assertions
-     */
+  /*
+   * phase 2: now do the weak assertions
+   */
   for (ii = 0; ii < solv->ruleassertions.count; ii++)
     {
       ri = solv->ruleassertions.elements[ii];
       r = solv->rules + ri;
       if (r->d < 0 || r->w2)                    /* disabled or no assertion */
        continue;
-      if (!MAPTST(&solv->weakrulemap, ri))       /* skip non-weak */
+      if (ri >= solv->learntrules || !MAPTST(&solv->weakrulemap, ri))       /* skip non-weak */
        continue;
       v = r->p;
       vv = v > 0 ? v : -v;
-       /*
-        * decide !
-        * (if not yet decided)
-        */
+      /*
+       * decide !
+       * (if not yet decided)
+       */
       if (!solv->decisionmap[vv])
        {
          queue_push(&solv->decisionq, v);
@@ -749,9 +323,9 @@ makeruledecisions(Solver *solv)
            }
          continue;
        }
-       /*
-        * previously decided, sane ?
-        */
+      /*
+       * previously decided, sane ?
+       */
       if (v > 0 && solv->decisionmap[vv] > 0)
        continue;
       if (v < 0 && solv->decisionmap[vv] < 0)
@@ -764,9 +338,9 @@ makeruledecisions(Solver *solv)
        v = -(solv->ruletojob.elements[ri - solv->jobrules] + 1);
       else
        v = ri;
-      disableproblem(solv, v);
+      solver_disableproblem(solv, v);
       if (v < 0)
-       disableupdaterules(solv, solv->job, -(v + 1));
+       solver_reenablepolicyrules(solv, -(v + 1));
     }
   
   POOL_DEBUG(SAT_DEBUG_SCHUBI, "----- makeruledecisions end; size decisionq: %d -----\n",solv->decisionq.count);
@@ -777,7 +351,7 @@ makeruledecisions(Solver *solv)
  * enable/disable learnt rules 
  *
  * we have enabled or disabled some of our rules. We now reenable all
- * of our learnt rules but the ones that were learnt from rules that
+ * of our learnt rules except the ones that were learnt from rules that
  * are now disabled.
  */
 static void
@@ -806,7 +380,7 @@ enabledisablelearntrules(Solver *solv)
              POOL_DEBUG(SAT_DEBUG_SOLUTIONS, "disabling ");
              solver_printruleclass(solv, SAT_DEBUG_SOLUTIONS, r);
            }
-          disablerule(solv, r);
+          solver_disablerule(solv, r);
        }
       else if (!why && r->d < 0)
        {
@@ -815,889 +389,157 @@ enabledisablelearntrules(Solver *solv)
              POOL_DEBUG(SAT_DEBUG_SOLUTIONS, "re-enabling ");
              solver_printruleclass(solv, SAT_DEBUG_SOLUTIONS, r);
            }
-          enablerule(solv, r);
+          solver_enablerule(solv, r);
        }
     }
 }
 
 
+/********************************************************************/
+/* watches */
+
+
 /*-------------------------------------------------------------------
- * enable weak rules
- * 
- * Enable all rules, except learnt rules, which are
- * - disabled and weak (set in weakrulemap)
- * 
+ * makewatches
+ *
+ * initial setup for all watches
  */
 
 static void
-enableweakrules(Solver *solv)
+makewatches(Solver *solv)
 {
-  int i;
   Rule *r;
+  int i;
+  int nsolvables = solv->pool->nsolvables;
 
-  for (i = 1, r = solv->rules + i; i < solv->learntrules; i++, r++)
-    {
-      if (r->d >= 0) /* skip non-direct literals */
-       continue;
-      if (!MAPTST(&solv->weakrulemap, i))
-       continue;
-      enablerule(solv, r);
-    }
-}
-
-
-/* FIXME: bad code ahead, replace as soon as possible */
-/* FIXME: should probably look at SOLVER_INSTALL|SOLVABLE_ONE_OF */
-
-/*-------------------------------------------------------------------
- * disable update rules
- */
-
-static void
-disableupdaterules(Solver *solv, Queue *job, int jobidx)
-{
-  Pool *pool = solv->pool;
-  int i, j;
-  Id how, select, what, p, pp;
-  Solvable *s;
-  Repo *installed;
-  Rule *r;
-  Id lastjob = -1;
-
-  installed = solv->installed;
-  if (!installed)
-    return;
-
-  if (jobidx != -1)
-    {
-      how = job->elements[jobidx];
-      select = how & SOLVER_SELECTMASK;
-      switch (how & SOLVER_JOBMASK)
-       {
-       case SOLVER_ERASE:
-         break;
-       case SOLVER_INSTALL:
-         if (select != SOLVER_SOLVABLE)
-           return;
-         break;
-       default:
-         return;
-       }
-    }
-  /* go through all enabled job rules */
-  MAPZERO(&solv->noupdate);
-  for (i = solv->jobrules; i < solv->jobrules_end; i++)
+  sat_free(solv->watches);
+                                      /* lower half for removals, upper half for installs */
+  solv->watches = sat_calloc(2 * nsolvables, sizeof(Id));
+#if 1
+  /* do it reverse so rpm rules get triggered first (XXX: obsolete?) */
+  for (i = 1, r = solv->rules + solv->nrules - 1; i < solv->nrules; i++, r--)
+#else
+  for (i = 1, r = solv->rules + 1; i < solv->nrules; i++, r++)
+#endif
     {
-      r = solv->rules + i;
-      if (r->d < 0)    /* disabled? */
-       continue;
-      j = solv->ruletojob.elements[i - solv->jobrules];
-      if (j == lastjob)
+      if (!r->w2)              /* assertions do not need watches */
        continue;
-      lastjob = j;
-      how = job->elements[j];
-      what = job->elements[j + 1];
-      select = how & SOLVER_SELECTMASK;
-      switch (how & SOLVER_JOBMASK)
-       {
-       case SOLVER_INSTALL:
-         if (select != SOLVER_SOLVABLE)
-           break;
-         s = pool->solvables + what;
-         if (solv->noobsoletes.size && MAPTST(&solv->noobsoletes, what))
-           break;
-         if (s->repo == installed)
-           {
-             MAPSET(&solv->noupdate, what - installed->start);
-             break;
-           }
-         if (s->obsoletes)
-           {
-             Id obs, *obsp;
-             obsp = s->repo->idarraydata + s->obsoletes;
-             while ((obs = *obsp++) != 0)
-               FOR_PROVIDES(p, pp, obs)
-                 {
-                   if (pool->solvables[p].repo != installed)
-                     continue;
-                   if (!solv->obsoleteusesprovides && !pool_match_nevr(pool, pool->solvables + p, obs))
-                     continue;
-                   MAPSET(&solv->noupdate, p - installed->start);
-                 }
-           }
-         FOR_PROVIDES(p, pp, s->name)
-           {
-             if (!solv->implicitobsoleteusesprovides && pool->solvables[p].name != s->name)
-               continue;
-             if (pool->solvables[p].repo == installed)
-               MAPSET(&solv->noupdate, p - installed->start);
-           }
-         break;
-       case SOLVER_ERASE:
-         FOR_JOB_SELECT(p, pp, select, what)
-           if (pool->solvables[p].repo == installed)
-             MAPSET(&solv->noupdate, p - installed->start);
-         break;
-       default:
-         break;
-       }
-    }
 
-  /* fixup update rule status */
-  if (jobidx != -1)
-    {
-      /* we just disabled job #jobidx. enable all update rules
-       * that aren't disabled by the remaining job rules */
-      how = job->elements[jobidx];
-      what = job->elements[jobidx + 1];
-      select = how & SOLVER_SELECTMASK;
-      switch (how & SOLVER_JOBMASK)
-       {
-       case SOLVER_INSTALL:
-         if (select != SOLVER_SOLVABLE)
-           break;
-         s = pool->solvables + what;
-         if (s->repo == installed)
-           {
-             if (MAPTST(&solv->noupdate, what - installed->start))
-               break;
-             r = solv->rules + solv->updaterules + (what - installed->start);
-             if (r->d >= 0)
-               break;
-             enablerule(solv, r);
-             IF_POOLDEBUG (SAT_DEBUG_SOLUTIONS)
-               {
-                 POOL_DEBUG(SAT_DEBUG_SOLUTIONS, "@@@ re-enabling ");
-                 solver_printrule(solv, SAT_DEBUG_SOLUTIONS, r);
-               }
-             break;
-           }
-         if (s->obsoletes)
-           {
-             Id obs, *obsp;
-             obsp = s->repo->idarraydata + s->obsoletes;
-             while ((obs = *obsp++) != 0)
-               FOR_PROVIDES(p, pp, obs)
-                 {
-                   if (pool->solvables[p].repo != installed)
-                     continue;
-                   if (!solv->obsoleteusesprovides && !pool_match_nevr(pool, pool->solvables + p, obs))
-                     continue;
-                   if (MAPTST(&solv->noupdate, p - installed->start))
-                     continue;
-                   r = solv->rules + solv->updaterules + (p - installed->start);
-                   if (r->d >= 0)
-                     continue;
-                   enablerule(solv, r);
-                   IF_POOLDEBUG (SAT_DEBUG_SOLUTIONS)
-                     {
-                       POOL_DEBUG(SAT_DEBUG_SOLUTIONS, "@@@ re-enabling ");
-                       solver_printrule(solv, SAT_DEBUG_SOLUTIONS, r);
-                     }
-                 }
-           }
-         FOR_PROVIDES(p, pp, s->name)
-           {
-             if (!solv->implicitobsoleteusesprovides && pool->solvables[p].name != s->name)
-               continue;
-             if (pool->solvables[p].repo != installed)
-               continue;
-             if (MAPTST(&solv->noupdate, p - installed->start))
-               continue;
-             r = solv->rules + solv->updaterules + (p - installed->start);
-             if (r->d >= 0)
-               continue;
-             enablerule(solv, r);
-             IF_POOLDEBUG (SAT_DEBUG_SOLUTIONS)
-               {
-                 POOL_DEBUG(SAT_DEBUG_SOLUTIONS, "@@@ re-enabling ");
-                 solver_printrule(solv, SAT_DEBUG_SOLUTIONS, r);
-               }
-           }
-         break;
-       case SOLVER_ERASE:
-         FOR_JOB_SELECT(p, pp, select, what)
-           {
-             if (pool->solvables[p].repo != installed)
-               continue;
-             if (MAPTST(&solv->noupdate, p - installed->start))
-               continue;
-             r = solv->rules + solv->updaterules + (p - installed->start);
-             if (r->d >= 0)
-               continue;
-             enablerule(solv, r);
-             IF_POOLDEBUG (SAT_DEBUG_SOLUTIONS)
-               {
-                 POOL_DEBUG(SAT_DEBUG_SOLUTIONS, "@@@ re-enabling ");
-                 solver_printrule(solv, SAT_DEBUG_SOLUTIONS, r);
-               }
-           }
-         break;
-       default:
-         break;
-       }
-      return;
-    }
+      /* see addwatches_rule(solv, r) */
+      r->n1 = solv->watches[nsolvables + r->w1];
+      solv->watches[nsolvables + r->w1] = r - solv->rules;
 
-  for (i = 0; i < installed->nsolvables; i++)
-    {
-      r = solv->rules + solv->updaterules + i;
-      if (r->d >= 0 && MAPTST(&solv->noupdate, i))
-        disablerule(solv, r);  /* was enabled, need to disable */
-      r = solv->rules + solv->featurerules + i;
-      if (r->d >= 0 && MAPTST(&solv->noupdate, i))
-        disablerule(solv, r);  /* was enabled, need to disable */
+      r->n2 = solv->watches[nsolvables + r->w2];
+      solv->watches[nsolvables + r->w2] = r - solv->rules;
     }
 }
 
 
-/*
- *  special multiversion patch conflict handling:
- *  a patch conflict is also satisfied, if some other
- *  version with the same name/arch that doesn't conflict
- *  get's installed. The generated rule is thus:
- *  -patch|-cpack|opack1|opack2|...
+/*-------------------------------------------------------------------
+ *
+ * add watches (for a new learned rule)
+ * sets up watches for a single rule
+ * 
+ * see also makewatches() above.
  */
-Id
-makemultiversionconflict(Solver *solv, Id n, Id con)
+
+static inline void
+addwatches_rule(Solver *solv, Rule *r)
 {
-  Pool *pool = solv->pool;
-  Solvable *s, *sn;
-  Queue q;
-  Id p, pp, qbuf[64];
+  int nsolvables = solv->pool->nsolvables;
 
-  sn = pool->solvables + n;
-  queue_init_buffer(&q, qbuf, sizeof(qbuf)/sizeof(*qbuf));
-  queue_push(&q, -n);
-  FOR_PROVIDES(p, pp, sn->name)
-    {
-      s = pool->solvables + p;
-      if (s->name != sn->name || s->arch != sn->arch)
-       continue;
-      if (!MAPTST(&solv->noobsoletes, p))
-       continue;
-      if (pool_match_nevr(pool, pool->solvables + p, con))
-       continue;
-      /* here we have a multiversion solvable that doesn't conflict */
-      /* thus we're not in conflict if it is installed */
-      queue_push(&q, p);
-    }
-  if (q.count == 1)
-    return -n; /* no other package found, generate normal conflict */
-  return pool_queuetowhatprovides(pool, &q);
+  r->n1 = solv->watches[nsolvables + r->w1];
+  solv->watches[nsolvables + r->w1] = r - solv->rules;
+
+  r->n2 = solv->watches[nsolvables + r->w2];
+  solv->watches[nsolvables + r->w2] = r - solv->rules;
 }
 
 
+/********************************************************************/
+/*
+ * rule propagation
+ */
+
+
+/* shortcuts to check if a literal (positive or negative) assignment
+ * evaluates to 'true' or 'false'
+ */
+#define DECISIONMAP_TRUE(p) ((p) > 0 ? (decisionmap[p] > 0) : (decisionmap[-p] < 0))
+#define DECISIONMAP_FALSE(p) ((p) > 0 ? (decisionmap[p] < 0) : (decisionmap[-p] > 0))
+#define DECISIONMAP_UNDEF(p) (decisionmap[(p) > 0 ? (p) : -(p)] == 0)
+
 /*-------------------------------------------------------------------
  * 
- * add (install) rules for solvable
- * 
- * s: Solvable for which to add rules
- * m: m[s] = 1 for solvables which have rules, prevent rule duplication
+ * propagate
+ *
+ * make decision and propagate to all rules
  * 
- * Algorithm: 'visit all nodes of a graph'. The graph nodes are
- *  solvables, the edges their dependencies.
- *  Starting from an installed solvable, this will create all rules
- *  representing the graph created by the solvables dependencies.
+ * Evaluate each term affected by the decision (linked through watches)
+ * If we find unit rules we make new decisions based on them
  * 
- * for unfulfilled requirements, conflicts, obsoletes,....
- * add a negative assertion for solvables that are not installable
+ * Everything's fixed there, it's just finding rules that are
+ * unit.
  * 
- * It will also create rules for all solvables referenced by 's'
- *  i.e. descend to all providers of requirements of 's'
- *
+ * return : 0 = everything is OK
+ *          rule = conflict found in this rule
  */
 
-static void
-addrpmrulesforsolvable(Solver *solv, Solvable *s, Map *m)
+static Rule *
+propagate(Solver *solv, int level)
 {
   Pool *pool = solv->pool;
-  Repo *installed = solv->installed;
-
-  /* 'work' queue. keeps Ids of solvables we still have to work on.
-     And buffer for it. */
-  Queue workq;
-  Id workqbuf[64];
-    
-  int i;
-    /* if to add rules for broken deps ('rpm -V' functionality)
-     * 0 = yes, 1 = no
-     */
-  int dontfix;
-    /* Id var and pointer for each dependency
-     * (not used in parallel)
-     */
-  Id req, *reqp;
-  Id con, *conp;
-  Id obs, *obsp;
-  Id rec, *recp;
-  Id sug, *sugp;
-    /* var and ptr for loops */
-  Id p, pp;
-    /* ptr to 'whatprovides' */
+  Id *rp, *next_rp;           /* rule pointer, next rule pointer in linked list */
+  Rule *r;                    /* rule */
+  Id p, pkg, other_watch;
   Id *dp;
-    /* Id for current solvable 's' */
-  Id n;
-
-  POOL_DEBUG(SAT_DEBUG_SCHUBI, "----- addrpmrulesforsolvable -----\n");
+  Id *decisionmap = solv->decisionmap;
+    
+  Id *watches = solv->watches + pool->nsolvables;   /* place ptr in middle */
 
-  queue_init_buffer(&workq, workqbuf, sizeof(workqbuf)/sizeof(*workqbuf));
-  queue_push(&workq, s - pool->solvables);     /* push solvable Id to work queue */
+  POOL_DEBUG(SAT_DEBUG_PROPAGATE, "----- propagate -----\n");
 
-  /* loop until there's no more work left */
-  while (workq.count)
+  /* foreach non-propagated decision */
+  while (solv->propagate_index < solv->decisionq.count)
     {
-      /*
-       * n: Id of solvable
-       * s: Pointer to solvable
-       */
-
-      n = queue_shift(&workq);             /* 'pop' next solvable to work on from queue */
-      if (MAPTST(m, n))                           /* continue if already visited */
-       continue;
-
-      MAPSET(m, n);                        /* mark as visited */
-      s = pool->solvables + n;            /* s = Solvable in question */
-
-      dontfix = 0;
-      if (installed                       /* Installed system available */
-         && !solv->fixsystem              /* NOT repair errors in rpm dependency graph */
-         && s->repo == installed)         /* solvable is installed? */
-      {
-       dontfix = 1;                       /* dont care about broken rpm deps */
-      }
-
-      if (!dontfix
-         && s->arch != ARCH_SRC
-         && s->arch != ARCH_NOSRC
-         && !pool_installable(pool, s))
-       {
-         POOL_DEBUG(SAT_DEBUG_RULE_CREATION, "package %s [%d] is not installable\n", solvable2str(pool, s), (Id)(s - pool->solvables));
-         addrule(solv, -n, 0);            /* uninstallable */
-       }
-
-      /*-----------------------------------------
-       * check requires of s
-       */
+       /*
+        * 'pkg' was just decided
+        * negate because our watches trigger if literal goes FALSE
+        */
+      pkg = -solv->decisionq.elements[solv->propagate_index++];
+       
+      IF_POOLDEBUG (SAT_DEBUG_PROPAGATE)
+        {
+         POOL_DEBUG(SAT_DEBUG_PROPAGATE, "propagate for decision %d level %d\n", -pkg, level);
+         solver_printruleelement(solv, SAT_DEBUG_PROPAGATE, 0, -pkg);
+        }
 
-      if (s->requires)
+      /* foreach rule where 'pkg' is now FALSE */
+      for (rp = watches + pkg; *rp; rp = next_rp)
        {
-         reqp = s->repo->idarraydata + s->requires;
-         while ((req = *reqp++) != 0)            /* go through all requires */
+         r = solv->rules + *rp;
+         if (r->d < 0)
            {
-             if (req == SOLVABLE_PREREQMARKER)   /* skip the marker */
-               continue;
-
-             /* find list of solvables providing 'req' */
-             dp = pool->whatprovidesdata + pool_whatprovides(pool, req);
-
-             if (*dp == SYSTEMSOLVABLE)          /* always installed */
-               continue;
-
-             if (dontfix)
-               {
-                 /* the strategy here is to not insist on dependencies
-                   * that are already broken. so if we find one provider
-                   * that was already installed, we know that the
-                   * dependency was not broken before so we enforce it */
-                
-                 /* check if any of the providers for 'req' is installed */
-                 for (i = 0; (p = dp[i]) != 0; i++)
-                   {
-                     if (pool->solvables[p].repo == installed)
-                       break;          /* provider was installed */
-                   }
-                 /* didn't find an installed provider: previously broken dependency */
-                 if (!p)
-                   {
-                     POOL_DEBUG(SAT_DEBUG_RULE_CREATION, "ignoring broken requires %s of installed package %s\n", dep2str(pool, req), solvable2str(pool, s));
-                     continue;
-                   }
-               }
-
-             if (!*dp)
-               {
-                 /* nothing provides req! */
-                 POOL_DEBUG(SAT_DEBUG_RULE_CREATION, "package %s [%d] is not installable (%s)\n", solvable2str(pool, s), (Id)(s - pool->solvables), dep2str(pool, req));
-                 addrule(solv, -n, 0); /* mark requestor as uninstallable */
-                 continue;
-               }
-
-             IF_POOLDEBUG (SAT_DEBUG_RULE_CREATION)
-               {
-                 POOL_DEBUG(SAT_DEBUG_RULE_CREATION,"  %s requires %s\n", solvable2str(pool, s), dep2str(pool, req));
-                 for (i = 0; dp[i]; i++)
-                   POOL_DEBUG(SAT_DEBUG_RULE_CREATION, "   provided by %s\n", solvable2str(pool, pool->solvables + dp[i]));
-               }
-
-             /* add 'requires' dependency */
-              /* rule: (-requestor|provider1|provider2|...|providerN) */
-             addrule(solv, -n, dp - pool->whatprovidesdata);
-
-             /* descend the dependency tree
-                push all non-visited providers on the work queue */
-             for (; *dp; dp++)
-               {
-                 if (!MAPTST(m, *dp))
-                   queue_push(&workq, *dp);
-               }
-
-           } /* while, requirements of n */
-
-       } /* if, requirements */
-
-      /* that's all we check for src packages */
-      if (s->arch == ARCH_SRC || s->arch == ARCH_NOSRC)
-       continue;
-
-      /*-----------------------------------------
-       * check conflicts of s
-       */
-
-      if (s->conflicts)
-       {
-         int ispatch = 0;
+             /* rule is disabled, goto next */
+             if (pkg == r->w1)
+               next_rp = &r->n1;
+             else
+               next_rp = &r->n2;
+             continue;
+           }
 
-         /* we treat conflicts in patches a bit differen:
-          * - nevr matching
-          * - multiversion handling
-          * XXX: we should really handle this different, looking
-          * at the name is a bad hack
-          */
-         if (!strncmp("patch:", id2str(pool, s->name), 6))
-           ispatch = 1;
-         conp = s->repo->idarraydata + s->conflicts;
-         /* foreach conflicts of 's' */
-         while ((con = *conp++) != 0)
+         IF_POOLDEBUG (SAT_DEBUG_PROPAGATE)
            {
-             /* foreach providers of a conflict of 's' */
-             FOR_PROVIDES(p, pp, con)
-               {
-                 if (ispatch && !pool_match_nevr(pool, pool->solvables + p, con))
-                   continue;
-                 /* dontfix: dont care about conflicts with already installed packs */
-                 if (dontfix && pool->solvables[p].repo == installed)
-                   continue;
-                 /* p == n: self conflict */
-                 if (p == n && !solv->allowselfconflicts)
-                   {
-                     if (ISRELDEP(con))
-                       {
-                         Reldep *rd = GETRELDEP(pool, con);
-                         if (rd->flags == REL_NAMESPACE && rd->name == NAMESPACE_OTHERPROVIDERS)
-                           continue;
-                       }
-                     p = 0;    /* make it a negative assertion, aka 'uninstallable' */
-                   }
-                 if (p && ispatch && solv->noobsoletes.size && MAPTST(&solv->noobsoletes, p) && ISRELDEP(con))
-                   {
-                     /* our patch conflicts with a noobsoletes (aka multiversion) package */
-                     p = -makemultiversionconflict(solv, p, con);
-                   }
-                 /* rule: -n|-p: either solvable _or_ provider of conflict */
-                 addrule(solv, -n, -p);
-               }
+             POOL_DEBUG(SAT_DEBUG_PROPAGATE,"  watch triggered ");
+             solver_printrule(solv, SAT_DEBUG_PROPAGATE, r);
            }
-       }
 
-      /*-----------------------------------------
-       * check obsoletes if not installed
-       * (only installation will trigger the obsoletes in rpm)
-       */
-      if (!installed || pool->solvables[n].repo != installed)
-       {                              /* not installed */
-         int noobs = solv->noobsoletes.size && MAPTST(&solv->noobsoletes, n);
-         if (s->obsoletes && !noobs)
-           {
-             obsp = s->repo->idarraydata + s->obsoletes;
-             /* foreach obsoletes */
-             while ((obs = *obsp++) != 0)
-               {
-                 /* foreach provider of an obsoletes of 's' */ 
-                 FOR_PROVIDES(p, pp, obs)
-                   {
-                     if (!solv->obsoleteusesprovides /* obsoletes are matched names, not provides */
-                         && !pool_match_nevr(pool, pool->solvables + p, obs))
-                       continue;
-                     addrule(solv, -n, -p);
-                   }
-               }
-           }
-         FOR_PROVIDES(p, pp, s->name)
-           {
-             Solvable *ps = pool->solvables + p;
-             /* we still obsolete packages with same nevra, like rpm does */
-             /* (actually, rpm mixes those packages. yuck...) */
-             if (noobs && (s->name != ps->name || s->evr != ps->evr || s->arch != ps->arch))
-               continue;
-             if (!solv->implicitobsoleteusesprovides && s->name != ps->name)
-               continue;
-             addrule(solv, -n, -p);
-           }
-       }
-
-      /*-----------------------------------------
-       * add recommends to the work queue
-       */
-      if (s->recommends)
-       {
-         recp = s->repo->idarraydata + s->recommends;
-         while ((rec = *recp++) != 0)
-           {
-             FOR_PROVIDES(p, pp, rec)
-               if (!MAPTST(m, p))
-                 queue_push(&workq, p);
-           }
-       }
-      if (s->suggests)
-       {
-         sugp = s->repo->idarraydata + s->suggests;
-         while ((sug = *sugp++) != 0)
-           {
-             FOR_PROVIDES(p, pp, sug)
-               if (!MAPTST(m, p))
-                 queue_push(&workq, p);
-           }
-       }
-    }
-  queue_free(&workq);
-  POOL_DEBUG(SAT_DEBUG_SCHUBI, "----- addrpmrulesforsolvable end -----\n");
-}
-
-
-/*-------------------------------------------------------------------
- * 
- * Add package rules for weak rules
- *
- * m: visited solvables
- */
-
-static void
-addrpmrulesforweak(Solver *solv, Map *m)
-{
-  Pool *pool = solv->pool;
-  Solvable *s;
-  Id sup, *supp;
-  int i, n;
-
-  POOL_DEBUG(SAT_DEBUG_SCHUBI, "----- addrpmrulesforweak -----\n");
-    /* foreach solvable in pool */
-  for (i = n = 1; n < pool->nsolvables; i++, n++)
-    {
-      if (i == pool->nsolvables)                 /* wrap i */
-       i = 1;
-      if (MAPTST(m, i))                          /* been there */
-       continue;
-
-      s = pool->solvables + i;
-      if (!pool_installable(pool, s))            /* only look at installable ones */
-       continue;
-
-      sup = 0;
-      if (s->supplements)
-       {
-         /* find possible supplements */
-         supp = s->repo->idarraydata + s->supplements;
-         while ((sup = *supp++) != ID_NULL)
-           if (dep_possible(solv, sup, m))
-             break;
-       }
-
-       /* if nothing found, check for enhances */
-      if (!sup && s->enhances)
-       {
-         supp = s->repo->idarraydata + s->enhances;
-         while ((sup = *supp++) != ID_NULL)
-           if (dep_possible(solv, sup, m))
-             break;
-       }
-       /* if nothing found, goto next solvables */
-      if (!sup)
-       continue;
-      addrpmrulesforsolvable(solv, s, m);
-      n = 0;
-    }
-  POOL_DEBUG(SAT_DEBUG_SCHUBI, "----- addrpmrulesforweak end -----\n");
-}
-
-
-/*-------------------------------------------------------------------
- * 
- * add package rules for possible updates
- * 
- * s: solvable
- * m: map of already visited solvables
- * allow_all: 0 = dont allow downgrades, 1 = allow all candidates
- */
-
-static void
-addrpmrulesforupdaters(Solver *solv, Solvable *s, Map *m, int allow_all)
-{
-  Pool *pool = solv->pool;
-  int i;
-    /* queue and buffer for it */
-  Queue qs;
-  Id qsbuf[64];
-
-  POOL_DEBUG(SAT_DEBUG_SCHUBI, "----- addrpmrulesforupdaters -----\n");
-
-  queue_init_buffer(&qs, qsbuf, sizeof(qsbuf)/sizeof(*qsbuf));
-    /* find update candidates for 's' */
-  policy_findupdatepackages(solv, s, &qs, allow_all);
-    /* add rule for 's' if not already done */
-  if (!MAPTST(m, s - pool->solvables))
-    addrpmrulesforsolvable(solv, s, m);
-    /* foreach update candidate, add rule if not already done */
-  for (i = 0; i < qs.count; i++)
-    if (!MAPTST(m, qs.elements[i]))
-      addrpmrulesforsolvable(solv, pool->solvables + qs.elements[i], m);
-  queue_free(&qs);
-
-  POOL_DEBUG(SAT_DEBUG_SCHUBI, "----- addrpmrulesforupdaters -----\n");
-}
-
-static Id
-finddistupgradepackages(Solver *solv, Solvable *s, Queue *qs, int allow_all)
-{
-  Pool *pool = solv->pool;
-  int i;
-
-  policy_findupdatepackages(solv, s, qs, allow_all);
-  if (!qs->count)
-    {
-      if (allow_all)
-        return 0;
-      policy_findupdatepackages(solv, s, qs, 1);
-      if (!qs->count)
-       return 0;       /* orphaned */
-      qs->count = 0;
-      return -SYSTEMSOLVABLE;
-    }
-  if (allow_all)
-    return s - pool->solvables;
-  /* check if it is ok to keep the installed package */
-  for (i = 0; i < qs->count; i++)
-    {
-      Solvable *ns = pool->solvables + qs->elements[i];
-      if (s->evr == ns->evr && solvable_identical(s, ns))
-        return s - pool->solvables;
-    }
-  /* nope, it must be some other package */
-  return -SYSTEMSOLVABLE;
-}
-
-/*-------------------------------------------------------------------
- * 
- * add rule for update
- *   (A|A1|A2|A3...)  An = update candidates for A
- *
- * s = (installed) solvable
- */
-
-static void
-addupdaterule(Solver *solv, Solvable *s, int allow_all)
-{
-  /* installed packages get a special upgrade allowed rule */
-  Pool *pool = solv->pool;
-  Id p, d;
-  Queue qs;
-  Id qsbuf[64];
-
-  POOL_DEBUG(SAT_DEBUG_SCHUBI, "-----  addupdaterule -----\n");
-  queue_init_buffer(&qs, qsbuf, sizeof(qsbuf)/sizeof(*qsbuf));
-  p = s - pool->solvables;
-  /* find update candidates for 's' */
-  if (solv->distupgrade)
-    p = finddistupgradepackages(solv, s, &qs, allow_all);
-  else
-    policy_findupdatepackages(solv, s, &qs, allow_all);
-  if (!allow_all && qs.count && solv->noobsoletes.size)
-    {
-      int i, j;
-
-      d = pool_queuetowhatprovides(pool, &qs);
-      /* filter out all noobsoletes packages as they don't update */
-      for (i = j = 0; i < qs.count; i++)
-       {
-         if (MAPTST(&solv->noobsoletes, qs.elements[i]))
-           {
-             /* it's ok if they have same nevra */
-             Solvable *ps = pool->solvables + qs.elements[i];
-             if (ps->name != s->name || ps->evr != s->evr || ps->arch != s->arch)
-               continue;
-           }
-         qs.elements[j++] = qs.elements[i];
-       }
-      if (j == 0 && p == -SYSTEMSOLVABLE && solv->distupgrade)
-       {
-         queue_push(&solv->orphaned, s - pool->solvables);     /* treat as orphaned */
-         j = qs.count;
-       }
-      if (j < qs.count)
-       {
-         if (d && solv->updatesystem && solv->installed && s->repo == solv->installed)
-           {
-             if (!solv->multiversionupdaters)
-               solv->multiversionupdaters = sat_calloc(solv->installed->end - solv->installed->start, sizeof(Id));
-             solv->multiversionupdaters[s - pool->solvables - solv->installed->start] = d;
-           }
-         qs.count = j;
-       }
-    }
-  if (qs.count && p == -SYSTEMSOLVABLE)
-    p = queue_shift(&qs);
-  d = qs.count ? pool_queuetowhatprovides(pool, &qs) : 0;
-  queue_free(&qs);
-  addrule(solv, p, d); /* allow update of s */
-  POOL_DEBUG(SAT_DEBUG_SCHUBI, "-----  addupdaterule end -----\n");
-}
-
-
-/********************************************************************/
-/* watches */
-
-
-/*-------------------------------------------------------------------
- * makewatches
- *
- * initial setup for all watches
- */
-
-static void
-makewatches(Solver *solv)
-{
-  Rule *r;
-  int i;
-  int nsolvables = solv->pool->nsolvables;
-
-  sat_free(solv->watches);
-                                      /* lower half for removals, upper half for installs */
-  solv->watches = sat_calloc(2 * nsolvables, sizeof(Id));
-#if 1
-  /* do it reverse so rpm rules get triggered first (XXX: obsolete?) */
-  for (i = 1, r = solv->rules + solv->nrules - 1; i < solv->nrules; i++, r--)
-#else
-  for (i = 1, r = solv->rules + 1; i < solv->nrules; i++, r++)
-#endif
-    {
-      if (!r->w2)              /* assertions do not need watches */
-       continue;
-
-      /* see addwatches_rule(solv, r) */
-      r->n1 = solv->watches[nsolvables + r->w1];
-      solv->watches[nsolvables + r->w1] = r - solv->rules;
-
-      r->n2 = solv->watches[nsolvables + r->w2];
-      solv->watches[nsolvables + r->w2] = r - solv->rules;
-    }
-}
-
-
-/*-------------------------------------------------------------------
- *
- * add watches (for rule)
- * sets up watches for a single rule
- * 
- * see also makewatches()
- */
-
-static inline void
-addwatches_rule(Solver *solv, Rule *r)
-{
-  int nsolvables = solv->pool->nsolvables;
-
-  r->n1 = solv->watches[nsolvables + r->w1];
-  solv->watches[nsolvables + r->w1] = r - solv->rules;
-
-  r->n2 = solv->watches[nsolvables + r->w2];
-  solv->watches[nsolvables + r->w2] = r - solv->rules;
-}
-
-
-/********************************************************************/
-/*
- * rule propagation
- */
-
-
-/* shortcuts to check if a literal (positive or negative) assignment
- * evaluates to 'true' or 'false'
- */
-#define DECISIONMAP_TRUE(p) ((p) > 0 ? (decisionmap[p] > 0) : (decisionmap[-p] < 0))
-#define DECISIONMAP_FALSE(p) ((p) > 0 ? (decisionmap[p] < 0) : (decisionmap[-p] > 0))
-#define DECISIONMAP_UNDEF(p) (decisionmap[(p) > 0 ? (p) : -(p)] == 0)
-
-/*-------------------------------------------------------------------
- * 
- * propagate
- *
- * make decision and propagate to all rules
- * 
- * Evaluate each term affected by the decision (linked through watches)
- * If we find unit rules we make new decisions based on them
- * 
- * Everything's fixed there, it's just finding rules that are
- * unit.
- * 
- * return : 0 = everything is OK
- *          rule = conflict found in this rule
- */
-
-static Rule *
-propagate(Solver *solv, int level)
-{
-  Pool *pool = solv->pool;
-  Id *rp, *next_rp;           /* rule pointer, next rule pointer in linked list */
-  Rule *r;                    /* rule */
-  Id p, pkg, other_watch;
-  Id *dp;
-  Id *decisionmap = solv->decisionmap;
-    
-  Id *watches = solv->watches + pool->nsolvables;   /* place ptr in middle */
-
-  POOL_DEBUG(SAT_DEBUG_PROPAGATE, "----- propagate -----\n");
-
-  /* foreach non-propagated decision */
-  while (solv->propagate_index < solv->decisionq.count)
-    {
-       /*
-        * 'pkg' was just decided
-        * negate because our watches trigger if literal goes FALSE
-        */
-      pkg = -solv->decisionq.elements[solv->propagate_index++];
-       
-      IF_POOLDEBUG (SAT_DEBUG_PROPAGATE)
-        {
-         POOL_DEBUG(SAT_DEBUG_PROPAGATE, "propagate for decision %d level %d\n", -pkg, level);
-         solver_printruleelement(solv, SAT_DEBUG_PROPAGATE, 0, -pkg);
-        }
-
-      /* foreach rule where 'pkg' is now FALSE */
-      for (rp = watches + pkg; *rp; rp = next_rp)
-       {
-         r = solv->rules + *rp;
-         if (r->d < 0)
-           {
-             /* rule is disabled, goto next */
-             if (pkg == r->w1)
-               next_rp = &r->n1;
-             else
-               next_rp = &r->n2;
-             continue;
-           }
-
-         IF_POOLDEBUG (SAT_DEBUG_PROPAGATE)
-           {
-             POOL_DEBUG(SAT_DEBUG_PROPAGATE,"  watch triggered ");
-             solver_printrule(solv, SAT_DEBUG_PROPAGATE, r);
-           }
-
-           /* 'pkg' was just decided (was set to FALSE)
-            * 
-            *  now find other literal watch, check clause
-            *   and advance on linked list
-            */
-         if (pkg == r->w1)
+           /* 'pkg' was just decided (was set to FALSE)
+            * 
+            *  now find other literal watch, check clause
+            *   and advance on linked list
+            */
+         if (pkg == r->w1)
            {
              other_watch = r->w2;
              next_rp = &r->n1;
@@ -1758,9 +600,9 @@ propagate(Solver *solv, int level)
                  IF_POOLDEBUG (SAT_DEBUG_PROPAGATE)
                    {
                      if (p > 0)
-                       POOL_DEBUG(SAT_DEBUG_PROPAGATE, "    -> move w%d to %s\n", (pkg == r->w1 ? 1 : 2), solvable2str(pool, pool->solvables + p));
+                       POOL_DEBUG(SAT_DEBUG_PROPAGATE, "    -> move w%d to %s\n", (pkg == r->w1 ? 1 : 2), solvid2str(pool, p));
                      else
-                       POOL_DEBUG(SAT_DEBUG_PROPAGATE,"    -> move w%d to !%s\n", (pkg == r->w1 ? 1 : 2), solvable2str(pool, pool->solvables - p));
+                       POOL_DEBUG(SAT_DEBUG_PROPAGATE,"    -> move w%d to !%s\n", (pkg == r->w1 ? 1 : 2), solvid2str(pool, -p));
                    }
                    
                  *rp = *next_rp;
@@ -1806,11 +648,10 @@ propagate(Solver *solv, int level)
 
          IF_POOLDEBUG (SAT_DEBUG_PROPAGATE)
            {
-             Solvable *s = pool->solvables + (other_watch > 0 ? other_watch : -other_watch);
              if (other_watch > 0)
-               POOL_DEBUG(SAT_DEBUG_PROPAGATE, "    -> decided to install %s\n", solvable2str(pool, s));
+               POOL_DEBUG(SAT_DEBUG_PROPAGATE, "    -> decided to install %s\n", solvid2str(pool, other_watch));
              else
-               POOL_DEBUG(SAT_DEBUG_PROPAGATE, "    -> decided to conflict %s\n", solvable2str(pool, s));
+               POOL_DEBUG(SAT_DEBUG_PROPAGATE, "    -> decided to conflict %s\n", solvid2str(pool, -other_watch));
            }
            
        } /* foreach rule involving 'pkg' */
@@ -1947,32 +788,31 @@ l1retry:
 
 /*-------------------------------------------------------------------
  * 
- * reset_solver
+ * solver_reset
  * 
  * reset the solver decisions to right after the rpm rules.
  * called after rules have been enabled/disabled
  */
 
-static void
-reset_solver(Solver *solv)
+void
+solver_reset(Solver *solv)
 {
   Pool *pool = solv->pool;
   int i;
   Id v;
 
-  /* rewind decisions to direct rpm rule assertions */
-  for (i = solv->decisionq.count - 1; i >= solv->directdecisions; i--)
+  /* rewind all decisions */
+  for (i = solv->decisionq.count - 1; i >= 0; i--)
     {
       v = solv->decisionq.elements[i];
       solv->decisionmap[v > 0 ? v : -v] = 0;
     }
-
-  POOL_DEBUG(SAT_DEBUG_UNSOLVABLE, "decisions done reduced from %d to %d\n", solv->decisionq.count, solv->directdecisions);
-
-  solv->decisionq_why.count = solv->directdecisions;
-  solv->decisionq.count = solv->directdecisions;
+  solv->decisionq_why.count = 0;
+  solv->decisionq.count = 0;
   solv->recommends_index = -1;
   solv->propagate_index = 0;
+  solv->recommendations.count = 0;
+  solv->branches.count = 0;
 
   /* adapt learnt rule status to new set of enabled/disabled rules */
   enabledisablelearntrules(solv);
@@ -2013,6 +853,20 @@ analyze_unsolvable_rule(Solver *solv, Rule *r, Id *lastweakp)
   /* turn rule into problem */
   if (why >= solv->jobrules && why < solv->jobrules_end)
     why = -(solv->ruletojob.elements[why - solv->jobrules] + 1);
+  /* normalize dup/infarch rules */
+  if (why > solv->infarchrules && why < solv->infarchrules_end)
+    {
+      Id name = pool->solvables[-solv->rules[why].p].name;
+      while (why > solv->infarchrules && pool->solvables[-solv->rules[why - 1].p].name == name)
+       why--;
+    }
+  if (why > solv->duprules && why < solv->duprules_end)
+    {
+      Id name = pool->solvables[-solv->rules[why].p].name;
+      while (why > solv->duprules && pool->solvables[-solv->rules[why - 1].p].name == name)
+       why--;
+    }
+
   /* return if problem already countains our rule */
   if (solv->problems.count)
     {
@@ -2130,10 +984,10 @@ analyze_unsolvable(Solver *solv, Rule *cr, int disablerules)
         v = lastweak;
       POOL_DEBUG(SAT_DEBUG_UNSOLVABLE, "disabling ");
       solver_printruleclass(solv, SAT_DEBUG_UNSOLVABLE, solv->rules + lastweak);
-      disableproblem(solv, v);
+      solver_disableproblem(solv, v);
       if (v < 0)
-       disableupdaterules(solv, solv->job, -(v + 1));
-      reset_solver(solv);
+       solver_reenablepolicyrules(solv, -(v + 1));
+      solver_reset(solv);
       return 1;
     }
 
@@ -2144,9 +998,9 @@ analyze_unsolvable(Solver *solv, Rule *cr, int disablerules)
   if (disablerules)
     {
       for (i = oldproblemcount + 1; i < solv->problems.count - 1; i++)
-        disableproblem(solv, solv->problems.elements[i]);
+        solver_disableproblem(solv, solv->problems.elements[i]);
       /* XXX: might want to enable all weak rules again */
-      reset_solver(solv);
+      solver_reset(solv);
       return 1;
     }
   POOL_DEBUG(SAT_DEBUG_UNSOLVABLE, "UNSOLVABLE\n");
@@ -2269,7 +1123,7 @@ setpropagatelearn(Solver *solv, int level, Id decision, int disablerules, Id rul
       POOL_DEBUG(SAT_DEBUG_ANALYZE, "reverting decisions (level %d -> %d)\n", level, l);
       level = l;
       revert(solv, level);
-      r = addrule(solv, p, d);       /* p requires d */
+      r = solver_addrule(solv, p, d);
       assert(r);
       assert(solv->learnt_why.count == (r - solv->rules) - solv->learntrules);
       queue_push(&solv->learnt_why, why);
@@ -2284,6 +1138,7 @@ setpropagatelearn(Solver *solv, int level, Id decision, int disablerules, Id rul
          /* learnt rule is an assertion */
           queue_push(&solv->ruleassertions, r - solv->rules);
        }
+      /* the new rule is unit by design */
       solv->decisionmap[p > 0 ? p : -p] = p > 0 ? level : -level;
       queue_push(&solv->decisionq, p);
       queue_push(&solv->decisionq_why, r - solv->rules);
@@ -2342,7 +1197,7 @@ selectandinstall(Solver *solv, int level, Queue *dq, int disablerules, Id ruleid
     }
   p = dq->elements[0];
 
-  POOL_DEBUG(SAT_DEBUG_POLICY, "installing %s\n", solvable2str(pool, pool->solvables + p));
+  POOL_DEBUG(SAT_DEBUG_POLICY, "installing %s\n", solvid2str(pool, p));
 
   return setpropagatelearn(solv, level, p, disablerules, ruleid);
 }
@@ -2373,6 +1228,8 @@ solver_create(Pool *pool)
   solv->pool = pool;
   solv->installed = pool->installed;
 
+  queue_init(&solv->transaction);
+  queue_init(&solv->transaction_info);
   queue_init(&solv->ruletojob);
   queue_init(&solv->decisionq);
   queue_init(&solv->decisionq_why);
@@ -2409,12 +1266,16 @@ solver_create(Pool *pool)
 void
 solver_free(Solver *solv)
 {
+  queue_free(&solv->transaction);
+  queue_free(&solv->transaction_info);
+  queue_free(&solv->job);
   queue_free(&solv->ruletojob);
   queue_free(&solv->decisionq);
   queue_free(&solv->decisionq_why);
   queue_free(&solv->learnt_why);
   queue_free(&solv->learnt_pool);
   queue_free(&solv->problems);
+  queue_free(&solv->solutions);
   queue_free(&solv->suggestions);
   queue_free(&solv->recommendations);
   queue_free(&solv->orphaned);
@@ -2429,26 +1290,31 @@ solver_free(Solver *solv)
   map_free(&solv->weakrulemap);
   map_free(&solv->noobsoletes);
 
+  map_free(&solv->updatemap);
+  map_free(&solv->dupmap);
+  map_free(&solv->dupinvolvedmap);
+
   sat_free(solv->decisionmap);
   sat_free(solv->rules);
   sat_free(solv->watches);
   sat_free(solv->obsoletes);
   sat_free(solv->obsoletes_data);
   sat_free(solv->multiversionupdaters);
+  sat_free(solv->transaction_installed);
   sat_free(solv);
 }
 
 
 /*-------------------------------------------------------------------
  * 
- * run_solver
+ * solver_run_sat
  *
  * all rules have been set up, now actually run the solver
  *
  */
 
-static void
-run_solver(Solver *solv, int disablerules, int doweak)
+void
+solver_run_sat(Solver *solv, int disablerules, int doweak)
 {
   Queue dq;            /* local decisionqueue */
   Queue dqs;           /* local decisionqueue for supplements */
@@ -2574,79 +1440,78 @@ run_solver(Solver *solv, int disablerules, int doweak)
 
       if (level < systemlevel && solv->installed && solv->installed->nsolvables)
        {
-         if (!solv->updatesystem)
+         Repo *installed = solv->installed;
+         int pass;
+
+         /* we use two passes if we need to update packages 
+           * to create a better user experience */
+         for (pass = solv->updatemap.size ? 0 : 1; pass < 2; pass++)
            {
-             /*
-              * Normal run (non-updating)
-              * Keep as many packages installed as possible
-              */
-             POOL_DEBUG(SAT_DEBUG_STATS, "installing old packages\n");
-               
-             for (i = solv->installed->start; i < solv->installed->end; i++)
+             FOR_REPO_SOLVABLES(installed, i, s)
                {
-                 s = pool->solvables + i;
-                   
-                   /* skip if not installed */
-                 if (s->repo != solv->installed)
-                   continue;
-                   
-                   /* skip if already decided */
-                 if (solv->decisionmap[i] != 0)
+                 Rule *rr;
+                 Id d;
+
+                 /* XXX: noupdate check is probably no longer needed, as all jobs should
+                   * already be satisfied */
+                 if (MAPTST(&solv->noupdate, i - installed->start))
                    continue;
-                   
-                 r = solv->rules + solv->updaterules + (i - solv->installed->start);
-                 if (!r->p)            /* update rule == feature rule? */
-                   r = r - solv->updaterules + solv->featurerules;
-                 if (r->p && r->p != i)        /* allowed to keep package? */
+                 if (solv->decisionmap[i] > 0)
                    continue;
+                 if (!pass && solv->updatemap.size && !MAPTST(&solv->updatemap, i - installed->start))
+                   continue;           /* updates first */
+                 r = solv->rules + solv->updaterules + (i - installed->start);
+                 rr = r;
+                 if (!rr->p || rr->d < 0)      /* disabled -> look at feature rule */
+                   rr -= solv->installed->end - solv->installed->start;
+                 if (!rr->p)           /* identical to update rule? */
+                   rr = r;
+                 if (!rr->p)
+                   continue;           /* orpaned package */
 
-                 POOL_DEBUG(SAT_DEBUG_PROPAGATE, "keeping %s\n", solvable2str(pool, s));
-                   
-                 olevel = level;
-                 level = setpropagatelearn(solv, level, i, disablerules, r->p ? r - solv->rules : 0);
-
-                 if (level == 0)                /* unsolvable */
+                 queue_empty(&dq);
+                 if (solv->decisionmap[i] < 0 || solv->updatesystem || (solv->updatemap.size && MAPTST(&solv->updatemap, i - installed->start)) || rr->p != i)
                    {
-                     queue_free(&dq);
-                     queue_free(&dqs);
-                     return;
+                     if (solv->noobsoletes.size && solv->multiversionupdaters
+                            && (d = solv->multiversionupdaters[i - installed->start]) != 0)
+                       {
+                         /* special multiversion handling, make sure best version is chosen */
+                         queue_push(&dq, i);
+                         while ((p = pool->whatprovidesdata[d++]) != 0)
+                           if (solv->decisionmap[p] >= 0)
+                             queue_push(&dq, p);
+                         policy_filter_unwanted(solv, &dq, POLICY_MODE_CHOOSE);
+                         p = dq.elements[0];
+                         if (p != i && solv->decisionmap[p] == 0)
+                           {
+                             rr = solv->rules + solv->featurerules + (i - solv->installed->start);
+                             if (!rr->p)               /* update rule == feature rule? */
+                               rr = rr - solv->featurerules + solv->updaterules;
+                             dq.count = 1;
+                           }
+                         else
+                           dq.count = 0;
+                       }
+                     else
+                       {
+                         /* update to best package */
+                         FOR_RULELITERALS(p, dp, rr)
+                           {
+                             if (solv->decisionmap[p] > 0)
+                               {
+                                 dq.count = 0;         /* already fulfilled */
+                                 break;
+                               }
+                             if (!solv->decisionmap[p])
+                               queue_push(&dq, p);
+                           }
+                       }
                    }
-                 if (level <= olevel)
-                   break;
-               }
-             systemlevel = level + 1;
-             if (i < solv->installed->end)
-               continue;
-           }
-         else if (solv->noobsoletes.size && solv->multiversionupdaters)
-           {
-             /* see if we can multi-version install the newest package */
-             for (i = solv->installed->start; i < solv->installed->end; i++)
-               {
-                 Id d;
-                 s = pool->solvables + i;
-                 if (s->repo != solv->installed)
-                   continue;
-                 if (MAPTST(&solv->noupdate, i - solv->installed->start))
-                   continue;
-                 d = solv->multiversionupdaters[i - solv->installed->start];
-                 if (!d)
-                   continue;
-                 queue_empty(&dq);
-                 queue_push(&dq, i);
-                 while ((p = pool->whatprovidesdata[d++]) != 0)
-                   if (solv->decisionmap[p] >= 0)
-                     queue_push(&dq, p);
-                 policy_filter_unwanted(solv, &dq, POLICY_MODE_CHOOSE);
-                 p = dq.elements[0];
-                 if (p != i && solv->decisionmap[p] == 0)
+                 /* install best version */
+                 if (dq.count)
                    {
-                     r = solv->rules + solv->featurerules + (i - solv->installed->start);
-                     if (!r->p)                /* update rule == feature rule? */
-                       r = r - solv->featurerules + solv->updaterules;
                      olevel = level;
-                     POOL_DEBUG(SAT_DEBUG_POLICY, "installing (multi-version) %s\n", solvable2str(pool, pool->solvables + p));
-                     level = setpropagatelearn(solv, level, p, disablerules, r->p ? r - solv->rules : 0);
+                     level = selectandinstall(solv, level, &dq, disablerules, rr - solv->rules);
                      if (level == 0)
                        {
                          queue_free(&dq);
@@ -2656,19 +1521,12 @@ run_solver(Solver *solv, int disablerules, int doweak)
                      if (level <= olevel)
                        break;
                    }
-                 p = i;
-                 /* now that the best version is installed, try to
-                   * keep the original one */
-                 if (solv->decisionmap[p])     /* already decided? */
-                  continue;
-                 r = solv->rules + solv->updaterules + (i - solv->installed->start);
-                 if (!r->p)            /* update rule == feature rule? */
-                   r = r - solv->updaterules + solv->featurerules;
-                 if (r->p == p)        /* allowed to keep package? */
+                 /* if still undecided keep package */
+                 if (solv->decisionmap[i] == 0)
                    {
                      olevel = level;
-                     POOL_DEBUG(SAT_DEBUG_POLICY, "keeping (multi-version) %s\n", solvable2str(pool, pool->solvables + p));
-                     level = setpropagatelearn(solv, level, p, disablerules, r - solv->rules);
+                     POOL_DEBUG(SAT_DEBUG_POLICY, "keeping %s\n", solvid2str(pool, i));
+                     level = setpropagatelearn(solv, level, i, disablerules, r - solv->rules);
                      if (level == 0)
                        {
                          queue_free(&dq);
@@ -2679,62 +1537,12 @@ run_solver(Solver *solv, int disablerules, int doweak)
                        break;
                    }
                }
-             systemlevel = level + 1;
-             if (i < solv->installed->end)
-               continue;
-           }
-           
-         POOL_DEBUG(SAT_DEBUG_STATS, "resolving update/feature rules\n");
-           
-         for (i = solv->installed->start, r = solv->rules + solv->updaterules; i < solv->installed->end; i++, r++)
-           {
-             Rule *rr;
-             s = pool->solvables + i;
-               
-               /* skip if not installed (can't update) */
-             if (s->repo != solv->installed)
-               continue;
-               /* skip if already decided */
-             if (solv->decisionmap[i] > 0)
-               continue;
-               
-               /* noupdate is set if a job is erasing the installed solvable or installing a specific version */
-             if (MAPTST(&solv->noupdate, i - solv->installed->start))
-               continue;
-               
-             queue_empty(&dq);
-
-             rr = r;
-             if (rr->d < 0)    /* disabled -> look at feature rule ? */
-               rr -= solv->installed->end - solv->installed->start;
-             if (!rr->p)       /* identical to update rule? */
-               rr = r;
-             if (rr->p <= 0)
-               continue;       /* no such rule or disabled */
-       
-             FOR_RULELITERALS(p, dp, rr)
-               {
-                 if (solv->decisionmap[p] > 0)
-                   break;
-                 if (solv->decisionmap[p] == 0)
-                   queue_push(&dq, p);
-               }
-             if (p || !dq.count)       /* already fulfilled or empty */
-               continue;
-             olevel = level;
-             level = selectandinstall(solv, level, &dq, disablerules, rr - solv->rules);
-             if (level == 0)
-               {
-                 queue_free(&dq);
-                 queue_free(&dqs);
-                 return;
-               }
-             if (level <= olevel)
+             if (i < installed->end)
                break;
            }
          systemlevel = level + 1;
-         if (i < solv->installed->end)
-           continue;
+         if (pass < 2)
+           continue;           /* had trouble, retry */
        }
 
       if (level < systemlevel)
@@ -2822,7 +1630,7 @@ run_solver(Solver *solv, int disablerules, int doweak)
              queue_free(&dqs);
              return;
            }
-         if (level < systemlevel)
+         if (level < systemlevel || level == 1)
            break;
          n = 0;
        } /* for(), decide */
@@ -2953,6 +1761,29 @@ run_solver(Solver *solv, int disablerules, int doweak)
                }
            }
 
+         /* multiversion doesn't mix well with supplements.
+          * filter supplemented packages where we already decided
+          * to install a different version (see bnc#501088) */
+          if (dqs.count && solv->noobsoletes.size)
+           {
+             for (i = j = 0; i < dqs.count; i++)
+               {
+                 p = dqs.elements[i];
+                 if (MAPTST(&solv->noobsoletes, p))
+                   {
+                     Id p2, pp2;
+                     s = pool->solvables + p;
+                     FOR_PROVIDES(p2, pp2, s->name)
+                       if (solv->decisionmap[p2] > 0 && pool->solvables[p2].name == s->name)
+                         break;
+                     if (p2)
+                       continue;       /* ignore this package */
+                   }
+                 dqs.elements[j++] = p;
+               }
+             dqs.count = j;
+           }
+
           /* make dq contain both recommended and supplemented pkgs */
          if (dqs.count)
            {
@@ -2960,7 +1791,6 @@ run_solver(Solver *solv, int disablerules, int doweak)
                queue_pushunique(&dq, dqs.elements[i]);
            }
 
-#if 1
          if (dq.count)
            {
              Map dqmap;
@@ -2971,16 +1801,18 @@ run_solver(Solver *solv, int disablerules, int doweak)
                  /* simple case, just one package. no need to choose  */
                  p = dq.elements[0];
                  if (dqs.count)
-                   POOL_DEBUG(SAT_DEBUG_POLICY, "installing supplemented %s\n", solvable2str(pool, pool->solvables + p));
+                   POOL_DEBUG(SAT_DEBUG_POLICY, "installing supplemented %s\n", solvid2str(pool, p));
                  else
-                   POOL_DEBUG(SAT_DEBUG_POLICY, "installing recommended %s\n", solvable2str(pool, pool->solvables + p));
+                   POOL_DEBUG(SAT_DEBUG_POLICY, "installing recommended %s\n", solvid2str(pool, p));
                  queue_push(&solv->recommendations, p);
                  level = setpropagatelearn(solv, level, p, 0, 0);
-                 continue;
+                 continue;     /* back to main loop */
                }
 
-             /* filter and create a map of result */
+             /* filter packages, this gives us the best versions */
              policy_filter_unwanted(solv, &dq, POLICY_MODE_RECOMMEND);
+
+             /* create map of result */
              map_init(&dqmap, pool->nsolvables);
              for (i = 0; i < dq.count; i++)
                MAPSET(&dqmap, dq.elements[i]);
@@ -2991,7 +1823,7 @@ run_solver(Solver *solv, int disablerules, int doweak)
                  p = dqs.elements[i];
                  if (solv->decisionmap[p] || !MAPTST(&dqmap, p))
                    continue;
-                 POOL_DEBUG(SAT_DEBUG_POLICY, "installing supplemented %s\n", solvable2str(pool, pool->solvables + p));
+                 POOL_DEBUG(SAT_DEBUG_POLICY, "installing supplemented %s\n", solvid2str(pool, p));
                  queue_push(&solv->recommendations, p);
                  olevel = level;
                  level = setpropagatelearn(solv, level, p, 0, 0);
@@ -3042,66 +1874,50 @@ run_solver(Solver *solv, int disablerules, int doweak)
                          queue_push(&solv->branches, -level);
                        }
                      p = dq.elements[0];
-                     POOL_DEBUG(SAT_DEBUG_POLICY, "installing recommended %s\n", solvable2str(pool, pool->solvables + p));
+                     POOL_DEBUG(SAT_DEBUG_POLICY, "installing recommended %s\n", solvid2str(pool, p));
                      queue_push(&solv->recommendations, p);
                      olevel = level;
                      level = setpropagatelearn(solv, level, p, 0, 0);
                      if (level <= olevel || solv->decisionq.count < decisioncount)
-                       break;
+                       break;  /* we had to revert some decisions */
                    }
                  if (rec)
                    break;      /* had a problem above, quit loop */
                }
              map_free(&dqmap);
-             continue;
-           }
-#else
-         if (dq.count)
-           {
-             if (dq.count > 1)
-               policy_filter_unwanted(solv, &dq, POLICY_MODE_RECOMMEND);
-             p = dq.elements[0];
-             /* prefer recommended patterns (bnc#450226) */
-             /* real fix is to minimize recommended packages as well */
-             for (i = 0; i < dq.count; i++)
-               if (!strncmp(id2str(pool, pool->solvables[dq.elements[i]].name), "pattern:", 8))
-                 {
-                   p = dq.elements[i];
-                   break;
-                 }
-             POOL_DEBUG(SAT_DEBUG_POLICY, "installing recommended %s\n", solvable2str(pool, pool->solvables + p));
-             queue_push(&solv->recommendations, p);
-             level = setpropagatelearn(solv, level, p, 0, 0);
-             continue;
+
+             continue;         /* back to main loop */
            }
-#endif
        }
 
      if (solv->distupgrade && solv->installed)
        {
+         int installedone = 0;
+
          /* let's see if we can install some unsupported package */
          POOL_DEBUG(SAT_DEBUG_STATS, "deciding unsupported packages\n");
          for (i = 0; i < solv->orphaned.count; i++)
            {
              p = solv->orphaned.elements[i];
-             if (!solv->decisionmap[p])
-               break;
-           }
-         if (i < solv->orphaned.count)
-           {
-             p = solv->orphaned.elements[i];
+             if (solv->decisionmap[p])
+               continue;       /* already decided */
+             olevel = level;
              if (solv->distupgrade_removeunsupported)
                {
-                 POOL_DEBUG(SAT_DEBUG_STATS, "removing unsupported %s\n", solvable2str(pool, pool->solvables + p));
+                 POOL_DEBUG(SAT_DEBUG_STATS, "removing unsupported %s\n", solvid2str(pool, p));
                  level = setpropagatelearn(solv, level, -p, 0, 0);
                }
              else
                {
-                 POOL_DEBUG(SAT_DEBUG_STATS, "keeping unsupported %s\n", solvable2str(pool, pool->solvables + p));
+                 POOL_DEBUG(SAT_DEBUG_STATS, "keeping unsupported %s\n", solvid2str(pool, p));
                  level = setpropagatelearn(solv, level, p, 0, 0);
+                 installedone = 1;
                }
-             continue;
+             if (level < olevel)
+               break;
            }
+         if (installedone || i < solv->orphaned.count)
+           continue;
        }
 
      if (solv->solution_callback)
@@ -3117,7 +1933,7 @@ run_solver(Solver *solv, int disablerules, int doweak)
                if (solv->branches.elements[i - 1] < 0)
                  break;
              p = solv->branches.elements[i];
-             POOL_DEBUG(SAT_DEBUG_STATS, "branching with %s\n", solvable2str(pool, pool->solvables + p));
+             POOL_DEBUG(SAT_DEBUG_STATS, "branching with %s\n", solvid2str(pool, p));
              queue_empty(&dq);
              for (j = i + 1; j < solv->branches.count; j++)
                queue_push(&dq, solv->branches.elements[j]);
@@ -3130,970 +1946,77 @@ run_solver(Solver *solv, int disablerules, int doweak)
              olevel = level;
              why = -solv->decisionq_why.elements[solv->decisionq_why.count];
              assert(why >= 0);
-             level = setpropagatelearn(solv, level, p, disablerules, why);
-             if (level == 0)
-               {
-                 queue_free(&dq);
-                 queue_free(&dqs);
-                 return;
-               }
-             continue;
-           }
-         /* all branches done, we're finally finished */
-         break;
-       }
-
-      /* minimization step */
-     if (solv->branches.count)
-       {
-         int l = 0, lasti = -1, lastl = -1;
-         Id why;
-
-         p = 0;
-         for (i = solv->branches.count - 1; i >= 0; i--)
-           {
-             p = solv->branches.elements[i];
-             if (p < 0)
-               l = -p;
-             else if (p > 0 && solv->decisionmap[p] > l + 1)
-               {
-                 lasti = i;
-                 lastl = l;
-               }
-           }
-         if (lasti >= 0)
-           {
-             /* kill old solvable so that we do not loop */
-             p = solv->branches.elements[lasti];
-             solv->branches.elements[lasti] = 0;
-             POOL_DEBUG(SAT_DEBUG_STATS, "minimizing %d -> %d with %s\n", solv->decisionmap[p], lastl, solvable2str(pool, pool->solvables + p));
-             minimizationsteps++;
-
-             level = lastl;
-             revert(solv, level);
-             why = -solv->decisionq_why.elements[solv->decisionq_why.count];
-             assert(why >= 0);
-             olevel = level;
-             level = setpropagatelearn(solv, level, p, disablerules, why);
-             if (level == 0)
-               {
-                 queue_free(&dq);
-                 queue_free(&dqs);
-                 return;
-               }
-             continue;
-           }
-       }
-      break;
-    }
-  POOL_DEBUG(SAT_DEBUG_STATS, "solver statistics: %d learned rules, %d unsolvable, %d minimization steps\n", solv->stats_learned, solv->stats_unsolvable, minimizationsteps);
-
-  POOL_DEBUG(SAT_DEBUG_STATS, "done solving.\n\n");
-  queue_free(&dq);
-  queue_free(&dqs);
-}
-
-
-/*-------------------------------------------------------------------
- * 
- * refine_suggestion
- * 
- * at this point, all rules that led to conflicts are disabled.
- * we re-enable all rules of a problem set but rule "sug", then
- * continue to disable more rules until there as again a solution.
- */
-
-/* FIXME: think about conflicting assertions */
-
-static void
-refine_suggestion(Solver *solv, Queue *job, Id *problem, Id sug, Queue *refined)
-{
-  Pool *pool = solv->pool;
-  int i, j;
-  Id v;
-  Queue disabled;
-  int disabledcnt;
-
-  IF_POOLDEBUG (SAT_DEBUG_SOLUTIONS)
-    {
-      POOL_DEBUG(SAT_DEBUG_SOLUTIONS, "refine_suggestion start\n");
-      for (i = 0; problem[i]; i++)
-       {
-         if (problem[i] == sug)
-           POOL_DEBUG(SAT_DEBUG_SOLUTIONS, "=> ");
-         solver_printproblem(solv, problem[i]);
-       }
-    }
-  queue_init(&disabled);
-  queue_empty(refined);
-  queue_push(refined, sug);
-
-  /* re-enable all problem rules with the exception of "sug"(gestion) */
-  revert(solv, 1);
-  reset_solver(solv);
-
-  for (i = 0; problem[i]; i++)
-    if (problem[i] != sug)
-      enableproblem(solv, problem[i]);
-
-  if (sug < 0)
-    disableupdaterules(solv, job, -(sug + 1));
-  else if (sug >= solv->updaterules && sug < solv->updaterules_end)
-    {
-      /* enable feature rule */
-      Rule *r = solv->rules + solv->featurerules + (sug - solv->updaterules);
-      if (r->p)
-       enablerule(solv, r);
-    }
-
-  enableweakrules(solv);
-
-  for (;;)
-    {
-      int njob, nfeature, nupdate;
-      queue_empty(&solv->problems);
-      revert(solv, 1);         /* XXX no longer needed? */
-      reset_solver(solv);
-
-      if (!solv->problems.count)
-        run_solver(solv, 0, 0);
-
-      if (!solv->problems.count)
-       {
-         POOL_DEBUG(SAT_DEBUG_SOLUTIONS, "no more problems!\n");
-         IF_POOLDEBUG (SAT_DEBUG_SCHUBI)
-           solver_printdecisions(solv);
-         break;                /* great, no more problems */
-       }
-      disabledcnt = disabled.count;
-      /* start with 1 to skip over proof index */
-      njob = nfeature = nupdate = 0;
-      for (i = 1; i < solv->problems.count - 1; i++)
-       {
-         /* ignore solutions in refined */
-          v = solv->problems.elements[i];
-         if (v == 0)
-           break;      /* end of problem reached */
-         for (j = 0; problem[j]; j++)
-           if (problem[j] != sug && problem[j] == v)
-             break;
-         if (problem[j])
-           continue;
-         if (v >= solv->featurerules && v < solv->featurerules_end)
-           nfeature++;
-         else if (v > 0)
-           nupdate++;
-         else
-           {
-             if ((job->elements[-v -1] & SOLVER_ESSENTIAL) != 0)
-               continue;       /* not that one! */
-             njob++;
-           }
-         queue_push(&disabled, v);
-       }
-      if (disabled.count == disabledcnt)
-       {
-         /* no solution found, this was an invalid suggestion! */
-         POOL_DEBUG(SAT_DEBUG_SOLUTIONS, "no solution found!\n");
-         refined->count = 0;
-         break;
-       }
-      if (!njob && nupdate && nfeature)
-       {
-         /* got only update rules, filter out feature rules */
-         POOL_DEBUG(SAT_DEBUG_SOLUTIONS, "throwing away feature rules\n");
-         for (i = j = disabledcnt; i < disabled.count; i++)
-           {
-             v = disabled.elements[i];
-             if (v < solv->featurerules || v >= solv->featurerules_end)
-               disabled.elements[j++] = v;
-           }
-         disabled.count = j;
-         nfeature = 0;
-       }
-      if (disabled.count == disabledcnt + 1)
-       {
-         /* just one suggestion, add it to refined list */
-         v = disabled.elements[disabledcnt];
-         if (!nfeature)
-           queue_push(refined, v);     /* do not record feature rules */
-         disableproblem(solv, v);
-         if (v >= solv->updaterules && v < solv->updaterules_end)
-           {
-             Rule *r = solv->rules + (v - solv->updaterules + solv->featurerules);
-             if (r->p)
-               enablerule(solv, r);    /* enable corresponding feature rule */
-           }
-         if (v < 0)
-           disableupdaterules(solv, job, -(v + 1));
-       }
-      else
-       {
-         /* more than one solution, disable all */
-         /* do not push anything on refine list, as we do not know which solution to choose */
-         /* thus, the user will get another problem if he selects this solution, where he
-           * can choose the right one */
-         IF_POOLDEBUG (SAT_DEBUG_SOLUTIONS)
-           {
-             POOL_DEBUG(SAT_DEBUG_SOLUTIONS, "more than one solution found:\n");
-             for (i = disabledcnt; i < disabled.count; i++)
-               solver_printproblem(solv, disabled.elements[i]);
-           }
-         for (i = disabledcnt; i < disabled.count; i++)
-           {
-             v = disabled.elements[i];
-             disableproblem(solv, v);
-             if (v >= solv->updaterules && v < solv->updaterules_end)
-               {
-                 Rule *r = solv->rules + (v - solv->updaterules + solv->featurerules);
-                 if (r->p)
-                   enablerule(solv, r);
-               }
-           }
-       }
-    }
-  /* all done, get us back into the same state as before */
-  /* enable refined rules again */
-  for (i = 0; i < disabled.count; i++)
-    enableproblem(solv, disabled.elements[i]);
-  /* disable problem rules again */
-
-  /* FIXME! */
-  for (i = 0; problem[i]; i++)
-    enableproblem(solv, problem[i]);
-  disableupdaterules(solv, job, -1);
-
-  /* disable problem rules again */
-  for (i = 0; problem[i]; i++)
-    disableproblem(solv, problem[i]);
-  POOL_DEBUG(SAT_DEBUG_SOLUTIONS, "refine_suggestion end\n");
-}
-
-
-/*-------------------------------------------------------------------
- * sorting helper for problems
- *
- * bring update rules before job rules
- * make essential job rules last
- */
-
-Queue *problems_sort_data;
-
-static int
-problems_sortcmp(const void *ap, const void *bp)
-{
-  Id a = *(Id *)ap, b = *(Id *)bp;
-  if (a < 0 && b > 0)
-    return 1;
-  if (a > 0 && b < 0)
-    return -1;
-  if (a < 0 && b < 0)
-    {
-      Queue *job = problems_sort_data;
-      int af = job->elements[-a - 1] & SOLVER_ESSENTIAL;
-      int bf = job->elements[-b - 1] & SOLVER_ESSENTIAL;
-      int x = af - bf;
-      if (x)
-       return x;
-    }
-  return a - b;
-}
-
-
-/*-------------------------------------------------------------------
- * sort problems
- */
-
-static void
-problems_sort(Solver *solv, Queue *job)
-{
-  int i, j;
-  if (!solv->problems.count)
-    return;
-  for (i = j = 1; i < solv->problems.count; i++)
-    {
-      if (!solv->problems.elements[i])
-       {
-         if (i > j + 1)
-           {
-             problems_sort_data = job;
-             qsort(solv->problems.elements + j, i - j, sizeof(Id), problems_sortcmp);
-           }
-         if (++i == solv->problems.count)
-           break;
-         j = i + 1;
-       }
-    }
-}
-
-
-/*-------------------------------------------------------------------
- * convert problems to solutions
- */
-
-static void
-problems_to_solutions(Solver *solv, Queue *job)
-{
-  Pool *pool = solv->pool;
-  Queue problems;
-  Queue solution;
-  Queue solutions;
-  Id *problem;
-  Id why;
-  int i, j, nsol, probsolved;
-  unsigned int now, refnow;
-
-  if (!solv->problems.count)
-    return;
-  now = sat_timems(0);
-  problems_sort(solv, job);
-  queue_clone(&problems, &solv->problems);
-  queue_init(&solution);
-  queue_init(&solutions);
-  /* copy over proof index */
-  queue_push(&solutions, problems.elements[0]);
-  problem = problems.elements + 1;
-  probsolved = 0;
-  refnow = sat_timems(0);
-  for (i = 1; i < problems.count; i++)
-    {
-      Id v = problems.elements[i];
-      if (v == 0)
-       {
-         /* mark end of this problem */
-         queue_push(&solutions, 0);
-         queue_push(&solutions, 0);
-         POOL_DEBUG(SAT_DEBUG_STATS, "refining took %d ms\n", sat_timems(refnow));
-         if (i + 1 == problems.count)
-           break;
-         /* copy over proof of next problem */
-          queue_push(&solutions, problems.elements[i + 1]);
-         i++;
-         problem = problems.elements + i + 1;
-         refnow = sat_timems(0);
-         probsolved = 0;
-         continue;
-       }
-      if (v < 0 && (job->elements[-v - 1] & SOLVER_ESSENTIAL))
-       {
-         /* essential job, skip if we already have a non-essential
-             solution */
-         if (probsolved > 0)
-           continue;
-         probsolved = -1;      /* show all solutions */
-       }
-      refine_suggestion(solv, job, problem, v, &solution);
-      if (!solution.count)
-       continue;       /* this solution didn't work out */
-
-      nsol = 0;
-      for (j = 0; j < solution.count; j++)
-       {
-         why = solution.elements[j];
-         /* must be either job descriptor or update rule */
-         assert(why < 0 || (why >= solv->updaterules && why < solv->updaterules_end));
-#if 0
-         solver_printproblem(solv, why);
-#endif
-         if (why < 0)
-           {
-             /* job descriptor */
-             queue_push(&solutions, 0);
-             queue_push(&solutions, -why);
-           }
-         else
-           {
-             /* update rule, find replacement package */
-             Id p, *dp, rp = 0;
-             Rule *rr;
-             p = solv->installed->start + (why - solv->updaterules);
-             rr = solv->rules + solv->featurerules + (why - solv->updaterules);
-             if (!rr->p)
-               rr = solv->rules + why;
-             if (solv->distupgrade && solv->rules[why].p != p && solv->decisionmap[p] > 0)
-               {
-                 /* distupgrade case, allow to keep old package */
-                 queue_push(&solutions, p);
-                 queue_push(&solutions, p);
-                 nsol++;
-                 continue;
-               }
-             if (solv->decisionmap[p] > 0)
-               continue;       /* false alarm, turned out we can keep the package */
-             if (rr->w2)
-               {
-                 int mvrp = 0;         /* multi-version replacement */
-                 FOR_RULELITERALS(rp, dp, rr)
-                   {
-                     if (rp > 0 && solv->decisionmap[rp] > 0 && pool->solvables[rp].repo != solv->installed)
-                       {
-                         mvrp = rp;
-                         if (!(solv->noobsoletes.size && MAPTST(&solv->noobsoletes, rp)))
-                           break;
-                       }
-                   }
-                 if (!rp && mvrp)
-                   {
-                     /* found only multi-version replacements */
-                     /* have to split solution into two parts */
-                     queue_push(&solutions, p);
-                     queue_push(&solutions, mvrp);
-                     nsol++;
-                   }
-               }
-             queue_push(&solutions, p);
-             queue_push(&solutions, rp);
-           }
-         nsol++;
-       }
-      /* mark end of this solution */
-      if (nsol)
-       {
-         if (!probsolved)
-           probsolved = 1;
-         queue_push(&solutions, 0);
-         queue_push(&solutions, 0);
-       }
-      else
-       {
-         POOL_DEBUG(SAT_DEBUG_SOLUTIONS, "Oops, everything was fine?\n");
-       }
-    }
-  queue_free(&solution);
-  queue_free(&problems);
-  /* copy queue over to solutions */
-  queue_free(&solv->problems);
-  queue_clone(&solv->problems, &solutions);
-
-  /* bring solver back into problem state */
-  revert(solv, 1);             /* XXX move to reset_solver? */
-  reset_solver(solv);
-
-  assert(solv->problems.count == solutions.count);
-  queue_free(&solutions);
-  POOL_DEBUG(SAT_DEBUG_STATS, "problems_to_solutions took %d ms\n", sat_timems(now));
-}
-
-
-/*-------------------------------------------------------------------
- * 
- * problem iterator
- * 
- * advance to next problem
- */
-
-Id
-solver_next_problem(Solver *solv, Id problem)
-{
-  Id *pp;
-  if (problem == 0)
-    return solv->problems.count ? 1 : 0;
-  pp = solv->problems.elements + problem;
-  while (pp[0] || pp[1])
-    {
-      /* solution */
-      pp += 2;
-      while (pp[0] || pp[1])
-        pp += 2;
-      pp += 2;
-    }
-  pp += 2;
-  problem = pp - solv->problems.elements;
-  if (problem >= solv->problems.count)
-    return 0;
-  return problem + 1;
-}
-
-
-/*-------------------------------------------------------------------
- * 
- * solution iterator
- */
-
-Id
-solver_next_solution(Solver *solv, Id problem, Id solution)
-{
-  Id *pp;
-  if (solution == 0)
-    {
-      solution = problem;
-      pp = solv->problems.elements + solution;
-      return pp[0] || pp[1] ? solution : 0;
-    }
-  pp = solv->problems.elements + solution;
-  while (pp[0] || pp[1])
-    pp += 2;
-  pp += 2;
-  solution = pp - solv->problems.elements;
-  return pp[0] || pp[1] ? solution : 0;
-}
-
-
-/*-------------------------------------------------------------------
- * 
- * solution element iterator
- */
-
-Id
-solver_next_solutionelement(Solver *solv, Id problem, Id solution, Id element, Id *p, Id *rp)
-{
-  Id *pp;
-  element = element ? element + 2 : solution;
-  pp = solv->problems.elements + element;
-  if (!(pp[0] || pp[1]))
-    return 0;
-  *p = pp[0];
-  *rp = pp[1];
-  return element;
-}
-
-
-/*-------------------------------------------------------------------
- * 
- * Retrieve information about a problematic rule
- *
- * this is basically the reverse of addrpmrulesforsolvable
- */
-
-SolverProbleminfo
-solver_problemruleinfo(Solver *solv, Queue *job, Id rid, Id *depp, Id *sourcep, Id *targetp)
-{
-  Pool *pool = solv->pool;
-  Repo *installed = solv->installed;
-  Rule *r;
-  Solvable *s;
-  int dontfix = 0;
-  Id p, d, w2, pp, req, *reqp, con, *conp, obs, *obsp, *dp;
-
-  assert(rid > 0);
-  if (rid >= solv->jobrules && rid < solv->jobrules_end)
-    {
-
-      r = solv->rules + rid;
-      p = solv->ruletojob.elements[rid - solv->jobrules];
-      *depp = job->elements[p + 1];
-      *sourcep = p;
-      *targetp = job->elements[p];
-      d = r->d < 0 ? -r->d - 1 : r->d;
-      if (d == 0 && r->w2 == 0 && r->p == -SYSTEMSOLVABLE && (job->elements[p] & SOLVER_SELECTMASK) != SOLVER_SOLVABLE_ONE_OF)
-       return SOLVER_PROBLEM_JOB_NOTHING_PROVIDES_DEP;
-      return SOLVER_PROBLEM_JOB_RULE;
-    }
-  if (rid >= solv->updaterules && rid < solv->updaterules_end)
-    {
-      *depp = 0;
-      *sourcep = solv->installed->start + (rid - solv->updaterules);
-      *targetp = 0;
-      return SOLVER_PROBLEM_UPDATE_RULE;
-    }
-  assert(rid < solv->rpmrules_end);
-  r = solv->rules + rid;
-  assert(r->p < 0);
-  d = r->d < 0 ? -r->d - 1 : r->d;
-  if (d == 0 && r->w2 == 0)
-    {
-      /* a rpm rule assertion */
-      s = pool->solvables - r->p;
-      if (installed && !solv->fixsystem && s->repo == installed)
-       dontfix = 1;
-      assert(!dontfix);        /* dontfix packages never have a neg assertion */
-      *sourcep = -r->p;
-      *targetp = 0;
-      /* see why the package is not installable */
-      if (s->arch != ARCH_SRC && s->arch != ARCH_NOSRC && !pool_installable(pool, s))
-       {
-         *depp = 0;
-         return SOLVER_PROBLEM_NOT_INSTALLABLE;
-       }
-      /* check requires */
-      if (s->requires)
-       {
-         reqp = s->repo->idarraydata + s->requires;
-         while ((req = *reqp++) != 0)
-           {
-             if (req == SOLVABLE_PREREQMARKER)
-               continue;
-             dp = pool->whatprovidesdata + pool_whatprovides(pool, req);
-             if (*dp == 0)
-               break;
-           }
-         if (req)
-           {
-             *depp = req;
-             return SOLVER_PROBLEM_NOTHING_PROVIDES_DEP;
-           }
-       }
-      if (!solv->allowselfconflicts && s->conflicts)
-       {
-         conp = s->repo->idarraydata + s->conflicts;
-         while ((con = *conp++) != 0)
-           FOR_PROVIDES(p, pp, con)
-             if (p == -r->p)
-               {
-                 *depp = con;
-                 return SOLVER_PROBLEM_SELF_CONFLICT;
-               }
-       }
-      /* should never happen */
-      *depp = 0;
-      return SOLVER_PROBLEM_RPM_RULE;
-    }
-  s = pool->solvables - r->p;
-  if (installed && !solv->fixsystem && s->repo == installed)
-    dontfix = 1;
-  w2 = r->w2;
-  if (d && pool->whatprovidesdata[d] < 0)
-    {
-      /* rule looks like -p|-c|x|x|x..., we only create this for patches with multiversion */
-      /* reduce it to -p|-c case */
-      w2 = pool->whatprovidesdata[d];
-    }
-  if (d == 0 && w2 < 0)
-    {
-      /* a package conflict */
-      Solvable *s2 = pool->solvables - w2;
-      int dontfix2 = 0;
-
-      if (installed && !solv->fixsystem && s2->repo == installed)
-       dontfix2 = 1;
-
-      /* if both packages have the same name and at least one of them
-       * is not installed, they conflict */
-      if (s->name == s2->name && !(installed && s->repo == installed && s2->repo == installed))
-       {
-         /* also check noobsoletes map */
-         if ((s->evr == s2->evr && s->arch == s2->arch) || !solv->noobsoletes.size
-               || ((!installed || s->repo != installed) && !MAPTST(&solv->noobsoletes, -r->p))
-               || ((!installed || s2->repo != installed) && !MAPTST(&solv->noobsoletes, -w2)))
-           {
-             *depp = 0;
-             *sourcep = -r->p;
-             *targetp = -w2;
-             return SOLVER_PROBLEM_SAME_NAME;
-           }
-       }
-
-      /* check conflicts in both directions */
-      if (s->conflicts)
-       {
-         conp = s->repo->idarraydata + s->conflicts;
-         while ((con = *conp++) != 0)
-            {
-              FOR_PROVIDES(p, pp, con)
-               {
-                 if (dontfix && pool->solvables[p].repo == installed)
-                   continue;
-                 if (p != -w2)
-                   continue;
-                 *depp = con;
-                 *sourcep = -r->p;
-                 *targetp = p;
-                 return SOLVER_PROBLEM_PACKAGE_CONFLICT;
-               }
-           }
-       }
-      if (s2->conflicts)
-       {
-         conp = s2->repo->idarraydata + s2->conflicts;
-         while ((con = *conp++) != 0)
-            {
-              FOR_PROVIDES(p, pp, con)
-               {
-                 if (dontfix2 && pool->solvables[p].repo == installed)
-                   continue;
-                 if (p != -r->p)
-                   continue;
-                 *depp = con;
-                 *sourcep = -w2;
-                 *targetp = p;
-                 return SOLVER_PROBLEM_PACKAGE_CONFLICT;
-               }
-           }
-       }
-      /* check obsoletes in both directions */
-      if ((!installed || s->repo != installed) && s->obsoletes && !(solv->noobsoletes.size && MAPTST(&solv->noobsoletes, -r->p)))
-       {
-         obsp = s->repo->idarraydata + s->obsoletes;
-         while ((obs = *obsp++) != 0)
-           {
-             FOR_PROVIDES(p, pp, obs)
-               {
-                 if (p != -w2)
-                   continue;
-                 if (!solv->obsoleteusesprovides && !pool_match_nevr(pool, pool->solvables + p, obs))
-                   continue;
-                 *depp = obs;
-                 *sourcep = -r->p;
-                 *targetp = p;
-                 return SOLVER_PROBLEM_PACKAGE_OBSOLETES;
-               }
-           }
-       }
-      if ((!installed || s2->repo != installed) && s2->obsoletes && !(solv->noobsoletes.size && MAPTST(&solv->noobsoletes, -w2)))
-       {
-         obsp = s2->repo->idarraydata + s2->obsoletes;
-         while ((obs = *obsp++) != 0)
-           {
-             FOR_PROVIDES(p, pp, obs)
-               {
-                 if (p != -r->p)
-                   continue;
-                 if (!solv->obsoleteusesprovides && !pool_match_nevr(pool, pool->solvables + p, obs))
-                   continue;
-                 *depp = obs;
-                 *sourcep = -w2;
-                 *targetp = p;
-                 return SOLVER_PROBLEM_PACKAGE_OBSOLETES;
-               }
-           }
-       }
-      if (solv->implicitobsoleteusesprovides && (!installed || s->repo != installed) && !(solv->noobsoletes.size && MAPTST(&solv->noobsoletes, -r->p)))
-       {
-         FOR_PROVIDES(p, pp, s->name)
-           {
-             if (p != -w2)
-               continue;
-             *depp = s->name;
-             *sourcep = -r->p;
-             *targetp = p;
-             return SOLVER_PROBLEM_PACKAGE_OBSOLETES;
-           }
-       }
-      if (solv->implicitobsoleteusesprovides && (!installed || s2->repo != installed) && !(solv->noobsoletes.size && MAPTST(&solv->noobsoletes, -w2)))
-       {
-         FOR_PROVIDES(p, pp, s2->name)
-           {
-             if (p != -r->p)
-               continue;
-             *depp = s2->name;
-             *sourcep = -w2;
-             *targetp = p;
-             return SOLVER_PROBLEM_PACKAGE_OBSOLETES;
-           }
-       }
-      /* all cases checked, can't happen */
-      *depp = 0;
-      *sourcep = -r->p;
-      *targetp = 0;
-      return SOLVER_PROBLEM_RPM_RULE;
-    }
-  /* simple requires */
-  if (s->requires)
-    {
-      reqp = s->repo->idarraydata + s->requires;
-      while ((req = *reqp++) != 0)
-       {
-         if (req == SOLVABLE_PREREQMARKER)
-           continue;
-         dp = pool->whatprovidesdata + pool_whatprovides(pool, req);
-         if (d == 0)
-           {
-             if (*dp == r->w2 && dp[1] == 0)
-               break;
-           }
-         else if (dp - pool->whatprovidesdata == d)
-           break;
-       }
-      if (req)
-       {
-         *depp = req;
-         *sourcep = -r->p;
-         *targetp = 0;
-         return SOLVER_PROBLEM_DEP_PROVIDERS_NOT_INSTALLABLE;
-       }
-    }
-  /* all cases checked, can't happen */
-  *depp = 0;
-  *sourcep = -r->p;
-  *targetp = 0;
-  return SOLVER_PROBLEM_RPM_RULE;
-}
-
-
-/*-------------------------------------------------------------------
- * 
- * find problem rule
- */
-
-static void
-findproblemrule_internal(Solver *solv, Id idx, Id *reqrp, Id *conrp, Id *sysrp, Id *jobrp)
-{
-  Id rid, d;
-  Id lreqr, lconr, lsysr, ljobr;
-  Rule *r;
-  int reqassert = 0;
-
-  lreqr = lconr = lsysr = ljobr = 0;
-  while ((rid = solv->learnt_pool.elements[idx++]) != 0)
-    {
-      assert(rid > 0);
-      if (rid >= solv->learntrules)
-       findproblemrule_internal(solv, solv->learnt_why.elements[rid - solv->learntrules], &lreqr, &lconr, &lsysr, &ljobr);
-      else if (rid >= solv->jobrules && rid < solv->jobrules_end)
-       {
-         if (!*jobrp)
-           *jobrp = rid;
-       }
-      else if (rid >= solv->updaterules && rid < solv->updaterules_end)
-       {
-         if (!*sysrp)
-           *sysrp = rid;
-       }
-      else
-       {
-         assert(rid < solv->rpmrules_end);
-         r = solv->rules + rid;
-         d = r->d < 0 ? -r->d - 1 : r->d;
-         if (!d && r->w2 < 0)
-           {
-             if (!*conrp)
-               *conrp = rid;
-           }
-         else
-           {
-             if (!d && r->w2 == 0 && !reqassert)
-               {
-                 if (*reqrp > 0 && r->p < -1)
-                   {
-                     Id op = -solv->rules[*reqrp].p;
-                     if (op > 1 && solv->pool->solvables[op].arch != solv->pool->solvables[-r->p].arch)
-                       continue;       /* different arch, skip */
-                   }
-                 /* prefer assertions */
-                 *reqrp = rid;
-                 reqassert = 1;
-               }
-             if (!*reqrp)
-               *reqrp = rid;
-             else if (solv->installed && r->p < 0 && solv->pool->solvables[-r->p].repo == solv->installed && !reqassert)
+             level = setpropagatelearn(solv, level, p, disablerules, why);
+             if (level == 0)
                {
-                 /* prefer rules of installed packages */
-                 *reqrp = rid;
+                 queue_free(&dq);
+                 queue_free(&dqs);
+                 return;
                }
+             continue;
            }
+         /* all branches done, we're finally finished */
+         break;
        }
-    }
-  if (!*reqrp && lreqr)
-    *reqrp = lreqr;
-  if (!*conrp && lconr)
-    *conrp = lconr;
-  if (!*jobrp && ljobr)
-    *jobrp = ljobr;
-  if (!*sysrp && lsysr)
-    *sysrp = lsysr;
-}
-
-
-/*-------------------------------------------------------------------
- * 
- * find problem rule
- *
- * search for a rule that describes the problem to the
- * user. A pretty hopeless task, actually. We currently
- * prefer simple requires.
- */
-
-Id
-solver_findproblemrule(Solver *solv, Id problem)
-{
-  Id reqr, conr, sysr, jobr;
-  Id idx = solv->problems.elements[problem - 1];
-  reqr = conr = sysr = jobr = 0;
-  findproblemrule_internal(solv, idx, &reqr, &conr, &sysr, &jobr);
-  if (reqr)
-    return reqr;
-  if (conr)
-    return conr;
-  if (sysr)
-    return sysr;
-  if (jobr)
-    return jobr;
-  assert(0);
-}
-
-
-/*-------------------------------------------------------------------
- * 
- * create reverse obsoletes map for installed solvables
- *
- * for each installed solvable find which packages with *different* names
- * obsolete the solvable.
- * this index is used in policy_findupdatepackages if noupdateprovide is set.
- */
-
-static void
-create_obsolete_index(Solver *solv)
-{
-  Pool *pool = solv->pool;
-  Solvable *s;
-  Repo *installed = solv->installed;
-  Id p, pp, obs, *obsp, *obsoletes, *obsoletes_data;
-  int i, n;
 
-  if (!installed || !installed->nsolvables)
-    return;
-  solv->obsoletes = obsoletes = sat_calloc(installed->end - installed->start, sizeof(Id));
-  for (i = 1; i < pool->nsolvables; i++)
-    {
-      s = pool->solvables + i;
-      if (!s->obsoletes)
-       continue;
-      if (!pool_installable(pool, s))
-       continue;
-      obsp = s->repo->idarraydata + s->obsoletes;
-      while ((obs = *obsp++) != 0)
+      /* minimization step */
+     if (solv->branches.count)
        {
-         FOR_PROVIDES(p, pp, obs)
+         int l = 0, lasti = -1, lastl = -1;
+         Id why;
+
+         p = 0;
+         for (i = solv->branches.count - 1; i >= 0; i--)
            {
-             if (pool->solvables[p].repo != installed)
-               continue;
-             if (pool->solvables[p].name == s->name)
-               continue;
-             if (!solv->obsoleteusesprovides && !pool_match_nevr(pool, pool->solvables + p, obs))
-               continue;
-             obsoletes[p - installed->start]++;
+             p = solv->branches.elements[i];
+             if (p < 0)
+               l = -p;
+             else if (p > 0 && solv->decisionmap[p] > l + 1)
+               {
+                 lasti = i;
+                 lastl = l;
+               }
            }
-       }
-    }
-  n = 0;
-  for (i = 0; i < installed->nsolvables; i++)
-    if (obsoletes[i])
-      {
-        n += obsoletes[i] + 1;
-        obsoletes[i] = n;
-      }
-  solv->obsoletes_data = obsoletes_data = sat_calloc(n + 1, sizeof(Id));
-  POOL_DEBUG(SAT_DEBUG_STATS, "obsoletes data: %d entries\n", n + 1);
-  for (i = pool->nsolvables - 1; i > 0; i--)
-    {
-      s = pool->solvables + i;
-      if (!s->obsoletes)
-       continue;
-      if (!pool_installable(pool, s))
-       continue;
-      obsp = s->repo->idarraydata + s->obsoletes;
-      while ((obs = *obsp++) != 0)
-       {
-         FOR_PROVIDES(p, pp, obs)
+         if (lasti >= 0)
            {
-             if (pool->solvables[p].repo != installed)
-               continue;
-             if (pool->solvables[p].name == s->name)
-               continue;
-             if (!solv->obsoleteusesprovides && !pool_match_nevr(pool, pool->solvables + p, obs))
-               continue;
-             p -= installed->start;
-             if (obsoletes_data[obsoletes[p]] != i)
-               obsoletes_data[--obsoletes[p]] = i;
+             /* kill old solvable so that we do not loop */
+             p = solv->branches.elements[lasti];
+             solv->branches.elements[lasti] = 0;
+             POOL_DEBUG(SAT_DEBUG_STATS, "minimizing %d -> %d with %s\n", solv->decisionmap[p], lastl, solvid2str(pool, p));
+             minimizationsteps++;
+
+             level = lastl;
+             revert(solv, level);
+             why = -solv->decisionq_why.elements[solv->decisionq_why.count];
+             assert(why >= 0);
+             olevel = level;
+             level = setpropagatelearn(solv, level, p, disablerules, why);
+             if (level == 0)
+               {
+                 queue_free(&dq);
+                 queue_free(&dqs);
+                 return;
+               }
+             continue;
            }
        }
+      break;
     }
+  POOL_DEBUG(SAT_DEBUG_STATS, "solver statistics: %d learned rules, %d unsolvable, %d minimization steps\n", solv->stats_learned, solv->stats_unsolvable, minimizationsteps);
+
+  POOL_DEBUG(SAT_DEBUG_STATS, "done solving.\n\n");
+  queue_free(&dq);
+  queue_free(&dqs);
 }
 
 
 /*-------------------------------------------------------------------
  * 
  * remove disabled conflicts
+ *
+ * purpose: update the decisionmap after some rules were disabled.
+ * this is used to calculate the suggested/recommended package list.
+ * Also returns a "removed" list to undo the discisionmap changes.
  */
 
 static void
@@ -4121,7 +2044,7 @@ removedisabledconflicts(Solver *solv, Queue *removed)
       if (r->d < 0 && decisionmap[-p])
        {
          /* rule is now disabled, remove from decisionmap */
-         POOL_DEBUG(SAT_DEBUG_SCHUBI, "removing conflict for package %s[%d]\n", solvable2str(pool, pool->solvables - p), -p);
+         POOL_DEBUG(SAT_DEBUG_SCHUBI, "removing conflict for package %s[%d]\n", solvid2str(pool, -p), -p);
          queue_push(removed, -p);
          queue_push(removed, decisionmap[-p]);
          decisionmap[-p] = 0;
@@ -4184,7 +2107,7 @@ removedisabledconflicts(Solver *solv, Queue *removed)
        }
       if (new)
        {
-         POOL_DEBUG(SAT_DEBUG_SCHUBI, "re-conflicting package %s[%d]\n", solvable2str(pool, pool->solvables - new), -new);
+         POOL_DEBUG(SAT_DEBUG_SCHUBI, "re-conflicting package %s[%d]\n", solvid2str(pool, -new), -new);
          decisionmap[-new] = -1;
          new = 0;
          n = 0;        /* redo all rules */
@@ -4192,6 +2115,14 @@ removedisabledconflicts(Solver *solv, Queue *removed)
     }
 }
 
+static inline void
+undo_removedisabledconflicts(Solver *solv, Queue *removed)
+{
+  int i;
+  for (i = 0; i < removed->count; i += 2)
+    solv->decisionmap[removed->elements[i]] = removed->elements[i + 1];
+}
+
 
 /*-------------------------------------------------------------------
  *
@@ -4214,8 +2145,207 @@ weaken_solvable_deps(Solver *solv, Id p)
     }
 }
 
-/********************************************************************/
-/* main() */
+
+/********************************************************************/
+/* main() */
+
+
+static void
+findrecommendedsuggested(Solver *solv)
+{
+  Pool *pool = solv->pool;
+  Queue redoq, disabledq;
+  int goterase, i;
+  Solvable *s;
+  Rule *r;
+  Map obsmap;
+
+  map_init(&obsmap, pool->nsolvables);
+  if (solv->installed)
+    {
+      Id obs, *obsp, p, po, ppo;
+      for (p = solv->installed->start; p < solv->installed->end; p++)
+       {
+         s = pool->solvables + p;
+         if (s->repo != solv->installed || !s->obsoletes)
+           continue;
+         if (solv->decisionmap[p] <= 0)
+           continue;
+         if (solv->noobsoletes.size && MAPTST(&solv->noobsoletes, p))
+           continue;
+         obsp = s->repo->idarraydata + s->obsoletes;
+         /* foreach obsoletes */
+         while ((obs = *obsp++) != 0)
+           FOR_PROVIDES(po, ppo, obs)
+             MAPSET(&obsmap, po);
+       }
+    }
+
+  queue_init(&redoq);
+  queue_init(&disabledq);
+  goterase = 0;
+  /* disable all erase jobs (including weak "keep uninstalled" rules) */
+  for (i = solv->jobrules, r = solv->rules + i; i < solv->jobrules_end; i++, r++)
+    {
+      if (r->d < 0)    /* disabled ? */
+       continue;
+      if (r->p >= 0)   /* install job? */
+       continue;
+      queue_push(&disabledq, i);
+      solver_disablerule(solv, r);
+      goterase++;
+    }
+  
+  if (goterase)
+    {
+      enabledisablelearntrules(solv);
+      removedisabledconflicts(solv, &redoq);
+    }
+
+  /*
+   * find recommended packages
+   */
+    
+  /* if redoq.count == 0 we already found all recommended in the
+   * solver run */
+  if (redoq.count || solv->dontinstallrecommended || !solv->dontshowinstalledrecommended || solv->ignorealreadyrecommended)
+    {
+      Id rec, *recp, p, pp;
+
+      /* create map of all recommened packages */
+      solv->recommends_index = -1;
+      MAPZERO(&solv->recommendsmap);
+      for (i = 0; i < solv->decisionq.count; i++)
+       {
+         p = solv->decisionq.elements[i];
+         if (p < 0)
+           continue;
+         s = pool->solvables + p;
+         if (s->recommends)
+           {
+             recp = s->repo->idarraydata + s->recommends;
+             while ((rec = *recp++) != 0)
+               {
+                 FOR_PROVIDES(p, pp, rec)
+                   if (solv->decisionmap[p] > 0)
+                     break;
+                 if (p)
+                   {
+                     if (!solv->dontshowinstalledrecommended)
+                       {
+                         FOR_PROVIDES(p, pp, rec)
+                           if (solv->decisionmap[p] > 0)
+                             MAPSET(&solv->recommendsmap, p);
+                       }
+                     continue; /* p != 0: already fulfilled */
+                   }
+                 FOR_PROVIDES(p, pp, rec)
+                   MAPSET(&solv->recommendsmap, p);
+               }
+           }
+       }
+      for (i = 1; i < pool->nsolvables; i++)
+       {
+         if (solv->decisionmap[i] < 0)
+           continue;
+         if (solv->decisionmap[i] > 0 && solv->dontshowinstalledrecommended)
+           continue;
+          if (MAPTST(&obsmap, i))
+           continue;
+         s = pool->solvables + i;
+         if (!MAPTST(&solv->recommendsmap, i))
+           {
+             if (!s->supplements)
+               continue;
+             if (!pool_installable(pool, s))
+               continue;
+             if (!solver_is_supplementing(solv, s))
+               continue;
+           }
+         if (solv->dontinstallrecommended)
+           queue_push(&solv->recommendations, i);
+         else
+           queue_pushunique(&solv->recommendations, i);
+       }
+      /* we use MODE_SUGGEST here so that repo prio is ignored */
+      policy_filter_unwanted(solv, &solv->recommendations, POLICY_MODE_SUGGEST);
+    }
+
+  /*
+   * find suggested packages
+   */
+    
+  if (1)
+    {
+      Id sug, *sugp, p, pp;
+
+      /* create map of all suggests that are still open */
+      solv->recommends_index = -1;
+      MAPZERO(&solv->suggestsmap);
+      for (i = 0; i < solv->decisionq.count; i++)
+       {
+         p = solv->decisionq.elements[i];
+         if (p < 0)
+           continue;
+         s = pool->solvables + p;
+         if (s->suggests)
+           {
+             sugp = s->repo->idarraydata + s->suggests;
+             while ((sug = *sugp++) != 0)
+               {
+                 FOR_PROVIDES(p, pp, sug)
+                   if (solv->decisionmap[p] > 0)
+                     break;
+                 if (p)
+                   {
+                     if (!solv->dontshowinstalledrecommended)
+                       {
+                         FOR_PROVIDES(p, pp, sug)
+                           if (solv->decisionmap[p] > 0)
+                             MAPSET(&solv->suggestsmap, p);
+                       }
+                     continue; /* already fulfilled */
+                   }
+                 FOR_PROVIDES(p, pp, sug)
+                   MAPSET(&solv->suggestsmap, p);
+               }
+           }
+       }
+      for (i = 1; i < pool->nsolvables; i++)
+       {
+         if (solv->decisionmap[i] < 0)
+           continue;
+         if (solv->decisionmap[i] > 0 && solv->dontshowinstalledrecommended)
+           continue;
+          if (MAPTST(&obsmap, i))
+           continue;
+         s = pool->solvables + i;
+         if (!MAPTST(&solv->suggestsmap, i))
+           {
+             if (!s->enhances)
+               continue;
+             if (!pool_installable(pool, s))
+               continue;
+             if (!solver_is_enhancing(solv, s))
+               continue;
+           }
+         queue_push(&solv->suggestions, i);
+       }
+      policy_filter_unwanted(solv, &solv->suggestions, POLICY_MODE_SUGGEST);
+    }
+
+  /* undo removedisabledconflicts */
+  if (redoq.count)
+    undo_removedisabledconflicts(solv, &redoq);
+  queue_free(&redoq);
+  
+  /* undo job rule disabling */
+  for (i = 0; i < disabledq.count; i++)
+    solver_enablerule(solv, solv->rules + disabledq.elements[i]);
+  queue_free(&disabledq);
+  map_free(&obsmap);
+}
+
 
 /*
  *
@@ -4233,40 +2363,43 @@ solver_solve(Solver *solv, Queue *job)
   Map addedmap;                       /* '1' == have rpm-rules for solvable */
   Map installcandidatemap;
   Id how, what, select, name, weak, p, pp, d;
-  Queue q, redoq;
+  Queue q;
   Solvable *s;
-  int goterase;
   Rule *r;
   int now, solve_start;
+  int hasdupjob = 0;
 
   solve_start = sat_timems(0);
+
+  /* log solver options */
   POOL_DEBUG(SAT_DEBUG_STATS, "solver started\n");
-  POOL_DEBUG(SAT_DEBUG_STATS, "fixsystem=%d updatesystem=%d dosplitprovides=%d, noupdateprovide=%d\n", solv->fixsystem, solv->updatesystem, solv->dosplitprovides, solv->noupdateprovide);
+  POOL_DEBUG(SAT_DEBUG_STATS, "fixsystem=%d updatesystem=%d dosplitprovides=%d, noupdateprovide=%d noinfarchcheck=%d\n", solv->fixsystem, solv->updatesystem, solv->dosplitprovides, solv->noupdateprovide, solv->noinfarchcheck);
   POOL_DEBUG(SAT_DEBUG_STATS, "distupgrade=%d distupgrade_removeunsupported=%d\n", solv->distupgrade, solv->distupgrade_removeunsupported);
   POOL_DEBUG(SAT_DEBUG_STATS, "allowuninstall=%d, allowdowngrade=%d, allowarchchange=%d, allowvendorchange=%d\n", solv->allowuninstall, solv->allowdowngrade, solv->allowarchchange, solv->allowvendorchange);
   POOL_DEBUG(SAT_DEBUG_STATS, "promoteepoch=%d, allowvirtualconflicts=%d, allowselfconflicts=%d\n", pool->promoteepoch, solv->allowvirtualconflicts, solv->allowselfconflicts);
   POOL_DEBUG(SAT_DEBUG_STATS, "obsoleteusesprovides=%d, implicitobsoleteusesprovides=%d\n", solv->obsoleteusesprovides, solv->implicitobsoleteusesprovides);
   POOL_DEBUG(SAT_DEBUG_STATS, "dontinstallrecommended=%d, ignorealreadyrecommended=%d, dontshowinstalledrecommended=%d\n", solv->dontinstallrecommended, solv->ignorealreadyrecommended, solv->dontshowinstalledrecommended);
+
   /* create whatprovides if not already there */
   if (!pool->whatprovides)
     pool_createwhatprovides(pool);
 
   /* create obsolete index */
-  create_obsolete_index(solv);
+  policy_create_obsolete_index(solv);
 
   /* remember job */
-  solv->job = job;
+  queue_free(&solv->job);
+  queue_init_clone(&solv->job, job);
 
   /*
    * create basic rule set of all involved packages
    * use addedmap bitmap to make sure we don't create rules twice
-   *
    */
 
   /* create noobsolete map if needed */
   for (i = 0; i < job->count; i += 2)
     {
-      how = job->elements[i] & ~SOLVER_WEAK;
+      how = job->elements[i];
       if ((how & SOLVER_JOBMASK) != SOLVER_NOOBSOLETES)
        continue;
       what = job->elements[i + 1];
@@ -4278,17 +2411,11 @@ solver_solve(Solver *solv, Queue *job)
     }
 
   map_init(&addedmap, pool->nsolvables);
+  MAPSET(&addedmap, SYSTEMSOLVABLE);
+
   map_init(&installcandidatemap, pool->nsolvables);
   queue_init(&q);
 
-  /*
-   * always install our system solvable
-   */
-  MAPSET(&addedmap, SYSTEMSOLVABLE);
-  queue_push(&solv->decisionq, SYSTEMSOLVABLE);
-  queue_push(&solv->decisionq_why, 0);
-  solv->decisionmap[SYSTEMSOLVABLE] = 1; /* installed at level '1' */
-
   now = sat_timems(0);
   /*
    * create rules for all package that could be involved with the solving
@@ -4300,12 +2427,12 @@ solver_solve(Solver *solv, Queue *job)
       oldnrules = solv->nrules;
       POOL_DEBUG(SAT_DEBUG_SCHUBI, "*** create rpm rules for installed solvables ***\n");
       FOR_REPO_SOLVABLES(installed, p, s)
-       addrpmrulesforsolvable(solv, s, &addedmap);
+       solver_addrpmrulesforsolvable(solv, s, &addedmap);
       POOL_DEBUG(SAT_DEBUG_STATS, "added %d rpm rules for installed solvables\n", solv->nrules - oldnrules);
       POOL_DEBUG(SAT_DEBUG_SCHUBI, "*** create rpm rules for updaters of installed solvables ***\n");
       oldnrules = solv->nrules;
       FOR_REPO_SOLVABLES(installed, p, s)
-       addrpmrulesforupdaters(solv, s, &addedmap, 1);
+       solver_addrpmrulesforupdaters(solv, s, &addedmap, 1);
       POOL_DEBUG(SAT_DEBUG_STATS, "added %d rpm rules for updaters of installed solvables\n", solv->nrules - oldnrules);
     }
 
@@ -4328,28 +2455,36 @@ solver_solve(Solver *solv, Queue *job)
          FOR_JOB_SELECT(p, pp, select, what)
            {
              MAPSET(&installcandidatemap, p);
-             addrpmrulesforsolvable(solv, pool->solvables + p, &addedmap);
+             solver_addrpmrulesforsolvable(solv, pool->solvables + p, &addedmap);
            }
          break;
-       case SOLVER_UPDATE:
-         /* FIXME: semantics? */
-         FOR_JOB_SELECT(p, pp, select, what)
-           addrpmrulesforupdaters(solv, pool->solvables + what, &addedmap, 0);
+       case SOLVER_DISTUPGRADE:
+         if (!solv->distupgrade)
+           hasdupjob = 1;
+         break;
+       default:
          break;
        }
     }
   POOL_DEBUG(SAT_DEBUG_STATS, "added %d rpm rules for packages involved in a job\n", solv->nrules - oldnrules);
 
-  POOL_DEBUG(SAT_DEBUG_SCHUBI, "*** create rpm rules for recommended/suggested packages ***\n");
-
-  oldnrules = solv->nrules;
     
-    /*
-     * add rules for suggests, enhances
-     */
-  addrpmrulesforweak(solv, &addedmap);
+  /*
+   * add rules for suggests, enhances
+   */
+  POOL_DEBUG(SAT_DEBUG_SCHUBI, "*** create rpm rules for suggested/enhanced packages ***\n");
+  oldnrules = solv->nrules;
+  solver_addrpmrulesforweak(solv, &addedmap);
   POOL_DEBUG(SAT_DEBUG_STATS, "added %d rpm rules because of weak dependencies\n", solv->nrules - oldnrules);
 
+  /*
+   * first pass done, we now have all the rpm rules we need.
+   * unify existing rules before going over all job rules and
+   * policy rules.
+   * at this point the system is always solvable,
+   * as an empty system (remove all packages) is a valid solution
+   */
+
   IF_POOLDEBUG (SAT_DEBUG_STATS)
     {
       int possible = 0, installable = 0;
@@ -4363,23 +2498,17 @@ solver_solve(Solver *solv, Queue *job)
       POOL_DEBUG(SAT_DEBUG_STATS, "%d of %d installable solvables considered for solving\n", possible, installable);
     }
 
-  /*
-   * first pass done, we now have all the rpm rules we need.
-   * unify existing rules before going over all job rules and
-   * policy rules.
-   * at this point the system is always solvable,
-   * as an empty system (remove all packages) is a valid solution
-   */
-
-  unifyrules(solv);                              /* remove duplicate rpm rules */
-
+  solver_unifyrules(solv);                          /* remove duplicate rpm rules */
   solv->rpmrules_end = solv->nrules;              /* mark end of rpm rules */
 
-  solv->directdecisions = solv->decisionq.count;
   POOL_DEBUG(SAT_DEBUG_STATS, "rpm rule memory usage: %d K\n", solv->nrules * (int)sizeof(Rule) / 1024);
-  POOL_DEBUG(SAT_DEBUG_STATS, "decisions so far: %d\n", solv->decisionq.count);
   POOL_DEBUG(SAT_DEBUG_STATS, "rpm rule creation took %d ms\n", sat_timems(now));
 
+  /* create dup maps if needed. We need the maps early to create our
+   * update rules */
+  if (hasdupjob)
+    solver_createdupmaps(solv);
+
   /*
    * create feature rules
    * 
@@ -4396,21 +2525,17 @@ solver_solve(Solver *solv, Queue *job)
   solv->featurerules = solv->nrules;              /* mark start of feature rules */
   if (installed)
     {
-       /* foreach possibly installed solvable */
+      /* foreach possibly installed solvable */
       for (i = installed->start, s = pool->solvables + i; i < installed->end; i++, s++)
        {
          if (s->repo != installed)
            {
-             addrule(solv, 0, 0);      /* create dummy rule */
+             solver_addrule(solv, 0, 0);       /* create dummy rule */
              continue;
            }
-         addupdaterule(solv, s, 1);    /* allow s to be updated */
+         solver_addupdaterule(solv, s, 1);    /* allow s to be updated */
        }
-       /*
-        * assert one rule per installed solvable,
-        * either an assertion (A)
-        * or a possible update (A|update1(A)|update2(A)|...)
-        */
+      /* make sure we accounted for all rules */
       assert(solv->nrules - solv->featurerules == installed->end - installed->start);
     }
   solv->featurerules_end = solv->nrules;
@@ -4434,13 +2559,13 @@ solver_solve(Solver *solv, Queue *job)
 
          if (s->repo != installed)
            {
-             addrule(solv, 0, 0);      /* create dummy rule */
+             solver_addrule(solv, 0, 0);       /* create dummy rule */
              continue;
            }
-         addupdaterule(solv, s, 0);    /* allowall = 0: downgrades not allowed */
-           /*
-            * check for and remove duplicate
-            */
+         solver_addupdaterule(solv, s, 0);     /* allowall = 0: downgrades not allowed */
+         /*
+          * check for and remove duplicate
+          */
          r = solv->rules + solv->nrules - 1;           /* r: update rule */
          sr = r - (installed->end - installed->start); /* sr: feature rule */
          /* it's orphaned if there is no feature rule or the feature rule
@@ -4452,10 +2577,9 @@ solver_solve(Solver *solv, Queue *job)
              assert(solv->distupgrade && !sr->p);
              continue;
            }
-         unifyrules_sortcmp_data = pool;
-         if (!unifyrules_sortcmp(r, sr))
+         if (!solver_samerule(solv, r, sr))
            {
-             /* identical rule, kill unneeded rule */
+             /* identical rule, kill unneeded one */
              if (solv->allowuninstall)
                {
                  /* keep feature rule, make it weak */
@@ -4475,7 +2599,7 @@ solver_solve(Solver *solv, Queue *job)
              queue_push(&solv->weakruleq, sr - solv->rules);
            }
          else
-           disablerule(solv, sr);
+           solver_disablerule(solv, sr);
        }
       /* consistency check: we added a rule for _every_ installed solvable */
       assert(solv->nrules - solv->updaterules == installed->end - installed->start);
@@ -4520,7 +2644,7 @@ solver_solve(Solver *solv, Queue *job)
              p = queue_shift(&q);      /* get first candidate */
              d = !q.count ? 0 : pool_queuetowhatprovides(pool, &q);    /* internalize */
            }
-         addrule(solv, p, d);          /* add install rule */
+         solver_addrule(solv, p, d);           /* add install rule */
          queue_push(&solv->ruletojob, i);
          if (weak)
            queue_push(&solv->weakruleq, solv->nrules - 1);
@@ -4546,7 +2670,7 @@ solver_solve(Solver *solv, Queue *job)
                  /* keep installcandidates of other jobs */
                  if (MAPTST(&installcandidatemap, p))
                    continue;
-                 addrule(solv, -p, 0);                 /* remove by Id */
+                 solver_addrule(solv, -p, 0);                  /* remove by Id */
                  queue_push(&solv->ruletojob, i);
                  if (weak)
                    queue_push(&solv->weakruleq, solv->nrules - 1);
@@ -4554,7 +2678,7 @@ solver_solve(Solver *solv, Queue *job)
            }
          FOR_JOB_SELECT(p, pp, select, what)
            {
-             addrule(solv, -p, 0);
+             solver_addrule(solv, -p, 0);
              queue_push(&solv->ruletojob, i);
              if (weak)
                queue_push(&solv->weakruleq, solv->nrules - 1);
@@ -4563,14 +2687,15 @@ solver_solve(Solver *solv, Queue *job)
 
        case SOLVER_UPDATE:
          POOL_DEBUG(SAT_DEBUG_JOB, "job: %supdate %s\n", weak ? "weak " : "", solver_select2str(solv, select, what));
-         if (select != SOLVER_SOLVABLE)
-           break;
-         s = pool->solvables + what;
-         POOL_DEBUG(SAT_DEBUG_JOB, "job: %supdate %s\n", weak ? "weak " : "", solvable2str(pool, s));
-         addupdaterule(solv, s, 0);
-         queue_push(&solv->ruletojob, i);
-         if (weak)
-           queue_push(&solv->weakruleq, solv->nrules - 1);
+         FOR_JOB_SELECT(p, pp, select, what)
+           {
+             s = pool->solvables + p;
+             if (!solv->installed || s->repo != solv->installed)
+               continue;
+             if (!solv->updatemap.size)
+               map_init(&solv->updatemap, pool->nsolvables);
+             MAPSET(&solv->updatemap, p);
+           }
          break;
        case SOLVER_WEAKENDEPS:
          POOL_DEBUG(SAT_DEBUG_JOB, "job: %sweaken deps %s\n", weak ? "weak " : "", solver_select2str(solv, select, what));
@@ -4588,14 +2713,17 @@ solver_solve(Solver *solv, Queue *job)
            {
              s = pool->solvables + p;
              if (installed && s->repo == installed)
-               addrule(solv, p, 0);
+               solver_addrule(solv, p, 0);
              else
-               addrule(solv, -p, 0);
+               solver_addrule(solv, -p, 0);
              queue_push(&solv->ruletojob, i);
              if (weak)
                queue_push(&solv->weakruleq, solv->nrules - 1);
            }
          break;
+       case SOLVER_DISTUPGRADE:
+         POOL_DEBUG(SAT_DEBUG_JOB, "job: distupgrade repo #%d\n", what);
+         break;
        default:
          POOL_DEBUG(SAT_DEBUG_JOB, "job: unknown job\n");
          break;
@@ -4620,16 +2748,33 @@ solver_solve(Solver *solv, Queue *job)
   assert(solv->ruletojob.count == solv->nrules - solv->jobrules);
   solv->jobrules_end = solv->nrules;
 
-    /* all rules created
-     * --------------------------------------------------------------
-     * prepare for solving
-     */
+  /* now create infarch and dup rules */
+  if (!solv->noinfarchcheck)
+    solver_addinfarchrules(solv, &addedmap);
+  else
+    solv->infarchrules = solv->infarchrules_end = solv->nrules;
+
+  if (hasdupjob)
+    {
+      solver_addduprules(solv, &addedmap);
+      solver_freedupmaps(solv);        /* no longer needed */
+    }
+  else
+    solv->duprules = solv->duprules_end = solv->nrules;
+
+
+  /* all rules created
+   * --------------------------------------------------------------
+   * prepare for solving
+   */
     
   /* free unneeded memory */
   map_free(&addedmap);
   map_free(&installcandidatemap);
   queue_free(&q);
 
+  POOL_DEBUG(SAT_DEBUG_STATS, "%d rpm rules, %d job rules, %d infarch rules, %d dup rules\n", solv->rpmrules_end - 1, solv->jobrules_end - solv->jobrules, solv->infarchrules_end - solv->infarchrules, solv->duprules_end - solv->duprules);
+
   /* create weak map */
   map_init(&solv->weakrulemap, solv->nrules);
   for (i = 0; i < solv->weakruleq.count; i++)
@@ -4641,6 +2786,9 @@ solver_solve(Solver *solv, Queue *job)
   /* all new rules are learnt after this point */
   solv->learntrules = solv->nrules;
 
+  /* create watches chains */
+  makewatches(solv);
+
   /* create assertion index. it is only used to speed up
    * makeruledecsions() a bit */
   for (i = 1, r = solv->rules + i; i < solv->nrules; i++, r++)
@@ -4648,14 +2796,10 @@ solver_solve(Solver *solv, Queue *job)
       queue_push(&solv->ruleassertions, i);
 
   /* disable update rules that conflict with our job */
-  disableupdaterules(solv, job, -1);
+  solver_disablepolicyrules(solv);
 
   /* make decisions based on job/update assertions */
   makeruledecisions(solv);
-
-  /* create watches chains */
-  makewatches(solv);
-
   POOL_DEBUG(SAT_DEBUG_STATS, "problems so far: %d\n", solv->problems.count);
 
   /*
@@ -4665,197 +2809,26 @@ solver_solve(Solver *solv, Queue *job)
    */
     
   now = sat_timems(0);
-  run_solver(solv, 1, solv->dontinstallrecommended ? 0 : 1);
+  solver_run_sat(solv, 1, solv->dontinstallrecommended ? 0 : 1);
   POOL_DEBUG(SAT_DEBUG_STATS, "solver took %d ms\n", sat_timems(now));
 
-  queue_init(&redoq);
-  goterase = 0;
-  /* disable all erase jobs (including weak "keep uninstalled" rules) */
-  for (i = solv->jobrules, r = solv->rules + i; i < solv->learntrules; i++, r++)
-    {
-      if (r->d < 0)    /* disabled ? */
-       continue;
-      if (r->p > 0)    /* install job? */
-       continue;
-      disablerule(solv, r);
-      goterase++;
-    }
-  
-  if (goterase)
-    {
-      enabledisablelearntrules(solv);
-      removedisabledconflicts(solv, &redoq);
-    }
-
   /*
-   * find recommended packages
+   * calculate recommended/suggested packages
    */
-    
-  /* if redoq.count == 0 we already found all recommended in the
-   * solver run */
-  if (redoq.count || solv->dontinstallrecommended || !solv->dontshowinstalledrecommended || solv->ignorealreadyrecommended)
-    {
-      Id rec, *recp, p, pp;
-
-      /* create map of all recommened packages */
-      solv->recommends_index = -1;
-      MAPZERO(&solv->recommendsmap);
-      for (i = 0; i < solv->decisionq.count; i++)
-       {
-         p = solv->decisionq.elements[i];
-         if (p < 0)
-           continue;
-         s = pool->solvables + p;
-         if (s->recommends)
-           {
-             recp = s->repo->idarraydata + s->recommends;
-             while ((rec = *recp++) != 0)
-               {
-                 FOR_PROVIDES(p, pp, rec)
-                   if (solv->decisionmap[p] > 0)
-                     break;
-                 if (p)
-                   {
-                     if (!solv->dontshowinstalledrecommended)
-                       {
-                         FOR_PROVIDES(p, pp, rec)
-                           if (solv->decisionmap[p] > 0)
-                             MAPSET(&solv->recommendsmap, p);
-                       }
-                     continue; /* p != 0: already fulfilled */
-                   }
-                 FOR_PROVIDES(p, pp, rec)
-                   MAPSET(&solv->recommendsmap, p);
-               }
-           }
-       }
-      for (i = 1; i < pool->nsolvables; i++)
-       {
-         if (solv->decisionmap[i] < 0)
-           continue;
-         if (solv->decisionmap[i] > 0 && solv->dontshowinstalledrecommended)
-           continue;
-         s = pool->solvables + i;
-         if (!MAPTST(&solv->recommendsmap, i))
-           {
-             if (!s->supplements)
-               continue;
-             if (!pool_installable(pool, s))
-               continue;
-             if (!solver_is_supplementing(solv, s))
-               continue;
-           }
-         if (solv->dontinstallrecommended)
-           queue_push(&solv->recommendations, i);
-         else
-           queue_pushunique(&solv->recommendations, i);
-       }
-      /* we use MODE_SUGGEST here so that repo prio is ignored */
-      policy_filter_unwanted(solv, &solv->recommendations, POLICY_MODE_SUGGEST);
-    }
+  findrecommendedsuggested(solv);
 
   /*
-   * find suggested packages
+   * prepare solution queue if there were problems
    */
-    
-  if (1)
-    {
-      Id sug, *sugp, p, pp;
-
-      /* create map of all suggests that are still open */
-      solv->recommends_index = -1;
-      MAPZERO(&solv->suggestsmap);
-      for (i = 0; i < solv->decisionq.count; i++)
-       {
-         p = solv->decisionq.elements[i];
-         if (p < 0)
-           continue;
-         s = pool->solvables + p;
-         if (s->suggests)
-           {
-             sugp = s->repo->idarraydata + s->suggests;
-             while ((sug = *sugp++) != 0)
-               {
-                 FOR_PROVIDES(p, pp, sug)
-                   if (solv->decisionmap[p] > 0)
-                     break;
-                 if (p)
-                   {
-                     if (!solv->dontshowinstalledrecommended)
-                       {
-                         FOR_PROVIDES(p, pp, sug)
-                           if (solv->decisionmap[p] > 0)
-                             MAPSET(&solv->suggestsmap, p);
-                       }
-                     continue; /* already fulfilled */
-                   }
-                 FOR_PROVIDES(p, pp, sug)
-                   MAPSET(&solv->suggestsmap, p);
-               }
-           }
-       }
-      for (i = 1; i < pool->nsolvables; i++)
-       {
-         if (solv->decisionmap[i] < 0)
-           continue;
-         if (solv->decisionmap[i] > 0 && solv->dontshowinstalledrecommended)
-           continue;
-         s = pool->solvables + i;
-         if (!MAPTST(&solv->suggestsmap, i))
-           {
-             if (!s->enhances)
-               continue;
-             if (!pool_installable(pool, s))
-               continue;
-             if (!solver_is_enhancing(solv, s))
-               continue;
-           }
-         queue_push(&solv->suggestions, i);
-       }
-      policy_filter_unwanted(solv, &solv->suggestions, POLICY_MODE_SUGGEST);
-    }
-
-  if (redoq.count)
-    {
-      /* restore decisionmap */
-      for (i = 0; i < redoq.count; i += 2)
-       solv->decisionmap[redoq.elements[i]] = redoq.elements[i + 1];
-    }
+  solver_prepare_solutions(solv);
 
-    /*
-     * if unsolvable, prepare solutions
-     */
-
-  if (solv->problems.count)
-    {
-      int recocount = solv->recommendations.count;
-      solv->recommendations.count = 0; /* so that revert() doesn't mess with it */
-      queue_empty(&redoq);
-      for (i = 0; i < solv->decisionq.count; i++)
-       {
-         Id p = solv->decisionq.elements[i];
-         queue_push(&redoq, p);
-         queue_push(&redoq, solv->decisionq_why.elements[i]);
-         queue_push(&redoq, solv->decisionmap[p > 0 ? p : -p]);
-       }
-      problems_to_solutions(solv, job);
-      memset(solv->decisionmap, 0, pool->nsolvables * sizeof(Id));
-      queue_empty(&solv->decisionq);
-      queue_empty(&solv->decisionq_why);
-      for (i = 0; i < redoq.count; i += 3)
-       {
-         Id p = redoq.elements[i];
-         queue_push(&solv->decisionq, p);
-         queue_push(&solv->decisionq_why, redoq.elements[i + 1]);
-         solv->decisionmap[p > 0 ? p : -p] = redoq.elements[i + 2];
-       }
-      solv->recommendations.count = recocount;
-    }
+  /*
+   * finally prepare transaction info
+   */
+  solver_create_transaction(solv);
 
-  queue_free(&redoq);
-  POOL_DEBUG(SAT_DEBUG_STATS, "final solver statistics: %d learned rules, %d unsolvable\n", solv->stats_learned, solv->stats_unsolvable);
+  POOL_DEBUG(SAT_DEBUG_STATS, "final solver statistics: %d problems, %d learned rules, %d unsolvable\n", solv->problems.count / 2, solv->stats_learned, solv->stats_unsolvable);
   POOL_DEBUG(SAT_DEBUG_STATS, "solver_solve took %d ms\n", sat_timems(solve_start));
-  solv->job = 0;
 }
 
 /***********************************************************************/
@@ -4894,6 +2867,17 @@ solver_calc_installsizechange(Solver *solv)
   return change;
 }
 
+void
+solver_trivial_installable(Solver *solv, Queue *pkgs, Queue *res)
+{
+  Map installedmap;
+  solver_create_state_maps(solv, &installedmap, 0);
+  pool_trivial_installable_noobsoletesmap(solv->pool, &installedmap, pkgs, res, solv->noobsoletes.size ? &solv->noobsoletes : 0);
+  map_free(&installedmap);
+}
+
+
+#if 0
 #define FIND_INVOLVED_DEBUG 0
 void
 solver_find_involved(Solver *solv, Queue *installedq, Solvable *ts, Queue *q)
@@ -4966,7 +2950,7 @@ solver_find_involved(Solver *solv, Queue *installedq, Solvable *ts, Queue *q)
                  if (MAPTST(&im, p))
                    {
 #if FIND_INVOLVED_DEBUG
-                     printf("%s requires %s\n", solvable2str(pool, pool->solvables + ip), solvable2str(pool, pool->solvables + p));
+                     printf("%s requires %s\n", solvid2str(pool, ip), solvid2str(pool, p));
 #endif
                      queue_push(&iq, p);
                    }
@@ -4989,7 +2973,7 @@ solver_find_involved(Solver *solv, Queue *installedq, Solvable *ts, Queue *q)
                  if (MAPTST(&im, p))
                    {
 #if FIND_INVOLVED_DEBUG
-                     printf("%s recommends %s\n", solvable2str(pool, pool->solvables + ip), solvable2str(pool, pool->solvables + p));
+                     printf("%s recommends %s\n", solvid2str(pool, ip), solvid2str(pool, p));
 #endif
                      queue_push(&iq, p);
                    }
@@ -5015,7 +2999,7 @@ solver_find_involved(Solver *solv, Queue *installedq, Solvable *ts, Queue *q)
              if (sup)
                {
 #if FIND_INVOLVED_DEBUG
-                 printf("%s supplemented\n", solvable2str(pool, pool->solvables + ip));
+                 printf("%s supplemented\n", solvid2str(pool, ip));
 #endif
                  queue_push(&iq, ip);
                }
@@ -5051,7 +3035,7 @@ solver_find_involved(Solver *solv, Queue *installedq, Solvable *ts, Queue *q)
                      if (p == tp)
                        continue;
 #if FIND_INVOLVED_DEBUG
-                     printf("%s requires %s\n", solvable2str(pool, pool->solvables + ip), solvable2str(pool, pool->solvables + p));
+                     printf("%s requires %s\n", solvid2str(pool, ip), solvid2str(pool, p));
 #endif
                      MAPSET(&im, p);
                      queue_push(&iq, p);
@@ -5071,7 +3055,7 @@ solver_find_involved(Solver *solv, Queue *installedq, Solvable *ts, Queue *q)
                      if (p == tp)
                        continue;
 #if FIND_INVOLVED_DEBUG
-                     printf("%s recommends %s\n", solvable2str(pool, pool->solvables + ip), solvable2str(pool, pool->solvables + p));
+                     printf("%s recommends %s\n", solvid2str(pool, ip), solvid2str(pool, p));
 #endif
                      MAPSET(&im, p);
                      queue_push(&iq, p);
@@ -5099,7 +3083,7 @@ solver_find_involved(Solver *solv, Queue *installedq, Solvable *ts, Queue *q)
              if (sup)
                {
 #if FIND_INVOLVED_DEBUG
-                 printf("%s supplemented\n", solvable2str(pool, pool->solvables + ip));
+                 printf("%s supplemented\n", solvid2str(pool, ip));
 #endif
                  MAPSET(&im, ip);
                  queue_push(&iq, ip);
@@ -5124,5 +3108,5 @@ solver_find_involved(Solver *solv, Queue *installedq, Solvable *ts, Queue *q)
   map_free(&installedm);
   queue_free(&installedq_internal);
 }
+#endif
 
-/* EOF */