df751c4fae884d0d215e9453c74b605ad7b1bd83
[platform/upstream/libsolv.git] / src / problems.c
1 /*
2  * Copyright (c) 2007-2009, Novell Inc.
3  *
4  * This program is licensed under the BSD license, read LICENSE.BSD
5  * for further information
6  */
7
8 /*
9  * problems.c
10  *
11  */
12
13 #include <stdio.h>
14 #include <stdlib.h>
15 #include <unistd.h>
16 #include <string.h>
17 #include <assert.h>
18
19 #include "solver.h"
20 #include "solver_private.h"
21 #include "bitmap.h"
22 #include "pool.h"
23 #include "util.h"
24 #include "evr.h"
25 #include "solverdebug.h"
26
27 /**********************************************************************************/
28
29 /* a problem is an item on the solver's problem list. It can either be >0, in that
30  * case it is a update/infarch/dup rule, or it can be <0, which makes it refer to a job
31  * consisting of multiple job rules.
32  */
33
34 static void
35 solver_disableproblem(Solver *solv, Id v)
36 {
37   int i;
38   Id *jp;
39
40   if (v > 0)
41     {
42       if (v >= solv->infarchrules && v < solv->infarchrules_end)
43         {
44           Pool *pool = solv->pool;
45           Id name = pool->solvables[-solv->rules[v].p].name;
46           while (v > solv->infarchrules && pool->solvables[-solv->rules[v - 1].p].name == name)
47             v--;
48           for (; v < solv->infarchrules_end && pool->solvables[-solv->rules[v].p].name == name; v++)
49             solver_disablerule(solv, solv->rules + v);
50           return;
51         }
52       if (v >= solv->duprules && v < solv->duprules_end)
53         {
54           Pool *pool = solv->pool;
55           Id name = pool->solvables[-solv->rules[v].p].name;
56           while (v > solv->duprules && pool->solvables[-solv->rules[v - 1].p].name == name)
57             v--;
58           for (; v < solv->duprules_end && pool->solvables[-solv->rules[v].p].name == name; v++)
59             solver_disablerule(solv, solv->rules + v);
60           return;
61         }
62       solver_disablerule(solv, solv->rules + v);
63       return;
64     }
65   v = -(v + 1);
66   jp = solv->ruletojob.elements;
67   if (solv->bestrules_pkg)
68     {
69       int ni = solv->bestrules_up - solv->bestrules;
70       for (i = 0; i < ni; i++)
71         {
72           int j = solv->bestrules_pkg[i];
73           if (j < 0 && jp[-j - solv->jobrules] == v)
74             solver_disablerule(solv, solv->rules + solv->bestrules + i);
75         }
76     }
77   for (i = solv->jobrules; i < solv->jobrules_end; i++, jp++)
78     if (*jp == v)
79       solver_disablerule(solv, solv->rules + i);
80 }
81
82 /*-------------------------------------------------------------------
83  * enableproblem
84  */
85
86 static void
87 solver_enableproblem(Solver *solv, Id v)
88 {
89   Rule *r;
90   int i;
91   Id *jp;
92
93   if (v > 0)
94     {
95       if (v >= solv->infarchrules && v < solv->infarchrules_end)
96         {
97           Pool *pool = solv->pool;
98           Id name = pool->solvables[-solv->rules[v].p].name;
99           while (v > solv->infarchrules && pool->solvables[-solv->rules[v - 1].p].name == name)
100             v--;
101           for (; v < solv->infarchrules_end && pool->solvables[-solv->rules[v].p].name == name; v++)
102             solver_enablerule(solv, solv->rules + v);
103           return;
104         }
105       if (v >= solv->duprules && v < solv->duprules_end)
106         {
107           Pool *pool = solv->pool;
108           Id name = pool->solvables[-solv->rules[v].p].name;
109           while (v > solv->duprules && pool->solvables[-solv->rules[v - 1].p].name == name)
110             v--;
111           for (; v < solv->duprules_end && pool->solvables[-solv->rules[v].p].name == name; v++)
112             solver_enablerule(solv, solv->rules + v);
113           return;
114         }
115       if (v >= solv->featurerules && v < solv->featurerules_end)
116         {
117           /* do not enable feature rule if update rule is enabled */
118           r = solv->rules + (v - solv->featurerules + solv->updaterules);
119           if (r->d >= 0)
120             return;
121         }
122       solver_enablerule(solv, solv->rules + v);
123       if (v >= solv->updaterules && v < solv->updaterules_end)
124         {
125           /* disable feature rule when enabling update rule */
126           r = solv->rules + (v - solv->updaterules + solv->featurerules);
127           if (r->p)
128             solver_disablerule(solv, r);
129         }
130       return;
131     }
132   v = -(v + 1);
133   jp = solv->ruletojob.elements;
134   if (solv->bestrules_pkg)
135     {
136       int ni = solv->bestrules_up - solv->bestrules;
137       for (i = 0; i < ni; i++)
138         {
139           int j = solv->bestrules_pkg[i];
140           if (j < 0 && jp[-j - solv->jobrules] == v)
141             solver_enablerule(solv, solv->rules + solv->bestrules + i);
142         }
143     }
144   for (i = solv->jobrules; i < solv->jobrules_end; i++, jp++)
145     if (*jp == v)
146       solver_enablerule(solv, solv->rules + i);
147 }
148
149
150 /*-------------------------------------------------------------------
151  * turn a problem rule into a problem id by normalizing it
152  */
153 static Id
154 solver_ruletoproblem(Solver *solv, Id rid)
155 {
156   if (rid >= solv->jobrules && rid < solv->jobrules_end)
157     rid = -(solv->ruletojob.elements[rid - solv->jobrules] + 1);
158   else if (rid >= solv->bestrules && rid < solv->bestrules_up && solv->bestrules_pkg[rid - solv->bestrules] < 0)
159     rid = -(solv->ruletojob.elements[-solv->bestrules_pkg[rid - solv->bestrules] - solv->jobrules] + 1);
160   else if (rid > solv->infarchrules && rid < solv->infarchrules_end)
161     {
162       Pool *pool = solv->pool;
163       Id name = pool->solvables[-solv->rules[rid].p].name;
164       while (rid > solv->infarchrules && pool->solvables[-solv->rules[rid - 1].p].name == name)
165         rid--;
166     }
167   else if (rid > solv->duprules && rid < solv->duprules_end)
168     {
169       Pool *pool = solv->pool;
170       Id name = pool->solvables[-solv->rules[rid].p].name;
171       while (rid > solv->duprules && pool->solvables[-solv->rules[rid - 1].p].name == name)
172         rid--;
173     }
174   return rid;
175 }
176
177 /*-------------------------------------------------------------------
178  * when the solver runs into a problem, it needs to disable all
179  * involved non-pkg rules and record the rules for solution
180  * generation.
181  */
182 void
183 solver_recordproblem(Solver *solv, Id rid)
184 {
185   Id v = solver_ruletoproblem(solv, rid);
186   /* return if problem already countains our rule */
187   if (solv->problems.count)
188     {
189       int i;
190       for (i = solv->problems.count - 1; i >= 0; i--)
191         if (solv->problems.elements[i] == 0)    /* end of last problem reached? */
192           break;
193         else if (solv->problems.elements[i] == v)
194           return;
195     }
196   queue_push(&solv->problems, v);
197 }
198
199 /*-------------------------------------------------------------------
200  * this is called when a problem is solved by disabling a rule.
201  * It calls disableproblem and then re-enables policy rules
202  */
203 void
204 solver_fixproblem(Solver *solv, Id rid)
205 {
206   Id v = solver_ruletoproblem(solv, rid);
207   solver_disableproblem(solv, v);
208   if (v < 0)
209     solver_reenablepolicyrules(solv, -v);
210 }
211
212 /*-------------------------------------------------------------------
213  * disable a set of problems
214  */
215 void
216 solver_disableproblemset(Solver *solv, int start)
217 {
218   int i;
219   for (i = start + 1; i < solv->problems.count - 1; i++)
220     solver_disableproblem(solv, solv->problems.elements[i]);
221 }
222
223 /*-------------------------------------------------------------------
224  * try to fix a problem by auto-uninstalling packages
225  */
226 Id
227 solver_autouninstall(Solver *solv, int start)
228 {
229   Pool *pool = solv->pool;
230   int i;
231   int lastfeature = 0, lastupdate = 0;
232   Id v;
233   Id extraflags = -1;
234   Map *m = 0;
235
236   if (!solv->allowuninstall && !solv->allowuninstall_all)
237     {
238       if (!solv->allowuninstallmap.size)
239         return 0;               /* why did we get called? */
240       m = &solv->allowuninstallmap;
241     }
242   for (i = start + 1; i < solv->problems.count - 1; i++)
243     {
244       v = solv->problems.elements[i];
245       if (v < 0)
246         extraflags &= solv->job.elements[-v - 1];
247       if (v >= solv->updaterules && v < solv->updaterules_end)
248         {
249           Rule *r;
250           if (m && !MAPTST(m, v - solv->updaterules))
251             continue;
252           /* check if identical to feature rule, we don't like that (except for orphans) */
253           r = solv->rules + solv->featurerules + (v - solv->updaterules);
254           if (!r->p)
255             {
256               /* update rule == feature rule */
257               if (v > lastfeature)
258                 lastfeature = v;
259               /* prefer orphaned packages in dup mode */
260               if (solv->keep_orphans)
261                 {
262                   r = solv->rules + v;
263                   if (!r->d && !r->w2 && r->p == (solv->installed->start + (v - solv->updaterules)))
264                     {
265                       lastfeature = v;
266                       lastupdate = 0;
267                       break;
268                     }
269                 }
270               continue;
271             }
272           if (v > lastupdate)
273             lastupdate = v;
274         }
275     }
276   if (!lastupdate && !lastfeature)
277     return 0;
278   v = lastupdate ? lastupdate : lastfeature;
279   POOL_DEBUG(SOLV_DEBUG_UNSOLVABLE, "allowuninstall disabling ");
280   solver_printruleclass(solv, SOLV_DEBUG_UNSOLVABLE, solv->rules + v);
281   /* should really be solver_fixproblem, but we know v is an update/feature rule */
282   solver_disableproblem(solv, v);
283   if (extraflags != -1 && (extraflags & SOLVER_CLEANDEPS) != 0 && solv->cleandepsmap.size)
284     {
285       /* add the package to the updatepkgs list, this will automatically turn
286        * on cleandeps mode */
287       Id p = solv->rules[v].p;
288       if (!solv->cleandeps_updatepkgs)
289         {
290           solv->cleandeps_updatepkgs = solv_calloc(1, sizeof(Queue));
291           queue_init(solv->cleandeps_updatepkgs);
292         }
293       if (p > 0)
294         {
295           int oldupdatepkgscnt = solv->cleandeps_updatepkgs->count;
296           queue_pushunique(solv->cleandeps_updatepkgs, p);
297           if (solv->cleandeps_updatepkgs->count != oldupdatepkgscnt)
298             solver_disablepolicyrules(solv);
299         }
300     }
301   return v;
302 }
303
304
305 /*-------------------------------------------------------------------
306  * enable weak rules
307  *
308  * Reenable all disabled weak rules (marked in weakrulemap)
309  *
310  */
311
312 static void
313 enableweakrules(Solver *solv)
314 {
315   int i;
316   Rule *r;
317
318   if (!solv->weakrulemap.size)
319     return;
320   for (i = 1, r = solv->rules + i; i < solv->learntrules; i++, r++)
321     {
322       if (r->d >= 0) /* already enabled? */
323         continue;
324       if (!MAPTST(&solv->weakrulemap, i))
325         continue;
326       solver_enablerule(solv, r);
327     }
328   /* make sure broken orphan rules stay disabled */
329   if (solv->brokenorphanrules)
330     for (i = 0; i < solv->brokenorphanrules->count; i++)
331       solver_disablerule(solv, solv->rules + solv->brokenorphanrules->elements[i]);
332 }
333
334
335 /*-------------------------------------------------------------------
336  *
337  * refine_suggestion
338  *
339  * at this point, all rules that led to conflicts are disabled.
340  * we re-enable all rules of a problem set but rule "sug", then
341  * continue to disable more rules until there as again a solution.
342  */
343
344 /* FIXME: think about conflicting assertions */
345
346 static void
347 refine_suggestion(Solver *solv, Id *problem, Id sug, Queue *refined, int essentialok)
348 {
349   Pool *pool = solv->pool;
350   int i, j;
351   Id v;
352   Queue disabled;
353   int disabledcnt;
354
355   IF_POOLDEBUG (SOLV_DEBUG_SOLUTIONS)
356     {
357       POOL_DEBUG(SOLV_DEBUG_SOLUTIONS, "refine_suggestion start\n");
358       for (i = 0; problem[i]; i++)
359         {
360           if (problem[i] == sug)
361             POOL_DEBUG(SOLV_DEBUG_SOLUTIONS, "=> ");
362           solver_printproblem(solv, problem[i]);
363         }
364     }
365   queue_empty(refined);
366   if (!essentialok && sug < 0 && (solv->job.elements[-sug - 1] & SOLVER_ESSENTIAL) != 0)
367     return;
368   queue_push(refined, sug);
369
370   /* re-enable all problem rules with the exception of "sug"(gestion) */
371   solver_reset(solv);
372
373   for (i = 0; problem[i]; i++)
374     if (problem[i] != sug)
375       solver_enableproblem(solv, problem[i]);
376   if (sug < 0)
377     solver_reenablepolicyrules(solv, -sug);
378
379   /* here is where the feature rules come into play: if we disabled an
380    * update rule, we enable the corresponding feature rule if there is
381    * one. We do this to make the solver downgrade packages instead of
382    * deinstalling them */
383   if (sug >= solv->updaterules && sug < solv->updaterules_end)
384     {
385       /* enable feature rule */
386       Rule *r = solv->rules + solv->featurerules + (sug - solv->updaterules);
387       if (r->p)
388         solver_enablerule(solv, r);
389     }
390
391   enableweakrules(solv);
392
393   /* disabled contains all of the rules we disabled in the refinement process */
394   queue_init(&disabled);
395   for (;;)
396     {
397       int nother, nfeature, nupdate, pass;
398       queue_empty(&solv->problems);
399       solver_reset(solv);
400       /* we set disablerules to zero because we are only interested in
401        * the first problem and we don't want the solver to disable the problems */
402       solver_run_sat(solv, 0, 0);
403
404       if (!solv->problems.count)
405         {
406           POOL_DEBUG(SOLV_DEBUG_SOLUTIONS, "no more problems!\n");
407           break;                /* great, no more problems */
408         }
409       disabledcnt = disabled.count;
410       nother = nfeature = nupdate = 0;
411       for (pass = 0; pass < 2; pass++)
412         {
413           /* start with 1 to skip over proof index */
414           for (i = 1; i < solv->problems.count - 1; i++)
415             {
416               /* ignore solutions in refined */
417               v = solv->problems.elements[i];
418               if (v == 0)
419                 break;  /* end of problem reached */
420               if (!essentialok && v < 0 && (solv->job.elements[-v - 1] & SOLVER_ESSENTIAL) != 0)
421                 continue;       /* not that one! */
422               if (sug != v)
423                 {
424                   /* check if v is in the given problems list
425                    * we allow disabling all problem rules *after* sug in
426                    * pass 2, to prevent getting the same solution twice */
427                   for (j = 0; problem[j]; j++)
428                     if (problem[j] == v || (pass && problem[j] == sug))
429                       break;
430                   if (problem[j] == v)
431                     continue;
432                 }
433               if (v >= solv->featurerules && v < solv->featurerules_end)
434                 nfeature++;
435               else if (v > solv->updaterules && v < solv->updaterules_end)
436                 nupdate++;
437               else
438                 nother++;
439               queue_push(&disabled, v);
440             }
441           if (disabled.count != disabledcnt)
442             break;
443         }
444       if (disabled.count == disabledcnt)
445         {
446           /* no solution found, this was an invalid suggestion! */
447           POOL_DEBUG(SOLV_DEBUG_SOLUTIONS, "no solution found!\n");
448           refined->count = 0;
449           break;
450         }
451       if (!nother && nupdate && nfeature)
452         {
453           /* got only update rules, filter out feature rules */
454           POOL_DEBUG(SOLV_DEBUG_SOLUTIONS, "throwing away feature rules\n");
455           for (i = j = disabledcnt; i < disabled.count; i++)
456             {
457               v = disabled.elements[i];
458               if (v < solv->featurerules || v >= solv->featurerules_end)
459                 disabled.elements[j++] = v;
460             }
461           disabled.count = j;
462           nfeature = 0;
463         }
464       if (disabled.count == disabledcnt + 1)
465         {
466           /* just one suggestion, add it to refined list */
467           v = disabled.elements[disabledcnt];
468           if (!nfeature && v != sug)
469             queue_push(refined, v);     /* do not record feature rules */
470           solver_disableproblem(solv, v);
471           if (v < 0)
472             solver_reenablepolicyrules(solv, -v);
473           if (v >= solv->updaterules && v < solv->updaterules_end)
474             {
475               Rule *r = solv->rules + (v - solv->updaterules + solv->featurerules);
476               if (r->p)
477                 solver_enablerule(solv, r);     /* enable corresponding feature rule */
478             }
479         }
480       else
481         {
482           /* more than one solution, disable all */
483           /* do not push anything on refine list, as we do not know which solution to choose */
484           /* thus, the user will get another problem if he selects this solution, where he
485            * can choose the right one */
486           IF_POOLDEBUG (SOLV_DEBUG_SOLUTIONS)
487             {
488               POOL_DEBUG(SOLV_DEBUG_SOLUTIONS, "more than one solution found:\n");
489               for (i = disabledcnt; i < disabled.count; i++)
490                 solver_printproblem(solv, disabled.elements[i]);
491             }
492           for (i = disabledcnt; i < disabled.count; i++)
493             {
494               v = disabled.elements[i];
495               solver_disableproblem(solv, v);
496               if (v >= solv->updaterules && v < solv->updaterules_end)
497                 {
498                   Rule *r = solv->rules + (v - solv->updaterules + solv->featurerules);
499                   if (r->p)
500                     solver_enablerule(solv, r);
501                 }
502             }
503         }
504     }
505   /* all done, get us back into the same state as before */
506   /* enable refined rules again */
507   for (i = 0; i < disabled.count; i++)
508     solver_enableproblem(solv, disabled.elements[i]);
509   queue_free(&disabled);
510
511   /* reset policy rules */
512   for (i = 0; problem[i]; i++)
513     solver_enableproblem(solv, problem[i]);
514   solver_disablepolicyrules(solv);
515   /* disable problem rules again */
516   for (i = 0; problem[i]; i++)
517     solver_disableproblem(solv, problem[i]);
518   POOL_DEBUG(SOLV_DEBUG_SOLUTIONS, "refine_suggestion end\n");
519 }
520
521
522 /*-------------------------------------------------------------------
523  * sorting helper for problems
524  *
525  * bring update rules before job rules
526  * make essential job rules last
527  */
528
529 static int
530 problems_sortcmp(const void *ap, const void *bp, void *dp)
531 {
532   Queue *job = dp;
533   Id a = *(Id *)ap, b = *(Id *)bp;
534   if (a < 0 && b > 0)
535     return 1;
536   if (a > 0 && b < 0)
537     return -1;
538   if (a < 0 && b < 0)
539     {
540       int af = job->elements[-a - 1] & SOLVER_ESSENTIAL;
541       int bf = job->elements[-b - 1] & SOLVER_ESSENTIAL;
542       int x = af - bf;
543       if (x)
544         return x;
545     }
546   return a - b;
547 }
548
549 /*
550  * convert a solution rule into a job modifier
551  */
552 static void
553 convertsolution(Solver *solv, Id why, Queue *solutionq)
554 {
555   Pool *pool = solv->pool;
556   if (why < 0)
557     {
558       why = -why;
559       if (why < solv->pooljobcnt)
560         {
561           queue_push(solutionq, SOLVER_SOLUTION_POOLJOB);
562           queue_push(solutionq, why);
563         }
564       else
565         {
566           queue_push(solutionq, SOLVER_SOLUTION_JOB);
567           queue_push(solutionq, why - solv->pooljobcnt);
568         }
569       return;
570     }
571   if (why >= solv->infarchrules && why < solv->infarchrules_end)
572     {
573       Id p, name;
574       /* infarch rule, find replacement */
575       assert(solv->rules[why].p < 0);
576       name = pool->solvables[-solv->rules[why].p].name;
577       while (why > solv->infarchrules && pool->solvables[-solv->rules[why - 1].p].name == name)
578         why--;
579       p = 0;
580       for (; why < solv->infarchrules_end && pool->solvables[-solv->rules[why].p].name == name; why++)
581         if (solv->decisionmap[-solv->rules[why].p] > 0)
582           {
583             p = -solv->rules[why].p;
584             break;
585           }
586       if (!p)
587         return;         /* false alarm */
588       queue_push(solutionq, SOLVER_SOLUTION_INFARCH);
589       queue_push(solutionq, p);
590       return;
591     }
592   if (why >= solv->duprules && why < solv->duprules_end)
593     {
594       Id p, name;
595       /* dist upgrade rule, find replacement */
596       assert(solv->rules[why].p < 0);
597       name = pool->solvables[-solv->rules[why].p].name;
598       while (why > solv->duprules && pool->solvables[-solv->rules[why - 1].p].name == name)
599         why--;
600       p = 0;
601       for (; why < solv->duprules_end && pool->solvables[-solv->rules[why].p].name == name; why++)
602         if (solv->decisionmap[-solv->rules[why].p] > 0)
603           {
604             p = -solv->rules[why].p;
605             break;
606           }
607       if (!p)
608         return;         /* false alarm */
609       queue_push(solutionq, SOLVER_SOLUTION_DISTUPGRADE);
610       queue_push(solutionq, p);
611       return;
612     }
613   if (why >= solv->updaterules && why < solv->updaterules_end)
614     {
615       /* update rule, find replacement package */
616       Id p, pp, rp = 0;
617       Rule *rr;
618
619       /* check if this is a false positive, i.e. the update rule is fulfilled */
620       rr = solv->rules + why;
621       FOR_RULELITERALS(p, pp, rr)
622         if (p > 0 && solv->decisionmap[p] > 0)
623           return;       /* false alarm */
624
625       p = solv->installed->start + (why - solv->updaterules);
626       if (solv->decisionmap[p] > 0)
627         return;         /* false alarm, turned out we can keep the package */
628       rr = solv->rules + solv->featurerules + (why - solv->updaterules);
629       if (!rr->p)
630         rr = solv->rules + why;
631       if (rr->w2)
632         {
633           int mvrp = 0;         /* multi-version replacement */
634           FOR_RULELITERALS(rp, pp, rr)
635             {
636               if (rp > 0 && solv->decisionmap[rp] > 0 && pool->solvables[rp].repo != solv->installed)
637                 {
638                   mvrp = rp;
639                   if (!(solv->multiversion.size && MAPTST(&solv->multiversion, rp)))
640                     break;
641                 }
642             }
643           if (!rp && mvrp)
644             {
645               /* found only multi-version replacements */
646               /* have to split solution into two parts */
647               queue_push(solutionq, p);
648               queue_push(solutionq, mvrp);
649             }
650         }
651       queue_push(solutionq, p);
652       queue_push(solutionq, rp);
653       return;
654     }
655   if (why >= solv->bestrules && why < solv->bestrules_end)
656     {
657       int mvrp;
658       Id p, pp, rp = 0;
659       Rule *rr;
660       /* check false positive */
661       rr = solv->rules + why;
662       FOR_RULELITERALS(p, pp, rr)
663         if (p > 0 && solv->decisionmap[p] > 0)
664           return;       /* false alarm */
665       /* check update/feature rule */
666       p = solv->bestrules_pkg[why - solv->bestrules];
667       if (p < 0)
668         {
669           /* install job */
670           queue_push(solutionq, 0);
671           queue_push(solutionq, solv->ruletojob.elements[-p - solv->jobrules] + 1);
672           return;
673         }
674       if (solv->decisionmap[p] > 0)
675         {
676           /* disable best rule by keeping the old package */
677           queue_push(solutionq, SOLVER_SOLUTION_BEST);
678           queue_push(solutionq, p);
679           return;
680         }
681       rr = solv->rules + solv->featurerules + (p - solv->installed->start);
682       if (!rr->p)
683         rr = solv->rules + solv->updaterules + (p - solv->installed->start);
684       mvrp = 0;         /* multi-version replacement */
685       FOR_RULELITERALS(rp, pp, rr)
686         if (rp > 0 && solv->decisionmap[rp] > 0 && pool->solvables[rp].repo != solv->installed)
687           {
688             mvrp = rp;
689             if (!(solv->multiversion.size && MAPTST(&solv->multiversion, rp)))
690               break;
691           }
692       if (!rp && mvrp)
693         {
694           queue_push(solutionq, SOLVER_SOLUTION_BEST);  /* split, see above */
695           queue_push(solutionq, mvrp);
696           queue_push(solutionq, p);
697           queue_push(solutionq, 0);
698           return;
699         }
700       if (rp)
701         {
702           queue_push(solutionq, SOLVER_SOLUTION_BEST);
703           queue_push(solutionq, rp);
704         }
705       return;
706     }
707 }
708
709 /*
710  * convert problem data into a form usable for refining.
711  * Returns the number of problems.
712  */
713 int
714 solver_prepare_solutions(Solver *solv)
715 {
716   int i, j = 1, idx;
717
718   if (!solv->problems.count)
719     return 0;
720   queue_empty(&solv->solutions);
721   queue_push(&solv->solutions, 0);      /* dummy so idx is always nonzero */
722   idx = solv->solutions.count;
723   queue_push(&solv->solutions, -1);     /* unrefined */
724   /* proofidx stays in position, thus we start with 1 */
725   for (i = 1; i < solv->problems.count; i++)
726     {
727       Id p = solv->problems.elements[i];
728       queue_push(&solv->solutions, p);
729       if (p)
730         continue;
731       /* end of problem reached */
732       solv->problems.elements[j++] = idx;
733       if (i + 1 >= solv->problems.count)
734         break;
735       /* start another problem */
736       solv->problems.elements[j++] = solv->problems.elements[++i];  /* copy proofidx */
737       idx = solv->solutions.count;
738       queue_push(&solv->solutions, -1); /* unrefined */
739     }
740   solv->problems.count = j;
741   return j / 2;
742 }
743
744 /*
745  * refine the simple solution rule list provided by
746  * the solver into multiple lists of job modifiers.
747  */
748 static void
749 create_solutions(Solver *solv, int probnr, int solidx)
750 {
751   Pool *pool = solv->pool;
752   Queue redoq;
753   Queue problem, solution, problems_save, branches_save, decisionq_reason_save;
754   int i, j, nsol;
755   int essentialok;
756   unsigned int now;
757   int oldmistakes = solv->cleandeps_mistakes ? solv->cleandeps_mistakes->count : 0;
758   Id extraflags = -1;
759
760   now = solv_timems(0);
761   queue_init(&redoq);
762   /* save decisionq, decisionq_why, decisionmap, and decisioncnt */
763   for (i = 0; i < solv->decisionq.count; i++)
764     {
765       Id p = solv->decisionq.elements[i];
766       queue_push(&redoq, p);
767       queue_push(&redoq, solv->decisionq_why.elements[i]);
768       queue_push(&redoq, solv->decisionmap[p > 0 ? p : -p]);
769     }
770
771   /* save problems queue */
772   problems_save = solv->problems;
773   memset(&solv->problems, 0, sizeof(solv->problems));
774
775   /* save branches queue */
776   branches_save = solv->branches;
777   memset(&solv->branches, 0, sizeof(solv->branches));
778
779   /* save decisionq_reason */
780   decisionq_reason_save = solv->decisionq_reason;
781   memset(&solv->decisionq_reason, 0, sizeof(solv->decisionq_reason));
782
783   /* extract problem from queue */
784   queue_init(&problem);
785   for (i = solidx + 1; i < solv->solutions.count; i++)
786     {
787       Id v = solv->solutions.elements[i];
788       if (!v)
789         break;
790       queue_push(&problem, v);
791       if (v < 0)
792         extraflags &= solv->job.elements[-v - 1];
793     }
794   if (extraflags == -1)
795     extraflags = 0;
796   if (problem.count > 1)
797     solv_sort(problem.elements, problem.count, sizeof(Id), problems_sortcmp, &solv->job);
798   queue_push(&problem, 0);      /* mark end for refine_suggestion */
799   problem.count--;
800 #if 0
801   for (i = 0; i < problem.count; i++)
802     printf("PP %d %d\n", i, problem.elements[i]);
803 #endif
804
805   /* refine each solution element */
806   nsol = 0;
807   essentialok = 0;
808   queue_init(&solution);
809   for (i = 0; i < problem.count; i++)
810     {
811       int solstart = solv->solutions.count;
812       refine_suggestion(solv, problem.elements, problem.elements[i], &solution, essentialok);
813       queue_push(&solv->solutions, 0);  /* reserve room for number of elements */
814       for (j = 0; j < solution.count; j++)
815         convertsolution(solv, solution.elements[j], &solv->solutions);
816       if (solv->solutions.count == solstart + 1)
817         {
818           solv->solutions.count--;      /* this one did not work out */
819           if (nsol || i + 1 < problem.count)
820             continue;                   /* got one or still hope */
821           if (!essentialok)
822             {
823               /* nothing found, start over */
824               POOL_DEBUG(SOLV_DEBUG_SOLUTIONS, "nothing found, re-run with essentialok = 1\n");
825               essentialok = 1;
826               i = -1;
827               continue;
828             }
829           /* this is bad, we found no solution */
830           /* for now just offer a rule */
831           POOL_DEBUG(SOLV_DEBUG_SOLUTIONS, "nothing found, already did essentialok, fake it\n");
832           queue_push(&solv->solutions, 0);
833           for (j = 0; j < problem.count; j++)
834             {
835               convertsolution(solv, problem.elements[j], &solv->solutions);
836               if (solv->solutions.count > solstart + 1)
837                 break;
838             }
839           if (solv->solutions.count == solstart + 1)
840             {
841               solv->solutions.count--;
842               continue;         /* sorry */
843             }
844         }
845       /* patch in number of solution elements */
846       solv->solutions.elements[solstart] = (solv->solutions.count - (solstart + 1)) / 2;
847       queue_push(&solv->solutions, 0);  /* add end marker */
848       queue_push(&solv->solutions, 0);  /* add end marker */
849       queue_push(&solv->solutions, problem.elements[i]);        /* just for bookkeeping */
850       queue_push(&solv->solutions, extraflags & SOLVER_CLEANDEPS);      /* our extraflags */
851       solv->solutions.elements[solidx + 1 + nsol++] = solstart;
852     }
853   solv->solutions.elements[solidx + 1 + nsol] = 0;      /* end marker */
854   solv->solutions.elements[solidx] = nsol;
855   queue_free(&problem);
856   queue_free(&solution);
857
858   /* restore decisions */
859   memset(solv->decisionmap, 0, pool->nsolvables * sizeof(Id));
860   queue_empty(&solv->decisionq);
861   queue_empty(&solv->decisionq_why);
862   for (i = 0; i < redoq.count; i += 3)
863     {
864       Id p = redoq.elements[i];
865       queue_push(&solv->decisionq, p);
866       queue_push(&solv->decisionq_why, redoq.elements[i + 1]);
867       solv->decisionmap[p > 0 ? p : -p] = redoq.elements[i + 2];
868     }
869   queue_free(&redoq);
870
871   /* restore decision reasons */
872   queue_free(&solv->decisionq_reason);
873   solv->decisionq_reason = decisionq_reason_save;
874
875   /* restore problems */
876   queue_free(&solv->problems);
877   solv->problems = problems_save;
878
879   /* restore branches */
880   queue_free(&solv->branches);
881   solv->branches = branches_save;
882
883   if (solv->cleandeps_mistakes)
884     {
885       if (oldmistakes)
886         queue_truncate(solv->cleandeps_mistakes, oldmistakes);
887       else
888         {
889           queue_free(solv->cleandeps_mistakes);
890           solv->cleandeps_mistakes = solv_free(solv->cleandeps_mistakes);
891         }
892     }
893
894   POOL_DEBUG(SOLV_DEBUG_STATS, "create_solutions for problem #%d took %d ms\n", probnr, solv_timems(now));
895 }
896
897
898 /**************************************************************************/
899
900 unsigned int
901 solver_problem_count(Solver *solv)
902 {
903   return solv->problems.count / 2;
904 }
905
906 Id
907 solver_next_problem(Solver *solv, Id problem)
908 {
909   if (!problem)
910     return solv->problems.count ? 1 : 0;
911   return (problem + 1) * 2 - 1 < solv->problems.count ? problem + 1 : 0;
912 }
913
914 unsigned int
915 solver_solution_count(Solver *solv, Id problem)
916 {
917   Id solidx = solv->problems.elements[problem * 2 - 1];
918   if (solv->solutions.elements[solidx] < 0)
919     create_solutions(solv, problem, solidx);
920   return solv->solutions.elements[solidx];
921 }
922
923 Id
924 solver_next_solution(Solver *solv, Id problem, Id solution)
925 {
926   Id solidx = solv->problems.elements[problem * 2 - 1];
927   if (solv->solutions.elements[solidx] < 0)
928     create_solutions(solv, problem, solidx);
929   return solv->solutions.elements[solidx + solution + 1] ? solution + 1 : 0;
930 }
931
932 unsigned int
933 solver_solutionelement_count(Solver *solv, Id problem, Id solution)
934 {
935   Id solidx = solv->problems.elements[problem * 2 - 1];
936   solidx = solv->solutions.elements[solidx + solution];
937   return solv->solutions.elements[solidx];
938 }
939
940 Id
941 solver_solutionelement_internalid(Solver *solv, Id problem, Id solution)
942 {
943   Id solidx = solv->problems.elements[problem * 2 - 1];
944   solidx = solv->solutions.elements[solidx + solution];
945   return solv->solutions.elements[solidx + 2 * solv->solutions.elements[solidx] + 3];
946 }
947
948 Id
949 solver_solutionelement_extrajobflags(Solver *solv, Id problem, Id solution)
950 {
951   Id solidx = solv->problems.elements[problem * 2 - 1];
952   solidx = solv->solutions.elements[solidx + solution];
953   return solv->solutions.elements[solidx + 2 * solv->solutions.elements[solidx] + 4];
954 }
955
956
957 /*
958  *  return the next item of the proposed solution
959  *  here are the possibilities for p / rp and what
960  *  the solver expects the application to do:
961  *    p                             rp
962  *  -------------------------------------------------------
963  *    SOLVER_SOLUTION_INFARCH       pkgid
964  *    -> add (SOLVER_INSTALL|SOLVER_SOLVABLE, rp) to the job
965  *    SOLVER_SOLUTION_DISTUPGRADE   pkgid
966  *    -> add (SOLVER_INSTALL|SOLVER_SOLVABLE, rp) to the job
967  *    SOLVER_SOLUTION_BEST          pkgid
968  *    -> add (SOLVER_INSTALL|SOLVER_SOLVABLE, rp) to the job
969  *    SOLVER_SOLUTION_JOB           jobidx
970  *    -> remove job (jobidx - 1, jobidx) from job queue
971  *    SOLVER_SOLUTION_POOLJOB       jobidx
972  *    -> remove job (jobidx - 1, jobidx) from pool job queue
973  *    pkgid (> 0)                   0
974  *    -> add (SOLVER_ERASE|SOLVER_SOLVABLE, p) to the job
975  *    pkgid (> 0)                   pkgid (> 0)
976  *    -> add (SOLVER_INSTALL|SOLVER_SOLVABLE, rp) to the job
977  *       (this will replace package p)
978  *
979  * Thus, the solver will either ask the application to remove
980  * a specific job from the job queue, or ask to add an install/erase
981  * job to it.
982  *
983  */
984
985 Id
986 solver_next_solutionelement(Solver *solv, Id problem, Id solution, Id element, Id *p, Id *rp)
987 {
988   Id solidx = solv->problems.elements[problem * 2 - 1];
989   solidx = solv->solutions.elements[solidx + solution];
990   if (!solidx)
991     return 0;
992   solidx += 1 + element * 2;
993   if (!solv->solutions.elements[solidx] && !solv->solutions.elements[solidx + 1])
994     return 0;
995   *p = solv->solutions.elements[solidx];
996   *rp = solv->solutions.elements[solidx + 1];
997   return element + 1;
998 }
999
1000 void
1001 solver_take_solutionelement(Solver *solv, Id p, Id rp, Id extrajobflags, Queue *job)
1002 {
1003   int i;
1004
1005   if (p == SOLVER_SOLUTION_POOLJOB)
1006     {
1007       solv->pool->pooljobs.elements[rp - 1] = SOLVER_NOOP;
1008       solv->pool->pooljobs.elements[rp] = 0;
1009       return;
1010     }
1011   if (p == SOLVER_SOLUTION_JOB)
1012     {
1013       job->elements[rp - 1] = SOLVER_NOOP;
1014       job->elements[rp] = 0;
1015       return;
1016     }
1017   if (rp <= 0 && p <= 0)
1018     return;     /* just in case */
1019   if (rp > 0)
1020     p = SOLVER_INSTALL|SOLVER_SOLVABLE|SOLVER_NOTBYUSER|extrajobflags;
1021   else
1022     {
1023       rp = p;
1024       p = SOLVER_ERASE|SOLVER_SOLVABLE|extrajobflags;
1025     }
1026   for (i = 0; i < job->count; i += 2)
1027     if (job->elements[i] == p && job->elements[i + 1] == rp)
1028       return;
1029   queue_push2(job, p, rp);
1030 }
1031
1032 void
1033 solver_take_solution(Solver *solv, Id problem, Id solution, Queue *job)
1034 {
1035   Id p, rp, element = 0;
1036   Id extrajobflags = solver_solutionelement_extrajobflags(solv, problem, solution);
1037   while ((element = solver_next_solutionelement(solv, problem, solution, element, &p, &rp)) != 0)
1038     solver_take_solutionelement(solv, p, rp, extrajobflags, job);
1039 }
1040
1041
1042 /*-------------------------------------------------------------------
1043  *
1044  * find problem rule
1045  */
1046
1047 static void
1048 findproblemrule_internal(Solver *solv, Id idx, Id *reqrp, Id *conrp, Id *sysrp, Id *jobrp, Map *rseen)
1049 {
1050   Id rid, d;
1051   Id lreqr, lconr, lsysr, ljobr;
1052   Rule *r;
1053   Id jobassert = 0;
1054   int i, reqset = 0;    /* 0: unset, 1: installed, 2: jobassert, 3: assert */
1055   int conset = 0;       /* 0: unset, 1: installed */
1056
1057   /* find us a jobassert rule */
1058   for (i = idx; (rid = solv->learnt_pool.elements[i]) != 0; i++)
1059     {
1060       if (rid < solv->jobrules || rid >= solv->jobrules_end)
1061         continue;
1062       r = solv->rules + rid;
1063       d = r->d < 0 ? -r->d - 1 : r->d;
1064       if (!d && r->w2 == 0 && r->p > 0)
1065         {
1066           jobassert = r->p;
1067           break;
1068         }
1069     }
1070
1071   /* the problem rules are somewhat ordered from "near to the problem" to
1072    * "near to the job" */
1073   lreqr = lconr = lsysr = ljobr = 0;
1074   while ((rid = solv->learnt_pool.elements[idx++]) != 0)
1075     {
1076       assert(rid > 0);
1077       if (rid >= solv->learntrules)
1078         {
1079           if (MAPTST(rseen, rid - solv->learntrules))
1080             continue;
1081           MAPSET(rseen, rid - solv->learntrules);
1082           findproblemrule_internal(solv, solv->learnt_why.elements[rid - solv->learntrules], &lreqr, &lconr, &lsysr, &ljobr, rseen);
1083         }
1084       else if ((rid >= solv->jobrules && rid < solv->jobrules_end) || (rid >= solv->infarchrules && rid < solv->infarchrules_end) || (rid >= solv->duprules && rid < solv->duprules_end) || (rid >= solv->bestrules && rid < solv->bestrules_end) || (rid >= solv->yumobsrules && rid <= solv->yumobsrules_end))
1085         {
1086           if (!*jobrp)
1087             *jobrp = rid;
1088         }
1089       else if (rid >= solv->updaterules && rid < solv->updaterules_end)
1090         {
1091           if (!*sysrp)
1092             *sysrp = rid;
1093         }
1094       else
1095         {
1096           assert(rid < solv->pkgrules_end);
1097           r = solv->rules + rid;
1098           d = r->d < 0 ? -r->d - 1 : r->d;
1099           if (!d && r->w2 < 0)
1100             {
1101               /* prefer conflicts of installed packages */
1102               if (solv->installed && !conset)
1103                 {
1104                   if (r->p < 0 && (solv->pool->solvables[-r->p].repo == solv->installed ||
1105                                   solv->pool->solvables[-r->w2].repo == solv->installed))
1106                     {
1107                       *conrp = rid;
1108                       conset = 1;
1109                     }
1110                 }
1111               if (!*conrp)
1112                 *conrp = rid;
1113             }
1114           else
1115             {
1116               if (!d && r->w2 == 0 && reqset < 3)
1117                 {
1118                   if (*reqrp > 0 && r->p < -1)
1119                     {
1120                       Pool *pool = solv->pool;
1121                       Id op = -solv->rules[*reqrp].p;
1122                       if (op > 1 && pool->solvables[op].arch != pool->solvables[-r->p].arch &&
1123                           pool->solvables[op].arch != pool->noarchid &&
1124                           pool->solvables[-r->p].arch != pool->noarchid)
1125                         continue;       /* different arch, skip */
1126                     }
1127                   /* prefer assertions */
1128                   *reqrp = rid;
1129                   reqset = 3;
1130                 }
1131               else if (jobassert && r->p == -jobassert)
1132                 {
1133                   /* prefer rules of job assertions */
1134                   *reqrp = rid;
1135                   reqset = 2;
1136                 }
1137               else if (solv->installed && r->p < 0 && solv->pool->solvables[-r->p].repo == solv->installed && reqset <= 1)
1138                 {
1139                   /* prefer rules of job installed package so that the user doesn't get confused by strange packages */
1140                   *reqrp = rid;
1141                   reqset = 1;
1142                 }
1143               else if (!*reqrp)
1144                 *reqrp = rid;
1145             }
1146         }
1147     }
1148   if (!*reqrp && lreqr)
1149     *reqrp = lreqr;
1150   if (!*conrp && lconr)
1151     *conrp = lconr;
1152   if (!*jobrp && ljobr)
1153     *jobrp = ljobr;
1154   if (!*sysrp && lsysr)
1155     *sysrp = lsysr;
1156 }
1157
1158 /*
1159  * find problem rule
1160  *
1161  * search for a rule that describes the problem to the
1162  * user. Actually a pretty hopeless task that may leave the user
1163  * puzzled. To get all of the needed information use
1164  * solver_findallproblemrules() instead.
1165  */
1166
1167 Id
1168 solver_findproblemrule(Solver *solv, Id problem)
1169 {
1170   Id reqr, conr, sysr, jobr;
1171   Id idx = solv->problems.elements[2 * problem - 2];
1172   Map rseen;
1173   reqr = conr = sysr = jobr = 0;
1174   map_init(&rseen, solv->learntrules ? solv->nrules - solv->learntrules : 0);
1175   findproblemrule_internal(solv, idx, &reqr, &conr, &sysr, &jobr, &rseen);
1176   map_free(&rseen);
1177   /* check if the request is about a not-installed package requiring a installed
1178    * package conflicting with the non-installed package. In that case return the conflict */
1179   if (reqr && conr && solv->installed && solv->rules[reqr].p < 0 && solv->rules[conr].p < 0 && solv->rules[conr].w2 < 0)
1180     {
1181       Pool *pool = solv->pool;
1182       Solvable *s  = pool->solvables - solv->rules[reqr].p;
1183       Solvable *s1 = pool->solvables - solv->rules[conr].p;
1184       Solvable *s2 = pool->solvables - solv->rules[conr].w2;
1185       Id cp = 0;
1186       if (s == s1 && s2->repo == solv->installed)
1187         cp = -solv->rules[conr].w2;
1188       else if (s == s2 && s1->repo == solv->installed)
1189         cp = -solv->rules[conr].p;
1190       if (cp && s1->name != s2->name && s->repo != solv->installed)
1191         {
1192           Id p, pp;
1193           Rule *r = solv->rules + reqr;
1194           FOR_RULELITERALS(p, pp, r)
1195             if (p == cp)
1196               return conr;
1197         }
1198     }
1199   if (reqr)
1200     return reqr;        /* some requires */
1201   if (conr)
1202     return conr;        /* some conflict */
1203   if (sysr)
1204     return sysr;        /* an update rule */
1205   if (jobr)
1206     return jobr;        /* a user request */
1207   assert(0);
1208   return 0;
1209 }
1210
1211 /*-------------------------------------------------------------------*/
1212
1213 static void
1214 findallproblemrules_internal(Solver *solv, Id idx, Queue *rules, Map *rseen)
1215 {
1216   Id rid;
1217   while ((rid = solv->learnt_pool.elements[idx++]) != 0)
1218     {
1219       if (rid >= solv->learntrules)
1220         {
1221           if (MAPTST(rseen, rid - solv->learntrules))
1222             continue;
1223           MAPSET(rseen, rid - solv->learntrules);
1224           findallproblemrules_internal(solv, solv->learnt_why.elements[rid - solv->learntrules], rules, rseen);
1225           continue;
1226         }
1227       queue_pushunique(rules, rid);
1228     }
1229 }
1230
1231 /*
1232  * find all problem rule
1233  *
1234  * return all rules that lead to the problem. This gives the user
1235  * all of the information to understand the problem, but the result
1236  * can be a large number of rules.
1237  */
1238
1239 void
1240 solver_findallproblemrules(Solver *solv, Id problem, Queue *rules)
1241 {
1242   Map rseen;
1243   queue_empty(rules);
1244   map_init(&rseen, solv->learntrules ? solv->nrules - solv->learntrules : 0);
1245   findallproblemrules_internal(solv, solv->problems.elements[2 * problem - 2], rules, &rseen);
1246   map_free(&rseen);
1247 }
1248
1249 const char *
1250 solver_problemruleinfo2str(Solver *solv, SolverRuleinfo type, Id source, Id target, Id dep)
1251 {
1252   Pool *pool = solv->pool;
1253   char *s;
1254   Solvable *ss;
1255   switch (type)
1256     {
1257     case SOLVER_RULE_DISTUPGRADE:
1258       return pool_tmpjoin(pool, pool_solvid2str(pool, source), " does not belong to a distupgrade repository", 0);
1259     case SOLVER_RULE_INFARCH:
1260       return pool_tmpjoin(pool, pool_solvid2str(pool, source), " has inferior architecture", 0);
1261     case SOLVER_RULE_UPDATE:
1262       return pool_tmpjoin(pool, "problem with installed package ", pool_solvid2str(pool, source), 0);
1263     case SOLVER_RULE_JOB:
1264       return "conflicting requests";
1265     case SOLVER_RULE_JOB_UNSUPPORTED:
1266       return "unsupported request";
1267     case SOLVER_RULE_JOB_NOTHING_PROVIDES_DEP:
1268       return pool_tmpjoin(pool, "nothing provides requested ", pool_dep2str(pool, dep), 0);
1269     case SOLVER_RULE_JOB_UNKNOWN_PACKAGE:
1270       return pool_tmpjoin(pool, "package ", pool_dep2str(pool, dep), " does not exist");
1271     case SOLVER_RULE_JOB_PROVIDED_BY_SYSTEM:
1272       return pool_tmpjoin(pool, pool_dep2str(pool, dep), " is provided by the system", 0);
1273     case SOLVER_RULE_PKG:
1274       return "some dependency problem";
1275     case SOLVER_RULE_BEST:
1276       if (source > 0)
1277         return pool_tmpjoin(pool, "cannot install the best update candidate for package ", pool_solvid2str(pool, source), 0);
1278      return "cannot install the best candidate for the job";
1279     case SOLVER_RULE_PKG_NOT_INSTALLABLE:
1280       ss = pool->solvables + source;
1281       if (pool_disabled_solvable(pool, ss))
1282         return pool_tmpjoin(pool, "package ", pool_solvid2str(pool, source), " is disabled");
1283       if (ss->arch && ss->arch != ARCH_SRC && ss->arch != ARCH_NOSRC &&
1284           pool->id2arch && pool_arch2score(pool, ss->arch) == 0)
1285         return pool_tmpjoin(pool, "package ", pool_solvid2str(pool, source), " does not have a compatible architecture");
1286       return pool_tmpjoin(pool, "package ", pool_solvid2str(pool, source), " is not installable");
1287     case SOLVER_RULE_PKG_NOTHING_PROVIDES_DEP:
1288       s = pool_tmpjoin(pool, "nothing provides ", pool_dep2str(pool, dep), 0);
1289       return pool_tmpappend(pool, s, " needed by ", pool_solvid2str(pool, source));
1290     case SOLVER_RULE_PKG_SAME_NAME:
1291       s = pool_tmpjoin(pool, "cannot install both ", pool_solvid2str(pool, source), 0);
1292       return pool_tmpappend(pool, s, " and ", pool_solvid2str(pool, target));
1293     case SOLVER_RULE_PKG_CONFLICTS:
1294       s = pool_tmpjoin(pool, "package ", pool_solvid2str(pool, source), 0);
1295       s = pool_tmpappend(pool, s, " conflicts with ", pool_dep2str(pool, dep));
1296       return pool_tmpappend(pool, s, " provided by ", pool_solvid2str(pool, target));
1297     case SOLVER_RULE_PKG_OBSOLETES:
1298       s = pool_tmpjoin(pool, "package ", pool_solvid2str(pool, source), 0);
1299       s = pool_tmpappend(pool, s, " obsoletes ", pool_dep2str(pool, dep));
1300       return pool_tmpappend(pool, s, " provided by ", pool_solvid2str(pool, target));
1301     case SOLVER_RULE_PKG_INSTALLED_OBSOLETES:
1302       s = pool_tmpjoin(pool, "installed package ", pool_solvid2str(pool, source), 0);
1303       s = pool_tmpappend(pool, s, " obsoletes ", pool_dep2str(pool, dep));
1304       return pool_tmpappend(pool, s, " provided by ", pool_solvid2str(pool, target));
1305     case SOLVER_RULE_PKG_IMPLICIT_OBSOLETES:
1306       s = pool_tmpjoin(pool, "package ", pool_solvid2str(pool, source), 0);
1307       s = pool_tmpappend(pool, s, " implicitly obsoletes ", pool_dep2str(pool, dep));
1308       return pool_tmpappend(pool, s, " provided by ", pool_solvid2str(pool, target));
1309     case SOLVER_RULE_PKG_REQUIRES:
1310       s = pool_tmpjoin(pool, "package ", pool_solvid2str(pool, source), " requires ");
1311       return pool_tmpappend(pool, s, pool_dep2str(pool, dep), ", but none of the providers can be installed");
1312     case SOLVER_RULE_PKG_SELF_CONFLICT:
1313       s = pool_tmpjoin(pool, "package ", pool_solvid2str(pool, source), " conflicts with ");
1314       return pool_tmpappend(pool, s, pool_dep2str(pool, dep), " provided by itself");
1315     case SOLVER_RULE_YUMOBS:
1316       s = pool_tmpjoin(pool, "both package ", pool_solvid2str(pool, source), " and ");
1317       s = pool_tmpjoin(pool, s, pool_solvid2str(pool, target), " obsolete ");
1318       return pool_tmpappend(pool, s, pool_dep2str(pool, dep), 0);
1319     default:
1320       return "bad problem rule type";
1321     }
1322 }
1323
1324 /* convenience function */
1325 const char *
1326 solver_problem2str(Solver *solv, Id problem)
1327 {
1328   Id type, source, target, dep;
1329   Id r = solver_findproblemrule(solv, problem);
1330   if (!r)
1331     return "no problem rule?";
1332   type = solver_ruleinfo(solv, r, &source, &target, &dep);
1333   return solver_problemruleinfo2str(solv, type, source, target, dep);
1334 }
1335
1336 const char *
1337 solver_solutionelement2str(Solver *solv, Id p, Id rp)
1338 {
1339   Pool *pool = solv->pool;
1340   if (p == SOLVER_SOLUTION_JOB || p == SOLVER_SOLUTION_POOLJOB)
1341     {
1342       Id how, what;
1343       if (p == SOLVER_SOLUTION_JOB)
1344         rp += solv->pooljobcnt;
1345       how = solv->job.elements[rp - 1];
1346       what = solv->job.elements[rp];
1347       return pool_tmpjoin(pool, "do not ask to ", pool_job2str(pool, how, what, 0), 0);
1348     }
1349   else if (p == SOLVER_SOLUTION_INFARCH)
1350     {
1351       Solvable *s = pool->solvables + rp;
1352       if (solv->installed && s->repo == solv->installed)
1353         return pool_tmpjoin(pool, "keep ", pool_solvable2str(pool, s), " despite the inferior architecture");
1354       else
1355         return pool_tmpjoin(pool, "install ", pool_solvable2str(pool, s), " despite the inferior architecture");
1356     }
1357   else if (p == SOLVER_SOLUTION_DISTUPGRADE)
1358     {
1359       Solvable *s = pool->solvables + rp;
1360       if (solv->installed && s->repo == solv->installed)
1361         return pool_tmpjoin(pool, "keep obsolete ", pool_solvable2str(pool, s), 0);
1362       else
1363         return pool_tmpjoin(pool, "install ", pool_solvable2str(pool, s), " from excluded repository");
1364     }
1365   else if (p == SOLVER_SOLUTION_BEST)
1366     {
1367       Solvable *s = pool->solvables + rp;
1368       if (solv->installed && s->repo == solv->installed)
1369         return pool_tmpjoin(pool, "keep old ", pool_solvable2str(pool, s), 0);
1370       else
1371         return pool_tmpjoin(pool, "install ", pool_solvable2str(pool, s), " despite the old version");
1372     }
1373   else if (p > 0 && rp == 0)
1374     return pool_tmpjoin(pool, "allow deinstallation of ", pool_solvid2str(pool, p), 0);
1375   else if (p > 0 && rp > 0)
1376     {
1377       const char *sp = pool_solvid2str(pool, p);
1378       const char *srp = pool_solvid2str(pool, rp);
1379       const char *str = pool_tmpjoin(pool, "allow replacement of ", sp, 0);
1380       return pool_tmpappend(pool, str, " with ", srp);
1381     }
1382   else
1383     return "bad solution element";
1384 }
1385