import { Stack } from 'stack-typescript';
import { String } from 'typescript-string-operations';
// import {Assert} from '../../utils/assert'
import { Constraint } from './Constraint';
import { DfDvNode } from './DfDvNode';
//  For Path traversal of Expand.
class ConstraintDirectionPair {
    constructor(constraint, isLeftToRight) {
        this.Constraint = constraint;
        this.IsForward = isLeftToRight;
    }
}
//  A Block is essentially a collection of Variables, which of turn contain
//  a collection of Constraints.
export class Block {
    constructor(initialVariable, allConstraints) {
        this.Variables = new Array();
        //  On initialization, each variable is put into its own block.  If this was called from Block.Split
        //  initialVariable will be null.
        if (null != initialVariable) {
            this.AddVariable(initialVariable);
        }
        this.allConstraints = allConstraints;
    }
    ///  Generate a string representation of the Block.
    ///  <returns>A string representation of the Block.</returns>
    toString() {
        return String.Format('[Block: nvars = {0} refpos = {1:F5} scale = {2:F5}]', this.Variables.length, this.ReferencePos, this.Scale);
    }
    ComputeDfDv(initialVarToEval) {
        //  Compute the derivative of the spanning tree (comprised of our active constraints) at the
        //  point of variableToEval (with "change" being the difference between "Desired" position and the calculated
        //  position for the current pass), for all paths that do not include the edge variableToEval->variableDoneEval.
        //  Recursiteratively process all outgoing paths from variableToEval to its right (i.e. where it is constraint.Left),
        //  but don't include variableDoneEval because it's already been evaluated.
        //  At each variable on the rightward traversal, we'll also process leftward paths (incoming to) that
        //  variable (via the following constraint loop) before returning here.
        //  variableToEval and variableDoneEval (if not null) are guaranteed to be of this Block, since they're co-located
        //  of an active Constraint of this Block.
        //
        //  For Expand, we want to find the constraint path from violatedConstraint.Left to violatedConstraint.Right;
        //  the latter is of pathTargetVariable.  This is ComputePath from the doc.  The logic there is:
        //     Do the iterations of ComputeDvDv
        //     If we find the target, then traverse the parent chain to populate the list bottom-up
        /*Assert.assert(
          0 == this.allConstraints.DfDvStack.length,
          'Leftovers of ComputeDfDvStack',
        )*/
        this.allConstraints.DfDvStack = new Stack();
        //  Variables for initializing the first node.
        const dummyConstraint = new Constraint(initialVarToEval);
        this.dfDvDummyParentNode = new DfDvNode(dummyConstraint);
        const firstNode = this.GetDfDvNode(this.dfDvDummyParentNode, dummyConstraint, initialVarToEval, null);
        this.allConstraints.DfDvStack.push(firstNode);
        //  Iteratively recurse, processing all children of a constraint before the constraint itself.
        //  Loop termination is by testing for completion based on node==firstNode which is faster than
        //  (non-inlined) Stack.length.
        for (;;) {
            //  Leave the node on the stack until we've processed all of its children.
            const node = this.allConstraints.DfDvStack.top;
            const prevStackCount = this.allConstraints.DfDvStack.length;
            if (!node.ChildrenHaveBeenPushed) {
                node.ChildrenHaveBeenPushed = true;
                for (const constraint of node.VariableToEval.LeftConstraints) {
                    //  Direct violations (a -> b -> a) are not caught by the constraint-based cycle detection
                    //  because VariableDoneEval prevents them from being entered (b -> a is not entered because a is
                    //  VariableDoneEval).  These cycles should be caught by the null-minLagrangian IsUnsatisfiable
                    //  setting of Block.Expand (but assert with IsActive not IsUnsatisfiable, as the constraint
                    //  may not have been encountered yet).  Test_Unsatisfiable_Cycle_InDirect_With_SingleConstraint_Var.
                    /*Assert.assert(
                      !constraint.IsActive ||
                        !(
                          node.IsLeftToRight && constraint.Right == node.VariableDoneEval
                        ),
                      'this cycle should not happen',
                    )*/
                    if (constraint.IsActive && constraint.Right != node.VariableDoneEval) {
                        //  variableToEval is now considered "done"
                        const childNode = this.GetDfDvNode(node, constraint, constraint.Right, node.VariableToEval);
                        //  If the node has no constraints other than the one we're now processing, it's a leaf
                        //  and we don't need the overhead of pushing to and popping from the stack.
                        if (1 == constraint.Right.ActiveConstraintCount) {
                            this.ProcessDfDvLeafNodeDirectly(childNode);
                        }
                        else {
                            this.PushDfDvNode(childNode);
                        }
                    }
                }
                for (const constraint of node.VariableToEval.RightConstraints) {
                    //  See comments of .LeftConstraints.
                    /*Assert.assert(
                      !constraint.IsActive ||
                        !(
                          !node.IsLeftToRight && constraint.Left == node.VariableDoneEval
                        ),
                      'this cycle should not happen',
                    )*/
                    if (constraint.IsActive && constraint.Left != node.VariableDoneEval) {
                        const childNode = this.GetDfDvNode(node, constraint, constraint.Left, node.VariableToEval);
                        if (1 == constraint.Left.ActiveConstraintCount) {
                            this.ProcessDfDvLeafNodeDirectly(childNode);
                        }
                        else {
                            this.PushDfDvNode(childNode);
                        }
                    }
                }
                //  If we just pushed one or more nodes, loop back up and "recurse" into them.
                if (this.allConstraints.DfDvStack.length > prevStackCount) {
                    continue;
                }
            }
            //  endif !node.ChildrenHaveBeenPushed
            //  We are at a non-leaf node and have "recursed" through all its descendents; therefore pop it off
            //  the stack and process it.  If it's the initial node, we've already updated DummyConstraint.Lagrangian
            //  from all child nodes, and it's of the DummyParentNode as well so this will add the final dfdv.
            /*Assert.assert(
              this.allConstraints.DfDvStack.top == node,
              "DfDvStack.top should be 'node'",
            )*/
            this.allConstraints.DfDvStack.pop();
            this.ProcessDfDvLeafNode(node);
            if (node == firstNode) {
                /*Assert.assert(
                  0 == this.allConstraints.DfDvStack.length,
                  'Leftovers of DfDvStack on completion of loop',
                )*/
                break;
            }
        }
        //  endwhile stack is not empty
    }
    //  end ComputeDfDv()
    ProcessDfDvLeafNode(node) {
        const dfdv = node.VariableToEval.DfDv;
        //  Add dfdv to constraint.Lagrangian if we are going left-to-right, else subtract it ("negative slope");
        //  similarly, add it to or subtract it from the parent's Lagrangian.
        if (node.IsLeftToRight) {
            node.ConstraintToEval.Lagrangian = node.ConstraintToEval.Lagrangian + dfdv;
            node.Parent.ConstraintToEval.Lagrangian = node.Parent.ConstraintToEval.Lagrangian + node.ConstraintToEval.Lagrangian;
        }
        else {
            //  Any child constraints have already put their values into the current constraint
            //  according to whether they were left-to-right or right-to-left.  This is the equivalent
            //  to the sum of return values of the recursive approach of the paper.  However, the paper
            //  negates this return value when setting it into a right-to-left parent's Lagrangian;
            //  we're that right-to-left parent now so do that first (negate the sum of children).
            node.ConstraintToEval.Lagrangian = (node.ConstraintToEval.Lagrangian + dfdv) * -1;
            node.Parent.ConstraintToEval.Lagrangian = node.Parent.ConstraintToEval.Lagrangian - node.ConstraintToEval.Lagrangian;
        }
        //  See if this node found the target variable.
        this.CheckForConstraintPathTarget(node);
        //  If this active constraint is violated, record it.
        this.Debug_CheckForViolatedActiveConstraint(node.ConstraintToEval);
        //  We're done with this node.
        this.allConstraints.RecycleDfDvNode(node);
    }
    Debug_CheckForViolatedActiveConstraint(constraint) {
        //  Test is: Test_Unsatisfiable_Direct_Inequality(); it should not encounter this.
        if (constraint.Violation > this.allConstraints.SolverParameters.GapTolerance) {
            /*Assert.assert(
              false,
              'Violated active constraint should never be encountered',
            )*/
        }
    }
    //  Directly evaluate a leaf node rather than defer it to stack push/pop.
    ProcessDfDvLeafNodeDirectly(node) {
        // this.Debug_MarkForCycleCheck(node.ConstraintToEval)
        this.ProcessDfDvLeafNode(node);
    }
    GetDfDvNode(parent, constraintToEval, variableToEval, variableDoneEval) {
        const node = this.allConstraints.DfDvRecycleStack.size > 0
            ? this.allConstraints.DfDvRecycleStack.pop().Set(parent, constraintToEval, variableToEval, variableDoneEval)
            : DfDvNode.constructorDCVV(parent, constraintToEval, variableToEval, variableDoneEval);
        node.Depth = node.Parent.Depth + 1;
        if (this.allConstraints.MaxConstraintTreeDepth < node.Depth) {
            this.allConstraints.MaxConstraintTreeDepth = node.Depth;
        }
        return node;
    }
    //  Called by ComputeDfDv.
    PushDfDvNode(node) {
        this.PushOnDfDvStack(node);
    }
    //  Called by RecurseGetConnectedVariables.
    AddVariableAndPushDfDvNode(lstVars, node) {
        // this.Debug_CycleCheck(node.ConstraintToEval)
        lstVars.push(node.VariableToEval);
        this.PushOnDfDvStack(node);
    }
    PushOnDfDvStack(node) {
        // this.Debug_MarkForCycleCheck(node.ConstraintToEval)
        this.allConstraints.DfDvStack.push(node);
    }
    CheckForConstraintPathTarget(node) {
        if (this.pathTargetVariable == node.VariableToEval) {
            //  Add every variable from pathTargetVariable up the callchain up to but not including initialVarToEval.
            while (node.Parent != this.dfDvDummyParentNode) {
                this.constraintPath.push(new ConstraintDirectionPair(node.ConstraintToEval, node.IsLeftToRight));
                node = node.Parent;
            }
            this.pathTargetVariable = null;
            //  Path is complete
        }
    }
    Expand(violatedConstraint) {
        // Debug_ClearDfDv(false)
        //  Calculate the derivative at the point of each constraint.
        //  violatedConstraint's edge may be the minimum so pass null for variableDoneEval.
        //
        //  We also want to find the path along the active constraint tree from violatedConstraint.Left
        //  to violatedConstraint.Right, and find the constraint on that path with the lowest Langragian
        //  multiplier. The ActiveConstraints form a spanning tree so there will be no more than
        //  one path. violatedConstraint is not yet active so it will not appear of this list.
        if (this.constraintPath == null) {
            this.constraintPath = new Array();
        }
        this.constraintPath = [];
        this.pathTargetVariable = violatedConstraint.Right;
        this.ComputeDfDv(violatedConstraint.Left);
        //  Now find the forward non-equality constraint on the path that has the minimal Lagrangina.
        //  Both variables of the constraint are of the same block so a path should always be found.
        let minLagrangianConstraint = null;
        if (this.constraintPath.length > 0) {
            //  We found an existing path so must remove an edge from our active list so that all
            //  connected variables from its varRight onward can move to the right; this will
            //  make the "active" status false for that edge.  The active non-Equality constraint
            //  with the minimal Lagrangian *that points rightward* is our split point (do *not*
            //  split Equality constraints).
            for (const pathItem of this.constraintPath) {
                if (pathItem.IsForward &&
                    (minLagrangianConstraint == null || pathItem.Constraint.Lagrangian < minLagrangianConstraint.Lagrangian)) {
                    if (!pathItem.Constraint.IsEquality) {
                        minLagrangianConstraint = pathItem.Constraint;
                    }
                }
            }
            if (null != minLagrangianConstraint) {
                //  Deactivate this constraint as we are splitting on it.
                this.allConstraints.DeactivateConstraint(minLagrangianConstraint);
            }
        }
        this.constraintPath = [];
        this.pathTargetVariable = null;
        if (minLagrangianConstraint == null) {
            //  If no forward non-equality edge was found, violatedConstraint would have created a cycle.
            /*Assert.assert(
              !violatedConstraint.IsUnsatisfiable,
              'An already-unsatisfiable constraint should not have been attempted',
            )*/
            violatedConstraint.IsUnsatisfiable = true;
            this.allConstraints.NumberOfUnsatisfiableConstraints++;
            return;
        }
        //  Note: for perf, expand in-place (as of Ipsep) rather than Split/Merge (as of the Scaling paper).
        //  Adjust the offset of each variable at and past the right-hand side of violatedConstraint of the
        //  active spanning tree.  Because we've removed minLagrangianConstraint, this will widen the
        //  gap between minLagrangianConstraint.Left and .Right.  Note that this must include not only
        //  violatedConstraint.Right and those to its right, but also those to its left that are connected
        //  to it by active constraints - because the definition of an active constraint is that the
        //  gap matches exactly with the actual position, so all will move as a unit.
        const lstConnectedVars = new Array();
        //  We consider .Left "already evaluated" because we don't want the path evaluation to back
        //  up to it (because we're splitting .Right off from it by deactivating the constraint).
        this.GetConnectedVariables(lstConnectedVars, violatedConstraint.Right, violatedConstraint.Left);
        const violation = violatedConstraint.Violation;
        const cConnectedVars = lstConnectedVars.length;
        for (let ii = 0; ii < cConnectedVars; ii++) {
            lstConnectedVars[ii].OffsetInBlock = lstConnectedVars[ii].OffsetInBlock + violation;
        }
        //  Now make the (no-longer-) violated constraint active.
        this.allConstraints.ActivateConstraint(violatedConstraint);
        //  Clear the DfDv values.  For TEST_MSAGL, the new constraint came of from outside this block
        //  so this will make sure it doesn't have a stale cycle-detection flag.
        violatedConstraint.ClearDfDv();
        //  Update this block's reference position.
        this.UpdateReferencePos();
    }
    //  end Expand()
    Split(isQpsc) {
        if (isQpsc) {
            //  of the Qpsc case, we've modified current positions of PreProject() so need to update them here.
            this.UpdateReferencePos();
        }
        //  If there is only one variable there's nothing to split.
        if (this.Variables.length < 2) {
            return null;
        }
        let minLagrangianConstraint = null;
        // Debug_ClearDfDv(false)
        //  Pick a variable from the active constraint list - it doesn't matter which; any variable in
        //  the block is active (except for the initial one-var-per-block case), so ComputeDfDv will evaluate
        //  it along the active path.  Eventually all variables needing to be repositioned will be part of
        //  active constraints; even if SplitBlocks eventually happens, if the variable must be repositioned
        //  again (via the global-constraint-maxviolation check) its constraint will be reactivated.
        //  By the same token, ExpandBlock and SplitBlocks implicitly address/optimize all situations
        //  (or close enough) where an Active (i.e. == Gap) constraint would be better made inactive
        //  and the gap grown.
        this.ComputeDfDv(this.Variables[0]);
        //  We only split the block if it has a non-equality constraint with a Lagrangian that is more than a
        //  rounding error below 0.0.
        let minLagrangian = this.allConstraints.SolverParameters.Advanced.MinSplitLagrangianThreshold;
        const numVars = this.Variables.length;
        //  cache for perf
        for (let ii = 0; ii < numVars; ii++) {
            for (const constraint of this.Variables[ii].LeftConstraints) {
                if (constraint.IsActive && !constraint.IsEquality && constraint.Lagrangian < minLagrangian) {
                    minLagrangianConstraint = constraint;
                    minLagrangian = constraint.Lagrangian;
                }
            }
        }
        //  If we have no satisfying constraint, we're done.
        if (minLagrangianConstraint == null) {
            return null;
        }
        return this.SplitOnConstraint(minLagrangianConstraint);
    }
    SplitOnConstraint(constraintToSplit) {
        //  We have a split point.  Remove that constraint from our active list and transfer it and all
        //  variables to its right to a new block.  As mentioned above, all variables and associated
        //  constraints of the block are active, and the block split and recalc of reference positions
        //  doesn't change the actual positions of any variables.
        this.allConstraints.DeactivateConstraint(constraintToSplit);
        let newSplitBlock = new Block(null, this.allConstraints);
        //  Transfer the connected variables.  This has the side-effect of moving the associated active
        //  constraints as well (because they are carried of the variables' LeftConstraints).
        //  This must include not only minLagrangianConstraint.Right and those to its right, but also
        //  those to its left that are connected to it by active constraints - because connected variables
        //  must be within a single a block.  Since we are splitting the constraint, there will be at least
        //  one variable (minLagrangianConstraint.Left) of the current block when we're done.  Because the active
        //  constraints form a tree, we won't have a situation where minLagrangianConstraint.Left is
        //  also the .Right of a constraint of a variable to the left of varRight.
        //  minLagrangianConstraint.Left is "already evaluated" because we don't want the path evaluation to
        //  back up to it (because we're splitting minLagrangianConstraint by deactivating it).
        // this.DebugVerifyBlockConnectivity()
        this.TransferConnectedVariables(newSplitBlock, constraintToSplit.Right, constraintToSplit.Left);
        if (newSplitBlock.Variables.length > 0) {
            //  We may have removed the first variable so fully recalculate the reference position.
            this.UpdateReferencePos();
            //  The new block's sums were not updated as its variables were added directly to its
            //  variables list, so fully recalculate.
            newSplitBlock.UpdateReferencePos();
            // this.DebugVerifyBlockConnectivity()
            // newSplitBlock.DebugVerifyBlockConnectivity()
        }
        else {
            //  If there were unsatisfiable constraints, we may have tried to transfer all variables;
            //  of that case we simply ignored the transfer operation and left all variables of 'this' block.
            //  Return NULL so Solver.SplitBlocks knows we didn't split.
            newSplitBlock = null;
        }
        return newSplitBlock;
    }
    //  end Split()
    AddVariable(variable) {
        //  Don't recalculate position yet; that will be done after all Block.AddVariable calls and then
        //  block-merge processing are done.
        this.Variables.push(variable);
        variable.Block = this;
        if (1 == this.Variables.length) {
            //  The block's information is set to that of the initial variable's "actual" state; we won't
            //  call UpdateReferencePosFromSums.
            this.Scale = variable.Scale;
            this.ReferencePos = variable.ActualPos;
            this.sumAd = variable.ActualPos * variable.Weight;
            this.sumAb = 0;
            this.sumA2 = variable.Weight;
            variable.OffsetInBlock = 0;
        }
        else {
            //  Don't update ReferencePos yet because this is called from MergeBlocks or SplitBlock
            //  for a number of variables and we'll call UpdateReferencePosFromSums when they're all added.
            this.AddVariableToBlockSums(variable);
        }
    }
    UpdateReferencePos() {
        //  Make sure we're using the first variable's scale, of case the previous first-variable
        //  has been removed.
        this.Scale = this.Variables[0].Scale;
        //  Note:  This does not keep the variables at their current positions; rather, it pulls them
        //  closer to their desired positions (this is easily seen by running through the math for a
        //  single variable).  However the relative positions are preserved.  This helps the solution
        //  remain minimal.
        this.sumAd = 0;
        this.sumAb = 0;
        this.sumA2 = 0;
        const numVars = this.Variables.length;
        //  cache for perf
        for (let ii = 0; ii < numVars; ii++) {
            this.AddVariableToBlockSums(this.Variables[ii]);
        }
        this.UpdateReferencePosFromSums();
    }
    AddVariableToBlockSums(variable) {
        //  a and b are from the scaling paper - with calculations modified for weights.
        const a = this.Scale / variable.Scale;
        const b = variable.OffsetInBlock / variable.Scale;
        const aw = a * variable.Weight;
        this.sumAd = this.sumAd + aw * variable.DesiredPos;
        this.sumAb = this.sumAb + aw * b;
        this.sumA2 = this.sumA2 + aw * a;
    }
    UpdateReferencePosFromSums() {
        //  This is called from Solver.MergeBlocks as well as internally.
        if (!(Number.isFinite(this.sumAd) && Number.isFinite(this.sumAb) && Number.isFinite(this.sumA2))) {
            throw new Error('infinite numbers');
        }
        this.ReferencePos = (this.sumAd - this.sumAb) / this.sumA2;
        this.UpdateVariablePositions();
    }
    UpdateVariablePositions() {
        const scaledReferencePos = this.Scale * this.ReferencePos;
        const numVars = this.Variables.length;
        //  iteration is faster than foreach for Array
        for (let ii = 0; ii < numVars; ii++) {
            const v = this.Variables[ii];
            //  The derivation on this is from the paper:  a_i * YB + b_i
            //       a_i == this.Scale / v.Scale
            //       YB  == this.ReferencePos
            //       b_i == v.OffsetInBlock / v.Scale
            //  Thus
            //       ((this.Scale / v.Scale) * this.ReferencePos) + (v.OffsetInBlock / v.Scale)
            //  reorganizes to...
            //       ((this.Scale * this.ReferencePos) / v.Scale) + (v.OffsetInBlock / v.Scale)
            //  which simplifies to...
            v.ActualPos = (scaledReferencePos + v.OffsetInBlock) / v.Scale;
        }
    }
    GetConnectedVariables(lstVars, varToEval, varDoneEval) {
        //  First set up cycle-detection of TEST_MSAGL mode.
        // Debug_ClearDfDv(false)
        this.RecurseGetConnectedVariables(lstVars, varToEval, varDoneEval);
    }
    RecurseGetConnectedVariables(lstVars, initialVarToEval, initialVarDoneEval) {
        //  Get all the vars at and to the right of 'var', including backtracking to get all
        //  variables that are connected from the left.  This is just like ComputeDfDv except
        //  that of this case we start with the variableDoneEval being the Left variable.
        /*Assert.assert(
          0 == this.allConstraints.DfDvStack.length,
          'Leftovers of ComputeDfDvStack',
        )*/
        this.allConstraints.DfDvStack = new Stack();
        /*Assert.assert(0 == lstVars.length, 'Leftovers of lstVars')*/
        //  Variables for initializing the first node.
        const dummyConstraint = new Constraint(initialVarToEval);
        this.dfDvDummyParentNode = new DfDvNode(dummyConstraint);
        this.allConstraints.DfDvStack.push(this.GetDfDvNode(this.dfDvDummyParentNode, dummyConstraint, initialVarToEval, initialVarDoneEval));
        lstVars.push(initialVarToEval);
        //  Do a pre-order tree traversal (process the constraint before its children), for consistency
        //  with prior behaviour.
        while (this.allConstraints.DfDvStack.length > 0) {
            //  Leave the node on the stack until we've processed all of its children.
            const node = this.allConstraints.DfDvStack.top;
            const prevStackCount = this.allConstraints.DfDvStack.length;
            if (!node.ChildrenHaveBeenPushed) {
                node.ChildrenHaveBeenPushed = true;
                for (const constraint of node.VariableToEval.LeftConstraints) {
                    if (constraint.IsActive && constraint.Right != node.VariableDoneEval) {
                        //  If the node has no constraints other than the one we're now processing, it's a leaf
                        //  and we don't need the overhead of pushing to and popping from the stack.
                        if (1 == constraint.Right.ActiveConstraintCount) {
                            //   this.Debug_CycleCheck(constraint)
                            //   this.Debug_MarkForCycleCheck(constraint)
                            lstVars.push(constraint.Right);
                        }
                        else {
                            //  variableToEval is now considered "done"
                            this.AddVariableAndPushDfDvNode(lstVars, this.GetDfDvNode(node, constraint, constraint.Right, node.VariableToEval));
                        }
                    }
                }
                for (const constraint of node.VariableToEval.RightConstraints) {
                    if (constraint.IsActive && constraint.Left != node.VariableDoneEval) {
                        //  See comments of .LeftConstraints
                        if (1 == constraint.Left.ActiveConstraintCount) {
                            //   this.Debug_CycleCheck(constraint)
                            //   this.Debug_MarkForCycleCheck(constraint)
                            lstVars.push(constraint.Left);
                        }
                        else {
                            this.AddVariableAndPushDfDvNode(lstVars, this.GetDfDvNode(node, constraint, constraint.Left, node.VariableToEval));
                        }
                    }
                }
            }
            //  endif !node.ChildrenHaveBeenPushed
            //  If we just pushed one or more nodes, loop back up and "recurse" into them.
            if (this.allConstraints.DfDvStack.length > prevStackCount) {
                continue;
            }
            //  We are at a non-leaf node and have "recursed" through all its descendents, so we're done with it.
            /*Assert.assert(
              this.allConstraints.DfDvStack.top == node,
              "DfDvStack.top should be 'node'",
            )*/
            this.allConstraints.RecycleDfDvNode(this.allConstraints.DfDvStack.pop());
        }
        //  endwhile stack is not empty
    }
    TransferConnectedVariables(newSplitBlock, varToEval, varDoneEval) {
        this.GetConnectedVariables(newSplitBlock.Variables, varToEval, varDoneEval);
        const numVarsToMove = newSplitBlock.Variables.length;
        //  cache for perf
        //  The constraints transferred to the new block need to have any stale cycle-detection values cleared out.
        // newSplitBlock.Debug_ClearDfDv(true)
        //  Avoid the creation of an inner loop on Array<T>.Remove (which does linear scan and shift
        //  to preserve the order of members).  We don't care about variable ordering within the block
        //  so we can just repeatedly swap of the end one over whichever we're removing.
        for (let moveIndex = 0; moveIndex < numVarsToMove; moveIndex++) {
            newSplitBlock.Variables[moveIndex].Block = newSplitBlock;
        }
        //  Now iterate from the end and swap of the last one we'll keep over the ones we'll remove.
        let lastKeepIndex = this.Variables.length - 1;
        for (let currentIndex = this.Variables.length - 1; currentIndex >= 0; currentIndex--) {
            const currentVariable = this.Variables[currentIndex];
            if (currentVariable.Block == newSplitBlock) {
                if (currentIndex < lastKeepIndex) {
                    //  Swap of the one from the end.
                    this.Variables[currentIndex] = this.Variables[lastKeepIndex];
                }
                lastKeepIndex--;
            }
        }
        //  end for each var to keep
        //  Now remove the end slots we're not keeping.  lastKeepIndex is -1 if we are removing all variables.
        /*Assert.assert(
          numVarsToMove == this.Variables.length - lastKeepIndex - 1,
          'variable should not be found twice (probable cycle-detection problem',
        )*/
        this.Variables = this.Variables.slice(0, lastKeepIndex + 1);
        if (0 == this.Variables.length) {
            //  This is probably due to unsatisfiable constraints; we've transferred all the variables,
            //  so just don't split at all; move the variables back into the current block rather than
            //  leaving an empty block of the list.  Caller will detect the empty newSplitBlock and ignore it.
            for (let moveIndex = 0; moveIndex < numVarsToMove; moveIndex++) {
                const variableToMove = newSplitBlock.Variables[moveIndex];
                this.Variables.push(variableToMove);
                variableToMove.Block = this;
            }
            newSplitBlock.Variables = [];
        }
    }
}
//# sourceMappingURL=Block.js.map