/**
  * Returns a new automaton that accepts strings representing decimal non-negative integers in the
  * given interval.
  *
  * @param min minimal value of interval
  * @param max maximal value of inverval (both end points are included in the interval)
  * @param digits if >0, use fixed number of digits (strings must be prefixed by 0's to obtain the
  *     right length) - otherwise, the number of digits is not fixed
  * @throws IllegalArgumentException if min>max or if numbers in the interval cannot be expressed
  *     with the given fixed number of digits
  */
 public static DefaultAutomaton makeInterval(int min, int max, int digits)
     throws IllegalArgumentException {
   DefaultAutomaton a = new DefaultAutomaton();
   String x = Integer.toString(min);
   String y = Integer.toString(max);
   if (min > max || (digits > 0 && y.length() > digits)) throw new IllegalArgumentException();
   int d;
   if (digits > 0) d = digits;
   else d = y.length();
   StringBuilder bx = new StringBuilder();
   for (int i = x.length(); i < d; i++) bx.append('0');
   bx.append(x);
   x = bx.toString();
   StringBuilder by = new StringBuilder();
   for (int i = y.length(); i < d; i++) by.append('0');
   by.append(y);
   y = by.toString();
   Collection<State> initials = new ArrayList<State>();
   a.initial = between(x, y, 0, initials, digits <= 0);
   if (digits <= 0) {
     ArrayList<StatePair> pairs = new ArrayList<StatePair>();
     for (State p : initials) if (a.initial != p) pairs.add(new StatePair(a.initial, p));
     addEpsilons(a, pairs);
     a.initial.addTransition(new Transition('0', a.initial));
     a.deterministic = false;
   } else a.deterministic = true;
   a.checkMinimizeAlways();
   return a;
 }
 /** Returns a new (deterministic) automaton with the empty language. */
 public static DefaultAutomaton makeEmpty() {
   DefaultAutomaton a = new DefaultAutomaton();
   State s = new State();
   a.initial = s;
   a.deterministic = true;
   return a;
 }
 /** Returns a new (deterministic) automaton that accepts all strings. */
 public static DefaultAutomaton makeAnyString() {
   DefaultAutomaton a = new DefaultAutomaton();
   State s = new State();
   a.initial = s;
   s.accept = true;
   s.transitions.add(new Transition(Character.MIN_VALUE, Character.MAX_VALUE, s));
   a.deterministic = true;
   return a;
 }
 /**
  * Returns a new (deterministic) automaton that accepts a single char whose value is in the given
  * interval (including both end points).
  */
 public static DefaultAutomaton makeCharRange(char min, char max) {
   if (min == max) return makeChar(min);
   DefaultAutomaton a = new DefaultAutomaton();
   State s1 = new State();
   State s2 = new State();
   a.initial = s1;
   s2.accept = true;
   if (min <= max) s1.transitions.add(new Transition(min, max, s2));
   a.deterministic = true;
   return a;
 }
 /** Returns a new (deterministic) automaton that accepts a single character in the given set. */
 public static DefaultAutomaton makeCharSet(String set) {
   if (set.length() == 1) return makeChar(set.charAt(0));
   DefaultAutomaton a = new DefaultAutomaton();
   State s1 = new State();
   State s2 = new State();
   a.initial = s1;
   s2.accept = true;
   for (int i = 0; i < set.length(); i++) s1.transitions.add(new Transition(set.charAt(i), s2));
   a.deterministic = true;
   a.reduce();
   return a;
 }
 /** Constructs deterministic automaton that matches strings that contain the given substring. */
 public static DefaultAutomaton makeStringMatcher(String s) {
   DefaultAutomaton a = new DefaultAutomaton();
   State[] states = new State[s.length() + 1];
   states[0] = a.initial;
   for (int i = 0; i < s.length(); i++) states[i + 1] = new State();
   State f = states[s.length()];
   f.accept = true;
   f.transitions.add(new Transition(Character.MIN_VALUE, Character.MAX_VALUE, f));
   for (int i = 0; i < s.length(); i++) {
     Set<Character> done = new HashSet<Character>();
     char c = s.charAt(i);
     states[i].transitions.add(new Transition(c, states[i + 1]));
     done.add(c);
     for (int j = i; j >= 1; j--) {
       char d = s.charAt(j - 1);
       if (!done.contains(d) && s.substring(0, j - 1).equals(s.substring(i - j + 1, i))) {
         states[i].transitions.add(new Transition(d, states[j]));
         done.add(d);
       }
     }
     char[] da = new char[done.size()];
     int h = 0;
     for (char w : done) da[h++] = w;
     Arrays.sort(da);
     int from = Character.MIN_VALUE;
     int k = 0;
     while (from <= Character.MAX_VALUE) {
       while (k < da.length && da[k] == from) {
         k++;
         from++;
       }
       if (from <= Character.MAX_VALUE) {
         int to = Character.MAX_VALUE;
         if (k < da.length) {
           to = da[k] - 1;
           k++;
         }
         states[i].transitions.add(new Transition((char) from, (char) to, states[0]));
         from = to + 2;
       }
     }
   }
   a.deterministic = true;
   return a;
 }
 /** Returns a new (deterministic) automaton that accepts a single character of the given value. */
 public static DefaultAutomaton makeChar(char c) {
   DefaultAutomaton a = new DefaultAutomaton();
   a.singleton = Character.toString(c);
   a.deterministic = true;
   return a;
 }
 /** Returns a new (deterministic) automaton that accepts only the empty string. */
 public static DefaultAutomaton makeEmptyString() {
   DefaultAutomaton a = new DefaultAutomaton();
   a.singleton = "";
   a.deterministic = true;
   return a;
 }
 /** Returns a new (deterministic) automaton that accepts the single given string. */
 public static DefaultAutomaton makeString(String s) {
   DefaultAutomaton a = new DefaultAutomaton();
   a.singleton = s;
   a.deterministic = true;
   return a;
 }