Imported Upstream version 0.6.27
[platform/upstream/libsolv.git] / src / rules.c
index 9139812..26b93ea 100644 (file)
 
 #define RULES_BLOCK 63
 
-static void addrpmruleinfo(Solver *solv, Id p, Id d, int type, Id dep);
+static void addpkgruleinfo(Solver *solv, Id p, Id p2, Id d, int type, Id dep);
 static void solver_createcleandepsmap(Solver *solv, Map *cleandepsmap, int unneeded);
 
 /*-------------------------------------------------------------------
  * Check if dependency is possible
  *
  * mirrors solver_dep_fulfilled but uses map m instead of the decisionmap.
- * used in solver_addrpmrulesforweak and solver_createcleandepsmap.
+ * used in solver_addpkgrulesforweak and solver_createcleandepsmap.
  */
 
 static inline int
@@ -52,7 +52,9 @@ dep_possible(Solver *solv, Id dep, Map *m)
       Reldep *rd = GETRELDEP(pool, dep);
       if (rd->flags >= 8)
         {
-         if (rd->flags == REL_AND || rd->flags == REL_COND)
+         if (rd->flags == REL_COND)
+           return 1;
+         if (rd->flags == REL_AND)
            {
              if (!dep_possible(solv, rd->name, m))
                return 0;
@@ -65,9 +67,7 @@ dep_possible(Solver *solv, Id dep, Map *m)
              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);
+           return solver_splitprovides(solv, rd->evr, m);
        }
     }
   FOR_PROVIDES(p, pp, dep)
@@ -114,24 +114,27 @@ unifyrules_sortcmp(const void *ap, const void *bp, void *dp)
 
   x = a->p - b->p;
   if (x)
-    return x;                         /* p differs */
+    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 && b->d == 0)          /* both assertions or binary */
+    return a->w2 - b->w2;
 
-  if (a->d == 0)                      /* a is assertion, b not */
+  if (a->d == 0)                       /* a is assertion or binary, b not */
     {
       x = a->w2 - pool->whatprovidesdata[b->d];
       return x ? x : -1;
     }
 
-  if (b->d == 0)                      /* b is assertion, a not */
+  if (b->d == 0)                       /* b is assertion or binary, a not */
     {
       x = pool->whatprovidesdata[a->d] - b->w2;
       return x ? x : 1;
     }
 
+  if (a->d == b->d)
+    return 0;
+
   /* compare whatprovidesdata */
   ad = pool->whatprovidesdata + a->d;
   bd = pool->whatprovidesdata + b->d;
@@ -161,22 +164,31 @@ solver_unifyrules(Solver *solv)
   int i, j;
   Rule *ir, *jr;
 
-  if (solv->nrules <= 2)              /* nothing to unify */
+  if (solv->nrules <= 2)               /* nothing to unify */
     return;
 
+  if (solv->recommendsruleq)
+    {
+      /* mis-use n2 as recommends rule marker */
+      for (i = 1, ir = solv->rules + i; i < solv->nrules; i++, ir++)
+       ir->n2 = 0;
+      for (i = 0; i < solv->recommendsruleq->count; i++)
+       solv->rules[solv->recommendsruleq->elements[i]].n2 = 1;
+    }
+
   /* sort rules first */
   solv_sort(solv->rules + 1, solv->nrules - 1, sizeof(Rule), unifyrules_sortcmp, solv->pool);
 
-  /* prune rules
-   * i = unpruned
-   * j = pruned
-   */
+  /* prune rules */
   jr = 0;
   for (i = j = 1, ir = solv->rules + i; i < solv->nrules; i++, ir++)
     {
       if (jr && !unifyrules_sortcmp(ir, jr, pool))
-       continue;                      /* prune! */
-      jr = solv->rules + j++;         /* keep! */
+       {
+         jr->n2 &= ir->n2;             /* bitwise-and recommends marker */
+         continue;                     /* prune! */
+       }
+      jr = solv->rules + j++;          /* keep! */
       if (ir != jr)
         *jr = *ir;
     }
@@ -185,8 +197,19 @@ solver_unifyrules(Solver *solv)
   POOL_DEBUG(SOLV_DEBUG_STATS, "pruned rules from %d to %d\n", solv->nrules, j);
 
   /* adapt rule buffer */
-  solv->nrules = j;
-  solv->rules = solv_extend_resize(solv->rules, solv->nrules, sizeof(Rule), RULES_BLOCK);
+  solver_shrinkrules(solv, j);
+
+  if (solv->recommendsruleq)
+    {
+      /* rebuild recommendsruleq */
+      queue_empty(solv->recommendsruleq);
+      for (i = 1, ir = solv->rules + i; i < solv->nrules; i++, ir++)
+       if (ir->n2)
+         {
+           ir->n2 = 0;
+           queue_push(solv->recommendsruleq, i);
+         }
+    }
 
   /*
    * debug: log rule statistics
@@ -243,170 +266,129 @@ hashrule(Solver *solv, Id p, Id d, int n)
 
 /*
  * 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)
+ * 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)
+ * p < 0  : pkg id of A
+ * p2 < 0 : Id of solvable (e.g. B1)
  *
- * d == 0: unary rule, assertion => (A) or (-A)
+ * d == 0, p2 == 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)
+ *   Conflicts:  p < 0, p2 < 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)
+ *   No-op ?:    p = 0, d = 0   (null)          (used as placeholder in update/feature rules)
  *
  *   resulting watches:
  *   ------------------
  *   Direct assertion (no watch needed) --> 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
+ *   always returns a rule for non-pkg rules
  */
 
 Rule *
-solver_addrule(Solver *solv, Id p, Id d)
+solver_addrule(Solver *solv, Id p, Id p2, 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->rpmrules_end)             /* we add rpm rules */
-    {
-      r = solv->rules + solv->nrules - 1;      /* get the last added rule */
-      if (r->p == p && r->d == d && (d != 0 || !r->w2))
-       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)                      /* convert to binary rule */
-       d = dp[-1];
-    }
+  Rule *r;
 
-  if (n == 1 && p > d && !solv->rpmrules_end)
+  if (d)
     {
-      /* 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 */
+      assert(!p2 && d > 0);
+      if (!pool->whatprovidesdata[d])
+       d = 0;
+      else if (!pool->whatprovidesdata[d + 1])
+       {
+         p2 = pool->whatprovidesdata[d];
+         d = 0;
+       }
     }
 
-  /*
-   * check for duplicate
+  /* now we have two cases:
+   * 1 or 2 literals:    d = 0, p, p2 contain the literals
+   * 3 or more literals: d > 0, p2 == 0, d is offset into whatprovidesdata
    */
 
-  /* 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)
+  /* it often happenes that requires lead to adding the same pkg rule
+   * multiple times, so we prune those duplicates right away to make
+   * the work for unifyrules a bit easier */
+  if (!solv->pkgrules_end)             /* we add pkg rules */
     {
-      /* 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
-   */
+      r = solv->rules + solv->lastpkgrule;
+      if (d)
+       {
+         Id *dp;
+         /* check if rule is identical */
+         if (r->p == p)
+           {
+             Id *dp2;
+             if (r->d == 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;
+           }
+         /* check if rule is self-fulfilling */
+         for (dp = pool->whatprovidesdata + d; *dp; dp++)
+           if (*dp == -p)
+             return 0;                 /* rule is self-fulfilling */
+       }
+      else
+       {
+         if (p2 && p > p2)
+           {
+             Id o = p;                 /* switch p1 and p2 */
+             p = p2;
+             p2 = o;
+           }
+         if (r->p == p && !r->d && r->w2 == p2)
+           return r;
+         if (p == -p2)
+           return 0;                   /* rule is self-fulfilling */
+       }
+      solv->lastpkgrule = solv->nrules;
+    }
 
-  /* extend rule buffer */
   solv->rules = solv_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->d = d;
+  r->w1 = p;
+  r->w2 = d ? pool->whatprovidesdata[d] : p2;
   r->n1 = 0;
   r->n2 = 0;
-
   IF_POOLDEBUG (SOLV_DEBUG_RULE_CREATION)
     {
       POOL_DEBUG(SOLV_DEBUG_RULE_CREATION, "  Add rule: ");
       solver_printrule(solv, SOLV_DEBUG_RULE_CREATION, r);
     }
-
   return r;
 }
 
+
 void
 solver_shrinkrules(Solver *solv, int nrules)
 {
   solv->nrules = nrules;
   solv->rules = solv_extend_resize(solv->rules, solv->nrules, sizeof(Rule), RULES_BLOCK);
+  solv->lastpkgrule = 0;
 }
 
 /******************************************************************************
  ***
- *** rpm rule part: create rules representing the package dependencies
+ *** pkg rule part: create rules representing the package dependencies
  ***
  ***/
 
@@ -442,21 +424,37 @@ makemultiversionconflict(Solver *solv, Id n, Id con)
       queue_push(&q, p);
     }
   if (q.count == 1)
-    return -n; /* no other package found, generate normal conflict */
-  return pool_queuetowhatprovides(pool, &q);
+    n = 0;     /* no other package found, normal conflict handling */
+  else
+    n = pool_queuetowhatprovides(pool, &q);
+  queue_free(&q);
+  return n;
 }
 
 static inline void
-addrpmrule(Solver *solv, Id p, Id d, int type, Id dep)
+addpkgrule(Solver *solv, Id p, Id p2, Id d, int type, Id dep)
 {
   if (!solv->ruleinfoq)
-    solver_addrule(solv, p, d);
+    solver_addrule(solv, p, p2, d);
   else
-    addrpmruleinfo(solv, p, d, type, dep);
+    addpkgruleinfo(solv, p, p2, d, type, dep);
 }
 
 #ifdef ENABLE_LINKED_PKGS
 
+static int
+addlinks_cmp(const void *ap, const void *bp, void *dp)
+{
+  Pool *pool = dp;
+  Id a = *(Id *)ap;
+  Id b = *(Id *)bp;
+  Solvable *sa = pool->solvables + a;
+  Solvable *sb = pool->solvables + b;
+  if (sa->name != sb->name)
+    return sa->name - sb->name;
+  return sa - sb;
+}
+
 static void
 addlinks(Solver *solv, Solvable *s, Id req, Queue *qr, Id prv, Queue *qp, Map *m, Queue *workq)
 {
@@ -464,6 +462,8 @@ addlinks(Solver *solv, Solvable *s, Id req, Queue *qr, Id prv, Queue *qp, Map *m
   int i;
   if (!qr->count)
     return;
+  if (qp->count > 1)
+    solv_sort(qp->elements, qp->count, sizeof(Id), addlinks_cmp, pool);
 #if 0
   printf("ADDLINKS %s\n -> %s\n", pool_solvable2str(pool, s), pool_dep2str(pool, req));
   for (i = 0; i < qr->count; i++)
@@ -474,22 +474,30 @@ addlinks(Solver *solv, Solvable *s, Id req, Queue *qr, Id prv, Queue *qp, Map *m
 #endif
 
   if (qr->count == 1)
-    addrpmrule(solv, qr->elements[0], -(s - pool->solvables), SOLVER_RULE_RPM_PACKAGE_REQUIRES, req);
+    addpkgrule(solv, -(s - pool->solvables), qr->elements[0], 0, SOLVER_RULE_PKG_REQUIRES, req);
   else
-    addrpmrule(solv, -(s - pool->solvables), pool_queuetowhatprovides(pool, qr), SOLVER_RULE_RPM_PACKAGE_REQUIRES, req);
+    addpkgrule(solv, -(s - pool->solvables), 0, pool_queuetowhatprovides(pool, qr), SOLVER_RULE_PKG_REQUIRES, req);
   if (qp->count > 1)
     {
-      Id d = pool_queuetowhatprovides(pool, qp);
-      for (i = 0; i < qr->count; i++)
-       addrpmrule(solv, -qr->elements[i], d, SOLVER_RULE_RPM_PACKAGE_REQUIRES, prv);
+      int j;
+      for (i = j = 0; i < qp->count; i = j)
+       {
+         Id d = pool->solvables[qp->elements[i]].name;
+         for (j = i + 1; j < qp->count; j++)
+           if (d != pool->solvables[qp->elements[j]].name)
+             break;
+         d = pool_ids2whatprovides(pool, qp->elements + i, j - i);
+         for (i = 0; i < qr->count; i++)
+           addpkgrule(solv, -qr->elements[i], 0, d, SOLVER_RULE_PKG_REQUIRES, prv);
+       }
     }
   else if (qp->count)
     {
       for (i = 0; i < qr->count; i++)
-       addrpmrule(solv, qp->elements[0], -qr->elements[i], SOLVER_RULE_RPM_PACKAGE_REQUIRES, prv);
+       addpkgrule(solv, -qr->elements[i], qp->elements[0], 0, SOLVER_RULE_PKG_REQUIRES, prv);
     }
   if (!m)
-    return;    /* nothing more to do if called from getrpmruleinfos() */
+    return;    /* nothing more to do if called from getpkgruleinfos() */
   for (i = 0; i < qr->count; i++)
     if (!MAPTST(m, qr->elements[i]))
       queue_push(workq, qr->elements[i]);
@@ -504,9 +512,9 @@ addlinks(Solver *solv, Solvable *s, Id req, Queue *qr, Id prv, Queue *qp, Map *m
         solv->instbuddy = solv_calloc(installed->end - installed->start, sizeof(Id));
       if (qr->count == 1)
         solv->instbuddy[s - pool->solvables - installed->start] = qr->elements[0];
-      for (i = 0; i < qp->count; i++)
+      for (i = 0; i < qr->count; i++)
        {
-         Id p = qp->elements[i];
+         Id p = qr->elements[i];
          if (pool->solvables[p].repo != installed)
            continue;   /* huh? */
          if (qp->count > 1 || (solv->instbuddy[p - installed->start] != 0 && solv->instbuddy[p - installed->start] != s - pool->solvables))
@@ -540,13 +548,16 @@ add_complex_deprules(Solver *solv, Id p, Id dep, int type, int dontfix, Queue *w
 {
   Pool *pool = solv->pool;
   Repo *installed = solv->installed;
-  int i, j;
+  int i, j, flags;
   Queue bq;
 
   queue_init(&bq);
-
+  flags = dontfix ? CPLXDEPS_DONTFIX : 0;
   /* CNF expansion for requires, DNF + INVERT expansion for conflicts */
-  i = pool_normalize_complex_dep(pool, dep, &bq, type == SOLVER_RULE_RPM_PACKAGE_REQUIRES ? 0 : (CPLXDEPS_TODNF | CPLXDEPS_EXPAND | CPLXDEPS_INVERT));
+  if (type == SOLVER_RULE_PKG_CONFLICTS)
+    flags |= CPLXDEPS_TODNF | CPLXDEPS_EXPAND | CPLXDEPS_INVERT;
+
+  i = pool_normalize_complex_dep(pool, dep, &bq, flags);
   /* handle special cases */
   if (i == 0)
     {
@@ -557,7 +568,7 @@ add_complex_deprules(Solver *solv, Id p, Id dep, int type, int dontfix, Queue *w
       else
        {
          POOL_DEBUG(SOLV_DEBUG_RULE_CREATION, "package %s [%d] is not installable (%s)\n", pool_solvid2str(pool, p), p, pool_dep2str(pool, dep));
-         addrpmrule(solv, -p, 0, type == SOLVER_RULE_RPM_PACKAGE_REQUIRES ? SOLVER_RULE_RPM_NOTHING_PROVIDES_DEP : type, dep);
+         addpkgrule(solv, -p, 0, 0, type == SOLVER_RULE_PKG_REQUIRES ? SOLVER_RULE_PKG_NOTHING_PROVIDES_DEP : type, dep);
        }
       queue_free(&bq);
       return;
@@ -584,19 +595,17 @@ add_complex_deprules(Solver *solv, Id p, Id dep, int type, int dontfix, Queue *w
                if (pool->solvables[dp[j]].repo == installed)
                  break;                /* provider was installed */
              if (!dp[j])
-               {
-                 POOL_DEBUG(SOLV_DEBUG_RULE_CREATION, "ignoring broken requires %s of installed package %s\n", pool_dep2str(pool, dep), pool_solvid2str(pool, p));
-                 continue;
-               }
-           }
-         if (!*dp)
-           {
-             /* nothing provides req! */
-             POOL_DEBUG(SOLV_DEBUG_RULE_CREATION, "package %s [%d] is not installable (%s)\n", pool_solvid2str(pool, p), p, pool_dep2str(pool, dep));
-             addrpmrule(solv, -p, 0, SOLVER_RULE_RPM_NOTHING_PROVIDES_DEP, dep);
-             continue;
+               continue;
            }
-         addrpmrule(solv, -p, dp - pool->whatprovidesdata, SOLVER_RULE_RPM_PACKAGE_REQUIRES, dep);
+         if (type == SOLVER_RULE_PKG_RECOMMENDS && !*dp)
+           continue;
+         /* check if the rule contains both p and -p */
+         for (j = 0; dp[j] != 0; j++)
+           if (dp[j] == p)
+             break;
+         if (dp[j])
+           continue;
+         addpkgrule(solv, -p, 0, dp - pool->whatprovidesdata, type, dep);
          /* push all non-visited providers on the work queue */
          if (m)
            for (; *dp; dp++)
@@ -606,29 +615,30 @@ add_complex_deprules(Solver *solv, Id p, Id dep, int type, int dontfix, Queue *w
        }
       if (!bq.elements[i + 1])
        {
-         Id p2 = bq.elements[i];
-         /* simple rule with just two literals */
-         if (dontfix && p2 < 0 && pool->solvables[-p2].repo == installed)
-           continue;
-         if (dontfix && p2 > 0 && pool->solvables[p2].repo != installed)
-           continue;
-         if (p == p2)
-           continue;
+         Id p2 = bq.elements[i++];
+         /* simple rule with just two literals, we'll add a (-p, p2) rule */
+         if (dontfix)
+           {
+             if (p2 < 0 && pool->solvables[-p2].repo == installed)
+               continue;
+             if (p2 > 0 && pool->solvables[p2].repo != installed)
+               continue;
+           }
          if (-p == p2)
            {
-             if (type == SOLVER_RULE_RPM_PACKAGE_CONFLICT)
+             if (type == SOLVER_RULE_PKG_CONFLICTS)
                {
                  if (pool->forbidselfconflicts && !is_otherproviders_dep(pool, dep))
-                   addrpmrule(solv, -p, 0, SOLVER_RULE_RPM_SELF_CONFLICT, dep);
+                   addpkgrule(solv, -p, 0, 0, SOLVER_RULE_PKG_SELF_CONFLICT, dep);
                  continue;
                }
-             addrpmrule(solv, -p, 0, type, dep);
+             addpkgrule(solv, -p, 0, 0, type, dep);
              continue;
            }
-         if (p2 > 0)
-           addrpmrule(solv, p2, -p, type, dep);        /* hack so that we don't need pool_queuetowhatprovides */
-         else
-           addrpmrule(solv, -p, p2, type, dep);
+         /* check if the rule contains both p and -p */
+         if (p == p2)
+           continue;
+         addpkgrule(solv, -p, p2, 0, type, dep);
          if (m && p2 > 0 && !MAPTST(m, p2))
            queue_push(workq, p2);
        }
@@ -672,19 +682,13 @@ add_complex_deprules(Solver *solv, Id p, Id dep, int type, int dontfix, Queue *w
          for (j = 0; j < qcnt; j++)
            if (qele[j] == p)
              break;
-         if (j == qcnt)
-           {
-             /* hack: create fake queue 'q' so that we can call pool_queuetowhatprovides */
-             Queue q;
-             memset(&q, 0, sizeof(q));
-             q.count = qcnt - 1;
-             q.elements = qele + 1;
-             addrpmrule(solv, qele[0], pool_queuetowhatprovides(pool, &q), type, dep);
-             if (m)
-               for (j = 0; j < qcnt; j++)
-                 if (qele[j] > 0 && !MAPTST(m, qele[j]))
-                   queue_push(workq, qele[j]);
-           }
+         if (j < qcnt)
+           continue;
+         addpkgrule(solv, qele[0], 0, pool_ids2whatprovides(pool, qele + 1, qcnt - 1), type, dep);
+         if (m)
+           for (j = 0; j < qcnt; j++)
+             if (qele[j] > 0 && !MAPTST(m, qele[j]))
+               queue_push(workq, qele[j]);
        }
     }
   queue_free(&bq);
@@ -694,7 +698,7 @@ add_complex_deprules(Solver *solv, Id p, Id dep, int type, int dontfix, Queue *w
 
 /*-------------------------------------------------------------------
  *
- * add (install) rules for solvable
+ * add dependency rules for solvable
  *
  * s: Solvable for which to add rules
  * m: m[s] = 1 for solvables which have rules, prevent rule duplication
@@ -713,13 +717,15 @@ add_complex_deprules(Solver *solv, Id p, Id dep, int type, int dontfix, Queue *w
  */
 
 void
-solver_addrpmrulesforsolvable(Solver *solv, Solvable *s, Map *m)
+solver_addpkgrulesforsolvable(Solver *solv, Solvable *s, Map *m)
 {
   Pool *pool = solv->pool;
   Repo *installed = solv->installed;
 
   Queue workq; /* list of solvables we still have to work on */
   Id workqbuf[64];
+  Queue prereqq;       /* list of pre-req ids to ignore */
+  Id prereqbuf[16];
 
   int i;
   int dontfix;         /* ignore dependency errors for installed solvables */
@@ -735,6 +741,8 @@ solver_addrpmrulesforsolvable(Solver *solv, Solvable *s, Map *m)
   queue_init_buffer(&workq, workqbuf, sizeof(workqbuf)/sizeof(*workqbuf));
   queue_push(&workq, s - pool->solvables);     /* push solvable Id to work queue */
 
+  queue_init_buffer(&prereqq, prereqbuf, sizeof(prereqbuf)/sizeof(*prereqbuf));
+
   /* loop until there's no more work left */
   while (workq.count)
     {
@@ -756,10 +764,10 @@ solver_addrpmrulesforsolvable(Solver *solv, Solvable *s, Map *m)
       dontfix = 0;
       if (installed                    /* Installed system available */
          && s->repo == installed       /* solvable is installed */
-         && !solv->fixmap_all          /* NOT repair errors in rpm dependency graph */
+         && !solv->fixmap_all          /* NOT repair errors in dependency graph */
          && !(solv->fixmap.size && MAPTST(&solv->fixmap, n - installed->start)))
         {
-         dontfix = 1;                  /* dont care about broken rpm deps */
+         dontfix = 1;                  /* dont care about broken deps */
         }
 
       if (!dontfix)
@@ -769,7 +777,7 @@ solver_addrpmrulesforsolvable(Solver *solv, Solvable *s, Map *m)
                : !pool_installable(pool, s))
            {
              POOL_DEBUG(SOLV_DEBUG_RULE_CREATION, "package %s [%d] is not installable\n", pool_solvid2str(pool, n), n);
-             addrpmrule(solv, -n, 0, SOLVER_RULE_RPM_NOT_INSTALLABLE, 0);
+             addpkgrule(solv, -n, 0, 0, SOLVER_RULE_PKG_NOT_INSTALLABLE, 0);
            }
        }
 
@@ -785,17 +793,39 @@ solver_addrpmrulesforsolvable(Solver *solv, Solvable *s, Map *m)
 
       if (s->requires)
        {
+         int filterpre = 0;
          reqp = s->repo->idarraydata + s->requires;
          while ((req = *reqp++) != 0)            /* go through all requires */
            {
              if (req == SOLVABLE_PREREQMARKER)   /* skip the marker */
-               continue;
+               {
+                 if (installed && s->repo == installed)
+                   {
+                     if (prereqq.count)
+                       queue_empty(&prereqq);
+                     solvable_lookup_idarray(s, SOLVABLE_PREREQ_IGNOREINST, &prereqq);
+                     filterpre = prereqq.count;
+                   }
+                 continue;
+               }
+             if (filterpre)
+               {
+                 /* check if this id is filtered. assumes that prereqq.count is small */
+                 for (i = 0; i < prereqq.count; i++)
+                   if (req == prereqq.elements[i])
+                     break;
+                 if (i < prereqq.count)
+                   {
+                     POOL_DEBUG(SOLV_DEBUG_RULE_CREATION, "package %s: ignoring filtered pre-req dependency %s\n", pool_solvable2str(pool, s), pool_dep2str(pool, req));
+                     continue;
+                   }
+               }
 
 #ifdef ENABLE_COMPLEX_DEPS
              if (pool_is_complex_dep(pool, req))
                {
                  /* we have AND/COND deps, normalize */
-                 add_complex_deprules(solv, n, req, SOLVER_RULE_RPM_PACKAGE_REQUIRES, dontfix, &workq, m);
+                 add_complex_deprules(solv, n, req, SOLVER_RULE_PKG_REQUIRES, dontfix, &workq, m);
                  continue;
                }
 #endif
@@ -826,10 +856,16 @@ solver_addrpmrulesforsolvable(Solver *solv, Solvable *s, Map *m)
              if (!*dp)
                {
                  POOL_DEBUG(SOLV_DEBUG_RULE_CREATION, "package %s [%d] is not installable (%s)\n", pool_solvid2str(pool, n), n, pool_dep2str(pool, req));
-                 addrpmrule(solv, -n, 0, SOLVER_RULE_RPM_NOTHING_PROVIDES_DEP, req);
+                 addpkgrule(solv, -n, 0, 0, SOLVER_RULE_PKG_NOTHING_PROVIDES_DEP, req);
                  continue;
                }
 
+             for (i = 0; dp[i] != 0; i++)
+               if (n == dp[i])
+                 break;
+             if (dp[i])
+               continue;               /* provided by itself, no need to add rule */
+
              IF_POOLDEBUG (SOLV_DEBUG_RULE_CREATION)
                {
                  POOL_DEBUG(SOLV_DEBUG_RULE_CREATION,"  %s requires %s\n", pool_solvable2str(pool, s), pool_dep2str(pool, req));
@@ -839,7 +875,7 @@ solver_addrpmrulesforsolvable(Solver *solv, Solvable *s, Map *m)
 
              /* add 'requires' dependency */
               /* rule: (-requestor|provider1|provider2|...|providerN) */
-             addrpmrule(solv, -n, dp - pool->whatprovidesdata, SOLVER_RULE_RPM_PACKAGE_REQUIRES, req);
+             addpkgrule(solv, -n, 0, dp - pool->whatprovidesdata, SOLVER_RULE_PKG_REQUIRES, req);
 
              /* push all non-visited providers on the work queue */
              if (m)
@@ -849,6 +885,48 @@ solver_addrpmrulesforsolvable(Solver *solv, Solvable *s, Map *m)
            }
        }
 
+      if (s->recommends && solv->strongrecommends)
+       {
+         int start = solv->nrules;
+         solv->lastpkgrule = 0;
+         reqp = s->repo->idarraydata + s->recommends;
+         while ((req = *reqp++) != 0)            /* go through all recommends */
+           {
+#ifdef ENABLE_COMPLEX_DEPS
+             if (pool_is_complex_dep(pool, req))
+               {
+                 /* we have AND/COND deps, normalize */
+                 add_complex_deprules(solv, n, req, SOLVER_RULE_PKG_RECOMMENDS, dontfix, &workq, m);
+                 continue;
+               }
+#endif
+             dp = pool_whatprovides_ptr(pool, req);
+             if (*dp == SYSTEMSOLVABLE || !*dp)          /* always installed or not installable */
+               continue;
+             for (i = 0; dp[i] != 0; i++)
+               if (n == dp[i])
+                 break;
+             if (dp[i])
+               continue;               /* provided by itself, no need to add rule */
+             addpkgrule(solv, -n, 0, dp - pool->whatprovidesdata, SOLVER_RULE_PKG_RECOMMENDS, req);
+             if (m)
+               for (; *dp; dp++)
+                 if (!MAPTST(m, *dp))
+                   queue_push(&workq, *dp);
+           }
+         if (!solv->ruleinfoq && start < solv->nrules)
+           {
+             if (!solv->recommendsruleq)
+               {
+                 solv->recommendsruleq = solv_calloc(1, sizeof(Queue));
+                 queue_init(solv->recommendsruleq);
+               }
+             for (i = start; i < solv->nrules; i++)
+               queue_push(solv->recommendsruleq, i);
+             solv->lastpkgrule = 0;
+           }
+       }
+
       /* that's all we check for src packages */
       if (s->arch == ARCH_SRC || s->arch == ARCH_NOSRC)
        continue;
@@ -877,7 +955,7 @@ solver_addrpmrulesforsolvable(Solver *solv, Solvable *s, Map *m)
              if (!ispatch && pool_is_complex_dep(pool, con))
                {
                  /* we have AND/COND deps, normalize */
-                 add_complex_deprules(solv, n, con, SOLVER_RULE_RPM_PACKAGE_CONFLICT, dontfix, &workq, m);
+                 add_complex_deprules(solv, n, con, SOLVER_RULE_PKG_CONFLICTS, dontfix, &workq, m);
                  continue;
                }
 #endif
@@ -893,16 +971,23 @@ solver_addrpmrulesforsolvable(Solver *solv, Solvable *s, Map *m)
                    {
                      if (!pool->forbidselfconflicts || is_otherproviders_dep(pool, con))
                        continue;
-                     addrpmrule(solv, -n, 0, SOLVER_RULE_RPM_SELF_CONFLICT, con);
+                     addpkgrule(solv, -n, 0, 0, SOLVER_RULE_PKG_SELF_CONFLICT, con);
                      continue;
                    }
                  if (ispatch && solv->multiversion.size && MAPTST(&solv->multiversion, p) && ISRELDEP(con))
                    {
                      /* our patch conflicts with a multiversion package */
-                     p = -makemultiversionconflict(solv, p, con);
+                     Id d = makemultiversionconflict(solv, p, con);
+                     if (d)
+                       {
+                         addpkgrule(solv, -n, 0, d, SOLVER_RULE_PKG_CONFLICTS, con);
+                         continue;
+                       }
                    }
+                 if (p == SYSTEMSOLVABLE)
+                   p = 0;
                   /* rule: -n|-p: either solvable _or_ provider of conflict */
-                 addrpmrule(solv, -n, -p, SOLVER_RULE_RPM_PACKAGE_CONFLICT, con);
+                 addpkgrule(solv, -n, -p, 0, SOLVER_RULE_PKG_CONFLICTS, con);
                }
            }
        }
@@ -935,10 +1020,12 @@ solver_addrpmrulesforsolvable(Solver *solv, Solvable *s, Map *m)
                        continue;
                      if (pool->obsoleteusescolors && !pool_colormatch(pool, s, ps))
                        continue;
+                     if (p == SYSTEMSOLVABLE)
+                       p = 0;
                      if (!isinstalled)
-                       addrpmrule(solv, -n, -p, SOLVER_RULE_RPM_PACKAGE_OBSOLETES, obs);
+                       addpkgrule(solv, -n, -p, 0, SOLVER_RULE_PKG_OBSOLETES, obs);
                      else
-                       addrpmrule(solv, -n, -p, SOLVER_RULE_RPM_INSTALLEDPKG_OBSOLETES, obs);
+                       addpkgrule(solv, -n, -p, 0, SOLVER_RULE_PKG_INSTALLED_OBSOLETES, obs);
                    }
                }
            }
@@ -959,15 +1046,32 @@ solver_addrpmrulesforsolvable(Solver *solv, Solvable *s, Map *m)
                  /* we still obsolete packages with same nevra, like rpm does */
                  /* (actually, rpm mixes those packages. yuck...) */
                  if (multi && (s->name != ps->name || s->evr != ps->evr || s->arch != ps->arch))
-                   continue;
+                   {
+                     if (isinstalled || ps->repo != installed)
+                       continue;
+                     /* also check the installed package for multi-ness */
+                     if (MAPTST(&solv->multiversion, p))
+                       continue;
+                   }
                  if (!pool->implicitobsoleteusesprovides && s->name != ps->name)
                    continue;
                  if (pool->implicitobsoleteusescolors && !pool_colormatch(pool, s, ps))
                    continue;
+                 if (p == SYSTEMSOLVABLE)
+                   p = 0;
                  if (s->name == ps->name)
-                   addrpmrule(solv, -n, -p, SOLVER_RULE_RPM_SAME_NAME, 0);
+                   {
+                     /* optimization: do not add the same-name conflict rule if it was
+                      * already added when we looked at the other package.
+                      * (this assumes pool_colormatch is symmetric) */
+                     if (p && m && ps->repo != installed && MAPTST(m, p) &&
+                         (ps->arch != ARCH_SRC && ps->arch != ARCH_NOSRC) &&
+                         !(solv->multiversion.size && MAPTST(&solv->multiversion, p)))
+                       continue;
+                     addpkgrule(solv, -n, -p, 0, SOLVER_RULE_PKG_SAME_NAME, 0);
+                   }
                  else
-                   addrpmrule(solv, -n, -p, SOLVER_RULE_RPM_IMPLICIT_OBSOLETES, s->name);
+                   addpkgrule(solv, -n, -p, 0, SOLVER_RULE_PKG_IMPLICIT_OBSOLETES, s->name);
                }
            }
        }
@@ -988,13 +1092,20 @@ solver_addrpmrulesforsolvable(Solver *solv, Solvable *s, Map *m)
        }
 
       /*-----------------------------------------
-       * add recommends to the work queue
+       * add recommends/suggests to the work queue
        */
       if (s->recommends && m)
        {
          recp = s->repo->idarraydata + s->recommends;
          while ((rec = *recp++) != 0)
            {
+#ifdef ENABLE_COMPLEX_DEPS
+             if (pool_is_complex_dep(pool, rec))
+               {
+                 pool_add_pos_literals_complex_dep(pool, rec, &workq, m, 0);
+                   continue;
+               }
+#endif
              FOR_PROVIDES(p, pp, rec)
                if (!MAPTST(m, p))
                  queue_push(&workq, p);
@@ -1005,18 +1116,26 @@ solver_addrpmrulesforsolvable(Solver *solv, Solvable *s, Map *m)
          sugp = s->repo->idarraydata + s->suggests;
          while ((sug = *sugp++) != 0)
            {
+#ifdef ENABLE_COMPLEX_DEPS
+             if (pool_is_complex_dep(pool, sug))
+               {
+                 pool_add_pos_literals_complex_dep(pool, sug, &workq, m, 0);
+                   continue;
+               }
+#endif
              FOR_PROVIDES(p, pp, sug)
                if (!MAPTST(m, p))
                  queue_push(&workq, p);
            }
        }
     }
+  queue_free(&prereqq);
   queue_free(&workq);
 }
 
 #ifdef ENABLE_LINKED_PKGS
 void
-solver_addrpmrulesforlinked(Solver *solv, Map *m)
+solver_addpkgrulesforlinked(Solver *solv, Map *m)
 {
   Pool *pool = solv->pool;
   Solvable *s;
@@ -1041,7 +1160,7 @@ solver_addrpmrulesforlinked(Solver *solv, Map *m)
          for (j = 0; j < qr.count; j++)
            if (MAPTST(m, qr.elements[j]))
              {
-               solver_addrpmrulesforsolvable(solv, s, m);
+               solver_addpkgrulesforsolvable(solv, s, m);
                break;
              }
          queue_empty(&qr);
@@ -1059,7 +1178,7 @@ solver_addrpmrulesforlinked(Solver *solv, Map *m)
  */
 
 void
-solver_addrpmrulesforweak(Solver *solv, Map *m)
+solver_addpkgrulesforweak(Solver *solv, Map *m)
 {
   Pool *pool = solv->pool;
   Solvable *s;
@@ -1101,7 +1220,7 @@ solver_addrpmrulesforweak(Solver *solv, Map *m)
       /* if nothing found, goto next solvables */
       if (!sup)
        continue;
-      solver_addrpmrulesforsolvable(solv, s, m);
+      solver_addpkgrulesforsolvable(solv, s, m);
       n = 0;                   /* check all solvables again because we added solvables to m */
     }
 }
@@ -1117,7 +1236,7 @@ solver_addrpmrulesforweak(Solver *solv, Map *m)
  */
 
 void
-solver_addrpmrulesforupdaters(Solver *solv, Solvable *s, Map *m, int allow_all)
+solver_addpkgrulesforupdaters(Solver *solv, Solvable *s, Map *m, int allow_all)
 {
   Pool *pool = solv->pool;
   int i;
@@ -1130,11 +1249,11 @@ solver_addrpmrulesforupdaters(Solver *solv, Solvable *s, Map *m, int allow_all)
   policy_findupdatepackages(solv, s, &qs, allow_all);
     /* add rule for 's' if not already done */
   if (!MAPTST(m, s - pool->solvables))
-    solver_addrpmrulesforsolvable(solv, s, m);
+    solver_addpkgrulesforsolvable(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]))
-      solver_addrpmrulesforsolvable(solv, pool->solvables + qs.elements[i], m);
+      solver_addpkgrulesforsolvable(solv, pool->solvables + qs.elements[i], m);
   queue_free(&qs);
 }
 
@@ -1147,37 +1266,61 @@ solver_addrpmrulesforupdaters(Solver *solv, Solvable *s, Map *m, int allow_all)
  ***
  ***/
 
-static Id
-finddistupgradepackages(Solver *solv, Solvable *s, Queue *qs, int allow_all)
+static int
+dup_maykeepinstalled(Solver *solv, Solvable *s)
 {
   Pool *pool = solv->pool;
-  int i;
+  Id ip, pp;
 
-  policy_findupdatepackages(solv, s, qs, allow_all ? allow_all : 2);
-  if (!qs->count)
+  if (solv->dupmap.size && MAPTST(&solv->dupmap,  s - pool->solvables))
+    return 1;
+  /* is installed identical to a good one? */
+  FOR_PROVIDES(ip, pp, s->name)
     {
-      if (allow_all)
-        return 0;      /* orphaned, don't create feature rule */
-      /* check if this is an orphaned package */
-      policy_findupdatepackages(solv, s, qs, 1);
-      if (!qs->count)
-       return 0;       /* orphaned, don't create update rule */
-      qs->count = 0;
-      return -SYSTEMSOLVABLE;  /* supported but not installable */
+      Solvable *is = pool->solvables + ip;
+      if (is->evr != s->evr)
+       continue;
+      if (solv->dupmap.size)
+       {
+         if (!MAPTST(&solv->dupmap, ip))
+           continue;
+       }
+      else if (is->repo == pool->installed)
+       continue;
+      if (solvable_identical(s, is))
+       return 1;
     }
-  if (allow_all)
-    return s - pool->solvables;
-  /* check if it is ok to keep the installed package */
-  for (i = 0; i < qs->count; i++)
+  return 0;
+}
+
+
+static Id
+finddistupgradepackages(Solver *solv, Solvable *s, Queue *qs)
+{
+  Pool *pool = solv->pool;
+  int i, j;
+
+  policy_findupdatepackages(solv, s, qs, 2);
+  if (qs->count)
     {
-      Solvable *ns = pool->solvables + qs->elements[i];
-      if (s->evr == ns->evr && solvable_identical(s, ns))
-        return s - pool->solvables;
+      /* remove installed packages we can't keep */
+      for (i = j = 0; i < qs->count; i++)
+       {
+         Solvable *ns = pool->solvables + qs->elements[i];
+         if (ns->repo == pool->installed && !dup_maykeepinstalled(solv, ns))
+           continue;
+         qs->elements[j++] = qs->elements[i];
+       }
+      queue_truncate(qs, j);
     }
+  /* check if it is ok to keep the installed package */
+  if (dup_maykeepinstalled(solv, s))
+    return s - pool->solvables;
   /* nope, it must be some other package */
   return -SYSTEMSOLVABLE;
 }
 
+#if 0
 /* add packages from the dup repositories to the update candidates
  * this isn't needed for the global dup mode as all packages are
  * from dup repos in that case */
@@ -1201,110 +1344,212 @@ addduppackages(Solver *solv, Solvable *s, Queue *qs)
     }
   queue_free(&dupqs);
 }
+#endif
 
-/*-------------------------------------------------------------------
- *
- * add rule for update
- *   (A|A1|A2|A3...)  An = update candidates for A
- *
- * s = (installed) solvable
- */
+/* stash away the original updaters for multiversion packages. We do this so that
+ * we can update the package later */
+static inline void
+set_specialupdaters(Solver *solv, Solvable *s, Id d)
+{
+  Repo *installed = solv->installed;
+  if (!solv->specialupdaters)
+    solv->specialupdaters = solv_calloc(installed->end - installed->start, sizeof(Id));
+  solv->specialupdaters[s - solv->pool->solvables - installed->start] = d;
+}
+
+#ifdef ENABLE_LINKED_PKGS
+/* Check if this is a linked pseudo package. As it is linked, we do not need an update/feature rule */
+static inline int
+is_linked_pseudo_package(Solver *solv, Solvable *s)
+{
+  Pool *pool = solv->pool;
+  if (solv->instbuddy && solv->instbuddy[s - pool->solvables - solv->installed->start])
+    {
+      const char *name = pool_id2str(pool, s->name);
+      if (strncmp(name, "pattern:", 8) == 0 || strncmp(name, "application:", 12) == 0)
+       return 1;
+    }
+  return 0;
+}
+#endif
 
 void
-solver_addupdaterule(Solver *solv, Solvable *s, int allow_all)
+solver_addfeaturerule(Solver *solv, Solvable *s)
 {
-  /* installed packages get a special upgrade allowed rule */
   Pool *pool = solv->pool;
-  Id p, d;
+  int i;
+  Id p;
   Queue qs;
   Id qsbuf[64];
 
-  queue_init_buffer(&qs, qsbuf, sizeof(qsbuf)/sizeof(*qsbuf));
-  p = s - pool->solvables;
-  /* find update candidates for 's' */
-  if (solv->dupmap_all)
-    p = finddistupgradepackages(solv, s, &qs, allow_all);
-  else
-    policy_findupdatepackages(solv, s, &qs, allow_all);
-  if (!allow_all && !solv->dupmap_all && solv->dupinvolvedmap.size && MAPTST(&solv->dupinvolvedmap, p))
-    addduppackages(solv, s, &qs);
-
 #ifdef ENABLE_LINKED_PKGS
-  if (solv->instbuddy && solv->instbuddy[s - pool->solvables - solv->installed->start])
+  if (is_linked_pseudo_package(solv, s))
     {
-      const char *name = pool_id2str(pool, s->name);
-      if (strncmp(name, "pattern:", 8) == 0 || strncmp(name, "application:", 12) == 0)
-       {
-         /* a linked pseudo package. As it is linked, we do not need an update rule */
-         /* nevertheless we set specialupdaters so we can update */
-         solver_addrule(solv, 0, 0);
-         if (!allow_all && qs.count)
-           {
-             if (p != -SYSTEMSOLVABLE)
-               queue_unshift(&qs, p);
-             if (!solv->specialupdaters)
-               solv->specialupdaters = solv_calloc(solv->installed->end - solv->installed->start, sizeof(Id));
-             solv->specialupdaters[s - pool->solvables - solv->installed->start] = pool_queuetowhatprovides(pool, &qs);
-           }
-         queue_free(&qs);
-         return;
-       }
+      solver_addrule(solv, 0, 0, 0);   /* no feature rules for those */
+      return;
     }
 #endif
-
-  if (!allow_all && qs.count && solv->multiversion.size)
+  queue_init_buffer(&qs, qsbuf, sizeof(qsbuf)/sizeof(*qsbuf));
+  p = s - pool->solvables;
+  policy_findupdatepackages(solv, s, &qs, 1);
+  if (solv->dupmap_all || (solv->dupinvolvedmap.size && MAPTST(&solv->dupinvolvedmap, p)))
     {
-      int i, j;
-
-      d = pool_queuetowhatprovides(pool, &qs);
-      /* filter out all multiversion packages as they don't update */
-      for (i = j = 0; i < qs.count; i++)
-       {
-         if (MAPTST(&solv->multiversion, qs.elements[i]))
-           {
-             Solvable *ps = pool->solvables + qs.elements[i];
-             /* if keepexplicitobsoletes is set and the name is different,
-              * we assume that there is an obsoletes. XXX: not 100% correct */
-             if (solv->keepexplicitobsoletes && ps->name != s->name)
-               {
-                 qs.elements[j++] = qs.elements[i];
-                 continue;
-               }
-             /* it's ok if they have same nevra */
-             if (ps->name != s->name || ps->evr != s->evr || ps->arch != s->arch)
-               continue;
-           }
-         qs.elements[j++] = qs.elements[i];
-       }
-      if (j < qs.count)
+      if (!dup_maykeepinstalled(solv, s))
        {
-         if (d && solv->installed && s->repo == solv->installed &&
-              (solv->updatemap_all || (solv->updatemap.size && MAPTST(&solv->updatemap, s - pool->solvables - solv->installed->start))))
+         for (i = 0; i < qs.count; i++)
            {
-             if (!solv->specialupdaters)
-               solv->specialupdaters = solv_calloc(solv->installed->end - solv->installed->start, sizeof(Id));
-             solv->specialupdaters[s - pool->solvables - solv->installed->start] = d;
+             Solvable *ns = pool->solvables + qs.elements[i];
+             if (ns->repo != pool->installed || dup_maykeepinstalled(solv, ns))
+               break;
            }
-         if (j == 0 && p == -SYSTEMSOLVABLE && solv->dupmap_all)
+         if (i == qs.count)
            {
-             queue_push(&solv->orphaned, s - pool->solvables); /* treat as orphaned */
-             j = qs.count;
+             solver_addrule(solv, 0, 0, 0);    /* this is an orphan */
+             queue_free(&qs);
+             return;
            }
-         qs.count = j;
        }
-      else if (p != -SYSTEMSOLVABLE)
-       {
-         /* could fallthrough, but then we would do pool_queuetowhatprovides twice */
-         queue_free(&qs);
-         solver_addrule(solv, p, d);   /* allow update of s */
-         return;
+    }
+  if (qs.count > 1)
+    {
+      Id d = pool_queuetowhatprovides(pool, &qs);
+      queue_free(&qs);
+      solver_addrule(solv, p, 0, d);   /* allow update of s */
+    }
+  else
+    {
+      Id d = qs.count ? qs.elements[0] : 0;
+      queue_free(&qs);
+      solver_addrule(solv, p, d, 0);   /* allow update of s */
+    }
+}
+
+/*-------------------------------------------------------------------
+ *
+ * add rule for update
+ *   (A|A1|A2|A3...)  An = update candidates for A
+ *
+ * s = (installed) solvable
+ */
+
+void
+solver_addupdaterule(Solver *solv, Solvable *s)
+{
+  /* installed packages get a special upgrade allowed rule */
+  Pool *pool = solv->pool;
+  Id p, d;
+  Queue qs;
+  Id qsbuf[64];
+  int isorphaned = 0;
+  Rule *r;
+
+  p = s - pool->solvables;
+  /* Orphan detection. We cheat by looking at the feature rule, which
+   * we already calculated */
+  r = solv->rules + solv->featurerules + (p - solv->installed->start);
+  if (!r->p)
+    {
+#ifdef ENABLE_LINKED_PKGS
+      if (is_linked_pseudo_package(solv, s))
+       {
+         solver_addrule(solv, 0, 0, 0);
+         return;
+       }
+#endif
+      p = 0;
+      queue_push(&solv->orphaned, s - pool->solvables);                /* an orphaned package */
+      if (solv->keep_orphans && !(solv->droporphanedmap_all || (solv->droporphanedmap.size && MAPTST(&solv->droporphanedmap, s - pool->solvables - solv->installed->start))))
+       p = s - pool->solvables;        /* keep this orphaned package installed */
+      solver_addrule(solv, p, 0, 0);
+      return;
+    }
+
+  queue_init_buffer(&qs, qsbuf, sizeof(qsbuf)/sizeof(*qsbuf));
+  /* find update candidates for 's' */
+  if (solv->dupmap_all || (solv->dupinvolvedmap.size && MAPTST(&solv->dupinvolvedmap, p)))
+    p = finddistupgradepackages(solv, s, &qs);
+  else
+    policy_findupdatepackages(solv, s, &qs, 0);
+
+  if (qs.count && solv->multiversion.size)
+    {
+      int i, j;
+
+      for (i = 0; i < qs.count; i++)
+       if (MAPTST(&solv->multiversion, qs.elements[i]))
+         break;
+      if (i < qs.count)
+       {
+         /* filter out all multiversion packages as they don't update */
+         d = pool_queuetowhatprovides(pool, &qs);      /* save qs away */
+         for (j = i; i < qs.count; i++)
+            {
+             if (MAPTST(&solv->multiversion, qs.elements[i]))
+               {
+                 Solvable *ps = pool->solvables + qs.elements[i];
+                 /* if keepexplicitobsoletes is set and the name is different,
+                  * we assume that there is an obsoletes. XXX: not 100% correct */
+                 if (solv->keepexplicitobsoletes && ps->name != s->name)
+                   {
+                     qs.elements[j++] = qs.elements[i];
+                     continue;
+                   }
+                 /* it's ok if they have same nevra */
+                 if (ps->name != s->name || ps->evr != s->evr || ps->arch != s->arch)
+                   continue;
+               }
+             qs.elements[j++] = qs.elements[i];
+           }
+         if (j < qs.count)             /* filtered at least one package? */
+           {
+             if (j == 0 && p == -SYSTEMSOLVABLE)
+               {
+                 /* this is a multiversion orphan */
+                 queue_push(&solv->orphaned, s - pool->solvables);
+                 set_specialupdaters(solv, s, d);
+                 if (solv->keep_orphans && !(solv->droporphanedmap_all || (solv->droporphanedmap.size && MAPTST(&solv->droporphanedmap, s - pool->solvables - solv->installed->start))))
+                   {
+                     /* we need to keep the orphan */
+                     queue_free(&qs);
+                     solver_addrule(solv, s - pool->solvables, 0, 0);
+                     return;
+                   }
+                 /* we can drop it as long as we update */
+                 isorphaned = 1;
+                 j = qs.count;         /* force the update */
+               }
+             else if (d && (solv->updatemap_all || (solv->updatemap.size && MAPTST(&solv->updatemap, s - pool->solvables - solv->installed->start))))
+               {
+                 /* non-orphan multiversion package, set special updaters if we want an update */
+                 set_specialupdaters(solv, s, d);
+               }
+             qs.count = j;
+           }
+         else if (p != -SYSTEMSOLVABLE)
+           {
+             /* could fallthrough, but then we would do pool_queuetowhatprovides twice */
+             queue_free(&qs);
+             solver_addrule(solv, s - pool->solvables, 0, d);  /* allow update of s */
+             return;
+           }
        }
     }
+  if (!isorphaned && p == -SYSTEMSOLVABLE && qs.count && solv->dupmap.size)
+    p = s - pool->solvables;           /* let the dup rules sort it out */
   if (qs.count && p == -SYSTEMSOLVABLE)
     p = queue_shift(&qs);
-  d = qs.count ? pool_queuetowhatprovides(pool, &qs) : 0;
-  queue_free(&qs);
-  solver_addrule(solv, p, d);  /* allow update of s */
+  if (qs.count > 1)
+    {
+      d = pool_queuetowhatprovides(pool, &qs);
+      queue_free(&qs);
+      solver_addrule(solv, p, 0, d);   /* allow update of s */
+    }
+  else
+    {
+      d = qs.count ? qs.elements[0] : 0;
+      queue_free(&qs);
+      solver_addrule(solv, p, d, 0);   /* allow update of s */
+    }
 }
 
 static inline void
@@ -1533,7 +1778,10 @@ solver_addinfarchrules(Solver *solv, Map *addedmap)
              if (installed && pool->solvables[p].repo == installed && !haveinstalled)
                continue;       /* installed package not in lock-step */
            }
-         solver_addrule(solv, -p, lsq.count ? pool_queuetowhatprovides(pool, &lsq) : 0);
+         if (lsq.count < 2)
+           solver_addrule(solv, -p, lsq.count ? lsq.elements[0] : 0, 0);
+         else
+           solver_addrule(solv, -p, 0, pool_queuetowhatprovides(pool, &lsq));
        }
     }
   queue_free(&lsq);
@@ -1596,7 +1844,7 @@ add_cleandeps_package(Solver *solv, Id p)
   queue_pushunique(solv->cleandeps_updatepkgs, p);
 }
 
-static inline void
+static void
 solver_addtodupmaps(Solver *solv, Id p, Id how, int targeted)
 {
   Pool *pool = solv->pool;
@@ -1716,6 +1964,15 @@ solver_createdupmaps(Solver *solv)
                  solver_addtodupmaps(solv, p, how, targeted);
                }
            }
+         else if (select == SOLVER_SOLVABLE_ALL)
+           {
+             FOR_POOL_SOLVABLES(p)
+               {
+                 MAPSET(&solv->dupinvolvedmap, p);
+                 if (installed && pool->solvables[p].repo != installed)
+                   MAPSET(&solv->dupmap, p);
+               }
+           }
          else
            {
              targeted = how & SOLVER_TARGETED ? 1 : 0;
@@ -1760,9 +2017,11 @@ void
 solver_addduprules(Solver *solv, Map *addedmap)
 {
   Pool *pool = solv->pool;
+  Repo *installed = solv->installed;
   Id p, pp;
   Solvable *s, *ps;
   int first, i;
+  Rule *r;
 
   solv->duprules = solv->nrules;
   for (i = 1; i < pool->nsolvables; i++)
@@ -1782,11 +2041,11 @@ solver_addduprules(Solver *solv, Map *addedmap)
            break;
          if (!MAPTST(&solv->dupinvolvedmap, p))
            continue;
-         if (solv->installed && ps->repo == solv->installed)
+         if (installed && ps->repo == installed)
            {
              if (!solv->updatemap.size)
-               map_grow(&solv->updatemap, solv->installed->end - solv->installed->start);
-             MAPSET(&solv->updatemap, p - solv->installed->start);
+               map_grow(&solv->updatemap, installed->end - installed->start);
+             MAPSET(&solv->updatemap, p - installed->start);
              if (!MAPTST(&solv->dupmap, p))
                {
                  Id ip, ipp;
@@ -1799,14 +2058,32 @@ solver_addduprules(Solver *solv, Map *addedmap)
                      if (is->evr == ps->evr && solvable_identical(ps, is))
                        break;
                    }
-                 if (!ip)
-                   solver_addrule(solv, -p, 0);        /* no match, sorry */
-                 else
-                   MAPSET(&solv->dupmap, p);           /* for best rules processing */
+                 if (ip)
+                   {
+                     /* ok, found a good one. we may keep this package. */
+                     MAPSET(&solv->dupmap, p);         /* for best rules processing */
+                     continue;
+                   }
+                 r = solv->rules + solv->updaterules + (p - installed->start);
+                 if (!r->p)
+                     r = solv->rules + solv->featurerules + (p - installed->start);
+                 if (r->p && solv->specialupdaters && solv->specialupdaters[p - installed->start])
+                   {
+                     /* this is a multiversion orphan, we're good if an update is installed */
+                     solver_addrule(solv, -p, 0, solv->specialupdaters[p - installed->start]);
+                     continue;
+                   }
+                 if (!r->p || (r->p == p && !r->d && !r->w2))
+                   {
+                     /* this is an orphan */
+                     MAPSET(&solv->dupmap, p);         /* for best rules processing */
+                     continue;
+                   }
+                 solver_addrule(solv, -p, 0, 0);       /* no match, sorry */
                }
            }
          else if (!MAPTST(&solv->dupmap, p))
-           solver_addrule(solv, -p, 0);
+           solver_addrule(solv, -p, 0, 0);
        }
     }
   solv->duprules_end = solv->nrules;
@@ -2153,8 +2430,10 @@ jobtodisablelist(Solver *solv, Id how, Id what, Queue *q)
       if (!installed)
        break;
       if (select == SOLVER_SOLVABLE_ALL || (select == SOLVER_SOLVABLE_REPO && what == installed->repoid))
-       FOR_REPO_SOLVABLES(installed, p, s)
-         queue_push2(q, DISABLE_UPDATE, p);
+       {
+         FOR_REPO_SOLVABLES(installed, p, s)
+           queue_push2(q, DISABLE_UPDATE, p);
+       }
       FOR_JOB_SELECT(p, pp, select, what)
        if (pool->solvables[p].repo == installed)
          {
@@ -2364,34 +2643,30 @@ solver_reenablepolicyrules_cleandeps(Solver *solv, Id pkg)
  ***/
 
 static void
-addrpmruleinfo(Solver *solv, Id p, Id d, int type, Id dep)
+addpkgruleinfo(Solver *solv, Id p, Id p2, Id d, int type, Id dep)
 {
   Pool *pool = solv->pool;
   Rule *r;
-  Id w2, op, od, ow2;
-
-  /* check if this creates the rule we're searching for */
-  r = solv->rules + solv->ruleinfoq->elements[0];
-  op = r->p;
-  od = r->d < 0 ? -r->d - 1 : r->d;
-  ow2 = 0;
 
-  /* normalize */
-  w2 = d > 0 ? 0 : d;
-  if (p < 0 && d > 0 && (!pool->whatprovidesdata[d] || !pool->whatprovidesdata[d + 1]))
-    {
-      w2 = pool->whatprovidesdata[d];
-      d = 0;
-    }
-  if (p > 0 && d < 0)          /* this hack is used for package links and complex deps */
+  if (d)
     {
-      w2 = p;
-      p = d;
+      assert(!p2 && d > 0);
+      if (!pool->whatprovidesdata[d])
+       d = 0;
+      else if (!pool->whatprovidesdata[d + 1])
+       {
+         p2 = pool->whatprovidesdata[d];
+         d = 0;
+       }
     }
 
-  if (d > 0)
+  /* check if this creates the rule we're searching for */
+  r = solv->rules + solv->ruleinfoq->elements[0];
+  if (d)
     {
-      if (p != op && !od)
+      /* three or more literals */
+      Id od = r->d < 0 ? -r->d - 1 : r->d;
+      if (p != r->p && !od)
        return;
       if (d != od)
        {
@@ -2403,43 +2678,33 @@ addrpmruleinfo(Solver *solv, Id p, Id d, int type, Id dep)
          if (*odp)
            return;
        }
-      w2 = 0;
-      /* handle multiversion conflict rules */
-      if (p < 0 && pool->whatprovidesdata[d] < 0)
-       {
-         w2 = pool->whatprovidesdata[d];
-         /* XXX: free memory */
-       }
+      if (p < 0 && pool->whatprovidesdata[d] < 0 && type == SOLVER_RULE_PKG_CONFLICTS)
+       p2 = pool->whatprovidesdata[d];
     }
   else
     {
-      if (od)
-       return;
-      ow2 = r->w2;
-      if (p > w2)
+      /* one or two literals */
+      Id op = p, op2 = p2;
+      if (op2 && op > op2)     /* normalize */
        {
-         if (w2 != op || p != ow2)
-           return;
+         Id o = op;
+         op = op2;
+         op2 = o;
        }
-      else
+      if (r->p != op || r->w2 != op2 || (r->d && r->d != -1))
+       return;
+      if (type == SOLVER_RULE_PKG_CONFLICTS && !p2)
+       p2 = -SYSTEMSOLVABLE;
+      if (type == SOLVER_RULE_PKG_SAME_NAME)
        {
-         if (p != op || w2 != ow2)
-           return;
+         p = op;       /* we normalize same name order */
+         p2 = op2;
        }
     }
   /* yep, rule matches. record info */
   queue_push(solv->ruleinfoq, type);
-  if (type == SOLVER_RULE_RPM_SAME_NAME)
-    {
-      /* we normalize same name order */
-      queue_push(solv->ruleinfoq, op < 0 ? -op : 0);
-      queue_push(solv->ruleinfoq, ow2 < 0 ? -ow2 : 0);
-    }
-  else
-    {
-      queue_push(solv->ruleinfoq, p < 0 ? -p : 0);
-      queue_push(solv->ruleinfoq, w2 < 0 ? -w2 : 0);
-    }
+  queue_push(solv->ruleinfoq, p < 0 ? -p : 0);
+  queue_push(solv->ruleinfoq, p2 < 0 ? -p2 : 0);
   queue_push(solv->ruleinfoq, dep);
 }
 
@@ -2465,7 +2730,7 @@ solver_allruleinfos_cmp(const void *ap, const void *bp, void *dp)
 }
 
 static void
-getrpmruleinfos(Solver *solv, Rule *r, Queue *rq)
+getpkgruleinfos(Solver *solv, Rule *r, Queue *rq)
 {
   Pool *pool = solv->pool;
   Id l, pp;
@@ -2477,13 +2742,17 @@ getrpmruleinfos(Solver *solv, Rule *r, Queue *rq)
     {
       if (l >= 0)
        break;
-      solver_addrpmrulesforsolvable(solv, pool->solvables - l, 0);
+      solver_addpkgrulesforsolvable(solv, pool->solvables - l, 0);
     }
 #ifdef ENABLE_LINKED_PKGS
   FOR_RULELITERALS(l, pp, r)
     {
-      if (l < 0 || l != r->p)
-       break;
+      if (l < 0)
+       {
+         if (l == r->p)
+           continue;
+         break;
+       }
       if (!strchr(pool_id2str(pool, pool->solvables[l].name), ':') || !has_package_link(pool, pool->solvables + l))
        break;
       add_package_link(solv, pool->solvables + l, 0, 0);
@@ -2500,7 +2769,7 @@ solver_allruleinfos(Solver *solv, Id rid, Queue *rq)
   int i, j;
 
   queue_empty(rq);
-  if (rid <= 0 || rid >= solv->rpmrules_end)
+  if (rid <= 0 || rid >= solv->pkgrules_end)
     {
       Id type, from, to, dep;
       type = solver_ruleinfo(solv, rid, &from, &to, &dep);
@@ -2510,7 +2779,7 @@ solver_allruleinfos(Solver *solv, Id rid, Queue *rq)
       queue_push(rq, dep);
       return 1;
     }
-  getrpmruleinfos(solv, r, rq);
+  getpkgruleinfos(solv, r, rq);
   /* now sort & unify em */
   if (!rq->count)
     return 0;
@@ -2548,18 +2817,18 @@ solver_ruleinfo(Solver *solv, Id rid, Id *fromp, Id *top, Id *depp)
     *top = 0;
   if (depp)
     *depp = 0;
-  if (rid > 0 && rid < solv->rpmrules_end)
+  if (rid > 0 && rid < solv->pkgrules_end)
     {
       Queue rq;
       int i;
 
       if (r->p >= 0)
-       return SOLVER_RULE_RPM;
+       return SOLVER_RULE_PKG;
       if (fromp)
        *fromp = -r->p;
       queue_init(&rq);
-      getrpmruleinfos(solv, r, &rq);
-      type = SOLVER_RULE_RPM;
+      getpkgruleinfos(solv, r, &rq);
+      type = SOLVER_RULE_PKG;
       for (i = 0; i < rq.count; i += 4)
        {
          Id qt, qo, qp, qd;
@@ -2567,7 +2836,7 @@ solver_ruleinfo(Solver *solv, Id rid, Id *fromp, Id *top, Id *depp)
          qp = rq.elements[i + 1];
          qo = rq.elements[i + 2];
          qd = rq.elements[i + 3];
-         if (type == SOLVER_RULE_RPM || type > qt)
+         if (type == SOLVER_RULE_PKG || type > qt)
            {
              type = qt;
              if (fromp)
@@ -2635,8 +2904,26 @@ solver_ruleinfo(Solver *solv, Id rid, Id *fromp, Id *top, Id *depp)
     }
   if (rid >= solv->bestrules && rid < solv->bestrules_end)
     {
+      if (fromp && solv->bestrules_pkg[rid - solv->bestrules] > 0)
+       *fromp = solv->bestrules_pkg[rid - solv->bestrules];
       return SOLVER_RULE_BEST;
     }
+  if (rid >= solv->yumobsrules && rid < solv->yumobsrules_end)
+    {
+      if (fromp)
+       *fromp = -r->p;
+      if (top)
+       {
+         /* first solvable is enough, we just need it for the name */
+         if (!r->d || r->d == -1)
+           *top = r->w2;
+         else
+           *top = pool->whatprovidesdata[r->d < 0 ? -r->d : r->d];
+       }
+      if (depp)
+       *depp = solv->yumobsrules_info[rid - solv->yumobsrules];
+      return SOLVER_RULE_YUMOBS;
+    }
   if (rid >= solv->choicerules && rid < solv->choicerules_end)
     {
       return SOLVER_RULE_CHOICE;
@@ -2653,8 +2940,8 @@ solver_ruleclass(Solver *solv, Id rid)
 {
   if (rid <= 0)
     return SOLVER_RULE_UNKNOWN;
-  if (rid > 0 && rid < solv->rpmrules_end)
-    return SOLVER_RULE_RPM;
+  if (rid > 0 && rid < solv->pkgrules_end)
+    return SOLVER_RULE_PKG;
   if (rid >= solv->jobrules && rid < solv->jobrules_end)
     return SOLVER_RULE_JOB;
   if (rid >= solv->updaterules && rid < solv->updaterules_end)
@@ -2667,9 +2954,11 @@ solver_ruleclass(Solver *solv, Id rid)
     return SOLVER_RULE_INFARCH;
   if (rid >= solv->bestrules && rid < solv->bestrules_end)
     return SOLVER_RULE_BEST;
+  if (rid >= solv->yumobsrules && rid < solv->yumobsrules_end)
+    return SOLVER_RULE_YUMOBS;
   if (rid >= solv->choicerules && rid < solv->choicerules_end)
     return SOLVER_RULE_CHOICE;
-  if (rid >= solv->learntrules)
+  if (rid >= solv->learntrules && rid < solv->nrules)
     return SOLVER_RULE_LEARNT;
   return SOLVER_RULE_UNKNOWN;
 }
@@ -2726,6 +3015,14 @@ solver_rule2solvable(Solver *solv, Id rid)
   return 0;
 }
 
+Id
+solver_rule2pkgrule(Solver *solv, Id rid)
+{
+  if (rid >= solv->choicerules && rid < solv->choicerules_end)
+    return solv->choicerules_ref[rid - solv->choicerules];
+  return 0;
+}
+
 static void
 solver_rule2rules_rec(Solver *solv, Id rid, Queue *q, Map *seen)
 {
@@ -2769,32 +3066,51 @@ solver_rule2rules(Solver *solv, Id rid, Queue *q, int recursive)
 
 /* check if the newest versions of pi still provides the dependency we're looking for */
 static int
-solver_choicerulecheck(Solver *solv, Id pi, Rule *r, Map *m)
+solver_choicerulecheck(Solver *solv, Id pi, Rule *r, Map *m, Queue *q)
 {
   Pool *pool = solv->pool;
   Rule *ur;
-  Queue q;
-  Id p, pp, qbuf[32];
+  Id p, pp;
   int i;
 
-  ur = solv->rules + solv->updaterules + (pi - pool->installed->start);
-  if (!ur->p)
-    ur = solv->rules + solv->featurerules + (pi - pool->installed->start);
-  if (!ur->p)
-    return 0;
-  queue_init_buffer(&q, qbuf, sizeof(qbuf)/sizeof(*qbuf));
-  FOR_RULELITERALS(p, pp, ur)
-    if (p > 0)
-      queue_push(&q, p);
-  if (q.count > 1)
-    policy_filter_unwanted(solv, &q, POLICY_MODE_CHOOSE);
-  for (i = 0; i < q.count; i++)
-    if (MAPTST(m, q.elements[i]))
-      break;
-  /* 1: none of the newest versions provide it */
-  i = i == q.count ? 1 : 0;
-  queue_free(&q);
-  return i;
+  if (!q->count || q->elements[0] != pi)
+    {
+      if (q->count)
+        queue_empty(q);
+      ur = solv->rules + solv->updaterules + (pi - pool->installed->start);
+      if (!ur->p)
+        ur = solv->rules + solv->featurerules + (pi - pool->installed->start);
+      if (!ur->p)
+       return 0;
+      queue_push2(q, pi, 0);
+      FOR_RULELITERALS(p, pp, ur)
+       if (p > 0)
+         queue_push(q, p);
+    }
+  if (q->count == 2)
+    return 1;
+  if (q->count == 3)
+    {
+      p = q->elements[2];
+      return MAPTST(m, p) ? 0 : 1;
+    }
+  if (!q->elements[1])
+    {
+      for (i = 2; i < q->count; i++)
+       if (!MAPTST(m, q->elements[i]))
+         break;
+      if (i == q->count)
+       return 0;       /* all provide it, no need to filter */
+      /* some don't provide it, have to filter */
+      queue_deleten(q, 0, 2);
+      policy_filter_unwanted(solv, q, POLICY_MODE_CHOOSE);
+      queue_unshift(q, 1);     /* filter mark */
+      queue_unshift(q, pi);
+    }
+  for (i = 2; i < q->count; i++)
+    if (MAPTST(m, q->elements[i]))
+      return 0;                /* at least one provides it */
+  return 1;    /* none of the new packages provided it */
 }
 
 static inline void
@@ -2819,7 +3135,7 @@ solver_addchoicerules(Solver *solv)
   Pool *pool = solv->pool;
   Map m, mneg;
   Rule *r;
-  Queue q, qi;
+  Queue q, qi, qcheck;
   int i, j, rid, havechoice;
   Id p, d, pp;
   Id p2, pp2;
@@ -2835,9 +3151,10 @@ solver_addchoicerules(Solver *solv)
       return;
     }
   now = solv_timems(0);
-  solv->choicerules_ref = solv_calloc(solv->rpmrules_end, sizeof(Id));
+  solv->choicerules_ref = solv_calloc(solv->pkgrules_end, sizeof(Id));
   queue_init(&q);
   queue_init(&qi);
+  queue_init(&qcheck);
   map_init(&m, pool->nsolvables);
   map_init(&mneg, pool->nsolvables);
   /* set up negative assertion map from infarch and dup rules */
@@ -2850,10 +3167,10 @@ solver_addchoicerules(Solver *solv)
   lastaddedp = 0;
   lastaddedd = 0;
   lastaddedcnt = 0;
-  for (rid = 1; rid < solv->rpmrules_end ; rid++)
+  for (rid = 1; rid < solv->pkgrules_end ; rid++)
     {
       r = solv->rules + rid;
-      if (r->p >= 0 || ((r->d == 0 || r->d == -1) && r->w2 < 0))
+      if (r->p >= 0 || ((r->d == 0 || r->d == -1) && r->w2 <= 0))
        continue;       /* only look at requires rules */
       /* solver_printrule(solv, SOLV_DEBUG_RESULT, r); */
       queue_empty(&q);
@@ -2955,7 +3272,7 @@ solver_addchoicerules(Solver *solv)
          p2 = qi.elements[i];
          if (!p2)
            continue;
-         if (solver_choicerulecheck(solv, p2, r, &m))
+         if (solver_choicerulecheck(solv, p2, r, &m, &qcheck))
            {
              /* oops, remove element p from q */
              queue_removeelement(&q, qi.elements[i + 1]);
@@ -2964,6 +3281,7 @@ solver_addchoicerules(Solver *solv)
          qi.elements[j++] = p2;
        }
       queue_truncate(&qi, j);
+
       if (!q.count || !qi.count)
        {
          FOR_RULELITERALS(p, pp, r)
@@ -3023,7 +3341,7 @@ solver_addchoicerules(Solver *solv)
       lastaddedd = d;
       lastaddedcnt = q.count;
 
-      solver_addrule(solv, r->p, d);
+      solver_addrule(solv, r->p, 0, d);
       queue_push(&solv->weakruleq, solv->nrules - 1);
       solv->choicerules_ref[solv->nrules - 1 - solv->choicerules] = rid;
 #if 0
@@ -3035,6 +3353,7 @@ solver_addchoicerules(Solver *solv)
     }
   queue_free(&q);
   queue_free(&qi);
+  queue_free(&qcheck);
   map_free(&m);
   map_free(&mneg);
   solv->choicerules_end = solv->nrules;
@@ -3155,7 +3474,10 @@ solver_addbestrules(Solver *solv, int havebestinstalljobs)
              if (q.count == oldcnt)
                continue;       /* nothing filtered */
              p2 = queue_shift(&q);
-             solver_addrule(solv, p2, q.count ? pool_queuetowhatprovides(pool, &q) : 0);
+             if (q.count < 2)
+               solver_addrule(solv, p2, q.count ? q.elements[0] : 0, 0);
+             else
+               solver_addrule(solv, p2, 0, pool_queuetowhatprovides(pool, &q));
              queue_push(&r2pkg, -(solv->jobrules + j));
            }
        }
@@ -3163,6 +3485,12 @@ solver_addbestrules(Solver *solv, int havebestinstalljobs)
 
   if (solv->bestupdatemap_all || solv->bestupdatemap.size)
     {
+      Map m;
+
+      if (solv->allowuninstall || solv->allowuninstall_all || solv->allowuninstallmap.size)
+       map_init(&m, pool->nsolvables);
+      else
+       map_init(&m, 0);
       FOR_REPO_SOLVABLES(installed, p, s)
        {
          Id d, p2, pp2;
@@ -3221,10 +3549,37 @@ solver_addbestrules(Solver *solv, int havebestinstalljobs)
                    queue_pushunique(&q, q2.elements[j]);
                }
            }
+         if (solv->allowuninstall || solv->allowuninstall_all || (solv->allowuninstallmap.size && MAPTST(&solv->allowuninstallmap, p - installed->start)))
+           {
+             /* package is flagged both for allowuninstall and best, add negative rules */
+             d = q.count == 1 ? q.elements[0] : -pool_queuetowhatprovides(pool, &q);
+             for (i = 0; i < q.count; i++)
+               MAPSET(&m, q.elements[i]);
+             r = solv->rules + solv->featurerules + (p - installed->start);
+             if (!r->p)        /* identical to update rule? */
+               r = solv->rules + solv->updaterules + (p - installed->start);
+             FOR_RULELITERALS(p2, pp2, r)
+               {
+                 if (MAPTST(&m, p2))
+                   continue;
+                 if (d >= 0)
+                   solver_addrule(solv, -p2, d, 0);
+                 else
+                   solver_addrule(solv, -p2, 0, -d);
+                 queue_push(&r2pkg, p);
+               }
+             for (i = 0; i < q.count; i++)
+               MAPCLR(&m, q.elements[i]);
+             continue;
+           }
          p2 = queue_shift(&q);
-         solver_addrule(solv, p2, q.count ? pool_queuetowhatprovides(pool, &q) : 0);
+         if (q.count < 2)
+           solver_addrule(solv, p2, q.count ? q.elements[0] : 0, 0);
+         else
+           solver_addrule(solv, p2, 0, pool_queuetowhatprovides(pool, &q));
          queue_push(&r2pkg, p);
        }
+      map_free(&m);
     }
   if (r2pkg.count)
     solv->bestrules_pkg = solv_memdup2(r2pkg.elements, r2pkg.count, sizeof(Id));
@@ -3234,6 +3589,228 @@ solver_addbestrules(Solver *solv, int havebestinstalljobs)
   queue_free(&r2pkg);
 }
 
+
+
+
+/* yumobs rule handling */
+
+static void
+find_obsolete_group(Solver *solv, Id obs, Queue *q)
+{
+  Pool *pool = solv->pool;
+  Queue qn;
+  Id p2, pp2, op, *opp, opp2;
+  int i, j, qnc, ncnt;
+
+  queue_empty(q);
+  FOR_PROVIDES(p2, pp2, obs)
+    {
+      Solvable *s2 = pool->solvables + p2;
+      if (s2->repo != pool->installed)
+       continue;
+      if (!pool->obsoleteusesprovides && !pool_match_nevr(pool, pool->solvables + p2, obs))
+       continue;
+      /* we obsolete installed package s2 with obs. now find all other packages that have the same dep  */
+      for (opp = solv->obsoletes_data + solv->obsoletes[p2 - solv->installed->start]; (op = *opp++) != 0;)
+       {
+         Solvable *os = pool->solvables + op;
+         Id obs2, *obsp2;
+         if (!os->obsoletes)
+           continue;
+         if (pool->obsoleteusescolors && !pool_colormatch(pool, s2, os))
+           continue;
+         obsp2 = os->repo->idarraydata + os->obsoletes; 
+         while ((obs2 = *obsp2++) != 0)
+           if (obs2 == obs)
+             break;
+         if (obs2)
+           queue_pushunique(q, op);
+       }
+      /* also search packages with the same name */
+      FOR_PROVIDES(op, opp2, s2->name)
+       {
+         Solvable *os = pool->solvables + op;
+         Id obs2, *obsp2;
+         if (os->name != s2->name)
+           continue;
+         if (!os->obsoletes)
+           continue;
+         if (pool->obsoleteusescolors && !pool_colormatch(pool, s2, os))
+           continue;
+         obsp2 = os->repo->idarraydata + os->obsoletes; 
+         while ((obs2 = *obsp2++) != 0)
+           if (obs2 == obs)
+             break;
+         if (obs2)
+           queue_pushunique(q, op);
+       }
+    }
+  /* find names so that we can build groups */
+  queue_init_clone(&qn, q);
+  prune_to_best_version(solv->pool, &qn);
+#if 0
+{
+  for (i = 0; i < qn.count; i++)
+    printf(" + %s\n", pool_solvid2str(pool, qn.elements[i]));
+}
+#endif
+  /* filter into name groups */
+  qnc = qn.count;
+  if (qnc == 1)
+    {
+      queue_free(&qn);
+      queue_empty(q);
+      return;
+    }
+  ncnt = 0;
+  for (i = 0; i < qnc; i++)
+    {
+      Id n = pool->solvables[qn.elements[i]].name;
+      int got = 0;
+      for (j = 0; j < q->count; j++)
+       {
+         Id p = q->elements[j];
+         if (pool->solvables[p].name == n)
+           {
+             queue_push(&qn, p);
+             got = 1;
+           }
+       }
+      if (got)
+       {
+         queue_push(&qn, 0);
+         ncnt++;
+       }
+    }
+  if (ncnt <= 1)
+    {
+      queue_empty(q);
+    }
+  else
+    {
+      queue_empty(q);
+      queue_insertn(q, 0, qn.count - qnc, qn.elements + qnc);
+    }
+  queue_free(&qn);
+}
+
+void
+solver_addyumobsrules(Solver *solv)
+{
+  Pool *pool = solv->pool;
+  Repo *installed = solv->installed;
+  Id p, op, *opp;
+  Solvable *s;
+  Queue qo, qq, yumobsinfoq;
+  int i, j, k;
+  unsigned int now;
+
+  solv->yumobsrules = solv->nrules;
+  if (!installed || !solv->obsoletes)
+    {
+      solv->yumobsrules_end = solv->nrules;
+      return;
+    }
+  now = solv_timems(0);
+  queue_init(&qo);
+  FOR_REPO_SOLVABLES(installed, p, s)
+    {
+      if (!solv->obsoletes[p - installed->start])
+       continue;
+#if 0
+printf("checking yumobs for %s\n", pool_solvable2str(pool, s));
+#endif
+      queue_empty(&qo);
+      for (opp = solv->obsoletes_data + solv->obsoletes[p - installed->start]; (op = *opp++) != 0;)
+       {
+         Solvable *os = pool->solvables + op;
+          Id obs, *obsp = os->repo->idarraydata + os->obsoletes;
+         Id p2, pp2;
+         while ((obs = *obsp++) != 0)
+           {
+             FOR_PROVIDES(p2, pp2, obs)
+               {
+                 Solvable *s2 = pool->solvables + p2;
+                 if (s2->repo != installed)
+                   continue;
+                 if (!pool->obsoleteusesprovides && !pool_match_nevr(pool, pool->solvables + p2, obs))
+                   continue;
+                 if (pool->obsoleteusescolors && !pool_colormatch(pool, s, s2))
+                   continue;
+                 queue_pushunique(&qo, obs);
+                 break;
+               }
+           }
+       }
+    }
+  if (!qo.count)
+    {
+      queue_free(&qo);
+      return;
+    }
+  queue_init(&yumobsinfoq);
+  queue_init(&qq);
+  for (i = 0; i < qo.count; i++)
+    {
+      int group, groupk, groupstart;
+      queue_empty(&qq);
+#if 0
+printf("investigating %s\n", pool_dep2str(pool, qo.elements[i]));
+#endif
+      find_obsolete_group(solv, qo.elements[i], &qq);
+#if 0
+printf("result:\n");
+for (j = 0; j < qq.count; j++)
+  if (qq.elements[j] == 0)
+    printf("---\n");
+  else
+    printf("%s\n", pool_solvid2str(pool, qq.elements[j]));
+#endif
+  
+      if (!qq.count)
+       continue;
+      /* at least two goups, build rules */
+      group = 0;
+      for (j = 0; j < qq.count; j++)
+       {
+         p = qq.elements[j];
+         if (!p)
+           {
+             group++;
+             continue;
+           }
+         if (pool->solvables[p].repo == installed)
+           continue;
+         groupk = 0;
+         groupstart = 0;
+         for (k = 0; k < qq.count; k++)
+           {
+             Id pk = qq.elements[k];
+             if (pk)
+               continue;
+             if (group != groupk && k > groupstart)
+               {
+                 /* add the rule */
+                 if (k - groupstart == 1)
+                   solver_addrule(solv, -p, qq.elements[groupstart], 0);
+                 else
+                   solver_addrule(solv, -p, 0, pool_ids2whatprovides(pool, qq.elements + groupstart, k - groupstart));
+                 queue_push(&yumobsinfoq, qo.elements[i]);
+               }
+             groupstart = k + 1;
+             groupk++;
+           }
+       }
+    }
+  if (yumobsinfoq.count)
+    solv->yumobsrules_info = solv_memdup2(yumobsinfoq.elements, yumobsinfoq.count, sizeof(Id));
+  queue_free(&yumobsinfoq);
+  queue_free(&qq);
+  queue_free(&qo);
+  solv->yumobsrules_end = solv->nrules;
+  POOL_DEBUG(SOLV_DEBUG_STATS, "yumobs rule creation took %d ms\n", solv_timems(now));
+}
+
 #undef CLEANDEPSDEBUG
 
 /*
@@ -3270,8 +3847,6 @@ dep_pkgcheck(Solver *solv, Id dep, Map *m, Queue *q)
            }
          if (rd->flags == REL_NAMESPACE && rd->name == NAMESPACE_SPLITPROVIDES)
            return;
-         if (rd->flags == REL_NAMESPACE && rd->name == NAMESPACE_INSTALLED)
-           return;
        }
     }
   FOR_PROVIDES(p, pp, dep)
@@ -3303,9 +3878,11 @@ check_xsupp(Solver *solv, Queue *depq, Id dep)
              return check_xsupp(solv, depq, rd->evr);
            }
          if (rd->flags == REL_NAMESPACE && rd->name == NAMESPACE_SPLITPROVIDES)
+#if 0
            return solver_splitprovides(solv, rd->evr);
-         if (rd->flags == REL_NAMESPACE && rd->name == NAMESPACE_INSTALLED)
-           return solver_dep_installed(solv, rd->evr);
+#else
+           return 0;
+#endif
        }
       if (depq && rd->flags == REL_NAMESPACE)
        {
@@ -3331,6 +3908,98 @@ queue_contains(Queue *q, Id id)
   return 0;
 }
 
+#ifdef ENABLE_COMPLEX_DEPS
+static void
+complex_cleandeps_remove(Pool *pool, Id ip, Id req, Map *im, Map *installedm, Queue *iq)
+{
+  int i;
+  Queue dq;
+  Id p;
+
+  queue_init(&dq);
+  i = pool_normalize_complex_dep(pool, req, &dq, CPLXDEPS_EXPAND);
+  if (i == 0 || i == 1)
+    {
+      queue_free(&dq);
+      return;
+    }
+  for (i = 0; i < dq.count; i++)
+    {
+      for (; (p = dq.elements[i]) != 0; i++)
+       {
+         if (p < 0)
+           {
+             if (!MAPTST(installedm, -p))
+               break;
+             continue;
+           }
+         if (p != SYSTEMSOLVABLE && MAPTST(im, p))
+           {
+#ifdef CLEANDEPSDEBUG
+             printf("%s requires/recommends %s\n", pool_solvid2str(pool, ip), pool_solvid2str(pool, p));
+#endif
+             queue_push(iq, p);
+           }
+       }
+      while (dq.elements[i])
+       i++;
+    }
+  queue_free(&dq);
+}
+
+static void
+complex_cleandeps_addback(Pool *pool, Id ip, Id req, Map *im, Map *installedm, Queue *iq, Map *userinstalled)
+{
+  int i, blk;
+  Queue dq;
+  Id p;
+
+  queue_init(&dq);
+  i = pool_normalize_complex_dep(pool, req, &dq, CPLXDEPS_EXPAND);
+  if (i == 0 || i == 1)
+    {
+      queue_free(&dq);
+      return;
+    }
+  for (i = 0; i < dq.count; i++)
+    {
+      blk = i;
+      for (; (p = dq.elements[i]) != 0; i++)
+       {
+         if (p < 0)
+           {
+             if (!MAPTST(installedm, -p))
+               break;
+           }
+         else if (p == ip)
+           break;
+       }
+      if (!p)
+       {
+         for (i = blk; (p = dq.elements[i]) != 0; i++)
+           {
+             if (p < 0)
+               continue;
+             if (MAPTST(im, p))
+               continue;
+             if (!MAPTST(installedm, p))
+               continue;
+             if (p == ip || MAPTST(userinstalled, p - pool->installed->start))
+               continue;
+#ifdef CLEANDEPSDEBUG
+             printf("%s requires/recommends %s\n", pool_solvid2str(pool, ip), pool_solvid2str(pool, p));
+#endif
+             MAPSET(im, p);
+             queue_push(iq, p);
+           }
+       }
+      while (dq.elements[i])
+       i++;
+    }
+  queue_free(&dq);
+}
+
+#endif
 
 /*
  * Find all installed packages that are no longer
@@ -3388,8 +4057,10 @@ solver_createcleandepsmap(Solver *solv, Map *cleandepsmap, int unneeded)
          what = job->elements[i + 1];
          select = how & SOLVER_SELECTMASK;
          if (select == SOLVER_SOLVABLE_ALL || (select == SOLVER_SOLVABLE_REPO && what == installed->repoid))
-           FOR_REPO_SOLVABLES(installed, p, s)
-             MAPSET(&userinstalled, p - installed->start);
+           {
+             FOR_REPO_SOLVABLES(installed, p, s)
+               MAPSET(&userinstalled, p - installed->start);
+           }
          FOR_JOB_SELECT(p, pp, select, what)
            if (pool->solvables[p].repo == installed)
              MAPSET(&userinstalled, p - installed->start);
@@ -3420,7 +4091,7 @@ solver_createcleandepsmap(Solver *solv, Map *cleandepsmap, int unneeded)
   /* have special namespace cleandeps erases */
   if (iq.count)
     {
-      for (ip = solv->installed->start; ip < solv->installed->end; ip++)
+      for (ip = installed->start; ip < installed->end; ip++)
        {
          s = pool->solvables + ip;
          if (s->repo != installed)
@@ -3429,7 +4100,7 @@ solver_createcleandepsmap(Solver *solv, Map *cleandepsmap, int unneeded)
            continue;
          supp = s->repo->idarraydata + s->supplements;
          while ((sup = *supp++) != 0)
-           if (check_xsupp(solv, &iq, sup) && !check_xsupp(solv, 0, sup))
+           if (ISRELDEP(sup) && check_xsupp(solv, &iq, sup) && !check_xsupp(solv, 0, sup))
              {
 #ifdef CLEANDEPSDEBUG
                printf("xsupp %s from %s\n", pool_dep2str(pool, sup), pool_solvid2str(pool, ip));
@@ -3480,12 +4151,14 @@ solver_createcleandepsmap(Solver *solv, Map *cleandepsmap, int unneeded)
          if (!strncmp("product:", pool_id2str(pool, s->name), 8))
            {
              MAPSET(&userinstalled, p - installed->start);
-             if (pool->nscallback)
+#ifdef ENABLE_LINKED_PKGS
+             if (solv->instbuddy && solv->instbuddy[p - installed->start] > 1)
                {
-                 Id buddy = pool->nscallback(pool, pool->nscallbackdata, NAMESPACE_PRODUCTBUDDY, p);
-                 if (buddy >= installed->start && buddy < installed->end && pool->solvables[buddy].repo == installed)
+                 Id buddy = solv->instbuddy[p - installed->start];
+                 if (buddy >= installed->start && buddy < installed->end)
                    MAPSET(&userinstalled, buddy - installed->start);
                }
+#endif
            }
        }
     }
@@ -3685,14 +4358,12 @@ solver_createcleandepsmap(Solver *solv, Map *cleandepsmap, int unneeded)
            {
              if (req == SOLVABLE_PREREQMARKER)
                continue;
-#if 0
-             /* count number of installed packages that match */
-             count = 0;
-             FOR_PROVIDES(p, pp, req)
-               if (MAPTST(&installedm, p))
-                 count++;
-             if (count > 1)
-               continue;
+#ifdef ENABLE_COMPLEX_DEPS
+             if (pool_is_complex_dep(pool, req))
+               {
+                 complex_cleandeps_remove(pool, ip, req, &im, &installedm, &iq);
+                 continue;
+               }
 #endif
              FOR_PROVIDES(p, pp, req)
                {
@@ -3711,13 +4382,12 @@ solver_createcleandepsmap(Solver *solv, Map *cleandepsmap, int unneeded)
          reqp = s->repo->idarraydata + s->recommends;
          while ((req = *reqp++) != 0)
            {
-#if 0
-             count = 0;
-             FOR_PROVIDES(p, pp, req)
-               if (MAPTST(&installedm, p))
-                 count++;
-             if (count > 1)
-               continue;
+#ifdef ENABLE_COMPLEX_DEPS
+             if (pool_is_complex_dep(pool, req))
+               {
+                 complex_cleandeps_remove(pool, ip, req, &im, &installedm, &iq);
+                 continue;
+               }
 #endif
              FOR_PROVIDES(p, pp, req)
                {
@@ -3821,19 +4491,58 @@ solver_createcleandepsmap(Solver *solv, Map *cleandepsmap, int unneeded)
 #ifdef CLEANDEPSDEBUG
       printf("adding back %s\n", pool_solvable2str(pool, s));
 #endif
+      if (s->repo == installed && pool->implicitobsoleteusescolors)
+       {
+         Id a, bestarch = 0;
+         FOR_PROVIDES(p, pp, s->name)
+           {
+             Solvable *ps = pool->solvables + p;
+             if (ps->name != s->name || ps->repo == installed)
+               continue;
+             a = ps->arch;
+             a = (a <= pool->lastarch) ? pool->id2arch[a] : 0;
+             if (a && a != 1 && (!bestarch || a < bestarch))
+               bestarch = a;
+           }
+         if (bestarch && (s->arch > pool->lastarch || pool->id2arch[s->arch] != bestarch))
+           {
+             FOR_PROVIDES(p, pp, s->name)
+               {
+                 Solvable *ps = pool->solvables + p;
+                 if (ps->repo == installed && ps->name == s->name && ps->evr == s->evr && ps->arch != s->arch && ps->arch < pool->lastarch && pool->id2arch[ps->arch] == bestarch)
+                   if (!MAPTST(&im, p))
+                     {
+#ifdef CLEANDEPSDEBUG
+                       printf("%s lockstep %s\n", pool_solvid2str(pool, ip), pool_solvid2str(pool, p));
+#endif
+                       MAPSET(&im, p);
+                       queue_push(&iq, p);
+                     }
+               }
+           }
+       }
       if (s->requires)
        {
          reqp = s->repo->idarraydata + s->requires;
          while ((req = *reqp++) != 0)
            {
+#ifdef ENABLE_COMPLEX_DEPS
+             if (pool_is_complex_dep(pool, req))
+               {
+                 complex_cleandeps_addback(pool, ip, req, &im, &installedm, &iq, &userinstalled);
+                 continue;
+               }
+#endif
              FOR_PROVIDES(p, pp, req)
-               if (MAPTST(&im, p))
+               if (p == ip)
                  break;
              if (p)
                continue;
              FOR_PROVIDES(p, pp, req)
                {
-                 if (!MAPTST(&im, p) && MAPTST(&installedm, p))
+                 if (MAPTST(&im, p))
+                   continue;
+                 if (MAPTST(&installedm, p))
                    {
                      if (p == ip)
                        continue;
@@ -3853,14 +4562,23 @@ solver_createcleandepsmap(Solver *solv, Map *cleandepsmap, int unneeded)
          reqp = s->repo->idarraydata + s->recommends;
          while ((req = *reqp++) != 0)
            {
+#ifdef ENABLE_COMPLEX_DEPS
+             if (pool_is_complex_dep(pool, req))
+               {
+                 complex_cleandeps_addback(pool, ip, req, &im, &installedm, &iq, &userinstalled);
+                 continue;
+               }
+#endif
              FOR_PROVIDES(p, pp, req)
-               if (MAPTST(&im, p))
+               if (p == ip)
                  break;
              if (p)
                continue;
              FOR_PROVIDES(p, pp, req)
                {
-                 if (!MAPTST(&im, p) && MAPTST(&installedm, p))
+                 if (MAPTST(&im, p))
+                   continue;
+                 if (MAPTST(&installedm, p))
                    {
                      if (p == ip)
                        continue;
@@ -3963,6 +4681,47 @@ trj_visit(struct trj_data *trj, Id node)
     }
 }
 
+#ifdef ENABLE_COMPLEX_DEPS
+static void
+complex_unneeded(Pool *pool, Id ip, Id req, Queue *edges, Map *cleandepsmap, Queue *unneededq)
+{
+  int i, j;
+  Queue dq;
+  Id p;
+
+  queue_init(&dq);
+  i = pool_normalize_complex_dep(pool, req, &dq, CPLXDEPS_EXPAND);
+  if (i == 0 || i == 1)
+    {
+      queue_free(&dq);
+      return;
+    }
+  for (i = 0; i < dq.count; i++)
+    {
+      for (; (p = dq.elements[i]) != 0; i++)
+       {
+         if (p < 0)
+           {
+             if (pool->solvables[-p].repo != pool->installed)
+               break;
+             continue;
+           }
+         if (p == ip || pool->solvables[p].repo != pool->installed || !MAPTST(cleandepsmap, p - pool->installed->start))
+           continue;
+         for (j = 0; j < unneededq->count; j++)
+           if (p == unneededq->elements[j])
+             {
+               if (edges->elements[edges->count - 1] != j + 1)
+                 queue_push(edges, j + 1);
+               break;
+             }
+       }
+      while (dq.elements[i])
+       i++;
+    }
+  queue_free(&dq);
+}
+#endif
 
 void
 solver_get_unneeded(Solver *solv, Queue *unneededq, int filtered)
@@ -4016,31 +4775,37 @@ solver_get_unneeded(Solver *solv, Queue *unneededq, int filtered)
       for (i = 0; i < count; i++)
        {
          Solvable *s = pool->solvables + unneededq->elements[i];
-         edges.elements[i + 1] = edges.count;
+         int oldcount = edges.count;
+         edges.elements[i + 1] = oldcount;
          for (pass = 0; pass < 2; pass++)
            {
-             int num = 0;
              unsigned int off = pass == 0 ? s->requires : s->recommends;
-             Id p, pp, *dp;
+             Id p, pp, dep, *dp;
              if (off)
-               for (dp = s->repo->idarraydata + off; *dp; dp++)
-                 FOR_PROVIDES(p, pp, *dp)
-                   {
-                     Solvable *sp = pool->solvables + p;
-                     if (s == sp || sp->repo != installed || !MAPTST(&cleandepsmap, p - installed->start))
-                       continue;
-                     for (j = 0; j < count; j++)
-                       if (p == unneededq->elements[j])
-                         break;
-                     if (j == count)
-                       continue;
-                     if (num && edges.elements[edges.count - 1] == j + 1)
+               for (dp = s->repo->idarraydata + off; (dep = *dp) != 0; dp++)
+                 {
+#ifdef ENABLE_COMPLEX_DEPS
+                   if (pool_is_complex_dep(pool, dep))
+                     {
+                       complex_unneeded(pool, s - pool->solvables, dep, &edges, &cleandepsmap, unneededq);
                        continue;
-                     queue_push(&edges, j + 1);
-                     num++;
-                   }
-               if (pass == 0)
-                 nrequires[i] = num;
+                     }
+#endif
+                   FOR_PROVIDES(p, pp, dep)
+                     {
+                       Solvable *sp = pool->solvables + p;
+                       if (s == sp || sp->repo != installed || !MAPTST(&cleandepsmap, p - installed->start))
+                         continue;
+                       for (j = 0; j < count; j++)
+                         if (p == unneededq->elements[j])
+                           {
+                             if (edges.elements[edges.count - 1] != j + 1)
+                               queue_push(&edges, j + 1);
+                           }
+                     }
+                 }
+             if (pass == 0)
+               nrequires[i] = edges.count - oldcount;
            }
          queue_push(&edges, 0);
        }
@@ -4164,4 +4929,96 @@ solver_get_unneeded(Solver *solv, Queue *unneededq, int filtered)
   map_free(&cleandepsmap);
 }
 
-/* EOF */
+
+void
+solver_breakorphans(Solver *solv)
+{
+  Pool *pool = solv->pool;
+  Repo *installed = solv->installed;
+  int i, rid;
+  Map m;
+
+  if (!installed || solv->droporphanedmap_all)
+    return;
+  solv->brokenorphanrules = solv_calloc(1, sizeof(Queue));
+  queue_init(solv->brokenorphanrules);
+  map_init(&m, installed->end - installed->start);
+  for (i = 0; i < solv->orphaned.count; i++)
+    {
+      Id p = solv->orphaned.elements[i];
+      if (pool->solvables[p].repo != installed)
+       continue;
+      if (solv->droporphanedmap.size && MAPTST(&solv->droporphanedmap, p - installed->start))
+       continue;
+      MAPSET(&m, p - installed->start);
+    }
+  for (rid = 1; rid < solv->pkgrules_end ; rid++)
+    {
+      Id p, *dp;
+      Rule *r = solv->rules + rid;
+      /* ignore non-deps and simple conflicts */
+      if (r->p >= 0 || ((r->d == 0 || r->d == -1) && r->w2 < 0))
+       continue;
+      p = -r->p;
+      if (p < installed->start || p >= installed->end || !MAPTST(&m, p - installed->start))
+       {
+         /* need to check other literals */
+         if (r->d == 0 || r->d == -1)
+           continue;
+         for (dp = pool->whatprovidesdata + (r->d < 0 ? -r->d - 1 : r->d); *dp < 0; dp++)
+           {
+             p = -*dp;
+             if (p >= installed->start && p < installed->end && MAPTST(&m, p - installed->start))
+               break;
+           }
+         if (*dp >= 0)
+           continue;
+       }
+      /* ok, disable this rule */
+      queue_push(solv->brokenorphanrules, rid);
+      if (r->d >= 0)
+       solver_disablerule(solv, r);
+    }
+  map_free(&m);
+  if (!solv->brokenorphanrules->count)
+    {
+      queue_free(solv->brokenorphanrules);
+      solv->brokenorphanrules = solv_free(solv->brokenorphanrules);
+    }
+}
+
+void
+solver_check_brokenorphanrules(Solver *solv, Queue *dq)
+{
+  Pool *pool = solv->pool;
+  int i;
+  Id l, pp;
+  
+  queue_empty(dq);
+  if (!solv->brokenorphanrules)
+    return;
+  for (i = 0; i < solv->brokenorphanrules->count; i++)
+    {
+      int rid = solv->brokenorphanrules->elements[i];
+      Rule *r = solv->rules + rid;
+      FOR_RULELITERALS(l, pp, r)
+       {
+         if (l < 0)
+           {
+             if (solv->decisionmap[-l] <= 0)
+               break;
+           }
+         else
+           {
+             if (solv->decisionmap[l] > 0 && pool->solvables[l].repo != solv->installed)
+               break;
+           }
+       }
+      if (l)
+       continue;
+      FOR_RULELITERALS(l, pp, r)
+        if (l > 0 && solv->decisionmap[l] == 0 && pool->solvables[l].repo != solv->installed)
+         queue_pushunique(dq, l);
+    }
+}
+