// Path: src/main/java/net/amygdalum/stringsearchalgorithms/search/AbstractStringFinder.java
// public abstract class AbstractStringFinder implements StringFinder {
// 
// 	private boolean nonOverlap;
// 	
// 	public AbstractStringFinder(StringFinderOption... options) {
// 		nonOverlap = NON_OVERLAP.in(options);
// 	}
// 	
// 	public abstract StringMatch findNext();
// 
// 	public abstract void skipTo(long pos);
// 
// 	@Override
// 	public List<StringMatch> findAll() {
// 		List<StringMatch> matches = new ArrayList<>();
// 		long last = -1;
// 		while (true) {
// 			StringMatch match = findNext();
// 			if (match == null) {
// 				return matches;
// 			} else {
// 				matches.add(match);
// 				if (nonOverlap && match.end() > last) {
// 					last = match.end(); 
// 					skipTo(last);
// 				}
// 			}
// 		}
// 	}
// 	
// }
// 
// 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/main/java/net/amygdalum/stringsearchalgorithms/search/StringMatch.java
// public class StringMatch implements Comparable<StringMatch> {
// 
// 	private long start;
// 	private long end;
// 	private String text;
// 
// 	/**
// 	 * @param start the start of the match
// 	 * @param end the end of the match
// 	 * @param match the matched string
// 	 */
// 	public StringMatch(long start, long end, String match) {
// 		this.start = start;
// 		this.end = end;
// 		this.text = match;
// 	}
// 
// 	public long start() {
// 		return start;
// 	}
// 
// 	public long end() {
// 		return end;
// 	}
// 
// 	public String text() {
// 		return text;
// 	}
// 
// 	public boolean isEmpty() {
// 		return start == end;
// 	}
// 
// 	public int length() {
// 		return text.length();
// 	}
// 
// 	@Override
// 	public int compareTo(StringMatch o) {
// 		long start = this.start - o.start;
// 		if (start != 0) {
// 			return start < 0 ? -1 : 1;
// 		}
// 		long end = this.end - o.end;
// 		if (end != 0) {
// 			return end < 0 ? -1 : 1;
// 		}
// 		return 0;
// 	}
// 
// 	@Override
// 	public String toString() {
// 		return start + ":" + end + "(" + text + ")";
// 	}
// 
// 	@Override
// 	public int hashCode() {
// 		return 31 + (int) end * 13 + (int) start * 7 + text.hashCode() * 3;
// 	}
// 
// 	@Override
// 	public boolean equals(Object obj) {
// 		if (this == obj) {
// 			return true;
// 		}
// 		if (obj == null) {
// 			return false;
// 		}
// 		if (getClass() != obj.getClass()) {
// 			return false;
// 		}
// 		StringMatch that = (StringMatch) obj;
// 		return this.start == that.start
// 			&& this.end == that.end
// 			&& this.text.equals(that.text);
// 	}
// 
// }


import static net.amygdalum.util.text.CharUtils.computeMaxChar;
import static net.amygdalum.util.text.CharUtils.computeMinChar;
import net.amygdalum.stringsearchalgorithms.search.AbstractStringFinder;
import net.amygdalum.stringsearchalgorithms.search.StringFinder;
import net.amygdalum.stringsearchalgorithms.search.StringFinderOption;
import net.amygdalum.stringsearchalgorithms.search.StringMatch;
import net.amygdalum.util.io.CharProvider;
import net.amygdalum.util.map.CharIntMap;
		} else {
			return new SmartShift(pattern);
		}
	}

	public static boolean isCompactRange(char[] pattern) {
		char minChar = computeMinChar(pattern);
		char maxChar = computeMaxChar(pattern);
		return maxChar - minChar < 256 || maxChar - minChar < pattern.length * 2;
	}

	@Override
	public int getPatternLength() {
		return patternLength;
	}

	@Override
	public StringFinder createFinder(CharProvider chars, StringFinderOption... options) {
		return new Finder(chars, options);
	}

	@Override
	public String toString() {
		return getClass().getSimpleName();
	}

	private class Finder extends AbstractStringFinder {

		private CharProvider chars;

// Path: src/main/java/net/amygdalum/stringsearchalgorithms/search/AbstractStringFinder.java
// public abstract class AbstractStringFinder implements StringFinder {
// 
// 	private boolean nonOverlap;
// 	
// 	public AbstractStringFinder(StringFinderOption... options) {
// 		nonOverlap = NON_OVERLAP.in(options);
// 	}
// 	
// 	public abstract StringMatch findNext();
// 
// 	public abstract void skipTo(long pos);
// 
// 	@Override
// 	public List<StringMatch> findAll() {
// 		List<StringMatch> matches = new ArrayList<>();
// 		long last = -1;
// 		while (true) {
// 			StringMatch match = findNext();
// 			if (match == null) {
// 				return matches;
// 			} else {
// 				matches.add(match);
// 				if (nonOverlap && match.end() > last) {
// 					last = match.end(); 
// 					skipTo(last);
// 				}
// 			}
// 		}
// 	}
// 	
// }
// 
// 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/main/java/net/amygdalum/stringsearchalgorithms/search/StringMatch.java
// public class StringMatch implements Comparable<StringMatch> {
// 
// 	private long start;
// 	private long end;
// 	private String text;
// 
// 	/**
// 	 * @param start the start of the match
// 	 * @param end the end of the match
// 	 * @param match the matched string
// 	 */
// 	public StringMatch(long start, long end, String match) {
// 		this.start = start;
// 		this.end = end;
// 		this.text = match;
// 	}
// 
// 	public long start() {
// 		return start;
// 	}
// 
// 	public long end() {
// 		return end;
// 	}
// 
// 	public String text() {
// 		return text;
// 	}
// 
// 	public boolean isEmpty() {
// 		return start == end;
// 	}
// 
// 	public int length() {
// 		return text.length();
// 	}
// 
// 	@Override
// 	public int compareTo(StringMatch o) {
// 		long start = this.start - o.start;
// 		if (start != 0) {
// 			return start < 0 ? -1 : 1;
// 		}
// 		long end = this.end - o.end;
// 		if (end != 0) {
// 			return end < 0 ? -1 : 1;
// 		}
// 		return 0;
// 	}
// 
// 	@Override
// 	public String toString() {
// 		return start + ":" + end + "(" + text + ")";
// 	}
// 
// 	@Override
// 	public int hashCode() {
// 		return 31 + (int) end * 13 + (int) start * 7 + text.hashCode() * 3;
// 	}
// 
// 	@Override
// 	public boolean equals(Object obj) {
// 		if (this == obj) {
// 			return true;
// 		}
// 		if (obj == null) {
// 			return false;
// 		}
// 		if (getClass() != obj.getClass()) {
// 			return false;
// 		}
// 		StringMatch that = (StringMatch) obj;
// 		return this.start == that.start
// 			&& this.end == that.end
// 			&& this.text.equals(that.text);
// 	}
// 
// }


// Path: src/main/java/net/amygdalum/stringsearchalgorithms/search/chars/Sunday.java
import static net.amygdalum.util.text.CharUtils.computeMaxChar;
import static net.amygdalum.util.text.CharUtils.computeMinChar;
import net.amygdalum.stringsearchalgorithms.search.AbstractStringFinder;
import net.amygdalum.stringsearchalgorithms.search.StringFinder;
import net.amygdalum.stringsearchalgorithms.search.StringFinderOption;
import net.amygdalum.stringsearchalgorithms.search.StringMatch;
import net.amygdalum.util.io.CharProvider;
import net.amygdalum.util.map.CharIntMap;

		} else {
			return new SmartShift(pattern);
		}
	}

	public static boolean isCompactRange(char[] pattern) {
		char minChar = computeMinChar(pattern);
		char maxChar = computeMaxChar(pattern);
		return maxChar - minChar < 256 || maxChar - minChar < pattern.length * 2;
	}

	@Override
	public int getPatternLength() {
		return patternLength;
	}

	@Override
	public StringFinder createFinder(CharProvider chars, StringFinderOption... options) {
		return new Finder(chars, options);
	}

	@Override
	public String toString() {
		return getClass().getSimpleName();
	}

	private class Finder extends AbstractStringFinder {

		private CharProvider chars;

		public Finder(CharProvider chars, StringFinderOption... options) {