/// <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(); }
/// <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()); }
/// <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); }
/// <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)); }