Ejemplo n.º 1
0
 public bool TryGetNextInColumn(ExpansionTerm option, out ExpansionTerm next)
 {
     if (ColumnHasItemsAfter(option))
     {
         next = option.StepBy(threeExpStep: 1, twoExpStep: 0);
         return(true);
     }
     else
     {
         next = default(ExpansionTerm);
         return(false);
     }
 }
        ///<summary>Enumerates through all possible term options in ascending order</summary>
        public static IEnumerable <ExpansionTerm> EnumerateSortedTermOptions()
        {
            var           rowEnds      = new List <ExpansionTerm>();
            ExpansionTerm lastReturned = new ExpansionTerm(0, 0);

            while (true)
            {
                //Note: There only ever needs to be one value for minNext (i.e. rather than
                //needing a list of ExpansionTerms that all evaluate to the same number), so just
                //finding a single minimum is ok.
                //This is because for the ExpansionTerms, each (ThreeExponent, TwoExponent) pair
                //leads to a unique value when the ExpansionTerm is evaluated (see ExpansionTerm remarks for proof).
                var minNext = (
                    Enumerable.Concat(
                        rowEnds.Select(                         //Each item one past the end of the current rows
                            x => x.StepBy(threeExpStep: 0, twoExpStep: 1)
                            ),
                        new[] {                         //The first item of a new row
                    new ExpansionTerm(
                        threeExponent: rowEnds.Count,
                        twoExponent: 0
                        )
                }
                        )
                    .MinBy(x => x.Evaluate())
                    );

                if (minNext.ThreeExponent == rowEnds.Count)
                {
                    rowEnds.Add(minNext);
                }
                else
                {
                    rowEnds[minNext.ThreeExponent] = minNext;
                }

                yield return(minNext);
            }
        }
Ejemplo n.º 3
0
        private static ReadOnlyCollection <ReadOnlyCollection <ExpansionTerm> > GetExpansionTermOptions(BigInteger z)
        {
            List <List <ExpansionTerm> > terms = new List <List <ExpansionTerm> >();

            var option = new ExpansionTerm(0, 0);

            while (option.Evaluate() <= z)
            {
                var optionsForTerm = new List <ExpansionTerm>();

                while (option.Evaluate() <= z)
                {
                    optionsForTerm.Add(option);
                    option = option.StepBy(0, 1);
                }

                terms.Add(optionsForTerm);
                option = new ExpansionTerm(option.ThreeExponent + 1, 0);
            }

            return(terms.Select(row => row.AsReadOnly()).ToList().AsReadOnly());
        }
Ejemplo n.º 4
0
        public IEnumerable <Expansion> EnumerateExpansionPaths(ExpansionTerm pathStartingPoint, bool capToMaximum)
        {
            if (!this.Contains(pathStartingPoint))
            {
                throw new ArgumentException(
                          "The " + nameof(TermOptionsMatrix) + " '" + this + "' "
                          + "does not contain provided " + nameof(pathStartingPoint) + " '" + pathStartingPoint + "'."
                          );
            }

            var stack = new ExpansionTermStack();

            stack.Push(pathStartingPoint);

            //To start with, keep moving up, adding each element to the stack, until the top row is reached
            while (stack.Peek().ThreeExponent > 0)
            {
                var termAbove = stack.Peek().StepBy(threeExpStep: -1, twoExpStep: 0);
                stack.Push(termAbove);
            }

            //Then check if that resulted in a path that isn't too big
            if (!capToMaximum || stack.Sum <= this.Maximum)
            {
                yield return(new Expansion(stack.ToList().AsReadOnly()));
            }
            else
            {
                yield break;                 //The stack currently holds the lowest possible path; if that doesn't fit in Maximum, no paths will
            }

            while (true)
            {
                if (DoneEnumeratingExpansionPaths(stack))
                {
                    yield break;
                }

                ExpansionTerm termToRight;
                if (this.TryGetNextInRow(stack.Peek(), out termToRight) && (!capToMaximum || stack.SumWithAlternateHead(termToRight) <= this.Maximum))
                {
                    stack.Pop();
                    stack.Push(termToRight);

                    if (capToMaximum && stack.SumWithoutHead + MinimumPathSum(stack.Peek()) > this.Maximum)
                    {
                        //If true, the shortest path from the current position is too big. As moving to the right always
                        //results in a higher term, all other paths from the current position, and from all positions to the right
                        //of the current one, will also be too big. This is only true given the *current* path stored in the stack,
                        //so to try different paths, pop() to move back down (and probably to the left) by one, and then loop around
                        //to try moving to the right from there
                        stack.Pop();
                        continue;
                    }
                    else
                    {
                        //The first route, which goes straight to the top, is the path with the minimum
                        //sum, which we've already checked is be less than this.Maximum, so take that route
                        while (stack.Peek().ThreeExponent > 0)
                        {
                            stack.Push(stack.Peek().StepBy(threeExpStep: -1, twoExpStep: 0));
                        }

                        //	Console.WriteLine("#12: " + stack.Peek() + ", " + stack.SumWithoutHead + ", " + MinimumPathSum(stack.Peek()));
                        yield return(new Expansion(stack.ToList().AsReadOnly()));

                        //Now loop around again to try moving to the right
                        continue;
                    }
                }
                else
                {
                    //Can't move to the right, so move back down by one instead & loop around to try again
                    stack.Pop();
                    continue;
                }
            }
        }
Ejemplo n.º 5
0
        option.TwoExponent + 1 < TermOptions[option.ThreeExponent].Count;                    //If the next item is within the row, return true

        public bool ColumnHasItemsAfter(ExpansionTerm option)
        => option.ThreeExponent + 1 < TermOptions.Count &&              //If the entire next row isn't inside the matrix, the column definitely doesn't have more items
        TermOptions[option.ThreeExponent + 1].Count > option.TwoExponent;                    //If the next row is long enough to include the relevant column, return true
Ejemplo n.º 6
0
 public bool RowHasItemsAfter(ExpansionTerm option)
 => option.ThreeExponent < TermOptions.Count &&              //If the row isn't inside the matrix, it definitely doesn't have more items
 option.TwoExponent + 1 < TermOptions[option.ThreeExponent].Count;                    //If the next item is within the row, return true
Ejemplo n.º 7
0
 public static BigInteger MinimumPathSum(ExpansionTerm startingPoint) => SumColumnTo(startingPoint);
Ejemplo n.º 8
0
 public static BigInteger SumRowTo(ExpansionTerm option) => MathUtils.BigIntThreeToThe(option.ThreeExponent) * MathUtils.BigIntSumPowersOfTwoTo(option.TwoExponent);
Ejemplo n.º 9
0
 public bool Contains(ExpansionTerm option)
 => option.ThreeExponent < TermOptions.Count &&
 option.TwoExponent < TermOptions[option.ThreeExponent].Count;
Ejemplo n.º 10
0
        public IEnumerable <int> EnumerateIntSummedExpansionPaths(ExpansionTerm pathStartingPoint)        //capToMaximum = true
        {
            if (this.Maximum > int.MaxValue)
            {
                throw new InvalidOperationException(
                          "Maximum is greater than int.MaxValue, so not all summed paths would be possible to return."
                          );
            }

            if (!this.Contains(pathStartingPoint))
            {
                throw new ArgumentException(
                          "The " + nameof(TermOptionsMatrix) + " '" + this + "' "
                          + "does not contain provided " + nameof(pathStartingPoint) + " '" + pathStartingPoint + "'."
                          );
            }

            var stack = new LongExpansionTermStack();

            stack.Push(pathStartingPoint);

            //To start with, keep moving up, adding each element to the stack, until the top row is reached
            while (stack.Peek().ThreeExponent > 0)
            {
                var termAbove = stack.Peek().StepBy(threeExpStep: -1, twoExpStep: 0);
                stack.Push(termAbove);
            }

            //Then check if that resulted in a path that isn't too big
            if (stack.Sum <= this.Maximum)
            {
                yield return((int)stack.Sum);                //stack.Sum <= this.Maximum <= int.MaxValue
            }
            else
            {
                yield break;                 //The stack currently holds the lowest possible path; if that doesn't fit in Maximum, no paths will
            }

            ExpansionTerm currentTerm = stack.Pop();

            while (true)
            {
                //If there's no items left on the stack, then the current item (which isn't on the stack)
                //is all that's left, and we're back at the starting point (or the starting point is in
                //the top row, and we never moved anywhere). Either way, we are therefore done.
                if (stack.Count <= 0)
                {
                    yield break;
                }

                ExpansionTerm termToRight = currentTerm.StepBy(threeExpStep: 0, twoExpStep: 1);
                // ^ But this might not actually be in the matrix (ie. less than or equal to this.Maximum) so need to check that next.
                //   But then, we don't actually need to check that, as we're already need to check if it, plus the stack's sum,
                //   is less than or equal to this.Maximum - so just need to do that check.
                //   But in fact, we also then need to check whether all of that, plus the lowest path from the new term,
                //   is less than or equal to this.Maximum...so just do *that* check to start with.

                if (stack.Sum + MinimumPathSum(termToRight) <= this.Maximum)
                {
                    //The first route from termToRight, which goes straight to the top, is the path with the
                    //minimum sum, which we've already checked is be less than this.Maximum, so take that route
                    //(first pushing termToRight). However, don't push the final item (hence "> 0"), just add
                    //that onto the sum manually - as it'd just be guaranteed to be popped straight after
                    //anyway, so there's no point.
                    for (int i = termToRight.ThreeExponent; i > 0; i--)
                    {
                        stack.Push(new ExpansionTerm(threeExponent: i, twoExponent: termToRight.TwoExponent));
                    }

                    //Set up currentTerm for next iteration (and for yield statement)
                    currentTerm = new ExpansionTerm(threeExponent: 0, twoExponent: termToRight.TwoExponent);

                    yield return((int)(stack.Sum) + currentTerm.EvaluateInt());

                    // ^ Only need the TwoExponent as the ThreeExponent is zero so it's just "1 * 2^whatever"
                    //   Also, ok to cast as (stack.Sum + currentTerm.TwoExponent <= this.Maximum <= int.MaxValue) (based on prior checks)

                    //Now loop around again to try moving to the right again
                    continue;
                }
                else
                {
                    //If false, then either the term to the right is itself bigger than this.Maximum (so it isn't inside the matrix);
                    //or it, plus the current stack's sum, is bigger than this.Maximum (so adding anything to it will always be outside
                    //the range of desired results); or finally it, plus the current stack's sum, plus the sum of the smallest path
                    //continuing from it, is bigger than this.Maximum. This last case means that all paths from it, and from all positions
                    //further to the right, will be too big (but this position may be valid, when reached from a different stack, that
                    //goes to the left more). In all three cases, we need move back down from the current term - which we've already done
                    //by popping earlier - and then loop back around to continue trying from there. What we do need to do, though, is set
                    //up currentTerm ready for the next loop, by popping the top value into it.
                    currentTerm = stack.Pop();
                    continue;
                }
            }

            //	while (true)
            //	{
            //		//If there's only one item left on the stack, we're back at the starting point
            //		//(or the starting point is in the top row, and we never moved anywhere).
            //		//Either way, we are therefore done.
            //		if (stack.Count <= 1) yield break;
            //
            //		ExpansionTerm currentTerm = stack.Pop();
            //		ExpansionTerm termToRight = currentTerm.StepBy(threeExpStep: 0, twoExpStep: 1);
            //		// ^ But this might not actually be in the matrix, ie. no greater than this.Maximum, so need to check that next.
            //		//   But then, we don't actually need to check that, as we're already need to check if it, plus the stack's sum,
            //		//   is no greater than this.Maximum - so just need to do that check.
            //		//   But in fact, we also then need to check whether all of that, plus the lowest path from the new term,
            //		//   is no greater than this.Maximum...so just do *that* check to start with.
            //
            //		//long termToRightEval = termToRight.EvaluateLong();
            //		//if (this.TryGetNextInRow(currentTerm, out termToRight) && stack.SumWithAlternateHead(termToRight) <= this.Maximum)
            //		//if (termToRightEval <= this.Maximum && termToRightEval + stack.Sum <= this.Maximum)
            //		//if (termToRightEval + stack.Sum <= this.Maximum)
            //		if (stack.Sum + MinimumPathSum(termToRight) <= this.Maximum)
            //		{
            //			//The first route from termToRight, which goes straight to the top, is the path with
            //			//the minimum sum, which we've already checked is be less than this.Maximum, so take
            //			//that route (first pushing termToRight)
            //			for (int i = termToRight.ThreeExponent; i >= 0; i--) {
            //				stack.Push(new ExpansionTerm(threeExponent: i, twoExponent: termToRight.TwoExponent));
            //			}
            //
            //			//	Console.WriteLine("#12: " + stack.Peek() + ", " + stack.SumWithoutHead + ", " + MinimumPathSum(stack.Peek()));
            //			yield return (int)stack.Sum; //Safe to cast based on prior checks (remember this.Maximum < int.MaxValue)
            //
            //			//Now loop around again to try moving to the right
            //			continue;
            //		}
            //		else
            //		{
            //			//If false, then either the term to the right is itself bigger than this.Maximum (so it isn't inside the matrix);
            //			//or it, plus the current stack's sum, is bigger than this.Maximum (so adding anything to it will always be outside
            //			//the range of desired results); or finally it, plus the current stack's sum, plus the sum of the smallest path
            //			//continuing from it, is bigger than this.Maximum. This last case means that all paths from it, and from all positions
            //			//further to the right, will be too big (but this position may be valid, when reached from a different stack, that
            //			//goes to the left more). In all three cases, we need move back down from the current term - which we've already done
            //			//by popping earlier - and then loop back around to continue trying from there.
            //			continue;
            //		}
            //	}
        }