Exemplo n.º 1
0
        /// <summary>
        ///     Given a set of values and a function that returns true when given this set, will efficiently remove items from
        ///     this set which are not essential for making the function return true. The relative order of items is
        ///     preserved. This method cannot generally guarantee that the result is optimal, but for some types of functions
        ///     the result will be guaranteed optimal.</summary>
        /// <typeparam name="T">
        ///     Type of the values in the set.</typeparam>
        /// <param name="items">
        ///     The set of items to reduce.</param>
        /// <param name="test">
        ///     The function that examines the set. Must always return the same value for the same set.</param>
        /// <param name="breadthFirst">
        ///     A value selecting a breadth-first or a depth-first approach. Depth-first is best at quickly locating a single
        ///     value which will be present in the final required set. Breadth-first is best at quickly placing a lower bound
        ///     on the total number of individual items in the required set.</param>
        /// <returns>
        ///     A hopefully smaller set of values that still causes the function to return true.</returns>
        public static IEnumerable <T> ReduceRequiredSet <T>(IEnumerable <T> items, Func <ReduceRequiredSetState <T>, bool> test, bool breadthFirst = false)
        {
            var state = new ReduceRequiredSetStateInternal <T>(items);

            while (state.AnyPartitions)
            {
                var rangeToSplit = breadthFirst ? state.LargestRange : state.SmallestRange;
                int mid          = (rangeToSplit.Item1 + rangeToSplit.Item2) / 2;
                var split1       = new Range(rangeToSplit.Item1, mid);
                var split2       = new Range(mid + 1, rangeToSplit.Item2);

                state.ApplyTemporarySplit(rangeToSplit, split1);
                if (test(state))
                {
                    state.RemoveRange(rangeToSplit);
                    state.AddRange(split1);
                    continue;
                }
                state.ApplyTemporarySplit(rangeToSplit, split2);
                if (test(state))
                {
                    state.RemoveRange(rangeToSplit);
                    state.AddRange(split2);
                    continue;
                }
                state.ResetTemporarySplit();
                state.RemoveRange(rangeToSplit);
                state.AddRange(split1);
                state.AddRange(split2);
            }

            state.ResetTemporarySplit();
            return(state.SetToTest);
        }
Exemplo n.º 2
0
        /// <summary>
        ///     Given a set of values and a function that returns true when given this set, will efficiently remove items from
        ///     this set which are not essential for making the function return true. The relative order of items is
        ///     preserved. This method cannot generally guarantee that the result is optimal, but for some types of functions
        ///     the result will be guaranteed optimal.</summary>
        /// <typeparam name="T">
        ///     Type of the values in the set.</typeparam>
        /// <param name="items">
        ///     The set of items to reduce.</param>
        /// <param name="test">
        ///     The function that examines the set. Must always return the same value for the same set.</param>
        /// <param name="breadthFirst">
        ///     A value selecting a breadth-first or a depth-first approach. Depth-first is best at quickly locating a single
        ///     value which will be present in the final required set. Breadth-first is best at quickly placing a lower bound
        ///     on the total number of individual items in the required set.</param>
        /// <param name="skipConsistencyTest">
        ///     When the function is particularly slow, you might want to set this to true to disable calls which are not
        ///     required to reduce the set and are only there to ensure that the function behaves consistently.</param>
        /// <returns>
        ///     A hopefully smaller set of values that still causes the function to return true.</returns>
        public static IEnumerable <T> ReduceRequiredSet <T>(IEnumerable <T> items, Func <ReduceRequiredSetState <T>, bool> test, bool breadthFirst = false, bool skipConsistencyTest = false)
        {
            var state = new ReduceRequiredSetStateInternal <T>(items);

            if (!skipConsistencyTest)
            {
                if (!test(state))
                {
                    throw new Exception("The function does not return true for the original set.");
                }
            }

            while (state.AnyPartitions)
            {
                if (!skipConsistencyTest)
                {
                    if (!test(state))
                    {
                        throw new Exception("The function is not consistently returning the same value for the same set, or there is an internal error in this algorithm.");
                    }
                }

                var rangeToSplit = breadthFirst ? state.LargestRange : state.SmallestRange;
                int mid          = (rangeToSplit.from + rangeToSplit.to) / 2;
                var split1       = (rangeToSplit.from, to : mid);
                var split2       = (from : mid + 1, rangeToSplit.to);

                state.ApplyTemporarySplit(rangeToSplit, split1);
                if (test(state))
                {
                    state.SolidifyTemporarySplit();
                    continue;
                }
                state.ApplyTemporarySplit(rangeToSplit, split2);
                if (test(state))
                {
                    state.SolidifyTemporarySplit();
                    continue;
                }
                state.ResetTemporarySplit();
                state.RemoveRange(rangeToSplit);
                state.AddRange(split1);
                state.AddRange(split2);
            }

            state.ResetTemporarySplit();
            return(state.SetToTest);
        }