"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.cast_de_nrw_schule_svws_core_kursblockung_satsolver_SatSolver3 = exports.SatSolver3 = void 0;
const AVLSet_1 = require("../../../core/adt/set/AVLSet");
const Variable_1 = require("../../../core/kursblockung/satsolver/Variable");
const SatSolverA_1 = require("../../../core/kursblockung/satsolver/SatSolverA");
const NullPointerException_1 = require("../../../java/lang/NullPointerException");
const LinkedCollection_1 = require("../../../core/adt/collection/LinkedCollection");
const Clause_1 = require("../../../core/kursblockung/satsolver/Clause");
const Arrays_1 = require("../../../java/util/Arrays");
const Heap_1 = require("../../../core/kursblockung/satsolver/Heap");
const System_1 = require("../../../java/lang/System");
class SatSolver3 extends SatSolverA_1.SatSolverA {
    static MAX_LEARNED_CLAUSE_SIZE = 3;
    heap;
    vSize = 0;
    vArrayPos;
    vArrayNeg;
    cSize = 0;
    learnClauseMin = 0;
    /**
     * Konstruktor.
     */
    constructor() {
        super();
        this.heap = new Heap_1.Heap();
        this.vSize = 0;
        this.cSize = 0;
        this.vArrayPos = Array(1).fill(null);
        this.vArrayNeg = Array(1).fill(null);
    }
    isVarTrue(pVar) {
        let v = this.getVarOf(pVar);
        return v.index === -1;
    }
    createNewVar() {
        this.vSize++;
        if (this.vSize >= this.vArrayPos.length) {
            let newSize = this.vSize * 2;
            this.vArrayPos = Arrays_1.Arrays.copyOf(this.vArrayPos, newSize);
            this.vArrayNeg = Arrays_1.Arrays.copyOf(this.vArrayNeg, newSize);
        }
        let vPos = new Variable_1.Variable(+this.vSize);
        let vNeg = new Variable_1.Variable(-this.vSize);
        vPos.negation = vNeg;
        vNeg.negation = vPos;
        this.vArrayPos[this.vSize] = vPos;
        this.vArrayNeg[this.vSize] = vNeg;
        this.heap.insert(vPos);
        this.heap.insert(vNeg);
        return this.vSize;
    }
    addClause(pVars) {
        if (pVars.length === 0) {
            console.log(JSON.stringify("WARNUNG: Leere Klausel bei SatSolver.addClause(int[] pVars)!"));
            return;
        }
        let set = new AVLSet_1.AVLSet();
        for (let v of pVars) {
            if (set.contains(-v)) {
                return;
            }
            set.add(v);
        }
        let list = new LinkedCollection_1.LinkedCollection();
        while (!set.isEmpty()) {
            list.addLast(set.pollFirst());
        }
        while (list.size() > 3) {
            let x = list.removeFirst().valueOf();
            let y = list.removeFirst().valueOf();
            let z = this.createNewVar();
            list.addLast(z);
            this.addClause2(-x, z);
            this.addClause2(-y, z);
            this.addClause3(x, y, -z);
        }
        if (list.size() === 3) {
            this.addClause3(list.removeFirst().valueOf(), list.removeFirst().valueOf(), list.removeFirst().valueOf());
        }
        if (list.size() === 2) {
            this.addClause2(list.removeFirst().valueOf(), list.removeFirst().valueOf());
        }
        if (list.size() === 1) {
            this.addClause1(list.removeFirst().valueOf());
        }
    }
    /**
     * Fügt der Datenstruktur eine 1-CNF-Klausel hinzu.
     *
     * @param x Das 1. Literal (Variablennummer) der Klausel.
     */
    addClause1(x) {
        let varX = this.getVarOf(x);
        if (varX.negation === null)
            throw new NullPointerException_1.NullPointerException();
        this.heap.remove(varX);
        this.heap.remove(varX.negation);
        let c = new Clause_1.Clause(varX);
        this.cSize++;
        varX.clauses.addLast(c);
        varX.statSatFree[c.sat][c.free]++;
        this.heap.insert(varX);
        this.heap.insert(varX.negation);
    }
    /**
     * Fügt der Datenstruktur eine 2-CNF-Klausel hinzu.
     *
     * @param x Das 1. Literal (Variablennummer) der Klausel.
     * @param y Das 2. Literal (Variablennummer) der Klausel.
     */
    addClause2(x, y) {
        let varX = this.getVarOf(x);
        let varY = this.getVarOf(y);
        if ((varX.negation === null) || (varY.negation === null))
            throw new NullPointerException_1.NullPointerException();
        this.heap.remove(varX);
        this.heap.remove(varY);
        this.heap.remove(varX.negation);
        this.heap.remove(varY.negation);
        let c = new Clause_1.Clause(varX, varY);
        this.cSize++;
        varX.clauses.addLast(c);
        varX.statSatFree[c.sat][c.free]++;
        varX.neighbours.addLast(varY);
        varY.clauses.addLast(c);
        varY.statSatFree[c.sat][c.free]++;
        varY.neighbours.addLast(varX);
        this.heap.insert(varX);
        this.heap.insert(varY);
        this.heap.insert(varX.negation);
        this.heap.insert(varY.negation);
    }
    /**
     * Fügt der Datenstruktur eine 3-CNF-Klausel hinzu.
     *
     * @param x Das 1. Literal (Variablennummer) der Klausel.
     * @param y Das 2. Literal (Variablennummer) der Klausel.
     * @param z Das 3. Literal (Variablennummer) der Klausel.
     */
    addClause3(x, y, z) {
        let varX = this.getVarOf(x);
        let varY = this.getVarOf(y);
        let varZ = this.getVarOf(z);
        if ((varX.negation === null) || (varY.negation === null) || (varZ.negation === null))
            throw new NullPointerException_1.NullPointerException();
        this.heap.remove(varX);
        this.heap.remove(varY);
        this.heap.remove(varZ);
        this.heap.remove(varX.negation);
        this.heap.remove(varY.negation);
        this.heap.remove(varZ.negation);
        let c = new Clause_1.Clause(varX, varY, varZ);
        this.cSize++;
        varX.clauses.addLast(c);
        varX.statSatFree[c.sat][c.free]++;
        varX.neighbours.addLast(varY);
        varX.neighbours.addLast(varZ);
        varY.clauses.addLast(c);
        varY.statSatFree[c.sat][c.free]++;
        varY.neighbours.addLast(varX);
        varY.neighbours.addLast(varZ);
        varZ.clauses.addLast(c);
        varZ.statSatFree[c.sat][c.free]++;
        varZ.neighbours.addLast(varX);
        varZ.neighbours.addLast(varY);
        this.heap.insert(varX);
        this.heap.insert(varY);
        this.heap.insert(varZ);
        this.heap.insert(varX.negation);
        this.heap.insert(varY.negation);
        this.heap.insert(varZ.negation);
    }
    getVarCount() {
        return this.vSize;
    }
    getClauseCount() {
        return this.cSize;
    }
    solve(pMaxTimeMillis) {
        let timeStart = System_1.System.currentTimeMillis();
        let backtrackV = Array(this.vSize).fill(null);
        let backtrackLearn = Array(SatSolver3.MAX_LEARNED_CLAUSE_SIZE).fill(null);
        let backtrackB = Array(this.vSize).fill(false);
        let index = 0;
        let max = 1;
        let countDown = 0;
        this.learnClauseMin = SatSolver3.MAX_LEARNED_CLAUSE_SIZE + 1;
        let maxIndex = 0;
        while ((index >= 0) && !this.heap.isEmpty()) {
            if (index > maxIndex) {
                maxIndex = index;
            }
            if (countDown === 0) {
                for (let i = index; i >= 0; i--) {
                    if (backtrackV[i] !== null) {
                        this.unitpropagation_undo((0, Variable_1.cast_de_nrw_schule_svws_core_kursblockung_satsolver_Variable)(backtrackV[i]));
                    }
                    backtrackV[i] = null;
                    backtrackB[i] = false;
                }
                index = 0;
                if ((this.learnClauseMin >= 1) && (this.learnClauseMin <= SatSolver3.MAX_LEARNED_CLAUSE_SIZE)) {
                    let clause = Array(this.learnClauseMin).fill(0);
                    for (let i = 0; i < clause.length; i++) {
                        clause[i] = -backtrackLearn[i].nr;
                    }
                    this.addClause(clause);
                }
                let result = this.simplify();
                if (result !== SatSolverA_1.SatSolverA.RESULT_UNKNOWN) {
                    return result;
                }
                max = max + 1 + Math.trunc(max / 2);
                countDown = max;
                this.learnClauseMin = SatSolver3.MAX_LEARNED_CLAUSE_SIZE + 1;
                if (System_1.System.currentTimeMillis() - timeStart > pMaxTimeMillis) {
                    return SatSolverA_1.SatSolverA.RESULT_UNKNOWN;
                }
                continue;
            }
            countDown--;
            let varP = backtrackV[index];
            if ((varP !== null) && (backtrackB[index])) {
                this.unitpropagation_undo(varP);
                backtrackV[index] = null;
                backtrackB[index] = false;
                this.learnClause(backtrackV, backtrackLearn, index);
                index--;
                continue;
            }
            if (varP === null) {
                varP = this.heap.top();
                if (!varP.isUnsat()) {
                    backtrackV[index] = varP;
                    this.unitpropagation(varP);
                    index++;
                    continue;
                }
            }
            else {
                this.unitpropagation_undo(varP);
            }
            if (varP.negation === null)
                throw new NullPointerException_1.NullPointerException();
            varP = varP.negation;
            if (varP.isUnsat()) {
                backtrackV[index] = null;
                this.learnClause(backtrackV, backtrackLearn, index);
                index--;
                continue;
            }
            backtrackV[index] = varP;
            backtrackB[index] = true;
            this.unitpropagation(varP);
            index++;
            continue;
        }
        return this.heap.isEmpty() ? SatSolverA_1.SatSolverA.RESULT_SATISFIABLE : SatSolverA_1.SatSolverA.RESULT_UNSATISFIABLE;
    }
    learnClause(backtrackV, backtrackLearn, size) {
        if (size < 1) {
            return;
        }
        if (size >= this.learnClauseMin) {
            return;
        }
        this.learnClauseMin = size;
        for (let i = 0; i < size; i++) {
            backtrackLearn[i] = backtrackV[i];
        }
    }
    simplify() {
        let count1 = 0;
        let changed = true;
        while (changed) {
            changed = false;
            for (let nr = 1; nr <= this.vSize; nr++) {
                let varP = this.vArrayPos[nr];
                if (varP.index < 0) {
                    continue;
                }
                if (varP.statSatFree[0][0] > 0) {
                    return SatSolverA_1.SatSolverA.RESULT_UNSATISFIABLE;
                }
                if (varP.negation === null)
                    throw new NullPointerException_1.NullPointerException();
                if (varP.negation.statSatFree[0][0] > 0) {
                    return SatSolverA_1.SatSolverA.RESULT_UNSATISFIABLE;
                }
                if ((varP.statSatFree[0][1] > 0) && (varP.negation.statSatFree[0][1] > 0)) {
                    return SatSolverA_1.SatSolverA.RESULT_UNSATISFIABLE;
                }
                if (varP.statSatFree[0][1] > 0) {
                    this.unitpropagation(varP);
                    count1++;
                    changed = true;
                    continue;
                }
                if (varP.negation.statSatFree[0][1] > 0) {
                    this.unitpropagation(varP.negation);
                    count1++;
                    changed = true;
                    continue;
                }
                if (varP.getClauseOccurences() === 0) {
                    this.unitpropagation(varP.negation);
                    count1++;
                    changed = true;
                    continue;
                }
                if (varP.negation.getClauseOccurences() === 0) {
                    this.unitpropagation(varP);
                    count1++;
                    changed = true;
                    continue;
                }
            }
        }
        return SatSolverA_1.SatSolverA.RESULT_UNKNOWN;
    }
    static fill(index) {
        let s = "";
        for (let i = 0; i < index; i++) {
            s = s.valueOf() + "    ";
        }
        return s;
    }
    /**
     * Setzt die Variable {@code varP} auf TRUE und aktualisiert die gesamte Datenstruktur entsprechend.
     *
     * @param varP Die Variable, die auf TRUE gesetzt wird.
     */
    unitpropagation(varP) {
        let varN = varP.negation;
        if (varN === null)
            throw new NullPointerException_1.NullPointerException();
        this.heap.remove(varP);
        this.heap.remove(varN);
        varP.index = -1;
        varN.index = -2;
        this.unitpropagationHelper(varP.clauses, +1, -1);
        this.unitpropagationHelper(varN.clauses, +0, -1);
    }
    /**
     * Setzt die Variable {@code varP} von TRUE auf FREI und aktualisiert die gesamte Datenstruktur entsprechend.
     *
     * @param varP Die Variable, die von TRUE auf FREI gesetzt wird.
     */
    unitpropagation_undo(varP) {
        let varN = varP.negation;
        if (varN === null)
            throw new NullPointerException_1.NullPointerException();
        this.unitpropagationHelper(varP.clauses, -1, +1);
        this.unitpropagationHelper(varN.clauses, +0, +1);
        this.heap.insert(varP);
        this.heap.insert(varN);
    }
    /**
     * Eine Hilsmethode, die alle Klauseln der Liste aktualisiert.
     *
     * @param clauses    Alle zu informmierenden Klausel.
     * @param pDeltaSat  Die Veränderung der erfüllten Klauseln, relativ zum 1. Index im 2D-Array
     *                   {@link Variable#statSatFree}.
     * @param pDeltaFree Die Veränderung der freien Variablen, relativ zum 2. Index im 2D-Array
     *                   {@link Variable#statSatFree}.
     */
    unitpropagationHelper(clauses, pDeltaSat, pDeltaFree) {
        for (let c of clauses) {
            let sat1 = c.sat;
            let free1 = c.free;
            let sat2 = sat1 + pDeltaSat;
            let free2 = free1 + pDeltaFree;
            for (let v of c.variables) {
                v.statSatFree[sat1][free1]--;
                v.statSatFree[sat2][free2]++;
                this.heap.update(v);
            }
            c.sat = sat2;
            c.free = free2;
        }
    }
    /**
     * Liefert das zugehörige Variablenobjekt.
     *
     * @param pNr Der Variablennummer.
     *
     * @return Das zugehörige Variablenobjekt.
     */
    getVarOf(pNr) {
        return (pNr > 0) ? this.vArrayPos[pNr] : this.vArrayNeg[-pNr];
    }
    isTranspiledInstanceOf(name) {
        return ['de.nrw.schule.svws.core.kursblockung.satsolver.SatSolverA', 'de.nrw.schule.svws.core.kursblockung.satsolver.SatSolver3'].includes(name);
    }
}
exports.SatSolver3 = SatSolver3;
function cast_de_nrw_schule_svws_core_kursblockung_satsolver_SatSolver3(obj) {
    return obj;
}
exports.cast_de_nrw_schule_svws_core_kursblockung_satsolver_SatSolver3 = cast_de_nrw_schule_svws_core_kursblockung_satsolver_SatSolver3;
//# sourceMappingURL=SatSolver3.js.map