예제 #1
0
        /// <summary> Creates a collection of indices pointing in the specified collection. </summary>
        /// <param name="collection"> The collection the resulting indices will point in. </param>
        /// <param name="indices"> The indices to point at. Must be in range. </param>
        public static Index <TElement>[] Range(IReadOnlyList <TElement> collection, params int[] indices)
        {
            Contract.Requires(collection != null);
            Contract.Requires(indices != null);
            Contract.RequiresForAll(indices, i => 0 <= i && i < collection.Count);

            return(indices.Map(i => new Index <TElement>(i, collection)));
        }
        public ConcatenatedReadOnlyObservableCollection(params ObservableCollection <T>[] collections)
        {
            Contract.Requires(collections != null);
            Contract.RequiresForAll(collections, Global.NotNull);

            this.collections = collections;
            foreach (var collection in collections)
            {
                collection.CollectionChanged += onCollectionCollectionChanged;
            }
            this.recomputeCount();
        }
예제 #3
0
        /// <summary> Gets whether the specified combination of occupations fills the entire specified length. Is public only to serve debugging purposes. </summary>
        public static bool DoesCombinationFill(IEnumerable <IReadOnlyList <bool> > combination, Interval toFill)
        {
            Contract.Requires(!toFill.IsEmpty);
            Contract.Requires(toFill.StartInclusive);
            Contract.Requires(!toFill.EndInclusive);
            Contract.Requires(combination != null);
            Contract.RequiresForAll(combination, NotNull);
            EnsureSingleEnumerationDEBUG(ref combination);
            Contract.Requires(combination.Any(justAny => true));
            Contract.Requires(combination.Select(combi => combi.Count).AreEqual());

            //Contract.LazilyRequires(ref combination, occupation => occupation.Count == toFill.Length);//assumed the combinations are already shifted to start at the beginning of the specified interval

            //an interval is filled if all bits up to length occur once in the combinations and no bits after length occur.
            //that is the same as counting the bits and that they must equal length and that the indices of the bits are unique

            BitArray filled = new BitArray(toFill.Length);

            foreach (var occupation in combination)
            {
                Contract.Requires(occupation.Count >= toFill.Length);

                for (int i = 0; i < filled.Length; i++)
                {
                    if (filled[i])
                    {
                        if (occupation[i + toFill.Start])
                        {
                            return(false);                            // bit is doubly filled
                        }
                    }
                    else
                    {
                        filled[i] |= occupation[i + toFill.Start];
                    }
                }
            }

            return(filled.IsFull());
        }
예제 #4
0
        /// <summary> Gets all combinations of options that fill the specified length. </summary>
        /// <param name="options"> The option to fill the interval with. Each option holds the information of which spots it fills (from 0 to end.Length). </param>
        /// <param name="toFill"> The interval to fill. </param>
        /// <param name="initialFilling"> Specifies which indices are pre-filled. Specify null to indicate no pre-filling. </param>
        public static IEnumerable <T[]> AllFillingIntervalCombinations <T>(this IReadOnlyList <T> options, Interval toFill, IReadOnlyList <bool> initialFilling = null) where T : IFillable
        {
            //TODO: redo this method, it is terribly inefficient (and the other overload as well)
            //also immediately remove options that have overlap with the initial filling: or change design to ignore overlap with initial filling. Should that overlap then be full?
            Contract.Requires(!toFill.IsEmpty);
            Contract.Requires(toFill.StartInclusive);
            Contract.Requires(!toFill.EndInclusive);
            Contract.Requires(toFill.Start >= 0);
            Contract.Requires(options != null);
            Contract.RequiresForAll(options, option => option != null);
            Contract.LazilyAssertForAll(options, option => option.Occupation.Count == toFill.End);

            if (initialFilling != null && initialFilling.All())
            {
                return(EmptyCollection <T> .Array.ToSingleton());
            }

            var result = Enumerable.Range(1, options.Count)
                         .SelectMany(i => AllCombinations(options, i))
                         .Where(combination => DoesCombinationFill(combination.Select(element => element.Occupation).ConcatIfNotNull(initialFilling), toFill));

            EnsureSingleEnumerationDEBUG(ref result);
            return(result);
        }
예제 #5
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));
        }