// Path: src/main/java/net/amygdalum/stringsearchalgorithms/search/StringFinder.java
// public interface StringFinder {
// 
// 	/**
// 	 * @return the next match in the given document, the order of the matches is determined by the specific implementation
// 	 */
// 	StringMatch findNext();
// 	/**
// 	 * @return all matches in the given document, dependent on the specific implementation some matches may be skipped
// 	 */
// 	List<StringMatch> findAll();
// 	/**
// 	 * skips the document to a specific position (ignoring any match before this position)
// 	 * @param pos the new position to start with
// 	 */
// 	void skipTo(long pos);
// }
// 
// Path: src/main/java/net/amygdalum/stringsearchalgorithms/search/StringFinderOption.java
// public interface StringFinderOption {
// 
// }


import static java.util.Arrays.asList;
import java.util.ArrayList;
import java.util.IdentityHashMap;
import java.util.List;
import java.util.Map;
import org.junit.rules.TestRule;
import org.junit.runner.Description;
import org.junit.runners.model.Statement;
import net.amygdalum.stringsearchalgorithms.search.SearchFor;
import net.amygdalum.stringsearchalgorithms.search.StringFinder;
import net.amygdalum.stringsearchalgorithms.search.StringFinderOption;
import net.amygdalum.util.io.CharProvider;
import net.amygdalum.util.io.StringCharProvider;

	private String extractPattern(final Description description) throws AssertionError {
		SearchFor searchFor = description.getAnnotation(SearchFor.class);
		if (searchFor == null) {
			throw new AssertionError("expected @SearchFor annotation");
		}
		String[] pattern = searchFor.value();
		if (pattern.length != 1) {
			throw new AssertionError("expected exactly one pattern");
		}
		return pattern[0];
	}

	private String computeMessage(Map<StringSearchAlgorithm, String> failures) {
		StringBuilder buffer = new StringBuilder();
		for (Map.Entry<StringSearchAlgorithm, String> entry : failures.entrySet()) {
			String name = algorithm.getClass().getSimpleName();
			while (algorithm instanceof StringSearchAlgorithmWrapper) {
				algorithm = ((StringSearchAlgorithmWrapper) algorithm).getAlgorithm();
				name = name + ' ' + algorithm.getClass().getSimpleName();
			}
			buffer.append("in algorithm <").append(name).append(">: ").append(entry.getValue()).append("\n");
		}
		return buffer.toString();
	}

	public StringFinder createSearcher(String chars, StringFinderOption... options) {
		return createSearcher(new StringCharProvider(chars, 0), options);
	}
	
// Path: src/main/java/net/amygdalum/stringsearchalgorithms/search/StringFinder.java
// public interface StringFinder {
// 
// 	/**
// 	 * @return the next match in the given document, the order of the matches is determined by the specific implementation
// 	 */
// 	StringMatch findNext();
// 	/**
// 	 * @return all matches in the given document, dependent on the specific implementation some matches may be skipped
// 	 */
// 	List<StringMatch> findAll();
// 	/**
// 	 * skips the document to a specific position (ignoring any match before this position)
// 	 * @param pos the new position to start with
// 	 */
// 	void skipTo(long pos);
// }
// 
// Path: src/main/java/net/amygdalum/stringsearchalgorithms/search/StringFinderOption.java
// public interface StringFinderOption {
// 
// }


// Path: src/test/java/net/amygdalum/stringsearchalgorithms/search/chars/StringSearchRule.java
import static java.util.Arrays.asList;
import java.util.ArrayList;
import java.util.IdentityHashMap;
import java.util.List;
import java.util.Map;
import org.junit.rules.TestRule;
import org.junit.runner.Description;
import org.junit.runners.model.Statement;
import net.amygdalum.stringsearchalgorithms.search.SearchFor;
import net.amygdalum.stringsearchalgorithms.search.StringFinder;
import net.amygdalum.stringsearchalgorithms.search.StringFinderOption;
import net.amygdalum.util.io.CharProvider;
import net.amygdalum.util.io.StringCharProvider;


	private String extractPattern(final Description description) throws AssertionError {
		SearchFor searchFor = description.getAnnotation(SearchFor.class);
		if (searchFor == null) {
			throw new AssertionError("expected @SearchFor annotation");
		}
		String[] pattern = searchFor.value();
		if (pattern.length != 1) {
			throw new AssertionError("expected exactly one pattern");
		}
		return pattern[0];
	}

	private String computeMessage(Map<StringSearchAlgorithm, String> failures) {
		StringBuilder buffer = new StringBuilder();
		for (Map.Entry<StringSearchAlgorithm, String> entry : failures.entrySet()) {
			String name = algorithm.getClass().getSimpleName();
			while (algorithm instanceof StringSearchAlgorithmWrapper) {
				algorithm = ((StringSearchAlgorithmWrapper) algorithm).getAlgorithm();
				name = name + ' ' + algorithm.getClass().getSimpleName();
			}
			buffer.append("in algorithm <").append(name).append(">: ").append(entry.getValue()).append("\n");
		}
		return buffer.toString();
	}

	public StringFinder createSearcher(String chars, StringFinderOption... options) {
		return createSearcher(new StringCharProvider(chars, 0), options);
	}
	
	public StringFinder createSearcher(CharProvider chars, StringFinderOption... options) {