/*
* Copyright (C) 2012-2013 University of Freiburg
*
* This file is part of SMTInterpol.
*
* SMTInterpol is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* SMTInterpol is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with SMTInterpol. If not, see <http://www.gnu.org/licenses/>.
*/
package de.uni_freiburg.informatik.ultimate.smtinterpol.delta;
import java.io.IOException;
import java.util.ArrayDeque;
import java.util.List;
public class BinSearch<E> {
public static interface Driver<E> {
public Boolean prepare(List<E> sublist);
public void failure(List<E> sublist);
public void success(List<E> sublist);
}
private static class IntPair {
final int mFirst;
final int mSecond;
final boolean mBuddy;
public IntPair(int f, int s) {
this(f, s, false);
}
public IntPair(int f, int s, boolean buddy) {
mFirst = f;
mSecond = s;
mBuddy = buddy;
}
}
private final List<E> mList;
private final Driver<E> mDriver;
private final ArrayDeque<IntPair> mTodo;
public BinSearch(List<E> list, Driver<E> driver) {
mList = list;
mDriver = driver;
mTodo = new ArrayDeque<IntPair>();
}
/**
* Split a region into two regions. The split pushes first the right part
* of the split onto the todo stack and then creates the left part and
* pushes it. The left part has the buddy flag set since we assume we
* already know that the interval we are splitting yields a test failure.
* Thus, if the left part is a success, applying the right part will
* recreate the whole interval we are currently splitting. Assuming
* deterministic behaviour of our system under test, we will get a failure
* for the right part.
* @param first The start of the interval.
* @param second The end of the interval.
*/
private void split(int first, int second) {
// Split into two new sublists
final int mid = first / 2 + second / 2
+ (first & second & 1);
if (mid != first) {
mTodo.push(new IntPair(mid, second));
mTodo.push(new IntPair(first, mid, true));
}
}
public boolean run(Minimizer tester)
throws IOException, InterruptedException {
if (mList.isEmpty()) {
return false;
}
boolean result = false;
mTodo.add(new IntPair(0, mList.size()));
while (!mTodo.isEmpty()) {
final IntPair p = mTodo.poll();
final List<E> sublist = mList.subList(p.mFirst, p.mSecond);
if (sublist.isEmpty()) {
continue;
}
final Boolean seen = mDriver.prepare(sublist);
final boolean success = (seen == null ? tester.test() : seen);
if (success) {
if (seen == null) {
mDriver.success(sublist);
}
result = true;
if (p.mBuddy) {
// We already know that the buddy cannot succeed.
// Split the buddy to prevent a meaningless test
final IntPair buddy = mTodo.poll();
split(buddy.mFirst, buddy.mSecond);
}
} else {
if (seen == null) {
mDriver.failure(sublist);
}
split(p.mFirst, p.mSecond);
}
}
return result;
}
}