// this is used to recursively place the missing Mines into the available boxes for the probability line
        private List <ProbabilityLine> DistributeMissingMines(ProbabilityLine pl, NextWitness nw, int missingMines, int index)
        {
            //console.log("Distributing " + missingMines + " missing mines to box " + nw.newBoxes[index].uid);

            this.recursions++;
            if (this.recursions % 100000 == 0)
            {
                information.Write("Solution Counter recursision = " + recursions);
            }

            List <ProbabilityLine> result = new List <ProbabilityLine>();

            // if there is only one box left to put the missing mines we have reach the end of this branch of recursion
            if (nw.GetNewBoxes().Count - index == 1)
            {
                // if there are too many for this box then the probability can't be valid
                if (nw.GetNewBoxes()[index].GetMaxMines() < missingMines)
                {
                    //console.log("Abandon (1)");
                    return(result);
                }
                // if there are too few for this box then the probability can't be valid
                if (nw.GetNewBoxes()[index].GetMinMines() > missingMines)
                {
                    //console.log("Abandon (2)");
                    return(result);
                }
                // if there are too many for this game then the probability can't be valid
                if (pl.GetMineCount() + missingMines > this.maxTotalMines)
                {
                    //console.log("Abandon (3)");
                    return(result);
                }

                // otherwise place the mines in the probability line
                pl.SetMineBoxCount(nw.GetNewBoxes()[index].GetUID(), new BigInteger(missingMines));

                pl.SetMineCount(pl.GetMineCount() + missingMines);
                result.Add(pl);
                //console.log("Distribute missing mines line after " + pl.mineBoxCount);
                return(result);
            }


            // this is the recursion
            int maxToPlace = Math.Min(nw.GetNewBoxes()[index].GetMaxMines(), missingMines);

            for (int i = nw.GetNewBoxes()[index].GetMinMines(); i <= maxToPlace; i++)
            {
                ProbabilityLine npl = ExtendProbabilityLine(pl, nw.GetNewBoxes()[index], i);

                List <ProbabilityLine> r1 = DistributeMissingMines(npl, nw, missingMines - i, index + 1);
                result.AddRange(r1);
            }

            return(result);
        }
        // create a new probability line by taking the old and adding the mines to the new Box
        private ProbabilityLine ExtendProbabilityLine(ProbabilityLine pl, Box newBox, int mines)
        {
            //console.log("Extended probability line: Adding " + mines + " mines to box " + newBox.uid);
            //console.log("Extended probability line before" + pl.mineBoxCount);

            ProbabilityLine result = new ProbabilityLine(this.boxes.Count);

            result.SetMineCount(pl.GetMineCount() + mines);

            result.CopyMineBoxCount(pl);

            result.SetMineBoxCount(newBox.GetUID(), new BigInteger(mines));

            //console.log("Extended probability line after " + result.mineBoxCount);

            return(result);
        }
        // calculate how many ways this solution can be generated and roll them into one
        private void MergeLineProbabilities(ProbabilityLine npl, ProbabilityLine pl)
        {
            BigInteger solutions = 1;

            for (int i = 0; i < this.boxes.Count; i++)
            {
                solutions = solutions * (BigInteger)SMALL_COMBINATIONS[this.boxes[i].GetTiles().Count][(int)pl.GetMineBoxCount(i)];
            }

            npl.SetSolutionCount(npl.GetSolutionCount() + solutions);

            for (int i = 0; i < this.boxes.Count; i++)
            {
                if (this.mask[i])    // if this box has been involved in this solution - if we don't do this the hash gets corrupted by boxes = 0 mines because they weren't part of this edge
                {
                    npl.SetMineBoxCount(i, npl.GetMineBoxCount(i) + pl.GetMineBoxCount(i) * solutions);
                }
            }
        }
        // this combines newly generated probabilities with ones we have already stored from other independent sets of witnesses
        private void CombineProbabilities()
        {
            List <ProbabilityLine> result = new List <ProbabilityLine>();

            if (this.workingProbs.Count == 0)
            {
                information.Write("working probabilites list is empty!!");
                return;
            }

            // see if we can find a common divisor
            BigInteger hcd = workingProbs[0].GetSolutionCount();

            foreach (ProbabilityLine pl in workingProbs)
            {
                hcd = BigInteger.GreatestCommonDivisor(hcd, pl.GetSolutionCount());
            }
            foreach (ProbabilityLine pl in heldProbs)
            {
                hcd = BigInteger.GreatestCommonDivisor(hcd, pl.GetSolutionCount());
            }
            information.Write("Greatest Common Divisor is " + hcd);

            solutionCountMultiplier = solutionCountMultiplier * hcd;

            int mineCountMin = workingProbs[0].GetMineCount() + heldProbs[0].GetMineCount();

            // shrink the window
            edgeMinesMinLeft = edgeMinesMinLeft - workingProbs[0].GetMineCount();
            edgeMinesMaxLeft = edgeMinesMaxLeft - workingProbs[workingProbs.Count - 1].GetMineCount();

            foreach (ProbabilityLine pl in workingProbs)
            {
                BigInteger plSolCount = pl.GetSolutionCount() / hcd;

                foreach (ProbabilityLine epl in heldProbs)
                {
                    // if the mine count can never reach the lower cuttoff then ignore it
                    if (pl.GetMineCount() + epl.GetMineCount() + edgeMinesMaxLeft < this.mineCountLowerCutoff)
                    {
                        continue;
                    }

                    // if the mine count will always be pushed beyonf the upper cuttoff then ignore it
                    if (pl.GetMineCount() + epl.GetMineCount() + edgeMinesMinLeft > this.mineCountUpperCutoff)
                    {
                        continue;
                    }

                    ProbabilityLine newpl = new ProbabilityLine(this.boxes.Count);

                    newpl.SetMineCount(pl.GetMineCount() + epl.GetMineCount());

                    BigInteger eplSolCount = epl.GetSolutionCount() / hcd;

                    newpl.SetSolutionCount(pl.GetSolutionCount() * eplSolCount);

                    for (int k = 0; k < this.boxes.Count; k++)
                    {
                        BigInteger w1 = pl.GetMineBoxCount(k) * eplSolCount;
                        BigInteger w2 = epl.GetMineBoxCount(k) * plSolCount;
                        newpl.SetMineBoxCount(k, w1 + w2);
                    }

                    result.Add(newpl);
                }
            }

            //Console.WriteLine("Solution multiplier is " + solutionCountMultiplier);

            this.heldProbs.Clear();

            // if result is empty this is an impossible position
            if (result.Count == 0)
            {
                Console.WriteLine("Impossible position encountered");
                return;
            }

            if (result.Count == 1)
            {
                heldProbs.AddRange(result);
                return;
            }

            // sort into mine order
            result.Sort();

            // and combine them into a single probability line for each mine count
            int             mc  = result[0].GetMineCount();
            ProbabilityLine npl = new ProbabilityLine(this.boxes.Count);

            npl.SetMineCount(mc);

            int startMC = mc;

            foreach (ProbabilityLine pl in result)
            {
                if (pl.GetMineCount() != mc)
                {
                    this.heldProbs.Add(npl);
                    mc  = pl.GetMineCount();
                    npl = new ProbabilityLine(this.boxes.Count);
                    npl.SetMineCount(mc);
                }
                npl.SetSolutionCount(npl.GetSolutionCount() + pl.GetSolutionCount());

                for (int j = 0; j < this.boxes.Count; j++)
                {
                    npl.SetMineBoxCount(j, npl.GetMineBoxCount(j) + pl.GetMineBoxCount(j));
                }
            }

            this.heldProbs.Add(npl);
        }