Imported Upstream version 0.6.9
[platform/upstream/libsolv.git] / src / solver.c
index 065cb7f..46d0ca3 100644 (file)
  * and is only true if pkg is installed and contains the specified path.
  * we also make sure that pkg is selected for an update, otherwise the
  * update would always be forced onto the user.
+ * Map m is the map used when called from dep_possible.
  */
+
+static int
+solver_is_updating(Solver *solv, Id p)
+{
+  /* check if the update rule is true */
+  Pool *pool = solv->pool;
+  Rule *r;
+  Id l, pp;
+  if (solv->decisionmap[p] >= 0)
+    return 0;  /* old package stayed */
+  r = solv->rules + solv->updaterules + (p - solv->installed->start);
+  FOR_RULELITERALS(l, pp, r)
+    if (l > 0 && l != p && solv->decisionmap[l] > 0)
+      return 1;
+  return 0;
+}
+
 int
-solver_splitprovides(Solver *solv, Id dep)
+solver_splitprovides(Solver *solv, Id dep, Map *m)
 {
   Pool *pool = solv->pool;
   Id p, pp;
   Reldep *rd;
   Solvable *s;
 
-  if (!solv->dosplitprovides || !solv->installed || (!solv->updatemap_all && !solv->updatemap.size))
+  if (!solv->dosplitprovides || !solv->installed)
     return 0;
   if (!ISRELDEP(dep))
     return 0;
@@ -76,8 +94,10 @@ solver_splitprovides(Solver *solv, Id dep)
       /* here we have packages that provide the correct name and contain the path,
        * now do extra filtering */
       s = pool->solvables + p;
-      if (s->repo == solv->installed && s->name == rd->name &&
-          (solv->updatemap_all || (solv->updatemap.size && MAPTST(&solv->updatemap, p - solv->installed->start))))
+      if (s->repo != solv->installed || s->name != rd->name)
+       continue;
+      /* check if the package is updated. if m is set, we're called from dep_possible */
+      if (m || solver_is_updating(solv, p))
        return 1;
     }
   return 0;
@@ -147,7 +167,7 @@ solver_check_installsuppdepq_dep(Solver *solv, Id dep)
           return r1 == 2 || r2 == 2 ? 2 : 1;
        }
       if (rd->flags == REL_NAMESPACE && rd->name == NAMESPACE_SPLITPROVIDES)
-        return solver_splitprovides(solv, rd->evr);
+        return solver_splitprovides(solv, rd->evr, 0);
       if (rd->flags == REL_NAMESPACE && rd->name == NAMESPACE_INSTALLED)
         return solver_dep_installed(solv, rd->evr);
       if (rd->flags == REL_NAMESPACE && (q = solv->installsuppdepq) != 0)
@@ -427,9 +447,9 @@ makeruledecisions(Solver *solv)
          assert(solv->decisionq_why.elements[i] > 0);
 
          /*
-          * conflict with an rpm rule ?
+          * conflict with a pkg rule ?
           */
-         if (solv->decisionq_why.elements[i] < solv->rpmrules_end)
+         if (solv->decisionq_why.elements[i] < solv->pkgrules_end)
            {
              if (record_proof)
                {
@@ -441,7 +461,7 @@ makeruledecisions(Solver *solv)
              else
                queue_push(&solv->problems, 0);
              assert(v > 0 || v == -SYSTEMSOLVABLE);
-             POOL_DEBUG(SOLV_DEBUG_UNSOLVABLE, "conflict with rpm rule, disabling rule #%d\n", ri);
+             POOL_DEBUG(SOLV_DEBUG_UNSOLVABLE, "conflict with pkg rule, disabling rule #%d\n", ri);
              if (ri >= solv->jobrules && ri < solv->jobrules_end)
                v = -(solv->ruletojob.elements[ri - solv->jobrules] + 1);
              else
@@ -583,7 +603,7 @@ makewatches(Solver *solv)
                                       /* lower half for removals, upper half for installs */
   solv->watches = solv_calloc(2 * nsolvables, sizeof(Id));
 #if 1
-  /* do it reverse so rpm rules get triggered first (XXX: obsolete?) */
+  /* do it reverse so pkg 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++)
@@ -1024,8 +1044,8 @@ analyze_unsolvable_rule(Solver *solv, Rule *r, Id *lastweakp, Map *rseen)
   if (MAPTST(&solv->weakrulemap, why))
     if (!*lastweakp || why > *lastweakp)
       *lastweakp = why;
-  /* do not add rpm rules to problem */
-  if (why < solv->rpmrules_end)
+  /* do not add pkg rules to problem */
+  if (why < solv->pkgrules_end)
     return;
   /* turn rule into problem */
   if (why >= solv->jobrules && why < solv->jobrules_end)
@@ -1063,7 +1083,7 @@ analyze_unsolvable_rule(Solver *solv, Rule *r, Id *lastweakp, Map *rseen)
  *
  * We know that the problem is not solvable. Record all involved
  * rules (i.e. the "proof") into solv->learnt_pool.
- * Record the learnt pool index and all non-rpm rules into
+ * Record the learnt pool index and all non-pkg rules into
  * solv->problems. (Our solutions to fix the problems are to
  * disable those rules.)
  *
@@ -1242,14 +1262,12 @@ revert(Solver *solv, int level)
       solv->decisionq_why.count--;
       solv->propagate_index = solv->decisionq.count;
     }
-  while (solv->branches.count && solv->branches.elements[solv->branches.count - 1] <= -level)
-    {
-      solv->branches.count--;
-      while (solv->branches.count && solv->branches.elements[solv->branches.count - 1] >= 0)
-       solv->branches.count--;
-    }
+  while (solv->branches.count && solv->branches.elements[solv->branches.count - 1] >= level)
+    solv->branches.count -= solv->branches.elements[solv->branches.count - 2];
   if (solv->recommends_index > solv->decisionq.count)
     solv->recommends_index = -1;       /* rebuild recommends/suggests maps */
+  if (solv->decisionq.count < solv->decisioncnt_jobs)
+    solv->decisioncnt_jobs = 0;
   if (solv->decisionq.count < solv->decisioncnt_update)
     solv->decisioncnt_update = 0;
   if (solv->decisionq.count < solv->decisioncnt_keep)
@@ -1375,6 +1393,7 @@ reorder_dq_for_jobrules(Solver *solv, int level, Queue *dq)
 {
   Pool *pool = solv->pool;
   int i, j, haveone = 0, dqcount = dq->count;
+  int decisionqcount = solv->decisionq.count;
   Id p;
   Solvable *s;
 
@@ -1386,32 +1405,97 @@ reorder_dq_for_jobrules(Solver *solv, int level, Queue *dq)
        continue;
       if (solv->decisionmap[p] == 0)
        {
-         solv->decisionmap[p] = level;
+         if (s->recommends || s->suggests)
+           queue_push(&solv->decisionq, p);
+         solv->decisionmap[p] = level + 1;
          haveone = 1;
        }
     }
   if (!haveone)
     return;
+  policy_update_recommendsmap(solv);
   for (i = 0; i < dqcount; i++)
-    if (!solver_is_enhancing(solv, pool->solvables + dq->elements[i]))
-      {
-       queue_push(dq, dq->elements[i]);
-       dq->elements[i] = 0;
-      }
+    {
+      p = dq->elements[i];
+      if (!(pool->solvables[p].repo == solv->installed || MAPTST(&solv->suggestsmap, p) || solver_is_enhancing(solv, pool->solvables + p)))
+        {
+         queue_push(dq, p);
+         dq->elements[i] = 0;
+        }
+    }
   dqcount = dq->count;
   for (i = 0; i < dqcount; i++)
-    if (dq->elements[i] && !solver_is_supplementing(solv, pool->solvables + dq->elements[i]))
-      {
-       queue_push(dq, dq->elements[i]);
-       dq->elements[i] = 0;
-      }
+    {
+      p = dq->elements[i];
+      if (p && !(pool->solvables[p].repo == solv->installed || MAPTST(&solv->recommendsmap, p) || solver_is_supplementing(solv, pool->solvables + p)))
+        {
+         queue_push(dq, p);
+         dq->elements[i] = 0;
+        }
+    }
   for (i = j = 0; i < dq->count; i++)
     if (dq->elements[i])
       dq->elements[j++] = dq->elements[i];
   queue_truncate(dq, j);
   FOR_REPO_SOLVABLES(solv->installed, p, s)
-    if (solv->decisionmap[p] == level)
+    if (solv->decisionmap[p] == level + 1)
       solv->decisionmap[p] = 0;
+  if (solv->decisionq.count != decisionqcount)
+    {
+      solv->recommends_index = -1;
+      queue_truncate(&solv->decisionq, decisionqcount);
+    }
+}
+
+/*-------------------------------------------------------------------
+ *
+ * branch handling
+ */
+
+static void
+createbranch(Solver *solv, int level, Queue *dq, Id p, Id data)
+{
+  Pool *pool = solv->pool;
+  int i;
+  IF_POOLDEBUG (SOLV_DEBUG_POLICY)
+    {
+      POOL_DEBUG (SOLV_DEBUG_POLICY, "creating a branch:\n");
+      for (i = 0; i < dq->count; i++)
+       POOL_DEBUG (SOLV_DEBUG_POLICY, "  - %s\n", pool_solvid2str(pool, dq->elements[i]));
+    }
+  queue_push(&solv->branches, -dq->elements[0]);
+  for (i = 1; i < dq->count; i++)
+    queue_push(&solv->branches, dq->elements[i]);
+  queue_push2(&solv->branches, p, data);
+  queue_push2(&solv->branches, dq->count + 4, level);
+}
+
+static int
+takebranch(Solver *solv, int pos, int end, const char *msg, int disablerules)
+{
+  Pool *pool = solv->pool;
+  int level;
+  Id p, why;
+#if 0
+  {
+    int i;
+    printf("branch group level %d [%d-%d] %d %d:\n", solv->branches.elements[end - 1], start, end, solv->branches.elements[end - 4], solv->branches.elements[end - 3]);
+    for (i = end - solv->branches.elements[end - 2]; i < end - 4; i++)
+      printf("%c %c%s\n", i == pos ? 'x' : ' ', solv->branches.elements[i] >= 0 ? ' ' : '-', pool_solvid2str(pool, solv->branches.elements[i] >= 0 ? solv->branches.elements[i] : -solv->branches.elements[i]));
+  }
+#endif
+  level = solv->branches.elements[end - 1];
+  p = solv->branches.elements[pos];
+  solv->branches.elements[pos] = -p;
+  POOL_DEBUG(SOLV_DEBUG_SOLVER, "%s %d -> %d with %s\n", msg, solv->decisionmap[p], level, pool_solvid2str(pool, p));
+  /* hack: set level to zero so that revert does not remove the branch */
+  solv->branches.elements[end - 1] = 0;
+  revert(solv, level);
+  solv->branches.elements[end - 1] = level;
+  /* hack: revert simply sets the count, so we can still access the reverted elements */
+  why = -solv->decisionq_why.elements[solv->decisionq_why.count];
+  assert(why >= 0);
+  return setpropagatelearn(solv, level, p, disablerules, why);
 }
 
 /*-------------------------------------------------------------------
@@ -1430,37 +1514,18 @@ selectandinstall(Solver *solv, int level, Queue *dq, int disablerules, Id ruleid
 {
   Pool *pool = solv->pool;
   Id p;
-  int i;
 
   if (dq->count > 1)
     policy_filter_unwanted(solv, dq, POLICY_MODE_CHOOSE);
-  if (dq->count > 1)
-    {
-      /* XXX: didn't we already do that? */
-      /* XXX: shouldn't we prefer installed packages? */
-      /* XXX: move to policy.c? */
-      /* choose the supplemented one */
-      for (i = 0; i < dq->count; i++)
-       if (solver_is_supplementing(solv, pool->solvables + dq->elements[i]))
-         {
-           dq->elements[0] = dq->elements[i];
-           dq->count = 1;
-           break;
-         }
-    }
-  if (dq->count > 1 && ruleid >= solv->jobrules && ruleid < solv->jobrules_end && solv->installed)
+  /* if we're resolving job rules and didn't resolve the installed packages yet,
+   * do some special supplements ordering */
+  if (dq->count > 1 && ruleid >= solv->jobrules && ruleid < solv->jobrules_end && solv->installed && !solv->focus_installed)
     reorder_dq_for_jobrules(solv, level, dq);
+  /* if we have multiple candidates we open a branch */
   if (dq->count > 1)
-    {
-      /* multiple candidates, open a branch */
-      for (i = 1; i < dq->count; i++)
-       queue_push(&solv->branches, dq->elements[i]);
-      queue_push(&solv->branches, -level);
-    }
+    createbranch(solv, level, dq, 0, ruleid);
   p = dq->elements[0];
-
   POOL_DEBUG(SOLV_DEBUG_POLICY, "installing %s\n", pool_solvid2str(pool, p));
-
   return setpropagatelearn(solv, level, p, disablerules, ruleid);
 }
 
@@ -1527,6 +1592,83 @@ solver_create(Pool *pool)
 }
 
 
+static int
+resolve_jobrules(Solver *solv, int level, int disablerules, Queue *dq)
+{
+  Pool *pool = solv->pool;
+  int oldlevel = level;
+  int i, olevel;
+  Rule *r;
+
+  POOL_DEBUG(SOLV_DEBUG_SOLVER, "resolving job rules\n");
+  if (!solv->decisioncnt_jobs)
+    solv->decisioncnt_jobs = solv->decisionq.count;
+  for (i = solv->jobrules, r = solv->rules + i; i < solv->jobrules_end; i++, r++)
+    {
+      Id l, pp;
+      if (r->d < 0)            /* ignore disabled rules */
+       continue;
+      queue_empty(dq);
+      FOR_RULELITERALS(l, pp, r)
+       {
+         if (l < 0)
+           {
+             if (solv->decisionmap[-l] <= 0)
+               break;
+           }
+         else
+           {
+             if (solv->decisionmap[l] > 0)
+               break;
+             if (solv->decisionmap[l] == 0)
+               queue_push(dq, l);
+           }
+       }
+      if (l || !dq->count)
+       continue;
+      /* prune to installed if not updating */
+      if (dq->count > 1 && solv->installed && !solv->updatemap_all &&
+         !(solv->job.elements[solv->ruletojob.elements[i - solv->jobrules]] & SOLVER_ORUPDATE))
+       {
+         int j, k;
+         for (j = k = 0; j < dq->count; j++)
+           {
+             Solvable *s = pool->solvables + dq->elements[j];
+             if (s->repo == solv->installed)
+               {
+                 dq->elements[k++] = dq->elements[j];
+                 if (solv->updatemap.size && MAPTST(&solv->updatemap, dq->elements[j] - solv->installed->start))
+                   {
+                     k = 0;    /* package wants to be updated, do not prune */
+                     break;
+                   }
+               }
+           }
+         if (k)
+           dq->count = k;
+       }
+      olevel = level;
+      level = selectandinstall(solv, level, dq, disablerules, i);
+      if (level <= olevel)
+       {
+         if (level == 0)
+           return 0;   /* unsolvable */
+         if (level == olevel)
+           {
+             i--;
+             r--;
+             continue; /* try something else */
+           }
+         if (level < oldlevel)
+           return level;
+         /* redo from start of jobrules */
+         i = solv->jobrules - 1;
+         r = solv->rules + i;
+       }
+    }
+  return level;
+}
+
 /*-------------------------------------------------------------------
  *
  * solver_free
@@ -1597,6 +1739,7 @@ solver_free(Solver *solv)
   solv_free(solv->specialupdaters);
   solv_free(solv->choicerules_ref);
   solv_free(solv->bestrules_pkg);
+  solv_free(solv->yumobsrules_info);
   solv_free(solv->instbuddy);
   solv_free(solv);
 }
@@ -1644,6 +1787,10 @@ solver_get_flag(Solver *solv, int flag)
     return solv->keep_orphans;
   case SOLVER_FLAG_BREAK_ORPHANS:
     return solv->break_orphans;
+  case SOLVER_FLAG_FOCUS_INSTALLED:
+    return solv->focus_installed;
+  case SOLVER_FLAG_YUM_OBSOLETES:
+    return solv->do_yum_obsoletes;
   default:
     break;
   }
@@ -1713,6 +1860,12 @@ solver_set_flag(Solver *solv, int flag, int value)
   case SOLVER_FLAG_BREAK_ORPHANS:
     solv->break_orphans = value;
     break;
+  case SOLVER_FLAG_FOCUS_INSTALLED:
+    solv->focus_installed = value;
+    break;
+  case SOLVER_FLAG_YUM_OBSOLETES:
+    solv->do_yum_obsoletes = value;
+    break;
   default:
     break;
   }
@@ -1992,74 +2145,27 @@ solver_run_sat(Solver *solv, int disablerules, int doweak)
        }
 
       /*
-       * resolve jobs first
+       * resolve jobs first (unless focus_installed is set)
        */
-     if (level < systemlevel)
+     if (level < systemlevel && !solv->focus_installed)
        {
-         POOL_DEBUG(SOLV_DEBUG_SOLVER, "resolving job rules\n");
-         for (i = solv->jobrules, r = solv->rules + i; i < solv->jobrules_end; i++, r++)
+         olevel = level;
+         level = resolve_jobrules(solv, level, disablerules, &dq);
+         if (level < olevel)
            {
-             Id l;
-             if (r->d < 0)             /* ignore disabled rules */
-               continue;
-             queue_empty(&dq);
-             FOR_RULELITERALS(l, pp, r)
-               {
-                 if (l < 0)
-                   {
-                     if (solv->decisionmap[-l] <= 0)
-                       break;
-                   }
-                 else
-                   {
-                     if (solv->decisionmap[l] > 0)
-                       break;
-                     if (solv->decisionmap[l] == 0)
-                       queue_push(&dq, l);
-                   }
-               }
-             if (l || !dq.count)
-               continue;
-             /* prune to installed if not updating */
-             if (dq.count > 1 && solv->installed && !solv->updatemap_all &&
-                 !(solv->job.elements[solv->ruletojob.elements[i - solv->jobrules]] & SOLVER_ORUPDATE))
-               {
-                 int j, k;
-                 for (j = k = 0; j < dq.count; j++)
-                   {
-                     Solvable *s = pool->solvables + dq.elements[j];
-                     if (s->repo == solv->installed)
-                       {
-                         dq.elements[k++] = dq.elements[j];
-                         if (solv->updatemap.size && MAPTST(&solv->updatemap, dq.elements[j] - solv->installed->start))
-                           {
-                             k = 0;    /* package wants to be updated, do not prune */
-                             break;
-                           }
-                       }
-                   }
-                 if (k)
-                   dq.count = k;
-               }
-             olevel = level;
-             level = selectandinstall(solv, level, &dq, disablerules, i);
              if (level == 0)
-               break;
-             if (level <= olevel)
-               break;
+               break;  /* unsolvable */
+             continue;
            }
-          if (level == 0)
-           break;      /* unsolvable */
          systemlevel = level + 1;
-         if (i < solv->jobrules_end)
-           continue;
-          if (!solv->decisioncnt_update)
-            solv->decisioncnt_update = solv->decisionq.count;
        }
 
+
       /*
        * installed packages
        */
+      if (!solv->decisioncnt_update)
+       solv->decisioncnt_update = solv->decisionq.count;
       if (level < systemlevel && solv->installed && solv->installed->nsolvables && !solv->installed->disabled)
        {
          Repo *installed = solv->installed;
@@ -2232,6 +2338,19 @@ solver_run_sat(Solver *solv, int disablerules, int doweak)
       if (!solv->decisioncnt_keep)
        solv->decisioncnt_keep = solv->decisionq.count;
 
+     if (level < systemlevel && solv->focus_installed)
+       {
+         olevel = level;
+         level = resolve_jobrules(solv, level, disablerules, &dq);
+         if (level < olevel)
+           {
+             if (level == 0)
+               break;  /* unsolvable */
+             continue;
+           }
+         systemlevel = level + 1;
+       }
+
       if (level < systemlevel)
         systemlevel = level;
 
@@ -2467,7 +2586,6 @@ solver_run_sat(Solver *solv, int disablerules, int doweak)
           /* filter out all already supplemented packages if requested */
           if (!solv->addalreadyrecommended && dqs.count)
            {
-             int dosplitprovides_old = solv->dosplitprovides;
              /* turn off all new packages */
              for (i = 0; i < solv->decisionq.count; i++)
                {
@@ -2478,7 +2596,6 @@ solver_run_sat(Solver *solv, int disablerules, int doweak)
                  if (s->repo && s->repo != solv->installed)
                    solv->decisionmap[p] = -solv->decisionmap[p];
                }
-             solv->dosplitprovides = 0;
              /* filter out old supplements */
              for (i = j = 0; i < dqs.count; i++)
                {
@@ -2502,7 +2619,6 @@ solver_run_sat(Solver *solv, int disablerules, int doweak)
                  if (s->repo && s->repo != solv->installed)
                    solv->decisionmap[p] = -solv->decisionmap[p];
                }
-             solv->dosplitprovides = dosplitprovides_old;
            }
 
          /* multiversion doesn't mix well with supplements.
@@ -2618,12 +2734,10 @@ solver_run_sat(Solver *solv, int disablerules, int doweak)
                      if (!dq.count)
                        continue;
                      if (dq.count > 1)
-                       {
-                         /* multiple candidates, open a branch */
-                         for (i = 1; i < dq.count; i++)
-                           queue_push(&solv->branches, dq.elements[i]);
-                         queue_push(&solv->branches, -level);
-                       }
+                       policy_filter_unwanted(solv, &dq, POLICY_MODE_CHOOSE);
+                     /* if we have multiple candidates we open a branch */
+                     if (dq.count > 1)
+                         createbranch(solv, level, &dq, s - pool->solvables, rec);
                      p = dq.elements[0];
                      POOL_DEBUG(SOLV_DEBUG_POLICY, "installing recommended %s\n", pool_solvid2str(pool, p));
                      olevel = level;
@@ -2749,31 +2863,31 @@ solver_run_sat(Solver *solv, int disablerules, int doweak)
          solv->solution_callback(solv, solv->solution_callback_data);
          if (solv->branches.count)
            {
-             int i = solv->branches.count - 1;
-             int l = -solv->branches.elements[i];
-             Id why;
-
-             for (; i > 0; i--)
-               if (solv->branches.elements[i - 1] < 0)
-                 break;
-             p = solv->branches.elements[i];
-             POOL_DEBUG(SOLV_DEBUG_SOLVER, "branching with %s\n", pool_solvid2str(pool, p));
-             queue_empty(&dq);
-             for (j = i + 1; j < solv->branches.count; j++)
-               queue_push(&dq, solv->branches.elements[j]);
-             solv->branches.count = i;
-             level = l;
-             revert(solv, level);
-             if (dq.count > 1)
-               for (j = 0; j < dq.count; j++)
-                 queue_push(&solv->branches, dq.elements[j]);
-             olevel = level;
-             why = -solv->decisionq_why.elements[solv->decisionq_why.count];
-             assert(why >= 0);
-             level = setpropagatelearn(solv, level, p, disablerules, why);
-             if (level == 0)
-               break;
-             continue;
+             int l, endi = 0;
+             p = l = 0;
+             for (i = solv->branches.count - 1; i >= 0; i--)
+               {
+                 p = solv->branches.elements[i];
+                 if (p > 0 && !l)
+                   {
+                     endi = i + 1;
+                     l = p;
+                     i -= 3;   /* skip: p data count */
+                   }
+                 else if (p > 0)
+                   break;
+                 else if (p < 0)
+                   l = 0;
+               }
+             if (i >= 0)
+               {
+                 while (i > 0 && solv->branches.elements[i - 1] > 0)
+                   i--;
+                 level = takebranch(solv, i, endi, "branching", disablerules);
+                 if (level == 0)
+                   break;
+                 continue;
+               }
            }
          /* all branches done, we're finally finished */
          break;
@@ -2782,35 +2896,51 @@ solver_run_sat(Solver *solv, int disablerules, int doweak)
       /* auto-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--)
+         int endi, lasti = -1, lastiend = -1;
+         if (solv->recommends_index < solv->decisionq.count)
+           policy_update_recommendsmap(solv);
+         for (endi = solv->branches.count; endi > 0;)
            {
-             p = solv->branches.elements[i];
-             if (p < 0)
-               l = -p;
-             else if (p > 0 && solv->decisionmap[p] > l + 1)
+             int l, lastsi = -1, starti = endi - solv->branches.elements[endi - 2];
+             l = solv->branches.elements[endi - 1];
+             for (i = starti; i < endi - 4; i++)
                {
-                 lasti = i;
-                 lastl = l;
+                 p = solv->branches.elements[i];
+                 if (p <= 0)
+                   continue;
+                 if (solv->decisionmap[p] > l)
+                   {
+                     lasti = i;
+                     lastiend = endi;
+                     lastsi = -1;
+                     break;
+                   }
+                 if (lastsi < 0 && (MAPTST(&solv->recommendsmap, p) || solver_is_supplementing(solv, pool->solvables + p)))
+                   lastsi = i;
                }
+             if (lastsi >= 0)
+               {
+                 /* we have a recommended package that could not be installed */
+                 /* take it if our current selection is not recommended */
+                 for (i = starti; i < endi - 4; i++)
+                   {
+                     p = -solv->branches.elements[i];
+                     if (p <= 0 || solv->decisionmap[p] != l + 1)
+                       continue;
+                     if (!(MAPTST(&solv->recommendsmap, p) || solver_is_supplementing(solv, pool->solvables + p)))
+                       {
+                         lasti = lastsi;
+                         lastiend = endi;
+                         break;
+                       }
+                   }
+               }
+             endi = starti;
            }
          if (lasti >= 0)
            {
-             /* kill old solvable so that we do not loop */
-             p = solv->branches.elements[lasti];
-             solv->branches.elements[lasti] = 0;
-             POOL_DEBUG(SOLV_DEBUG_SOLVER, "minimizing %d -> %d with %s\n", solv->decisionmap[p], lastl, pool_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);
+             level = takebranch(solv, lasti, lastiend, "minimizing", disablerules);
              if (level == 0)
                break;
              continue;         /* back to main loop */
@@ -2828,6 +2958,7 @@ solver_run_sat(Solver *solv, int disablerules, int doweak)
   if (level == 0)
     {
       /* unsolvable */
+      solv->decisioncnt_jobs = solv->decisionq.count;
       solv->decisioncnt_update = solv->decisionq.count;
       solv->decisioncnt_keep = solv->decisionq.count;
       solv->decisioncnt_resolve = solv->decisionq.count;
@@ -2969,7 +3100,7 @@ weaken_solvable_deps(Solver *solv, Id p)
   int i;
   Rule *r;
 
-  for (i = 1, r = solv->rules + i; i < solv->rpmrules_end; i++, r++)
+  for (i = 1, r = solv->rules + i; i < solv->pkgrules_end; i++, r++)
     {
       if (r->p != -p)
        continue;
@@ -3171,7 +3302,7 @@ addedmap2deduceq(Solver *solv, Map *addedmap)
   Rule *r;
 
   queue_empty(&solv->addedmap_deduceq);
-  for (i = 2, j = solv->rpmrules_end - 1; i < pool->nsolvables && j > 0; j--)
+  for (i = 2, j = solv->pkgrules_end - 1; i < pool->nsolvables && j > 0; j--)
     {
       r = solv->rules + j;
       if (r->p >= 0)
@@ -3207,7 +3338,7 @@ deduceq2addedmap(Solver *solv, Map *addedmap)
   int j;
   Id p;
   Rule *r;
-  for (j = solv->rpmrules_end - 1; j > 0; j--)
+  for (j = solv->pkgrules_end - 1; j > 0; j--)
     {
       r = solv->rules + j;
       if (r->d < 0 && r->p)
@@ -3243,7 +3374,7 @@ solver_solve(Solver *solv, Queue *job)
   Repo *installed = solv->installed;
   int i;
   int oldnrules, initialnrules;
-  Map addedmap;                       /* '1' == have rpm-rules for solvable */
+  Map addedmap;                       /* '1' == have pkg-rules for solvable */
   Map installcandidatemap;
   Id how, what, select, name, weak, p, pp, d;
   Queue q;
@@ -3283,6 +3414,7 @@ solver_solve(Solver *solv, Queue *job)
   queuep_free(&solv->cleandeps_updatepkgs);
   queue_empty(&solv->ruleassertions);
   solv->bestrules_pkg = solv_free(solv->bestrules_pkg);
+  solv->yumobsrules_info = solv_free(solv->yumobsrules_info);
   solv->choicerules_ref = solv_free(solv->choicerules_ref);
   if (solv->noupdate.size)
     map_empty(&solv->noupdate);
@@ -3307,7 +3439,7 @@ solver_solve(Solver *solv, Queue *job)
     memset(solv->decisionmap, 0, pool->nsolvables * sizeof(Id));
   queue_empty(&solv->decisionq);
   queue_empty(&solv->decisionq_why);
-  solv->decisioncnt_update = solv->decisioncnt_keep = solv->decisioncnt_resolve = solv->decisioncnt_weak = solv->decisioncnt_orphan = 0;
+  solv->decisioncnt_jobs = solv->decisioncnt_update = solv->decisioncnt_keep = solv->decisioncnt_resolve = solv->decisioncnt_weak = solv->decisioncnt_orphan = 0;
   queue_empty(&solv->learnt_why);
   queue_empty(&solv->learnt_pool);
   queue_empty(&solv->branches);
@@ -3345,16 +3477,16 @@ solver_solve(Solver *solv, Queue *job)
   now = solv_timems(0);
   /*
    * create rules for all package that could be involved with the solving
-   * so called: rpm rules
+   * so called: pkg rules
    *
    */
-  initialnrules = solv->rpmrules_end ? solv->rpmrules_end : 1;
+  initialnrules = solv->pkgrules_end ? solv->pkgrules_end : 1;
   if (initialnrules > 1)
     deduceq2addedmap(solv, &addedmap);
   if (solv->nrules != initialnrules)
     solver_shrinkrules(solv, initialnrules);
   solv->nrules = initialnrules;
-  solv->rpmrules_end = 0;
+  solv->pkgrules_end = 0;
 
   if (installed)
     {
@@ -3468,12 +3600,12 @@ solver_solve(Solver *solv, Queue *job)
 
       oldnrules = solv->nrules;
       FOR_REPO_SOLVABLES(installed, p, s)
-       solver_addrpmrulesforsolvable(solv, s, &addedmap);
-      POOL_DEBUG(SOLV_DEBUG_STATS, "added %d rpm rules for installed solvables\n", solv->nrules - oldnrules);
+       solver_addpkgrulesforsolvable(solv, s, &addedmap);
+      POOL_DEBUG(SOLV_DEBUG_STATS, "added %d pkg rules for installed solvables\n", solv->nrules - oldnrules);
       oldnrules = solv->nrules;
       FOR_REPO_SOLVABLES(installed, p, s)
-       solver_addrpmrulesforupdaters(solv, s, &addedmap, 1);
-      POOL_DEBUG(SOLV_DEBUG_STATS, "added %d rpm rules for updaters of installed solvables\n", solv->nrules - oldnrules);
+       solver_addpkgrulesforupdaters(solv, s, &addedmap, 1);
+      POOL_DEBUG(SOLV_DEBUG_STATS, "added %d pkg rules for updaters of installed solvables\n", solv->nrules - oldnrules);
     }
 
   /*
@@ -3494,7 +3626,7 @@ solver_solve(Solver *solv, Queue *job)
          FOR_JOB_SELECT(p, pp, select, what)
            {
              MAPSET(&installcandidatemap, p);
-             solver_addrpmrulesforsolvable(solv, pool->solvables + p, &addedmap);
+             solver_addpkgrulesforsolvable(solv, pool->solvables + p, &addedmap);
            }
          break;
        case SOLVER_DISTUPGRADE:
@@ -3512,24 +3644,27 @@ solver_solve(Solver *solv, Queue *job)
          break;
        }
     }
-  POOL_DEBUG(SOLV_DEBUG_STATS, "added %d rpm rules for packages involved in a job\n", solv->nrules - oldnrules);
+  POOL_DEBUG(SOLV_DEBUG_STATS, "added %d pkg rules for packages involved in a job\n", solv->nrules - oldnrules);
 
 
   /*
    * add rules for suggests, enhances
    */
   oldnrules = solv->nrules;
-  solver_addrpmrulesforweak(solv, &addedmap);
-  POOL_DEBUG(SOLV_DEBUG_STATS, "added %d rpm rules because of weak dependencies\n", solv->nrules - oldnrules);
+  if (hasdupjob && !solv->updatemap_all && solv->dosplitprovides && solv->installed)
+    solver_addpkgrulesforweak(solv, &addedmap);
+  else
+    solver_addpkgrulesforweak(solv, &addedmap);
+  POOL_DEBUG(SOLV_DEBUG_STATS, "added %d pkg rules because of weak dependencies\n", solv->nrules - oldnrules);
 
 #ifdef ENABLE_LINKED_PKGS
   oldnrules = solv->nrules;
-  solver_addrpmrulesforlinked(solv, &addedmap);
-  POOL_DEBUG(SOLV_DEBUG_STATS, "added %d rpm rules because of linked packages\n", solv->nrules - oldnrules);
+  solver_addpkgrulesforlinked(solv, &addedmap);
+  POOL_DEBUG(SOLV_DEBUG_STATS, "added %d pkg rules because of linked packages\n", solv->nrules - oldnrules);
 #endif
 
   /*
-   * first pass done, we now have all the rpm rules we need.
+   * first pass done, we now have all the pkg rules we need.
    * unify existing rules before going over all job rules and
    * policy rules.
    * at this point the system is always solvable,
@@ -3550,14 +3685,14 @@ solver_solve(Solver *solv, Queue *job)
     }
 
   if (solv->nrules > initialnrules)
-    solver_unifyrules(solv);                   /* remove duplicate rpm rules */
-  solv->rpmrules_end = solv->nrules;           /* mark end of rpm rules */
+    solver_unifyrules(solv);                   /* remove duplicate pkg rules */
+  solv->pkgrules_end = solv->nrules;           /* mark end of pkg rules */
 
   if (solv->nrules > initialnrules)
     addedmap2deduceq(solv, &addedmap);         /* so that we can recreate the addedmap */
 
-  POOL_DEBUG(SOLV_DEBUG_STATS, "rpm rule memory used: %d K\n", solv->nrules * (int)sizeof(Rule) / 1024);
-  POOL_DEBUG(SOLV_DEBUG_STATS, "rpm rule creation took %d ms\n", solv_timems(now));
+  POOL_DEBUG(SOLV_DEBUG_STATS, "pkg rule memory used: %d K\n", solv->nrules * (int)sizeof(Rule) / 1024);
+  POOL_DEBUG(SOLV_DEBUG_STATS, "pkg rule creation took %d ms\n", solv_timems(now));
 
   /* create dup maps if needed. We need the maps early to create our
    * update rules */
@@ -3845,6 +3980,11 @@ solver_solve(Solver *solv, Queue *job)
   if (hasdupjob)
     solver_freedupmaps(solv);  /* no longer needed */
 
+  if (solv->do_yum_obsoletes)
+    solver_addyumobsrules(solv);
+  else
+    solv->yumobsrules = solv->yumobsrules_end = solv->nrules;
+
   if (1)
     solver_addchoicerules(solv);
   else
@@ -3865,7 +4005,7 @@ solver_solve(Solver *solv, Queue *job)
   map_free(&installcandidatemap);
   queue_free(&q);
 
-  POOL_DEBUG(SOLV_DEBUG_STATS, "%d rpm rules, 2 * %d update rules, %d job rules, %d infarch rules, %d dup rules, %d choice rules, %d best rules\n", solv->rpmrules_end - 1, solv->updaterules_end - solv->updaterules, solv->jobrules_end - solv->jobrules, solv->infarchrules_end - solv->infarchrules, solv->duprules_end - solv->duprules, solv->choicerules_end - solv->choicerules, solv->bestrules_end - solv->bestrules);
+  POOL_DEBUG(SOLV_DEBUG_STATS, "%d pkg rules, 2 * %d update rules, %d job rules, %d infarch rules, %d dup rules, %d choice rules, %d best rules\n", solv->pkgrules_end - 1, solv->updaterules_end - solv->updaterules, solv->jobrules_end - solv->jobrules, solv->infarchrules_end - solv->infarchrules, solv->duprules_end - solv->duprules, solv->choicerules_end - solv->choicerules, solv->bestrules_end - solv->bestrules);
   POOL_DEBUG(SOLV_DEBUG_STATS, "overall rule memory used: %d K\n", solv->nrules * (int)sizeof(Rule) / 1024);
 
   /* create weak map */
@@ -4310,12 +4450,10 @@ solver_describe_decision(Solver *solv, Id p, Id *infop)
   if (why > 0)
     return SOLVER_REASON_UNIT_RULE;
   why = -why;
+  if (i == 0)
+    return SOLVER_REASON_KEEP_INSTALLED;       /* the systemsolvable */
   if (i < solv->decisioncnt_update)
-    {
-      if (i == 0)
-       return SOLVER_REASON_KEEP_INSTALLED;
-      return SOLVER_REASON_RESOLVE_JOB;
-    }
+    return SOLVER_REASON_RESOLVE_JOB;
   if (i < solv->decisioncnt_keep)
     {
       if (why == 0 && pp < 0)
@@ -4324,6 +4462,8 @@ solver_describe_decision(Solver *solv, Id p, Id *infop)
     }
   if (i < solv->decisioncnt_resolve)
     {
+      if (solv->focus_installed && i >= solv->decisioncnt_jobs)
+       return SOLVER_REASON_RESOLVE_JOB;
       if (why == 0 && pp < 0)
        return SOLVER_REASON_CLEANDEPS_ERASE;
       return SOLVER_REASON_KEEP_INSTALLED;
@@ -4331,7 +4471,7 @@ solver_describe_decision(Solver *solv, Id p, Id *infop)
   if (why > 0)
     return SOLVER_REASON_RESOLVE;
   /* weak or orphaned */
-  if (solv->decisionq.count < solv->decisioncnt_orphan)
+  if (i < solv->decisioncnt_orphan)
     return SOLVER_REASON_WEAKDEP;
   return SOLVER_REASON_RESOLVE_ORPHAN;
 }
@@ -4386,7 +4526,7 @@ solver_describe_weakdep_decision(Solver *solv, Id p, Queue *whyq)
          if (!p2 && found)
            {
              queue_push(whyq, SOLVER_REASON_RECOMMENDED);
-             queue_push2(whyq, p2, rec);
+             queue_push2(whyq, i, rec);
            }
        }
     }
@@ -4756,6 +4896,54 @@ pool_add_userinstalled_jobs(Pool *pool, Queue *q, Queue *job, int flags)
     }
 }
 
+int
+solver_alternatives_count(Solver *solv)
+{
+  Id *elements = solv->branches.elements;
+  int res, count;
+  for (res = 0, count = solv->branches.count; count; res++)
+    count -= elements[count - 2];
+  return res;
+}
+
+int
+solver_get_alternative(Solver *solv, Id alternative, Id *idp, Id *fromp, Id *chosenp, Queue *choices, int *levelp)
+{
+  int cnt = solver_alternatives_count(solv);
+  int count = solv->branches.count;
+  Id *elements = solv->branches.elements;
+  if (choices)
+    queue_empty(choices);
+  if (alternative <= 0 || alternative > cnt)
+    return 0;
+  elements += count;
+  for (; cnt > alternative; cnt--)
+    elements -= elements[-2];
+  if (levelp)
+    *levelp = elements[-1];
+  if (fromp)
+    *fromp = elements[-4];
+  if (idp)
+    *idp = elements[-3];
+  if (chosenp)
+    {
+      int i;
+      *chosenp = 0;
+      for (i = elements[-2]; i > 4; i--)
+       {
+         Id p = -elements[-i];
+         if (p > 0 && solv->decisionmap[p] == elements[-1] + 1)
+           {
+             *chosenp = p;
+             break;
+           }
+       }
+    }
+  if (choices)
+    queue_insertn(choices, 0, elements[-2] - 4, elements - elements[-2]);
+  return elements[-4] ? SOLVER_ALTERNATIVE_TYPE_RECOMMENDS : SOLVER_ALTERNATIVE_TYPE_RULE;
+}
+
 const char *
 solver_select2str(Pool *pool, Id select, Id what)
 {
@@ -4895,3 +5083,37 @@ pool_job2str(Pool *pool, Id how, Id what, Id flagmask)
   return pool_tmpappend(pool, s, "]", 0);
 }
 
+const char *
+solver_alternative2str(Solver *solv, int type, Id id, Id from)
+{
+  Pool *pool = solv->pool;
+  if (type == SOLVER_ALTERNATIVE_TYPE_RECOMMENDS)
+    {
+      const char *s = pool_dep2str(pool, id);
+      return pool_tmpappend(pool, s, ", recommended by ", pool_solvid2str(pool, from));
+    }
+  if (type == SOLVER_ALTERNATIVE_TYPE_RULE)
+    {
+      int rtype;
+      Id depfrom, depto, dep;
+      char buf[64];
+      if (solver_ruleclass(solv, id) == SOLVER_RULE_CHOICE)
+       id = solver_rule2pkgrule(solv, id);
+      rtype = solver_ruleinfo(solv, id, &depfrom, &depto, &dep);
+      if ((rtype & SOLVER_RULE_TYPEMASK) == SOLVER_RULE_JOB)
+       {
+         if ((depto & SOLVER_SELECTMASK) == SOLVER_SOLVABLE_PROVIDES)
+           return pool_dep2str(pool, dep);
+         return solver_select2str(pool, depto & SOLVER_SELECTMASK, dep);
+       }
+      if (rtype == SOLVER_RULE_PKG_REQUIRES)
+       {
+         const char *s = pool_dep2str(pool, dep);
+         return pool_tmpappend(pool, s, ", required by ", pool_solvid2str(pool, depfrom));
+       }
+      sprintf(buf, "Rule #%d", id);
+      return pool_tmpjoin(pool, buf, 0, 0);
+    }
+  return "unknown alternative type";
+}
+