private static Result FindSplit(ArrayList dataArray, Interval bounds, int intervals, float optimum, ResultCache cache) { #if PERFCOUNTERS recursionCount++; #endif #if SCOREGRAPH bool topLevel = (bounds.Left == 0) && (bounds.Right == dataArray.Count); #endif // check if there is enough split points Debug.Assert(bounds.Right - bounds.Left >= intervals); // test the end of recursion (no splitting) if (intervals == 1) { Result result = new Result(); result.Intervals.Add(bounds); int count = 0; for (int i = bounds.Left; i < bounds.Right; i++) count += ((Data) dataArray[i]).Count; result.Cost = ResultPenalty(count, optimum); return result; } // test the end of recursion (exact splitting, no choice) if (intervals == bounds.Right - bounds.Left) { Result result = new Result(); result.Cost = 0.0f; for (int i = bounds.Left; i < bounds.Right; i++) { result.Intervals.Add(new Interval(i, i + 1)); result.Cost += ResultPenalty(((Data) dataArray[i]).Count, optimum); } return result; } // cache lookup { Result result = cache.GetResult(intervals, bounds); if (result != null) return result; } // count objects that must be in the left part int leftIntervals = intervals / 2; int leftSum = 0; for (int i = 0; i < leftIntervals; i++) leftSum += ((Data) dataArray[bounds.Left + i]).Count; // add some more intervals until optimal point is reached int bestSplit; int leftOptimalSum = (int) Math.Round(optimum * leftIntervals); for (bestSplit = bounds.Left + leftIntervals; bestSplit < bounds.Right - (intervals - leftIntervals); bestSplit++) { if (leftSum + ((Data) dataArray[bestSplit]).Count > leftOptimalSum) break; leftSum += ((Data) dataArray[bestSplit]).Count; } // start testing these split points (spreading to left and right) int leftSplit = bestSplit; int rightSplit = bestSplit + 1; bool leftStop = false; // there's always at least one solution bool rightStop = (rightSplit > bounds.Right - (intervals - leftIntervals)); // go right only if there is another possible split point // spread to both sides and search for better solutions Result bestResult = new Result(), leftTmpResult, rightTmpResult; bestResult.Cost = initPenalty; float leftLastScore = initPenalty, rightLastScore = initPenalty; int leftGrowCount = 0, rightGrowCount = 0; while (!leftStop || !rightStop) { if (!leftStop) { // find solution for left and right part leftTmpResult = FindSplit(dataArray, new Interval(bounds.Left, leftSplit), leftIntervals, optimum, cache); rightTmpResult = FindSplit(dataArray, new Interval(leftSplit, bounds.Right), intervals - leftIntervals, optimum, cache); // sum the costs of partial results float sum = leftTmpResult.Cost + rightTmpResult.Cost; #if SCOREGRAPH if (topLevel) { // top level in the recursion Trace.WriteLine(String.Format("{0};{1}", leftSplit, sum)); } #endif // first solution is propagated to the right side if (rightLastScore == initPenalty) { // save to right last value rightLastScore = sum; } // compare this result to what we have so far if (sum < bestResult.Cost) { // merge two partial solution to one bestResult.Merge(leftTmpResult, rightTmpResult); // absolute stop criterium (perfect result) if (sum == 0.0f) break; } #if SCOREGRAPH if (!topLevel) { #endif // check stop criterium (result penalty is too big) if (sum > stopLimit * bestResult.Cost) { // stop spreading to the left leftStop = true; } // check stop criterium (result penalty is constantly growing, so there is // probably no hope of getting better result than we have...) if (sum < leftLastScore) { // not growing, reset the counter leftGrowCount = 0; } else { // growing, increase leftGrowCount++; if (leftGrowCount == growLimit) leftStop = true; } leftLastScore = sum; #if SCOREGRAPH } #endif // check if there is possibility to spread further to the left if (leftSplit <= bounds.Left + leftIntervals) { // stop testing spreading to the left leftStop = true; } else { // shift the left split to the next position leftSplit--; } } if (!rightStop) { // find solution for left and right part leftTmpResult = FindSplit(dataArray, new Interval(bounds.Left, rightSplit), leftIntervals, optimum, cache); rightTmpResult = FindSplit(dataArray, new Interval(rightSplit, bounds.Right), intervals - leftIntervals, optimum, cache); // sum the costs of partial results float sum = leftTmpResult.Cost + rightTmpResult.Cost; #if SCOREGRAPH if (topLevel) { // top level in the recursion Trace.WriteLine(String.Format("{0};{1}", rightSplit, sum)); } #endif // compare this result to what we have so far if (sum < bestResult.Cost) { // merge two partial solution to one bestResult.Merge(leftTmpResult, rightTmpResult); } #if SCOREGRAPH if (!topLevel) { #endif // check stop criterium (result penalty is too big) if (sum > stopLimit * bestResult.Cost) { // stop testing spreading to the right rightStop = true; } // check stop criterium (result penalty is constantly growing, so there is // probably no hope of getting better result than we have...) if (sum < rightLastScore) { // not growing, reset the counter rightGrowCount = 0; } else { // growing, increase rightGrowCount++; if (rightGrowCount == growLimit) rightStop = true; } rightLastScore = sum; #if SCOREGRAPH } #endif // check if there is possibility to spread further to the right if (rightSplit >= bounds.Right - (intervals - leftIntervals)) { // stop testing spreading to the right rightStop = true; } else { // shift the right split to the next position rightSplit++; } } } // check the solution Debug.Assert(bestResult.Cost < initPenalty); // add the best result to cache cache.SetResult(intervals, bounds, bestResult); // ...and return it return bestResult; }
static void CheckAny(Result result, IList<INamedPredicate> predicates, string pathHere, IEnumerable<object> container) { var successCount = 0; var subResult = new Result(); foreach (var route in container) { var cleanResult = new Result {Target = route}; var localPath = pathHere; ApplyPredicatesToSimpleTerminal(localPath, cleanResult, predicates); subResult.Merge(cleanResult); if (cleanResult.Success) successCount++; } if (successCount < 1) { result.Merge(subResult); } }
static void CheckAnySubpaths(Result result, IList<INamedPredicate> predicates, List<ChainStep> remainingChain, string pathHere, IEnumerable<object> container) { var successCount = 0; var subResult = new Result(); foreach (var route in container) { var cleanResult = new Result {Target = route}; var localPath = pathHere; WalkObjectTree(remainingChain, ref localPath, cleanResult, predicates); subResult.Merge(cleanResult); if (cleanResult.Success) successCount++; } if (successCount < 1) { result.Merge(subResult); } }
static void CheckAllSubpaths(Result result, IList<INamedPredicate> predicates, string pathHere, List<ChainStep> remainingChain, IEnumerable<object> container) { var i = 0; foreach (var route in container) { var cleanResult = new Result {Target = route}; var localPath = pathHere + "[" + i + "]"; WalkObjectTree(remainingChain, ref localPath, cleanResult, predicates); result.Merge(cleanResult); i++; } }
static void CheckAll(Result result, IList<INamedPredicate> predicates, string pathHere, IEnumerable<object> container) { var i = 0; foreach (var route in container) { var cleanResult = new Result {Target = route}; var localPath = pathHere + "[" + i + "]"; ApplyPredicatesToSimpleTerminal(localPath, cleanResult, predicates); result.Merge(cleanResult); i++; } }
static void ApplyPredicatesToTerminalEnumerable(string path, Result result, IList<INamedPredicate> predicates, ChainStep step) { string stepMsg; var container = FilterWithNamedPredicate((IEnumerable)result.Target, step, out stepMsg); path += stepMsg; object target; switch (step.ListAssertionType) { case ListAssertion.Simple: ApplyPredicatesToSimpleTerminal(path, result, predicates); break; case ListAssertion.Single: if (!StepSingle(result, path, container, out target)) return; var singleResult = new Result{Target = target}; ApplyPredicatesToSimpleTerminal(path, singleResult, predicates); result.Merge(singleResult); break; case ListAssertion.Index: if (!StepIndex(result, step, container, out target, ref path)) return; var indexResult = new Result{Target = target}; ApplyPredicatesToSimpleTerminal(path, indexResult, predicates); result.Merge(indexResult); break; case ListAssertion.All: CheckAll(result, predicates, path, container); return; case ListAssertion.Any: CheckAny(result, predicates, path, container); return; default: throw new Exception("Unexpected list assertion type"); } }
static Result AllWithSubject(object subject, params Func<dynamic, Result>[] cases) { var result = new Result(); foreach (var check in cases) { result.Merge(check(That(subject))); } return result; }
static Result AllWithStems(IEnumerable<Check> subjects, params Func<dynamic, Result>[] cases) { var result = new Result(); foreach (var subject in subjects) { foreach (var check in cases) { result.Merge(check(subject)); } } return result; }