protected static string GetCutoffString(Solution solution)
 {
     Validate.IsNotNull(solution, "Solution must not be null");
     string strCutoff = string.Empty;
     if (solution.ExecutionLimitExceeded)
     {
         strCutoff = " [Cutoff:";
         if (solution.TimeLimitExceeded)
         {
             strCutoff += " TimeLimit";
         }
         if (solution.OuterProjectIterationsLimitExceeded)
         {
             strCutoff += " OuterIterLimit";
         }
         if (solution.InnerProjectIterationsLimitExceeded)
         {
             strCutoff += " InnerIterLimit";
         }
         strCutoff += "]";
     }
     return strCutoff;
 }
        /// <summary>
        /// Sets Variable.ActualPos to the positions of the Variables that minimally satisfy the constraints
        /// along this axis.  This overload takes a parameter specification.
        /// </summary>
        /// <param name="solverParameters">Solution-generation options.</param>
        /// <returns>The only failure condition is if there are one or more unsatisfiable constraints, such as cycles
        ///         or mutually exclusive equality constraints; if these are encountered, a list of lists of these 
        ///         constraints is returned, where each list contains a single cycle, which may be of length one for
        ///         unsatisfiable equality constraints.  Otherwise, the return value is null.</returns>
        public Solution Solve(Parameters solverParameters)
        {
            if (null != solverParameters)
            {
                this.solverParams = (Parameters)solverParameters.Clone();
            }

            // Reset some parameter defaults to per-solver-instance values.
            if (this.solverParams.OuterProjectIterationsLimit < 0)
            {
                // If this came in 0, it stays that way, and there is no limit.  Otherwise, set it to a value
                // reflecting the expectation of convergence roughly log-linearly in the number of variables.
#if SHARPKIT //https://github.com/SharpKit/SharpKit/issues/4 integer rounding issue
                this.solverParams.OuterProjectIterationsLimit = 100 * (((int)Math.Log(this.numberOfVariables, 2.0)) + 1);
#else
                this.solverParams.OuterProjectIterationsLimit = 100 * ((int)Math.Log(this.numberOfVariables, 2.0) + 1);
#endif
            }
            if (this.solverParams.InnerProjectIterationsLimit < 0)
            {
                // If this came in 0, it stays that way, and there is no limit.  Otherwise, assume that for
                // any pass, each constraint may be violated (most likely this happens only on the first pass),
                // and add some extra based upon constraint count.  Now that we split and retry on unsatisfied
                // constraints, assume that any constraint may be seen twice on a pass.
#if SHARPKIT //https://github.com/SharpKit/SharpKit/issues/4 integer rounding issue
                this.solverParams.InnerProjectIterationsLimit = (this.numberOfConstraints * 2) + (100 * (((int)Math.Log(this.numberOfConstraints, 2.0)) + 1));
#else
                this.solverParams.InnerProjectIterationsLimit = (this.numberOfConstraints * 2) + (100 * ((int)Math.Log(this.numberOfConstraints, 2.0) + 1));
#endif
            }

            // ReSolving can be done for updated constraints.
            bool isReSolve = !this.allConstraints.IsEmpty;
            CheckForUpdatedConstraints();

            this.solverSolution = new Solution { MinInnerProjectIterations = int.MaxValue };
            this.allConstraints.MaxConstraintTreeDepth = 0;
            this.allConstraints.SolverParameters = this.solverParams;

            //
            // First set up all the internal stuff we'll use for solutions.
            //
#if CACHE_STATS
            cacheStats.Clear();
#endif // CACHE_STATS

            // If no constraints have been loaded, there's nothing to do.  Two distinct variables
            // are required to create a constraint, so this also ensures a minimum number of variables.
            if (0 == this.numberOfConstraints)
            {
                // For Qpsc, we may have neighbours but no constraints.
                if (!this.IsQpsc)
                {
                    return (Solution)this.solverSolution.Clone();
                }
            }
            else if (!isReSolve)
            {
                SetupConstraints();
            }

            // This is the number of unsatisfiable constraints encountered.
            this.allConstraints.NumberOfUnsatisfiableConstraints = 0;

            // Merge Equality constraints first.  These do not do any constraint-splitting, and thus
            // remain in the same blocks, always satisfied, regardless of whether we're solving the full
            // Qpsc or the simpler loop.
            MergeEqualityConstraints();

            // Prepare for timeout checking.
            if (this.solverParams.TimeLimit > 0)
            {
                this.timeoutStopwatch = new Stopwatch();
                this.timeoutStopwatch.Start();
            }

            //
            // Done with initial setup.  Now if we have neighbour pairs, we do the full SolveQpsc logic
            // complete with Gradient projection.  Otherwise, we have a much simpler Project/Split loop.
            //
            if (this.IsQpsc)
            {
                this.SolveQpsc();
            }
            else
            {
                this.SolveByStandaloneProject();
                this.CalculateStandaloneProjectGoalFunctionValue();
            }

            // We initialized this to int.MaxValue so make sure it's sane if we didn't complete a Project iteration.
            if (this.solverSolution.MinInnerProjectIterations > this.solverSolution.MaxInnerProjectIterations)
            {
                // Probably this is 0.
                this.solverSolution.MinInnerProjectIterations = this.solverSolution.MaxInnerProjectIterations;
            }

#if CACHE_STATS
            cacheStats.Print();
            Console.WriteLine("  NumFinalBlocks = {0}, MinCacheBlocks = {1}, MaxCacheSize = {2}",
                                allBlocks.Count, violationCacheMinBlockCutoff, ViolationCache.MaxConstraints);
#endif // CACHE_STATS

            // Done.  Caller will copy each var.ActualPos back to the Nodes.  If we had any unsatisfiable
            // constraints, copy them back out to the caller.
            this.solverSolution.NumberOfUnsatisfiableConstraints = this.allConstraints.NumberOfUnsatisfiableConstraints;
#if BLOCK_STATS
            int minBlockVars = this.numberOfVariables;
            int maxBlockVars = 0;
            foreach (Block block in allBlocks.Vector) {
                if (minBlockVars > block.Variables.Count) {
                    minBlockVars = block.Variables.Count;
                }
                if (maxBlockVars < block.Variables.Count) {
                    maxBlockVars = block.Variables.Count;
                }
            } // endforeach block

            Console.WriteLine("Num final Blocks: {0}, Min Block Vars: {1}, Max Block Vars: {2}",
                    allBlocks.Count, minBlockVars, maxBlockVars);
#endif // BLOCK_STATS
            this.solverSolution.MaxConstraintTreeDepth = this.allConstraints.MaxConstraintTreeDepth;
            return (Solution)this.solverSolution.Clone();
        } // end Solve()
 private void DebugVerifyClusterHierarchy(Solution solution)
 {
     if (avoidOverlaps && (null != clusterHierarchies) && (0 != solution.NumberOfUnsatisfiableConstraints ))
     {
         foreach (var c in clusterHierarchies)
             DebugVerifyClusters(cg, c, c);
     }
 }
 internal bool VerifySolutionMembers(Solution solution, IEnumerable<NeighborDef> iterNeighbourDefs)
 {
     bool usedQpsc = (solution.AlgorithmUsed == SolverAlgorithm.QpscWithScaling)
                  || (solution.AlgorithmUsed == SolverAlgorithm.QpscWithoutScaling);
     if (usedQpsc != (ForceQpsc || ((null != iterNeighbourDefs) && iterNeighbourDefs.Any())))
     {
         WriteLine("UsedQPSC is not as expected");
         return false;
     }
     return true;
 }
 protected static string GetIterationsString(Solution solution)
 {
     Validate.IsNotNull(solution, "Solution must not be null");
     string strCutoff = GetCutoffString(solution);
     return string.Format(
         "outer {0}; inner min={1} max={2} total={3} average={4:F2}; algo = {5}{6}",
         solution.OuterProjectIterations,
         solution.MinInnerProjectIterations,
         solution.MaxInnerProjectIterations,
         solution.InnerProjectIterationsTotal,
         (0.0 == solution.OuterProjectIterations) ? 0.0 : ((double)solution.InnerProjectIterationsTotal / solution.OuterProjectIterations),
         solution.AlgorithmUsed,
         strCutoff);
 }
        internal static void WriteFile(int seed,
                        double maxWeightToGenerate,
                        Solver solverX,
                        Solver solverY,
                        Solution solutionX,
                        Solution solutionY,
                        double minPaddingX,
                        double minPaddingY,
                        double minClusterSizeX,
                        double minClusterSizeY,
                        double maxMargin,
                        List<VariableDef> lstVarDefs,
                        List<ClusterDef> lstClusDefs,
                        StreamWriter outputFileWriter)
        {
            // TODO_cleanup: make shared strings; regenerate test files to verify

            // Add the summary information as comments. @@ (high-level) and @# (low-level) allow
            // findstr etc. scans of the file metadata; @[@#] gets both.
            outputFileWriter.WriteLine("// @@Variables: {0}", lstVarDefs.Count);
            outputFileWriter.WriteLine("// @@Clusters: {0}", (null == lstClusDefs) ? 0 : lstClusDefs.Count);
            outputFileWriter.WriteLine("// @@Constraints_X: {0}", solverX.Constraints.Count());
            outputFileWriter.WriteLine("// @@Constraints_Y: {0}", solverY.Constraints.Count());
            outputFileWriter.WriteLine();

            // Values we want to read back in.
            outputFileWriter.WriteLine("Seed 0x{0}", seed.ToString("X"));
            outputFileWriter.WriteLine("Weight {0:F5}", maxWeightToGenerate);
            outputFileWriter.WriteLine("Padding {0:F5} {1:F5}", minPaddingX, minPaddingY);
            outputFileWriter.WriteLine("MinClusterSize {0:F5} {1:F5}", minClusterSizeX, minClusterSizeY);
            outputFileWriter.WriteLine("Margin {0}", maxMargin);
            outputFileWriter.WriteLine("Goal {0} {1}", solutionX.GoalFunctionValue, solutionY.GoalFunctionValue);
            outputFileWriter.WriteLine();

            outputFileWriter.WriteLine(TestFileStrings.BeginVariables);
            for (int idxVar = 0; idxVar < lstVarDefs.Count; ++idxVar)
            {
                VariableDef varDef = lstVarDefs[idxVar];
                outputFileWriter.WriteLine(
                    TestFileStrings.WriteVariable2D,
                    idxVar,
                    varDef.DesiredPosX,
                    varDef.DesiredPosY,
                    varDef.SizeX,
                    varDef.SizeY,
                    varDef.WeightX,
                    varDef.WeightY);
            }
            outputFileWriter.WriteLine(TestFileStrings.EndVariables);
            outputFileWriter.WriteLine();
            outputFileWriter.Flush();

            if (null != lstClusDefs)
            {
                // Write out the clusters, starting at 1 to skip the root.  Since we populate the
                // clusterdefs left-to-right we'll always print out the parents before the children.
                foreach (ClusterDef clusDef in lstClusDefs)
                {
                    outputFileWriter.WriteLine(TestFileStrings.BeginCluster);

                    // Write the current cluster definition.
                    outputFileWriter.WriteLine(TestFileStrings.WriteClusterId, clusDef.ClusterId);
                    outputFileWriter.WriteLine(
                        TestFileStrings.WriteClusterParent,
                        null == clusDef.ParentClusterDef ? 0 : clusDef.ParentClusterDef.ClusterId);
                    outputFileWriter.WriteLine(
                        TestFileStrings.WriteClusterMinSize, clusDef.MinimumSizeX, clusDef.MinimumSizeY);
                    if (clusDef.IsNewHierarchy)
                    {
                        outputFileWriter.WriteLine("NewHierarchy");
                    }
                    outputFileWriter.WriteLine(
                        TestFileStrings.WriteClusterBorder,
                        "Left",
                        clusDef.LeftBorderInfo.InnerMargin,
                        ClusterDef.IsFixedString(clusDef.LeftBorderInfo),
                        clusDef.LeftBorderInfo.Weight);
                    outputFileWriter.WriteLine(
                        TestFileStrings.WriteClusterBorder,
                        "Right",
                        clusDef.RightBorderInfo.InnerMargin,
                        ClusterDef.IsFixedString(clusDef.RightBorderInfo),
                        clusDef.RightBorderInfo.Weight);
                    outputFileWriter.WriteLine(
                        TestFileStrings.WriteClusterBorder,
                        "Top",
                        clusDef.TopBorderInfo.InnerMargin,
                        ClusterDef.IsFixedString(clusDef.TopBorderInfo),
                        clusDef.TopBorderInfo.Weight);
                    outputFileWriter.WriteLine(
                        TestFileStrings.WriteClusterBorder,
                        "Bottom",
                        clusDef.BottomBorderInfo.InnerMargin,
                        ClusterDef.IsFixedString(clusDef.BottomBorderInfo),
                        clusDef.BottomBorderInfo.Weight);
                    outputFileWriter.WriteLine("// @#ClusterVars: {0}", clusDef.Variables.Count);
                    foreach (VariableDef varDef in clusDef.Variables)
                    {
                        outputFileWriter.WriteLine(TestFileStrings.WriteClusterVariable, varDef.IdString);
                    }
                    outputFileWriter.WriteLine(TestFileStrings.EndCluster);
                    outputFileWriter.WriteLine();
                }
                outputFileWriter.Flush();
            } // endif clusters exist

            // Write the constraints.
            // TODOclus: This is outputting vars Lnn Rnn in DEBUG and an empty string in RELEASE; consider making the file
            //           output (with clusters, anyway) run in DEBUG-only and have TestFileReader.cs know how to decode them.
            outputFileWriter.WriteLine(TestFileStrings.BeginConstraintsX);
            foreach (Constraint cst in solverX.Constraints.OrderBy(cst => cst))
            {
                // There are no equality constraints in OverlapRemoval so pass an empty string.
                outputFileWriter.WriteLine(
                    TestFileStrings.WriteConstraint,
                    ((OverlapRemovalNode)cst.Left.UserData).UserData,
                    ((OverlapRemovalNode)cst.Right.UserData).UserData,
                    string.Empty,
                    cst.Gap);
            }
            outputFileWriter.WriteLine(TestFileStrings.EndConstraints);
            outputFileWriter.WriteLine();
            outputFileWriter.WriteLine(TestFileStrings.BeginConstraintsY);
            foreach (Constraint cst in solverY.Constraints.OrderBy(cst => cst))
            {
                // There are no equality constraints in OverlapRemoval so pass an empty string.
                outputFileWriter.WriteLine(
                    TestFileStrings.WriteConstraint,
                    ((OverlapRemovalNode)cst.Left.UserData).UserData,
                    ((OverlapRemovalNode)cst.Right.UserData).UserData,
                    string.Empty,
                    cst.Gap);
            }
            outputFileWriter.WriteLine(TestFileStrings.EndConstraints);
            outputFileWriter.WriteLine();

            // Now write the results.
            outputFileWriter.WriteLine(TestFileStrings.BeginResults);
            foreach (VariableDef varDef in lstVarDefs)
            {
                outputFileWriter.WriteLine(TestFileStrings.WriteResults2D, varDef.IdString, varDef.VariableX.ActualPos, varDef.VariableY.ActualPos);
            } // endforeach varDef
            outputFileWriter.WriteLine(TestFileStrings.EndResults);

            if (null != lstClusDefs)
            {
                outputFileWriter.WriteLine();
                outputFileWriter.WriteLine(TestFileStrings.BeginClusterResults);
                outputFileWriter.WriteLine("// (includes only clusters that are not IsNewHierarchy)");
                foreach (ClusterDef clusDef in lstClusDefs)
                {
                    // Clusters at the root of a hierarchy have no borders.
                    if (!clusDef.IsNewHierarchy)
                    {
                        outputFileWriter.WriteLine(
                            TestFileStrings.WriteClusterResults,
                            clusDef.ClusterId,
                            clusDef.Left,
                            clusDef.Right,
                            clusDef.Top,
                            clusDef.Bottom);
                    }
                }
                outputFileWriter.WriteLine(TestFileStrings.EndClusterResults);
            }

            // Done.
            outputFileWriter.Flush();
            outputFileWriter.Close();
        }