/** Returns an expression that denotes a specified subvector. */
 public STPExpr extract(int high_bit_no, int low_bit_no, int encodingSize) {
   STPExpr cached = cachedExtract.get(high_bit_no, low_bit_no);
   if (cached != null) return cached;
   ExtractExpr exp = new ExtractExpr(getSolver(), this, high_bit_no, low_bit_no, encodingSize);
   cachedExtract.put(high_bit_no, low_bit_no, exp);
   return exp;
 }
  private Regexp internalGetBoundedRegexp(Grammar g, String startSymbol, int bound) {
    if (regexpCache.containsKeys(startSymbol, bound)) return regexpCache.get(startSymbol, bound);

    // removing epsilons may not succeed for start symbol. We check if that's the case.
    boolean canGenerateEmptyString = g.containsEpsilonProduction(startSymbol);

    List<Regexp> x = new ArrayList<Regexp>();
    for (GrammarProduction prod : g.getRule(startSymbol).getProductions()) {
      List<GrammarProductionElement> elems = prod.getElements();
      if (canGenerateEmptyString
          || elems.size()
              <= bound) { // uses the fact that every symbol (other than start) produces at least
                          // one terminal
        List<List<Integer>> distros = new ArrayList<List<Integer>>(distributions(bound, elems));

        distrosLoop:
        for (int j = 0; j < distros.size(); j++) {
          List<Integer> distro = distros.get(j);
          Regexp[] exps = new Regexp[distro.size()];
          for (int i = 0; i < elems.size(); i++) {
            GrammarProductionElement elem = elems.get(i);
            int sizeForElem = distro.get(i);
            if (terms_are_single_char) {
              if (sizeForElem > 1
                  && (elem.getKind() == GrammarElementKind.GTERMINAL
                      || elem.getKind() == GrammarElementKind.GSPECIAL)) {
                continue
                    distrosLoop; // no way you can generate a string longer than 1 from a terminal
              }
              if (sizeForElem == 1 && elem.getKind() == GrammarElementKind.GTERMINAL) {
                TerminalElement te = (TerminalElement) elem;
                exps[i] = HampiConstraints.constRegexp(te.getNameNoQuotes());
              } else if (sizeForElem == 1 && elem.getKind() == GrammarElementKind.GSPECIAL) {
                SpecialElement spec = (SpecialElement) elem;
                exps[i] = HampiConstraints.constRegexp(spec.getNameNoDelimiters());
              } else if (elem.getKind() == GrammarElementKind.GNONTERMINAL) {
                NonterminalElement nt = (NonterminalElement) elem;
                if (bounds.containsKey(nt)
                    && bounds.get(nt)
                        < sizeForElem) { // cannot generate a string longer than the upper bound on
                                         // all strings generatable from the nonterminal
                  continue distrosLoop;
                }
                Regexp subRegexp = internalGetBoundedRegexp(g, nt.getName(), sizeForElem);
                if (subRegexp != null) {
                  exps[i] = subRegexp;
                } else {
                  continue distrosLoop;
                }
              } else throw new IllegalStateException("expected a nonterminal or special" + elem);
            } else {
              if (elem.getKind() == GrammarElementKind.GSPECIAL)
                throw new UnsupportedOperationException("not implemented yet");
              if (elem.getKind() == GrammarElementKind.GTERMINAL) {
                TerminalElement term = (TerminalElement) elem;
                if (term.getNameNoQuotes().length() != sizeForElem) {
                  continue distrosLoop; // no way you can generate a string this long
                } else {
                  exps[i] = HampiConstraints.constRegexp(term.getNameNoQuotes());
                }
              } else if (elem.getKind() == GrammarElementKind.GNONTERMINAL) {
                NonterminalElement nt = (NonterminalElement) elem;
                if (bounds.containsKey(nt)
                    && bounds.get(nt)
                        < sizeForElem) { // cannot generate a string longer than the upper bound on
                                         // all strings generatable from the nonterminal
                  continue distrosLoop;
                }
                Regexp subRegexp = internalGetBoundedRegexp(g, nt.getName(), sizeForElem);
                if (subRegexp != null) {
                  exps[i] = subRegexp;
                } else {
                  continue distrosLoop;
                }
              } else throw new IllegalStateException("expected a nonterminal or special" + elem);
            }
          }
          Regexp e;
          if (exps.length == 1) {
            e = exps[0];
          } else {
            e = HampiConstraints.concatRegexp(exps);
          }
          if (!x.contains(e)) {
            x.add(e);
          }
        }
      }
    }

    Regexp result;
    if (x.isEmpty() && !canGenerateEmptyString) {
      result = null;
    } else if (x.isEmpty() && canGenerateEmptyString) {
      Hampi h = new Hampi();
      result = h.constRegexp("");
    } else if (x.size() == 1) {
      result = x.get(0);
    } else {
      Hampi h = new Hampi();
      result = h.orRegexp(x.toArray(new Regexp[x.size()]));
    }
    regexpCache.put(startSymbol, bound, result);
    return result;
  }