Пример #1
0
        public void TestExpandableIntervalCombinations()
        {
            var f0  = new Filling("0", new[] { true });
            var f01 = new Filling("01", new[] { true, true });
            var f1  = new Filling("1", new[] { false, true });
            var f12 = new Filling("12", new[] { false, true, true });
            var f02 = new Filling("02", new[] { true, false, true });
            var f2  = new Filling("2", new[] { false, false, true });

            CanExpandFillingDelegate <Filling> canExpand = (filling, gap) => filling.Occupation.Count >= 2 &&
                                                           filling.Occupation[1] &&
                                                           !filling.Occupation[0] &&
                                                           gap == new Gap(0, 1);

            IEnumerable <CombinationWithGaps <Filling> > result = Combinatorics.AllFillingIntervalCombinations(f0.ToSingletonReadOnlyList(), 1, canExpand).ToList();
            IEnumerable <Filling[]> expectation = new Filling[][] { new Filling[] { f0 } }.ToList();

            Contract.Assert(result.ContainsSameElements(expectation, Equals));

            result      = Combinatorics.AllFillingIntervalCombinations(f1.ToSingletonReadOnlyList(), 2, canExpand).ToList();
            expectation = new Filling[][] { new Filling[] { f1 } }.ToList();            //even though f1 doesn't fill position 0
            Contract.Assert(result.ContainsSameElements(expectation, Equals));

            result      = Combinatorics.AllFillingIntervalCombinations(f1.ToSingletonReadOnlyList(), 3, canExpand).ToList();
            expectation = new Filling[0][].ToList();            //f1 may expand to position 0, but not to position 2 (which wasn't available in the case above
            Contract.Assert(result.ContainsSameElements(expectation, Equals));

            result      = Combinatorics.AllFillingIntervalCombinations(f12.ToSingletonReadOnlyList(), 3, canExpand).ToList();
            expectation = new Filling[][] { new Filling[] { f12 } }.ToList();            //even though f12 doesn't fill position 0
            Contract.Assert(result.ContainsSameElements(expectation, Equals));

            result      = Combinatorics.AllFillingIntervalCombinations(new[] { f1, f2 }, 3, canExpand).ToList();
            expectation = new Filling[][] { new Filling[] { f1, f2 } }.ToList();            //f1 fills position 0
            Contract.Assert(result.ContainsSameElements(expectation, Equals));

            result      = Combinatorics.AllFillingIntervalCombinations(new[] { f0, f2 }, 3, canExpand).ToList();
            expectation = new Filling[0][].ToList();            //position 1 is unfilled
            Contract.Assert(result.ContainsSameElements(expectation, Equals));

            result      = Combinatorics.AllFillingIntervalCombinations(new Filling[0], 1, canExpand).ToList();
            expectation = new Filling[0][].ToList();
            Contract.Assert(result.ContainsSameElements(expectation, Equals));

            result      = Combinatorics.AllFillingIntervalCombinations(new Filling[0], 3, canExpand).ToList();
            expectation = new Filling[0][].ToList();
            Contract.Assert(result.ContainsSameElements(expectation, Equals));

            result      = Combinatorics.AllFillingIntervalCombinations(new[] { f1, f12 }, 3, canExpand).ToList();
            expectation = new Filling[][] { new Filling[] { f12 } }.ToList();            //f12 fills position 0, f1 remains unused
            Contract.Assert(result.ContainsSameElements(expectation, Equals));

            //realization: an expansion shouldn't be applied when it could be omitted. Or at least, those without the expansion should be yielded first.
            //So the output of AllFillingIntervalCombinations should be ordered by the number of expansions used
        }
Пример #2
0
        /// <summary> Determines whether this combination of fillings can fill the entire length, where some fillings may be expanded. </summary>
        /// <returns> The specified combination with the gaps; or null if the entire length couldn't be filled (even with expansions). </returns>
        private static CombinationWithGaps <T> CanCombinationFill <T>(IReadOnlyList <T> combination, IEnumerable <IReadOnlyList <bool> > combinationFilling, IReadOnlyList <bool> initialFilling, Interval toFill, CanExpandFillingDelegate <T> canExpand) where T : class, IFillable
        {
            //an interval is filled if all positions up to length occur once in the combinationFilling and no positions after length occur.
            //if there are gaps, positions next to the gaps may be queried whether they are capable of filling the gaps. A gap may be multiple positions wide.
            //a gap may not be filled by two partial expansions

            //these restricions are imposed for simplicity: perhaps later in general any gap may be filled by any position, but that doesn't seem required for now

            Contract.Requires(combination != null);
            Contract.RequiresForAll(combination, selection => toFill.Contains(new Interval(selection.Occupation)), "combination selected outside of interval to fill");

            Contract.Requires(!toFill.IsEmpty);
            Contract.Requires(toFill.StartInclusive);
            Contract.Requires(!toFill.EndInclusive);

            int           length = combination[0].Occupation.Count;
            Func <int, T> getOptionThatFilledIndex = positionIndex =>
            {
                int i = combinationFilling.IndexOf(filling => positionIndex >= filling.Count ? false : filling[positionIndex]);
                if (i == -1)
                {
                    //in case the leaf at the specified position index was flagged 'alreadyBound'
                    Contract.Assert(initialFilling[positionIndex]);
                    return(null);
                }
                return(combination[i]);
            };

            SortedList <int> filledPositionIndices = combinationFilling.ConcatIfNotNull(initialFilling)
                                                     .SelectManySorted(elementFilling => elementFilling.IndicesOf(_ => _))
                                                     .ToSortedList();                                                                                     //TODO: more lazily

            if (containsDoubles(filledPositionIndices))
            {
                return(null);
            }

            List <Gap> gaps = findGaps(filledPositionIndices, toFill).ToList();

            foreach (Gap gap in gaps)
            {
                T substitute = null;
                //check whether the element to the left can fill the gap
                if (gap.Start != 0)
                {
                    var optionThatFilled = getOptionThatFilledIndex(gap.Start - 1);
                    if (optionThatFilled != null && canExpand(optionThatFilled, gap))
                    {
                        continue;
                    }
                }
                //check whether the element to the right can fill the gap
                if (gap.End < length)
                {
                    Contract.Assert(substitute == null, "gap is being filled from both sides");                    //just place 'else' before 'if' above: but realize that this then makes filling from the left preferable
                    var optionThatFilled = getOptionThatFilledIndex(gap.End);
                    if (optionThatFilled != null && canExpand(optionThatFilled, gap))
                    {
                        continue;
                    }
                }

                //some gap could not be filled
                return(null);
            }

            //all gaps were filled
            return(new CombinationWithGaps <T>(combination, gaps));
        }
Пример #3
0
        /// <summary> Gets all combinations of options that fill the specified length, allowing some options to expand their occupation.
        /// The resulting selected combinations are ordered by cumulative expansion length. </summary>
        /// <param name="options"> The option to fill the interval with. Each option holds the information of which spots it fills. </param>
        /// <param name="length"> The number of spots to fill. </param>
        /// <param name="initialFilling"> Specifies which indices are pre-filled. Specify null to indicate no pre-filling. </param>
        public static IEnumerable <CombinationWithGaps <T> > AllFillingIntervalCombinations <T>(this IReadOnlyList <T> options, int length, CanExpandFillingDelegate <T> canExpand, IReadOnlyList <bool> initialFilling = null) where T : class, IFillable
        {
            Contract.Requires(options != null);
            Contract.Requires(0 <= length);
            Contract.Requires(canExpand != null);

            int elementsYieldedCount_DEBUG = 0;
            IEnumerable <CombinationWithGaps <T> > result;

            result = Enumerable.Range(1, options.Count)
                     .SelectMany(i => AllCombinations(options, i))
                     .LazilyAssertForAll(_ => elementsYieldedCount_DEBUG++)
                     .Select(combination => CanCombinationFill(combination, combination.Select(element => element.Occupation), initialFilling, new Interval(0, length), canExpand))
                     .Where(NotNull)
                     .OrderBy(_ => _);                                      //perhaps AllCombinations could be made such that this is guaranteed to be already in order? To improve performance

            EnsureSingleEnumerationDEBUG(ref result);
            return(result);
        }