import { CycleRemoval } from '../../layout/layered/CycleRemoval';
import { mkGraphOnEdgesN } from '../../structs/basicGraphOnEdges';
// import {Assert} from '../../utils/assert'
import { IntPair } from '../../utils/IntPair';
import { SolverShell } from './SolverShell';
import { UniformSolverVar } from './UniformSolverVar';
export class UniformOneDimensionalSolver {
    //  desired variable separation
    //  <param name="variableSeparation"></param>
    constructor(variableSeparation) {
        this.idealPositions = new Map();
        this.varList = new Array();
        this.constraints = new Set();
        this.solverShell = new SolverShell();
        this.boundsToInt = new Map();
        this.varSepartion = variableSeparation;
    }
    //         delegate Array<NudgerConstraint> Edges(int i);
    //
    //         delegate int End(NudgerConstraint constraint);
    //         Edges outEdgesDel;
    //         Edges inEdgesDel;
    //         End sourceDelegate;
    //         End targetDelegate;
    //         Supremum minDel;
    //         Supremum maxDel;
    SetLowBound(bound, id) {
        const v = this.Var(id);
        v.LowBound = Math.max(bound, v.LowBound);
    }
    Var(id) {
        return this.varList[id];
    }
    SetUpperBound(id, bound) {
        const v = this.Var(id);
        v.UpperBound = Math.min(bound, v.UpperBound);
    }
    Solve() {
        this.SolveByRegularSolver();
    }
    SolveByRegularSolver() {
        this.CreateVariablesForBounds();
        for (let i = 0; i < this.varList.length; i++) {
            const v = this.varList[i];
            if (v.IsFixed) {
                this.solverShell.AddFixedVariable(i, v.Position);
            }
            else {
                this.solverShell.AddVariableWithIdealPositionNN(i, this.idealPositions.get(i));
                if (v.LowBound != Number.NEGATIVE_INFINITY) {
                    this.constraints.add(new IntPair(this.GetBoundId(v.LowBound), i));
                }
                if (v.UpperBound != Number.POSITIVE_INFINITY) {
                    this.constraints.add(new IntPair(i, this.GetBoundId(v.UpperBound)));
                }
            }
        }
        this.CreateGraphAndRemoveCycles();
        for (const edge of this.graph.edges) {
            let w = 0;
            if (edge.x < this.varList.length) {
                w += this.varList[edge.x].Width;
            }
            if (edge.y < this.varList.length) {
                w += this.varList[edge.y].Width;
            }
            w /= 2;
            this.solverShell.AddLeftRightSeparationConstraintNNN(edge.x, edge.y, this.varSepartion + w);
        }
        this.solverShell.Solve();
        for (let i = 0; i < this.varList.length; i++) {
            this.varList[i].Position = this.solverShell.GetVariableResolvedPosition(i);
        }
    }
    GetBoundId(bound) {
        return this.boundsToInt.get(bound);
    }
    CreateVariablesForBounds() {
        for (const v of this.varList) {
            if (v.IsFixed) {
                continue;
            }
            if (v.LowBound != Number.NEGATIVE_INFINITY) {
                this.RegisterBoundVar(v.LowBound);
            }
            if (v.UpperBound != Number.POSITIVE_INFINITY) {
                this.RegisterBoundVar(v.UpperBound);
            }
        }
    }
    RegisterBoundVar(bound) {
        if (!this.boundsToInt.has(bound)) {
            const varIndex = this.varList.length + this.boundsToInt.size;
            this.boundsToInt.set(bound, varIndex);
            this.solverShell.AddFixedVariable(varIndex, bound);
        }
    }
    CreateGraphAndRemoveCycles() {
        // edges in the graph go from a smaller value to a bigger value
        this.graph = mkGraphOnEdgesN(Array.from(this.constraints), this.varList.length + this.boundsToInt.size);
        // removing cycles
        const feedbackSet = CycleRemoval.getFeedbackSet(this.graph);
        if (feedbackSet != null) {
            for (const edge of feedbackSet) {
                this.graph.removeEdge(edge);
            }
        }
    }
    GetVariablePosition(id) {
        return this.varList[id].Position;
    }
    AddConstraint(i, j) {
        this.constraints.add(new IntPair(i, j));
    }
    AddVariableNNNN(id, currentPosition, idealPosition, width) {
        this.idealPositions.set(id, idealPosition);
        this.AddVariableNNBN(id, currentPosition, false, width);
    }
    AddFixedVariable(id, position) {
        this.AddVariableNNBN(id, position, true, 0);
        // 0 for width
    }
    AddVariableNNBN(id, position, isFixed, width) {
        /*Assert.assert(id == this.varList.length)*/
        //new UniformSolverVar { IsFixed = isFixed, Position = position, Width=width
        const v = new UniformSolverVar();
        v.Position = position;
        v.IsFixed = isFixed;
        v.Width = width;
        this.varList.push(v);
    }
}
//# sourceMappingURL=UniformOneDimensionalSolver.js.map