#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <assert.h>
#include "solver.h"
#include "solver_private.h"
#include "bitmap.h"
#include "pool.h"
#include "util.h"
#include "evr.h"
int
solver_get_decisionlevel(Solver *solv, Id p)
{
return solv->decisionmap[p];
}
void
solver_get_decisionqueue(Solver *solv, Queue *decisionq)
{
queue_free(decisionq);
queue_init_clone(decisionq, &solv->decisionq);
}
int
solver_get_lastdecisionblocklevel(Solver *solv)
{
Id p;
if (solv->decisionq.count == 0)
return 0;
p = solv->decisionq.elements[solv->decisionq.count - 1];
if (p < 0)
p = -p;
return solv->decisionmap[p] < 0 ? -solv->decisionmap[p] : solv->decisionmap[p];
}
void
solver_get_decisionblock(Solver *solv, int level, Queue *decisionq)
{
Id p;
int i;
queue_empty(decisionq);
for (i = 0; i < solv->decisionq.count; i++)
{
p = solv->decisionq.elements[i];
if (p < 0)
p = -p;
if (solv->decisionmap[p] == level || solv->decisionmap[p] == -level)
break;
}
if (i == solv->decisionq.count)
return;
for (i = 0; i < solv->decisionq.count; i++)
{
p = solv->decisionq.elements[i];
if (p < 0)
p = -p;
if (solv->decisionmap[p] == level || solv->decisionmap[p] == -level)
queue_push(decisionq, p);
else
break;
}
}
int
solver_describe_decision(Solver *solv, Id p, Id *infop)
{
int i;
Id pp, why;
if (infop)
*infop = 0;
if (!solv->decisionmap[p])
return SOLVER_REASON_UNRELATED;
pp = solv->decisionmap[p] < 0 ? -p : p;
for (i = 0; i < solv->decisionq.count; i++)
if (solv->decisionq.elements[i] == pp)
break;
if (i == solv->decisionq.count)
return SOLVER_REASON_UNRELATED;
why = solv->decisionq_why.elements[i];
if (infop)
*infop = why >= 0 ? why : -why;
if (why > 0)
return SOLVER_REASON_UNIT_RULE;
i = solv->decisionmap[p] >= 0 ? solv->decisionmap[p] : -solv->decisionmap[p];
return solv->decisionq_reason.elements[i];
}
int
solver_allweakdepinfos(Solver *solv, Id p, Queue *whyq)
{
Pool *pool = solv->pool;
int i;
int level = solv->decisionmap[p];
int decisionno;
Solvable *s;
queue_empty(whyq);
if (level < 0)
return 0;
for (decisionno = 0; decisionno < solv->decisionq.count; decisionno++)
if (solv->decisionq.elements[decisionno] == p)
break;
if (decisionno == solv->decisionq.count)
return 0;
i = solv->decisionmap[p] >= 0 ? solv->decisionmap[p] : -solv->decisionmap[p];
if (solv->decisionq_reason.elements[i] != SOLVER_REASON_WEAKDEP)
return 0;
for (i = 1; i < pool->nsolvables; i++)
{
Id *recp, rec, pp2, p2;
if (solv->decisionmap[i] <= 0 || solv->decisionmap[i] >= level)
continue;
s = pool->solvables + i;
if (!s->recommends)
continue;
if (!solv->addalreadyrecommended && s->repo == solv->installed)
continue;
recp = s->repo->idarraydata + s->recommends;
while ((rec = *recp++) != 0)
{
int found = 0;
FOR_PROVIDES(p2, pp2, rec)
{
if (p2 == p)
found = 1;
else
{
if (solv->decisionmap[p2] > 0 && solv->decisionmap[p2] < level)
break;
}
}
if (!p2 && found)
{
queue_push2(whyq, SOLVER_RULE_PKG_RECOMMENDS, i);
queue_push2(whyq, 0, rec);
}
}
}
s = pool->solvables + p;
if (s->supplements && level > 0)
{
Id *supp, sup, pp2, p2;
for (i = decisionno; i < solv->decisionq.count; i++ )
{
p2 = solv->decisionq.elements[i];
if (p2 > 0)
solv->decisionmap[p2] = -solv->decisionmap[p2];
}
supp = s->repo->idarraydata + s->supplements;
while ((sup = *supp++) != 0)
if (solver_dep_fulfilled(solv, sup))
{
int found = 0;
FOR_PROVIDES(p2, pp2, sup)
{
if (!solv->addalreadyrecommended && solv->installed)
{
if (pool->solvables[p2].repo == solv->installed)
continue;
}
if (solv->decisionmap[p2] > 0 && solv->decisionmap[p2] < level)
{
queue_push2(whyq, SOLVER_RULE_PKG_SUPPLEMENTS, p);
queue_push2(whyq, p2, sup);
found = 1;
}
}
if (!found)
{
queue_push2(whyq, SOLVER_RULE_PKG_SUPPLEMENTS, p);
queue_push2(whyq, 0, sup);
}
}
for (i = decisionno; i < solv->decisionq.count; i++)
{
p2 = solv->decisionq.elements[i];
if (p2 > 0)
solv->decisionmap[p2] = -solv->decisionmap[p2];
}
}
return whyq->count / 4;
}
SolverRuleinfo
solver_weakdepinfo(Solver *solv, Id p, Id *fromp, Id *top, Id *depp)
{
Queue iq;
queue_init(&iq);
solver_allweakdepinfos(solv, p, &iq);
if (fromp)
*fromp = iq.count ? iq.elements[1] : 0;
if (top)
*top = iq.count ? iq.elements[2] : 0;
if (depp)
*depp = iq.count ? iq.elements[3] : 0;
return iq.count ? iq.elements[0] : SOLVER_RULE_UNKNOWN;
}
void
solver_describe_weakdep_decision(Solver *solv, Id p, Queue *whyq)
{
int i, j;
solver_allweakdepinfos(solv, p, whyq);
for (i = j = 0; i < whyq->count; i += 4)
{
if (whyq->elements[i] == SOLVER_RULE_PKG_RECOMMENDS)
{
whyq->elements[j++] = SOLVER_REASON_RECOMMENDED;
whyq->elements[j++] = whyq->elements[i + 1];
whyq->elements[j++] = whyq->elements[i + 3];
}
else if (whyq->elements[i] == SOLVER_RULE_PKG_SUPPLEMENTS)
{
whyq->elements[j++] = SOLVER_REASON_SUPPLEMENTED;
whyq->elements[j++] = whyq->elements[i + 2];
whyq->elements[j++] = whyq->elements[i + 3];
}
}
queue_truncate(whyq, j);
}
static int
decisionsort_cmp(const void *va, const void *vb, void *vd)
{
Solver *solv = vd;
Pool *pool = solv->pool;
const Id *a = va, *b = vb;
Solvable *as, *bs;
if (a[4] != b[4])
return a[4] - b[4];
if (a[7] != b[7])
return a[7] - b[7];
as = pool->solvables + a[5];
bs = pool->solvables + b[5];
if (as->name != bs->name)
return strcmp(pool_id2str(pool, as->name), pool_id2str(pool, bs->name));
if (as->evr != bs->evr)
{
int r = pool_evrcmp(pool, as->evr, bs->evr, EVRCMP_COMPARE);
if (r)
return r;
}
as = pool->solvables + a[6];
bs = pool->solvables + b[6];
if (as->name != bs->name)
return strcmp(pool_id2str(pool, as->name), pool_id2str(pool, bs->name));
if (as->evr != bs->evr)
{
int r = pool_evrcmp(pool, as->evr, bs->evr, EVRCMP_COMPARE);
if (r)
return r;
}
return 0;
}
static void
decisionmerge(Solver *solv, Queue *q)
{
Pool *pool = solv->pool;
int i, j;
for (i = 0; i < q->count; i += 8)
{
Id p = q->elements[i] >= 0 ? q->elements[i] : -q->elements[i];
int reason = q->elements[i + 1];
int bits = q->elements[i + 3];
Id name = pool->solvables[p].name;
for (j = i + 8; j < q->count; j += 8)
{
int merged;
p = q->elements[j] >= 0 ? q->elements[j] : -q->elements[j];
if (reason != q->elements[j + 1] || name != pool->solvables[p].name)
break;
merged = solver_merge_decisioninfo_bits(solv, bits, q->elements[i + 4], q->elements[i + 5], q->elements[i + 6], q->elements[i + 7], q->elements[j + 3], q->elements[j + 4], q->elements[j + 5], q->elements[j + 6], q->elements[j + 7]);
if (!merged)
break;
bits = merged;
}
j -= 8;
for (; i < j; i += 8)
q->elements[i + 3] = bits;
}
}
static inline void
move_decision(Queue *q, int to, int from)
{
queue_insertn(q, to, 8, 0);
memmove(q->elements + to, q->elements + from + 8, 8 * sizeof(Id));
queue_deleten(q, from + 8, 8);
}
static void
sort_unit_decisions(Solver *solv, Queue *q, int start, int end, Map *m)
{
Pool *pool = solv->pool;
int i, j, k, doing = 1;
if (start + 8 == end)
{
Id truelit = q->elements[start];
MAPSET(m, truelit >= 0 ? truelit : -truelit);
return;
}
for (i = start; i < end; doing ^= 1)
{
for (j = k = i; j < end; j += 8)
{
Rule *or;
Id p, pp;
Id truelit = q->elements[j];
if ((doing == 0 && truelit < 0) || (doing != 0 && truelit >= 0))
continue;
or = solv->rules + q->elements[j + 2];
FOR_RULELITERALS(p, pp, or)
if (p != truelit && !MAPTST(m, p >= 0 ? p : -p))
break;
if (p)
continue;
if (j > k)
move_decision(q, k, j);
k += 8;
}
if (k == i)
continue;
if (i + 8 < k)
solv_sort(q->elements + i, (k - i) / 8, 8 * sizeof(Id), decisionsort_cmp, solv);
for (; i < k; i += 8)
{
Id truelit = q->elements[i];
MAPSET(m, truelit >= 0 ? truelit : -truelit);
}
}
}
static void
solver_get_proof(Solver *solv, Id id, int flags, Queue *q)
{
Pool *pool = solv->pool;
Map seen, seent;
Id rid, truelit;
int first = 1;
int why, i, cnt;
queue_empty(q);
if ((flags & SOLVER_DECISIONLIST_TYPEMASK) == SOLVER_DECISIONLIST_PROBLEM)
why = solv->problems.elements[2 * id - 2];
else if ((flags & SOLVER_DECISIONLIST_TYPEMASK) == SOLVER_DECISIONLIST_LEARNTRULE && id >= solv->learntrules && id < solv->nrules)
why = solv->learnt_why.elements[id - solv->learntrules];
else
return;
if (!solv->learnt_pool.elements[why])
return;
map_init(&seen, pool->nsolvables);
map_init(&seent, pool->nsolvables);
while ((rid = solv->learnt_pool.elements[why++]) != 0)
{
Rule *r = solv->rules + rid;
Id p, pp;
truelit = 0;
FOR_RULELITERALS(p, pp, r)
{
Id vv = p > 0 ? p : -p;
if (MAPTST(&seen, vv))
{
if ((p > 0 ? 1 : 0) == (MAPTST(&seent, vv) ? 1 : 0))
{
if (truelit)
abort();
truelit = p;
}
}
else
{
MAPSET(&seen, vv);
if (p < 0)
MAPSET(&seent, vv);
}
}
if (truelit)
queue_push(q, truelit);
else if (!first)
abort();
queue_push(q, rid);
first = 0;
}
cnt = q->count;
for (i = q->count - 1; i >= 0; i -= 2)
{
SolverRuleinfo type;
Id from = 0, to = 0, dep = 0;
rid = q->elements[i];
type = solver_ruleinfo(solv, rid, &from, &to, &dep);
if (type == SOLVER_RULE_CHOICE || type == SOLVER_RULE_RECOMMENDS)
{
Id rid2 = solver_rule2pkgrule(solv, rid);
if (rid2)
type = solver_ruleinfo(solv, rid2, &from, &to, &dep);
}
queue_insertn(q, q->count, 8, 0);
q->elements[q->count - 8] = i > 0 ? q->elements[i - 1] : 0;
q->elements[q->count - 8 + 1] = i > 0 ? SOLVER_REASON_UNIT_RULE : SOLVER_REASON_UNSOLVABLE;
q->elements[q->count - 8 + 2] = rid;
q->elements[q->count - 8 + 4] = type;
q->elements[q->count - 8 + 5] = from;
q->elements[q->count - 8 + 6] = to;
q->elements[q->count - 8 + 7] = dep;
}
queue_deleten(q, 0, cnt);
if (q->count >= 16 && q->elements[q->count - 8 + 4] == SOLVER_RULE_RPM_SAME_NAME && q->elements[q->count - 16] > 0)
{
Rule *r = solv->rules + q->elements[q->count - 8 + 2];
if (r->p < 0 && (r->d == 0 || r->d == -1) && r->w2 < 0
&& (q->elements[q->count - 16] == -r->p || q->elements[q->count - 16] == -r->w2))
{
move_decision(q, q->count - 16, q->count - 8);
q->elements[q->count - 16] = -q->elements[q->count - 8];
q->elements[q->count - 16 + 1] = SOLVER_REASON_UNIT_RULE;
q->elements[q->count - 8] = 0;
q->elements[q->count - 8 + 1] = SOLVER_REASON_UNSOLVABLE;
}
}
MAPZERO(&seen);
MAPSET(&seen, 1);
i = 0;
if ((flags & SOLVER_DECISIONLIST_TYPEMASK) == SOLVER_DECISIONLIST_LEARNTRULE)
{
Rule *r = solv->rules + id;
Id p, pp;
i = 0;
FOR_RULELITERALS(p, pp, r)
{
queue_insertn(q, i, 8, 0);
q->elements[i] = -p;
q->elements[i + 1] = SOLVER_REASON_PREMISE;
q->elements[i + 5] = p >= 0 ? p : -p;
MAPSET(&seen, p >= 0 ? p : -p);
i += 8;
}
}
if (flags & SOLVER_DECISIONLIST_SORTED)
{
if (i > 8)
solv_sort(q->elements, i / 8, 8 * sizeof(Id), decisionsort_cmp, solv);
sort_unit_decisions(solv, q, i, q->count - 8, &seen);
}
map_free(&seen);
map_free(&seent);
if (!(flags & SOLVER_DECISIONLIST_WITHINFO))
{
int j;
for (i = j = 0; i < q->count; i += 8)
{
q->elements[j++] = q->elements[i];
q->elements[j++] = q->elements[i + 1];
q->elements[j++] = q->elements[i + 2];
}
queue_truncate(q, j);
}
else
{
for (i = 0; i < q->count; i += 8)
q->elements[i + 3] = solver_calc_decisioninfo_bits(solv, q->elements[i], q->elements[i + 4], q->elements[i + 5], q->elements[i + 6], q->elements[i + 7]);
if (flags & SOLVER_DECISIONLIST_MERGEDINFO)
decisionmerge(solv, q);
}
}
void
solver_get_learnt(Solver *solv, Id id, int flags, Queue *q)
{
int why = -1;
Queue todo;
queue_empty(q);
queue_init(&todo);
if ((flags & SOLVER_DECISIONLIST_TYPEMASK) == SOLVER_DECISIONLIST_PROBLEM)
why = solv->problems.elements[2 * id - 2];
else if ((flags & SOLVER_DECISIONLIST_TYPEMASK) == SOLVER_DECISIONLIST_LEARNTRULE && id >= solv->learntrules && id < solv->nrules)
why = solv->learnt_why.elements[id - solv->learntrules];
else if ((flags & SOLVER_DECISIONLIST_TYPEMASK) == SOLVER_DECISIONLIST_SOLVABLE)
{
int i, j, cnt;
solver_get_decisionlist(solv, id, 0, &todo);
cnt = todo.count;
for (i = 0; i < cnt; i += 3)
{
int rid = todo.elements[i + 2];
if (rid < solv->learntrules || rid >= solv->nrules)
continue;
for (j = 0; j < q->count; j++)
{
if (q->elements[j] < rid)
continue;
if (q->elements[j] == rid)
rid = 0;
break;
}
if (!rid)
continue;
queue_insert(q, j, rid);
queue_push(&todo, solv->learnt_why.elements[rid - solv->learntrules]);
}
queue_deleten(&todo, 0, cnt);
}
else
{
queue_free(&todo);
return;
}
if (why >= 0)
queue_push(&todo, why);
while (todo.count)
{
int i, rid;
why = queue_pop(&todo);
while ((rid = solv->learnt_pool.elements[why++]) != 0)
{
if (rid < solv->learntrules || rid >= solv->nrules)
continue;
for (i = 0; i < q->count; i++)
{
if (q->elements[i] < rid)
continue;
if (q->elements[i] == rid)
rid = 0;
break;
}
if (!rid)
continue;
queue_insert(q, i, rid);
queue_push(&todo, solv->learnt_why.elements[rid - solv->learntrules]);
}
}
queue_free(&todo);
}
static void
getdecisionlist(Solver *solv, Map *dm, int flags, Queue *decisionlistq)
{
Pool *pool = solv->pool;
int i, ii, reason, info;
Queue iq;
queue_empty(decisionlistq);
if ((flags & SOLVER_DECISIONLIST_TYPEMASK) != SOLVER_DECISIONLIST_SOLVABLE)
return;
queue_init(&iq);
for (ii = solv->decisionq.count - 1; ii > 0; ii--)
{
Id v = solv->decisionq.elements[ii];
Id vv = (v > 0 ? v : -v);
if (!MAPTST(dm, vv))
continue;
info = solv->decisionq_why.elements[ii];
if (info > 0)
reason = SOLVER_REASON_UNIT_RULE;
else if (info <= 0)
{
info = -info;
reason = solv->decisionmap[vv];
reason = solv->decisionq_reason.elements[reason >= 0 ? reason : -reason];
}
if (flags & (SOLVER_DECISIONLIST_SORTED | SOLVER_DECISIONLIST_WITHINFO))
{
queue_insertn(decisionlistq, 0, 5, 0);
if (reason == SOLVER_REASON_WEAKDEP)
{
solver_allweakdepinfos(solv, v, &iq);
if (iq.count)
{
decisionlistq->elements[1] = iq.elements[0];
decisionlistq->elements[2] = iq.elements[1];
decisionlistq->elements[3] = iq.elements[2];
decisionlistq->elements[4] = iq.elements[3];
}
}
else if (info > 0)
{
Id from, to, dep;
int type = solver_ruleinfo(solv, info, &from, &to, &dep);
if (type == SOLVER_RULE_CHOICE || type == SOLVER_RULE_RECOMMENDS)
{
Id rid2 = solver_rule2pkgrule(solv, info);
if (rid2)
type = solver_ruleinfo(solv, rid2, &from, &to, &dep);
}
decisionlistq->elements[1] = type;
decisionlistq->elements[2] = from;
decisionlistq->elements[3] = to;
decisionlistq->elements[4] = dep;
}
}
queue_unshift(decisionlistq, info);
queue_unshift(decisionlistq, reason);
queue_unshift(decisionlistq, v);
switch (reason)
{
case SOLVER_REASON_WEAKDEP:
if (v <= 0)
break;
solver_allweakdepinfos(solv, v, &iq);
for (i = 0; i < iq.count; i += 4)
{
if (iq.elements[i + 1])
MAPSET(dm, iq.elements[i + 1]);
if (iq.elements[i + 2])
MAPSET(dm, iq.elements[i + 2]);
else if (iq.elements[i] == SOLVER_RULE_PKG_SUPPLEMENTS)
{
Id p2, pp2, id = iq.elements[i + 3];
FOR_PROVIDES(p2, pp2, id)
if (solv->decisionmap[p2] > 0)
MAPSET(dm, p2);
}
}
break;
case SOLVER_REASON_RESOLVE_JOB:
case SOLVER_REASON_UNIT_RULE:
case SOLVER_REASON_RESOLVE:
solver_ruleliterals(solv, info, &iq);
for (i = 0; i < iq.count; i++)
{
Id p2 = iq.elements[i];
if (p2 < 0)
MAPSET(dm, -p2);
else if (reason == SOLVER_REASON_UNIT_RULE || solv->decisionmap[p2] > 0)
MAPSET(dm, p2);
}
break;
default:
break;
}
}
queue_free(&iq);
if ((flags & SOLVER_DECISIONLIST_SORTED) != 0)
{
MAPZERO(dm);
MAPSET(dm, 1);
for (i = 0; i < decisionlistq->count; i += 8)
{
if (decisionlistq->elements[i + 1] != SOLVER_REASON_UNIT_RULE)
{
Id p = decisionlistq->elements[i] < 0 ? -decisionlistq->elements[i] : decisionlistq->elements[i];
MAPSET(dm, p);
}
else
{
int j;
for (j = i + 8; j < decisionlistq->count; j += 8)
if (decisionlistq->elements[j + 1] != SOLVER_REASON_UNIT_RULE)
break;
sort_unit_decisions(solv, decisionlistq, i, j, dm);
i = j - 8;
}
}
}
if ((flags & SOLVER_DECISIONLIST_WITHINFO) != 0)
{
for (i = 0; i < decisionlistq->count; i += 8)
decisionlistq->elements[i + 3] = solver_calc_decisioninfo_bits(solv, decisionlistq->elements[i], decisionlistq->elements[i + 4], decisionlistq->elements[i + 5], decisionlistq->elements[i + 6], decisionlistq->elements[i + 7]);
if (flags & SOLVER_DECISIONLIST_MERGEDINFO)
decisionmerge(solv, decisionlistq);
}
else if ((flags & SOLVER_DECISIONLIST_SORTED) != 0)
{
int j;
for (i = j = 0; i < decisionlistq->count; i += 8)
{
decisionlistq->elements[j++] = decisionlistq->elements[i];
decisionlistq->elements[j++] = decisionlistq->elements[i + 1];
decisionlistq->elements[j++] = decisionlistq->elements[i + 2];
}
queue_truncate(decisionlistq, j);
}
}
void
solver_get_decisionlist(Solver *solv, Id id, int flags, Queue *decisionlistq)
{
Pool *pool = solv->pool;
Map dm;
if ((flags & SOLVER_DECISIONLIST_TYPEMASK) != SOLVER_DECISIONLIST_SOLVABLE)
return solver_get_proof(solv, id, flags, decisionlistq);
map_init(&dm, pool->nsolvables);
MAPSET(&dm, id);
getdecisionlist(solv, &dm, flags, decisionlistq);
map_free(&dm);
if (!decisionlistq->count)
{
queue_push(decisionlistq, -id);
queue_push2(decisionlistq, SOLVER_REASON_UNRELATED, 0);
if ((flags & SOLVER_DECISIONLIST_WITHINFO) != 0)
{
queue_push(decisionlistq, solver_calc_decisioninfo_bits(solv, -id, 0, 0, 0, 0));
queue_push2(decisionlistq, 0, 0);
queue_push2(decisionlistq, 0, 0);
}
}
}
void
solver_get_decisionlist_multiple(Solver *solv, Queue *idq, int flags, Queue *decisionlistq)
{
Pool *pool = solv->pool;
int i;
Map dm;
queue_empty(decisionlistq);
if ((flags & SOLVER_DECISIONLIST_TYPEMASK) != SOLVER_DECISIONLIST_SOLVABLE)
return;
map_init(&dm, pool->nsolvables);
for (i = 0; i < idq->count; i++)
{
Id p = idq->elements[i];
if (solv->decisionmap[p] != 0)
MAPSET(&dm, p);
}
getdecisionlist(solv, &dm, flags, decisionlistq);
map_free(&dm);
for (i = 0; i < idq->count; i++)
{
Id p = idq->elements[i];
if (solv->decisionmap[p] != 0)
continue;
queue_push(decisionlistq, -p);
queue_push2(decisionlistq, SOLVER_REASON_UNRELATED, 0);
if ((flags & SOLVER_DECISIONLIST_WITHINFO) != 0)
{
queue_push(decisionlistq, solver_calc_decisioninfo_bits(solv, -p, 0, 0, 0, 0));
queue_push2(decisionlistq, 0, 0);
queue_push2(decisionlistq, 0, 0);
}
}
}
const char *
solver_reason2str(Solver *solv, int reason)
{
switch(reason)
{
case SOLVER_REASON_WEAKDEP:
return "a weak dependency";
case SOLVER_REASON_RESOLVE_JOB:
return "a job rule";
case SOLVER_REASON_RESOLVE:
return "a rule";
case SOLVER_REASON_UNIT_RULE:
return "an unit rule";
case SOLVER_REASON_KEEP_INSTALLED:
return "update/keep installed";
case SOLVER_REASON_UPDATE_INSTALLED:
return "update installed";
case SOLVER_REASON_CLEANDEPS_ERASE:
return "cleandeps erase";
case SOLVER_REASON_RESOLVE_ORPHAN:
return "orphaned package";
case SOLVER_REASON_UNSOLVABLE:
return "unsolvable";
case SOLVER_REASON_PREMISE:
return "learnt rule premise";
case SOLVER_REASON_UNRELATED:
return "it is unrelated";
default:
break;
}
return "an unknown reason";
}
static const char *
decisionruleinfo2str(Solver *solv, Id decision, int type, Id from, Id to, Id dep)
{
int bits = solver_calc_decisioninfo_bits(solv, decision, type, from, to, dep);
return solver_decisioninfo2str(solv, bits, type, from, to, dep);
}
const char *
solver_decisionreason2str(Solver *solv, Id decision, int reason, Id info)
{
if (reason == SOLVER_REASON_WEAKDEP && decision > 0)
{
Id from, to, dep;
int type = solver_weakdepinfo(solv, decision, &from, &to, &dep);
if (type)
return decisionruleinfo2str(solv, decision, type, from, to, dep);
}
if ((reason == SOLVER_REASON_RESOLVE_JOB || reason == SOLVER_REASON_UNIT_RULE || reason == SOLVER_REASON_RESOLVE || reason == SOLVER_REASON_UNSOLVABLE) && info > 0)
{
Id from, to, dep;
int type = solver_ruleinfo(solv, info, &from, &to, &dep);
if (type == SOLVER_RULE_CHOICE || type == SOLVER_RULE_RECOMMENDS)
{
Id rid2 = solver_rule2pkgrule(solv, info);
if (rid2)
{
type = solver_ruleinfo(solv, rid2, &from, &to, &dep);
if (type)
return decisionruleinfo2str(solv, decision, type, from, to, dep);
}
}
if (type)
return decisionruleinfo2str(solv, decision, type, from, to, dep);
}
return solver_reason2str(solv, reason);
}
#define DMS_INITED (1 << 0)
#define DMS_IDENTICAL_FROM (1 << 1)
#define DMS_IDENTICAL_TO (1 << 2)
#define DMS_MERGED (1 << 3)
#define DMS_NEGATIVE (1 << 4)
#define DMS_NOMERGE (1 << 5)
int
solver_calc_decisioninfo_bits(Solver *solv, Id decision, int type, Id from, Id to, Id dep)
{
Id decisionpkg = decision >= 0 ? decision : -decision;
int bits = DMS_INITED | (decision < 0 ? DMS_NEGATIVE : 0);
if (!decision)
return bits | DMS_NOMERGE;
switch (type)
{
case SOLVER_RULE_DISTUPGRADE:
case SOLVER_RULE_INFARCH:
case SOLVER_RULE_UPDATE:
case SOLVER_RULE_FEATURE:
case SOLVER_RULE_BLACK:
case SOLVER_RULE_STRICT_REPO_PRIORITY:
case SOLVER_RULE_PKG_NOTHING_PROVIDES_DEP:
case SOLVER_RULE_PKG_REQUIRES:
case SOLVER_RULE_PKG_RECOMMENDS:
case SOLVER_RULE_PKG_SUPPLEMENTS:
if (decisionpkg == from)
bits |= DMS_IDENTICAL_FROM;
break;
case SOLVER_RULE_PKG_SAME_NAME:
case SOLVER_RULE_PKG_CONFLICTS:
case SOLVER_RULE_PKG_OBSOLETES:
case SOLVER_RULE_PKG_INSTALLED_OBSOLETES:
case SOLVER_RULE_PKG_IMPLICIT_OBSOLETES:
case SOLVER_RULE_PKG_CONSTRAINS:
if (decisionpkg == from)
bits |= DMS_IDENTICAL_FROM;
else if (decisionpkg == to)
bits |= DMS_IDENTICAL_TO;
break;
default:
break;
}
return bits;
}
int
solver_merge_decisioninfo_bits(Solver *solv, int bits1, int type1, Id from1, Id to1, Id dep1, int bits2, int type2, Id from2, Id to2, Id dep2)
{
int merged = 0;
if (type1 != type2 || dep1 != dep2)
return 0;
if (!bits1 || !bits2 || ((bits1 | bits2) & DMS_NOMERGE) != 0 || ((bits1 ^ bits2) & DMS_NEGATIVE) != 0)
return 0;
merged = (((bits1 ^ (DMS_IDENTICAL_FROM | DMS_IDENTICAL_TO)) | (bits2 ^ (DMS_IDENTICAL_FROM | DMS_IDENTICAL_TO))) ^ (DMS_IDENTICAL_FROM | DMS_IDENTICAL_TO)) | DMS_MERGED;
if (((bits1 & DMS_MERGED) != 0 && bits1 != merged) || ((bits2 & DMS_MERGED) != 0 && bits2 != merged))
return 0;
if (((merged & DMS_IDENTICAL_FROM) == 0 && from1 != from2) || ((merged & DMS_IDENTICAL_TO) == 0 && to1 != to2))
return 0;
return merged;
}
void
solver_decisionlist_solvables(Solver *solv, Queue *decisionlistq, int pos, Queue *q)
{
queue_empty(q);
for (; pos < decisionlistq->count; pos += 8)
{
Id p = decisionlistq->elements[pos];
queue_push(q, p > 0 ? p : -p);
if ((decisionlistq->elements[pos + 3] & DMS_MERGED) == 0)
break;
}
}
int
solver_decisionlist_merged(Solver *solv, Queue *decisionlistq, int pos)
{
int cnt = 0;
for (; pos < decisionlistq->count; pos += 8, cnt++)
if ((decisionlistq->elements[pos + 3] & DMS_MERGED) == 0)
break;
return cnt;
}
const char *
solver_decisioninfo2str(Solver *solv, int bits, int type, Id from, Id to, Id dep)
{
Pool *pool = solv->pool;
const char *s;
int multiple = bits & DMS_MERGED;
if ((bits & DMS_IDENTICAL_FROM) != 0)
{
switch (type)
{
case SOLVER_RULE_DISTUPGRADE:
return multiple ? "they do not belong to a distupgrade repository" : "it does not belong to a distupgrade repository";
case SOLVER_RULE_INFARCH:
return multiple ? "they have inferior architecture": "it has inferior architecture";
case SOLVER_RULE_UPDATE:
return multiple ? "they need to stay installed or be updated" : "it needs to stay installed or be updated";
case SOLVER_RULE_FEATURE:
return multiple ? "they need to stay installed or be updated/downgraded" : "it needs to stay installed or be updated/downgraded";
case SOLVER_RULE_BLACK:
return multiple ? "they can only be installed by a direct request" : "it can only be installed by a direct request";
case SOLVER_RULE_STRICT_REPO_PRIORITY:
return multiple ? "they are excluded by strict repo priority" : "it is excluded by strict repo priority";
case SOLVER_RULE_PKG_NOTHING_PROVIDES_DEP:
return pool_tmpjoin(pool, "nothing provides ", pool_dep2str(pool, dep), 0);
case SOLVER_RULE_PKG_REQUIRES:
return pool_tmpjoin(pool, multiple ? "they require " : "it requires ", pool_dep2str(pool, dep), 0);
case SOLVER_RULE_PKG_RECOMMENDS:
return pool_tmpjoin(pool, multiple ? "they recommend " : "it recommends ", pool_dep2str(pool, dep), 0);
case SOLVER_RULE_PKG_SUPPLEMENTS:
s = pool_tmpjoin(pool, multiple ? "they supplement " : "it supplements ", pool_dep2str(pool, dep), 0);
if (to)
s = pool_tmpappend(pool, s, " provided by ", pool_solvid2str(pool, to));
return s;
case SOLVER_RULE_PKG_SAME_NAME:
return pool_tmpappend(pool, multiple ? "they have the same name as " : "it has the same name as ", pool_solvid2str(pool, to), 0);
case SOLVER_RULE_PKG_CONFLICTS:
s = pool_tmpappend(pool, multiple ? "they conflict with " : "it conflicts with ", pool_dep2str(pool, dep), 0);
if (to)
s = pool_tmpappend(pool, s, " provided by ", pool_solvid2str(pool, to));
return s;
case SOLVER_RULE_PKG_OBSOLETES:
s = pool_tmpappend(pool, multiple ? "they obsolete " : "it obsoletes ", pool_dep2str(pool, dep), 0);
if (to)
s = pool_tmpappend(pool, s, " provided by ", pool_solvid2str(pool, to));
return s;
case SOLVER_RULE_PKG_INSTALLED_OBSOLETES:
s = pool_tmpjoin(pool, multiple ? "they are installed and obsolete " : "it is installed and obsoletes ", pool_dep2str(pool, dep), 0);
if (to)
s = pool_tmpappend(pool, s, " provided by ", pool_solvid2str(pool, to));
return s;
case SOLVER_RULE_PKG_IMPLICIT_OBSOLETES:
s = pool_tmpjoin(pool, multiple ? "they implicitly obsolete " : "it implicitly obsoletes ", pool_dep2str(pool, dep), 0);
if (to)
s = pool_tmpappend(pool, s, " provided by ", pool_solvid2str(pool, to));
return s;
case SOLVER_RULE_PKG_CONSTRAINS:
s = pool_tmpappend(pool, multiple ? "they have constraint " : "it has constraint ", pool_dep2str(pool, dep), 0);
if (to)
s = pool_tmpappend(pool, s, " conflicting with ", pool_solvid2str(pool, to));
return s;
default:
break;
}
}
if ((bits & DMS_IDENTICAL_TO) != 0)
{
switch (type)
{
case SOLVER_RULE_PKG_SAME_NAME:
return pool_tmpappend(pool, multiple ? "they have the same name as " : "it has the same name as ", pool_solvid2str(pool, from), 0);
case SOLVER_RULE_PKG_CONFLICTS:
case SOLVER_RULE_PKG_OBSOLETES:
case SOLVER_RULE_PKG_IMPLICIT_OBSOLETES:
case SOLVER_RULE_PKG_INSTALLED_OBSOLETES:
case SOLVER_RULE_PKG_CONSTRAINS:
bits &= ~DMS_IDENTICAL_TO;
to = 0;
break;
default:
break;
}
}
if (multiple && (bits & (DMS_IDENTICAL_FROM|DMS_IDENTICAL_TO)) != 0)
return "unsupported decision merge?";
return solver_ruleinfo2str(solv, type, from, to, dep);
}
int
solver_alternatives_count(Solver *solv)
{
Id *elements = solv->branches.elements;
int res, count;
for (res = 0, count = solv->branches.count; count; res++)
count -= elements[count - 2];
return res;
}
int
solver_get_alternative(Solver *solv, Id alternative, Id *idp, Id *fromp, Id *chosenp, Queue *choices, int *levelp)
{
int cnt = solver_alternatives_count(solv);
int count = solv->branches.count;
Id *elements = solv->branches.elements;
if (choices)
queue_empty(choices);
if (alternative <= 0 || alternative > cnt)
return 0;
elements += count;
for (; cnt > alternative; cnt--)
elements -= elements[-2];
if (levelp)
*levelp = elements[-1];
if (fromp)
*fromp = elements[-4];
if (idp)
*idp = elements[-3];
if (chosenp)
{
int i;
*chosenp = 0;
for (i = elements[-2]; i > 4; i--)
{
Id p = -elements[-i];
if (p > 0 && solv->decisionmap[p] == elements[-1] + 1)
{
*chosenp = p;
break;
}
}
}
if (choices)
queue_insertn(choices, 0, elements[-2] - 4, elements - elements[-2]);
return elements[-4] ? SOLVER_ALTERNATIVE_TYPE_RECOMMENDS : SOLVER_ALTERNATIVE_TYPE_RULE;
}
static int
find_alternative_rule_from_learnt_rec(Solver *solv, int rid, Map *m, int cnt)
{
Pool *pool = solv->pool;
int why = solv->learnt_why.elements[rid - solv->learntrules];
while ((rid = solv->learnt_pool.elements[why++]) != 0)
{
Rule *r = solv->rules + rid;
Id p, pp;
int c;
if (rid >= solv->learntrules)
{
if ((rid = find_alternative_rule_from_learnt_rec(solv, rid, m, cnt)))
return rid;
continue;
}
c = 0;
FOR_RULELITERALS(p, pp, r)
if (p > 0 && MAPTST(m, p))
c++;
if (c == cnt)
return rid;
}
return 0;
}
static int
find_alternative_rule_from_learnt(Solver *solv, int rid)
{
Pool *pool = solv->pool;
Map m;
int i, count, cnt;
Id *elements = solv->branches.elements;
for (count = solv->branches.count; count; count -= elements[count - 2])
if (elements[count - 4] == 0 && elements[count - 3] == rid)
break;
if (!count)
return 0;
map_init(&m, pool->nsolvables);
cnt = 0;
for (i = count - elements[count - 2]; i < count - 4; i++)
if (elements[i] > 0)
{
MAPSET(&m, elements[i]);
cnt++;
}
rid = find_alternative_rule_from_learnt_rec(solv, rid, &m, cnt);
map_free(&m);
return rid;
}
int
solver_alternativeinfo(Solver *solv, int type, Id id, Id from, Id *fromp, Id *top, Id *depp)
{
if (fromp)
*fromp = 0;
if (top)
*top = 0;
if (depp)
*depp = 0;
if (type == SOLVER_ALTERNATIVE_TYPE_RECOMMENDS)
{
if (fromp)
*fromp = from;
if (depp)
*depp = id;
return SOLVER_RULE_PKG_RECOMMENDS;
}
else if (type == SOLVER_ALTERNATIVE_TYPE_RULE)
{
int rclass = solver_ruleclass(solv, id);
if (rclass == SOLVER_RULE_LEARNT)
{
id = find_alternative_rule_from_learnt(solv, id);
if (!id)
return SOLVER_RULE_LEARNT;
rclass = solver_ruleclass(solv, id);
}
if (rclass == SOLVER_RULE_CHOICE || rclass == SOLVER_RULE_RECOMMENDS)
id = solver_rule2pkgrule(solv, id);
else if (rclass == SOLVER_RULE_BEST)
{
Id info = solv->bestrules_info[id - solv->bestrules];
if (info > 0)
{
if (fromp)
*fromp = info;
return SOLVER_RULE_UPDATE;
}
id = -info;
}
return solver_ruleinfo(solv, id, fromp, top, depp);
}
return 0;
}
const char *
solver_alternative2str(Solver *solv, int type, Id id, Id from)
{
const char *s;
Pool *pool = solv->pool;
Id to, dep;
type = solver_alternativeinfo(solv, type, id, from, &from, &to, &dep);
switch (type)
{
case SOLVER_RULE_PKG_RECOMMENDS:
s = pool_dep2str(pool, dep);
if (from)
s = pool_tmpappend(pool, s, ", recommended by ", pool_solvid2str(pool, from));
return s;
case SOLVER_RULE_PKG_REQUIRES:
s = pool_dep2str(pool, dep);
if (from)
s = pool_tmpappend(pool, s, ", required by ", pool_solvid2str(pool, from));
return s;
case SOLVER_RULE_PKG_CONFLICTS:
s = pool_dep2str(pool, dep);
if (from)
s = pool_tmpappend(pool, s, ", conflicted by ", pool_solvid2str(pool, from));
return s;
case SOLVER_RULE_YUMOBS:
return pool_tmpjoin(pool, pool_id2str(pool, pool->solvables[to].name), ", obsoleting ", pool_dep2str(pool, dep));;
case SOLVER_RULE_JOB:
if ((to & SOLVER_SELECTMASK) == SOLVER_SOLVABLE_PROVIDES || (to & SOLVER_SELECTMASK) == SOLVER_SOLVABLE_NAME)
return pool_dep2str(pool, dep);
return solver_select2str(pool, to & SOLVER_SELECTMASK, dep);
case SOLVER_RULE_UPDATE:
case SOLVER_RULE_FEATURE:
return pool_solvid2str(pool, from);
default:
break;
}
return solver_ruleinfo2str(solv, type, from, to, dep);
}