    private boolean compute(Object left, Object right) {
        left = reduce(left);
        right = reduce(right);
        if (left instanceof InitialContext) {
            ((InitialContext) left).reset();
        }
        if (right instanceof InitialContext) {
            ((InitialContext) right).reset();
        }
        if (left instanceof Iterator && right instanceof Iterator) {
            return findMatch((Iterator) left, (Iterator) right);
        }
        if (left instanceof Iterator) {
            return containsMatch((Iterator) left, right);
        }
        if (right instanceof Iterator) {
            return containsMatch((Iterator) right, left);
        }
        double ld = InfoSetUtil.doubleValue(left);
        if (Double.isNaN(ld)) {
            return false;
        }
        double rd = InfoSetUtil.doubleValue(right);
        if (Double.isNaN(rd)) {
            return false;
        }
        return evaluateCompare(ld == rd ? 0 : ld < rd ? -1 : 1);
    }


    private boolean compute(Object left, Object right) {
        left = reduce(left);
        right = reduce(right);
        if (left instanceof InitialContext) {
            ((InitialContext) left).reset();
        }
        if (right instanceof InitialContext) {
            ((InitialContext) right).reset();
        }
        if (left instanceof Iterator && right instanceof Iterator) {
            return findMatch((Iterator) left, (Iterator) right);
        }
        if (left instanceof Iterator) {
            return containsMatch((Iterator) left, right);
        }
        if (right instanceof Iterator) {
            return containsMatch((Iterator) right, left);
        }
        double ld = InfoSetUtil.doubleValue(left);
        double rd = InfoSetUtil.doubleValue(right);
        return evaluateCompare(ld == rd ? 0 : ld < rd ? -1 : 1);
    }


    private double calculateAsymptoticPValue(final double Umin,
                                             final int n1,
                                             final int n2)
        throws ConvergenceException, MaxCountExceededException {
        final double n1n2prod = n1 * n2;
        final double EU = n1n2prod / 2.0;
        final double VarU = n1n2prod * (n1 + n2 + 1) / 12.0;
        final double z = (Umin - EU) / FastMath.sqrt(VarU);
        final NormalDistribution standardNormal = new NormalDistribution(0, 1);
        return 2 * standardNormal.cumulativeProbability(z);
    }


    private double calculateAsymptoticPValue(final double Umin,
                                             final int n1,
                                             final int n2)
        throws ConvergenceException, MaxCountExceededException {
        final int n1n2prod = n1 * n2;
        final double EU = n1n2prod / 2.0;
        final double VarU = n1n2prod * (n1 + n2 + 1) / 12.0;
        final double z = (Umin - EU) / FastMath.sqrt(VarU);
        final NormalDistribution standardNormal = new NormalDistribution(0, 1);
        return 2 * standardNormal.cumulativeProbability(z);
    }


    public List getValues(final Option option,
                          List defaultValues) {
        List valueList = (List) values.get(option);
        if (defaultValues == null || defaultValues.isEmpty()) {
            defaultValues = (List) this.defaultValues.get(option);
        }
        if (defaultValues != null && !defaultValues.isEmpty()) {
            if (valueList == null || valueList.isEmpty()) {
                valueList = defaultValues;
            } else {
                if (defaultValues.size() > valueList.size()) {
                    valueList = new ArrayList(valueList);
                    for (int i=valueList.size(); i<defaultValues.size(); i++) {
                        valueList.add(defaultValues.get(i));
                    }
                }
            }
        }
        return valueList == null ? Collections.EMPTY_LIST : valueList;
    }


    public List getValues(final Option option,
                          List defaultValues) {
        List valueList = (List) values.get(option);
        if ((valueList == null) || valueList.isEmpty()) {
            valueList = defaultValues;
        }
        if ((valueList == null) || valueList.isEmpty()) {
            valueList = (List) this.defaultValues.get(option);
        }
        return valueList == null ? Collections.EMPTY_LIST : valueList;
    }


    public String html() {
        StringBuilder accum = new StringBuilder();
        html(accum);
        return getOutputSettings().prettyPrint() ? accum.toString().trim() : accum.toString();
    }


    public String html() {
        StringBuilder accum = new StringBuilder();
        html(accum);
        return accum.toString().trim();
    }


    protected double doSolve() {
        final double[] x = new double[maximalOrder + 1];
        final double[] y = new double[maximalOrder + 1];
        x[0] = getMin();
        x[1] = getStartValue();
        x[2] = getMax();
        verifySequence(x[0], x[1], x[2]);
        y[1] = computeObjectiveValue(x[1]);
        if (Precision.equals(y[1], 0.0, 1)) {
            return x[1];
        }
        y[0] = computeObjectiveValue(x[0]);
        if (Precision.equals(y[0], 0.0, 1)) {
            return x[0];
        }
        int nbPoints;
        int signChangeIndex;
        if (y[0] * y[1] < 0) {
            nbPoints        = 2;
            signChangeIndex = 1;
        } else {
            y[2] = computeObjectiveValue(x[2]);
            if (Precision.equals(y[2], 0.0, 1)) {
                return x[2];
            }
            if (y[1] * y[2] < 0) {
                nbPoints        = 3;
                signChangeIndex = 2;
            } else {
                throw new NoBracketingException(x[0], x[2], y[0], y[2]);
            }
        }
        final double[] tmpX = new double[x.length];
        double xA    = x[signChangeIndex - 1];
        double yA    = y[signChangeIndex - 1];
        double absYA = FastMath.abs(yA);
        int agingA   = 0;
        double xB    = x[signChangeIndex];
        double yB    = y[signChangeIndex];
        double absYB = FastMath.abs(yB);
        int agingB   = 0;
        while (true) {
            final double xTol = getAbsoluteAccuracy() +
                                getRelativeAccuracy() * FastMath.max(FastMath.abs(xA), FastMath.abs(xB));
            if (((xB - xA) <= xTol) || (FastMath.max(absYA, absYB) < getFunctionValueAccuracy())) {
                switch (allowed) {
                case ANY_SIDE :
                    return absYA < absYB ? xA : xB;
                case LEFT_SIDE :
                    return xA;
                case RIGHT_SIDE :
                    return xB;
                case BELOW_SIDE :
                    return (yA <= 0) ? xA : xB;
                case ABOVE_SIDE :
                    return (yA <  0) ? xB : xA;
                default :
                    throw new MathInternalError(null);
                }
            }
            double targetY;
            if (agingA >= MAXIMAL_AGING) {
                final int p = agingA - MAXIMAL_AGING;
                final double weightA = (1 << p) - 1;
                final double weightB = p + 1;
                targetY = (weightA * yA - weightB * REDUCTION_FACTOR * yB) / (weightA + weightB);
            } else if (agingB >= MAXIMAL_AGING) {
                final int p = agingB - MAXIMAL_AGING;
                final double weightA = p + 1;
                final double weightB = (1 << p) - 1;
                targetY = (weightB * yB - weightA * REDUCTION_FACTOR * yA) / (weightA + weightB);
            } else {
                targetY = 0;
            }
            double nextX;
            int start = 0;
            int end   = nbPoints;
            do {
                System.arraycopy(x, start, tmpX, start, end - start);
                nextX = guessX(targetY, tmpX, y, start, end);
                if (!((nextX > xA) && (nextX < xB))) {
                    if (signChangeIndex - start >= end - signChangeIndex) {
                        ++start;
                    } else {
                        --end;
                    }
                    nextX = Double.NaN;
                }
            } while (Double.isNaN(nextX) && (end - start > 1));
            if (Double.isNaN(nextX)) {
                nextX = xA + 0.5 * (xB - xA);
                start = signChangeIndex - 1;
                end   = signChangeIndex;
            }
            final double nextY = computeObjectiveValue(nextX);
            if (Precision.equals(nextY, 0.0, 1)) {
                return nextX;
            }
            if ((nbPoints > 2) && (end - start != nbPoints)) {
                nbPoints = end - start;
                System.arraycopy(x, start, x, 0, nbPoints);
                System.arraycopy(y, start, y, 0, nbPoints);
                signChangeIndex -= start;
            } else  if (nbPoints == x.length) {
                nbPoints--;
                if (signChangeIndex >= (x.length + 1) / 2) {
                    System.arraycopy(x, 1, x, 0, nbPoints);
                    System.arraycopy(y, 1, y, 0, nbPoints);
                    --signChangeIndex;
                }
            }
            System.arraycopy(x, signChangeIndex, x, signChangeIndex + 1, nbPoints - signChangeIndex);
            x[signChangeIndex] = nextX;
            System.arraycopy(y, signChangeIndex, y, signChangeIndex + 1, nbPoints - signChangeIndex);
            y[signChangeIndex] = nextY;
            ++nbPoints;
            if (nextY * yA <= 0) {
                xB = nextX;
                yB = nextY;
                absYB = FastMath.abs(yB);
                ++agingA;
                agingB = 0;
            } else {
                xA = nextX;
                yA = nextY;
                absYA = FastMath.abs(yA);
                agingA = 0;
                ++agingB;
                signChangeIndex++;
            }
        }
    }


    protected double doSolve() {
        final double[] x = new double[maximalOrder + 1];
        final double[] y = new double[maximalOrder + 1];
        x[0] = getMin();
        x[1] = getStartValue();
        x[2] = getMax();
        verifySequence(x[0], x[1], x[2]);
        y[1] = computeObjectiveValue(x[1]);
        if (Precision.equals(y[1], 0.0, 1)) {
            return x[1];
        }
        y[0] = computeObjectiveValue(x[0]);
        if (Precision.equals(y[0], 0.0, 1)) {
            return x[0];
        }
        int nbPoints;
        int signChangeIndex;
        if (y[0] * y[1] < 0) {
            nbPoints        = 2;
            signChangeIndex = 1;
        } else {
            y[2] = computeObjectiveValue(x[2]);
            if (Precision.equals(y[2], 0.0, 1)) {
                return x[2];
            }
            if (y[1] * y[2] < 0) {
                nbPoints        = 3;
                signChangeIndex = 2;
            } else {
                throw new NoBracketingException(x[0], x[2], y[0], y[2]);
            }
        }
        final double[] tmpX = new double[x.length];
        double xA    = x[signChangeIndex - 1];
        double yA    = y[signChangeIndex - 1];
        double absYA = FastMath.abs(yA);
        int agingA   = 0;
        double xB    = x[signChangeIndex];
        double yB    = y[signChangeIndex];
        double absYB = FastMath.abs(yB);
        int agingB   = 0;
        while (true) {
            final double xTol = getAbsoluteAccuracy() +
                                getRelativeAccuracy() * FastMath.max(FastMath.abs(xA), FastMath.abs(xB));
            if (((xB - xA) <= xTol) || (FastMath.max(absYA, absYB) < getFunctionValueAccuracy())) {
                switch (allowed) {
                case ANY_SIDE :
                    return absYA < absYB ? xA : xB;
                case LEFT_SIDE :
                    return xA;
                case RIGHT_SIDE :
                    return xB;
                case BELOW_SIDE :
                    return (yA <= 0) ? xA : xB;
                case ABOVE_SIDE :
                    return (yA <  0) ? xB : xA;
                default :
                    throw new MathInternalError(null);
                }
            }
            double targetY;
            if (agingA >= MAXIMAL_AGING) {
                targetY = -REDUCTION_FACTOR * yB;
            } else if (agingB >= MAXIMAL_AGING) {
                targetY = -REDUCTION_FACTOR * yA;
            } else {
                targetY = 0;
            }
            double nextX;
            int start = 0;
            int end   = nbPoints;
            do {
                System.arraycopy(x, start, tmpX, start, end - start);
                nextX = guessX(targetY, tmpX, y, start, end);
                if (!((nextX > xA) && (nextX < xB))) {
                    if (signChangeIndex - start >= end - signChangeIndex) {
                        ++start;
                    } else {
                        --end;
                    }
                    nextX = Double.NaN;
                }
            } while (Double.isNaN(nextX) && (end - start > 1));
            if (Double.isNaN(nextX)) {
                nextX = xA + 0.5 * (xB - xA);
                start = signChangeIndex - 1;
                end   = signChangeIndex;
            }
            final double nextY = computeObjectiveValue(nextX);
            if (Precision.equals(nextY, 0.0, 1)) {
                return nextX;
            }
            if ((nbPoints > 2) && (end - start != nbPoints)) {
                nbPoints = end - start;
                System.arraycopy(x, start, x, 0, nbPoints);
                System.arraycopy(y, start, y, 0, nbPoints);
                signChangeIndex -= start;
            } else  if (nbPoints == x.length) {
                nbPoints--;
                if (signChangeIndex >= (x.length + 1) / 2) {
                    System.arraycopy(x, 1, x, 0, nbPoints);
                    System.arraycopy(y, 1, y, 0, nbPoints);
                    --signChangeIndex;
                }
            }
            System.arraycopy(x, signChangeIndex, x, signChangeIndex + 1, nbPoints - signChangeIndex);
            x[signChangeIndex] = nextX;
            System.arraycopy(y, signChangeIndex, y, signChangeIndex + 1, nbPoints - signChangeIndex);
            y[signChangeIndex] = nextY;
            ++nbPoints;
            if (nextY * yA <= 0) {
                xB = nextX;
                yB = nextY;
                absYB = FastMath.abs(yB);
                ++agingA;
                agingB = 0;
            } else {
                xA = nextX;
                yA = nextY;
                absYA = FastMath.abs(yA);
                agingA = 0;
                ++agingB;
                signChangeIndex++;
            }
        }
    }


  public TypePair getTypesUnderInequality(JSType that) {
    if (that instanceof UnionType) {
      TypePair p = that.getTypesUnderInequality(this);
      return new TypePair(p.typeB, p.typeA);
    }
    switch (this.testForEquality(that)) {
      case TRUE:
        JSType noType = getNativeType(JSTypeNative.NO_TYPE);
        return new TypePair(noType, noType);
      case FALSE:
      case UNKNOWN:
        return new TypePair(this, that);
    }
    throw new IllegalStateException();
  }


  public TypePair getTypesUnderInequality(JSType that) {
    if (that instanceof UnionType) {
      TypePair p = that.getTypesUnderInequality(this);
      return new TypePair(p.typeB, p.typeA);
    }
    switch (this.testForEquality(that)) {
      case TRUE:
        return new TypePair(null, null);
      case FALSE:
      case UNKNOWN:
        return new TypePair(this, that);
    }
    throw new IllegalStateException();
  }


    protected StringBuffer renderWrappedText(StringBuffer sb, int width,
                                             int nextLineTabStop, String text)
    {
        int pos = findWrapPos(text, width, 0);
        if (pos == -1)
        {
            sb.append(rtrim(text));
            return sb;
        }
        sb.append(rtrim(text.substring(0, pos))).append(defaultNewLine);
        final String padding = createPadding(nextLineTabStop);
        while (true)
        {
            text = padding + text.substring(pos).trim();
            pos = findWrapPos(text, width, 0);
            if (pos == -1)
            {
                sb.append(text);
                return sb;
            }
            sb.append(rtrim(text.substring(0, pos))).append(defaultNewLine);
        }
    }


    protected StringBuffer renderWrappedText(StringBuffer sb, int width,
                                             int nextLineTabStop, String text)
    {
        int pos = findWrapPos(text, width, 0);
        if (pos == -1)
        {
            sb.append(rtrim(text));
            return sb;
        }
        sb.append(rtrim(text.substring(0, pos))).append(defaultNewLine);
        final String padding = createPadding(nextLineTabStop);
        while (true)
        {
            text = padding + text.substring(pos).trim();
            pos = findWrapPos(text, width, nextLineTabStop);
            if (pos == -1)
            {
                sb.append(text);
                return sb;
            }
            sb.append(rtrim(text.substring(0, pos))).append(defaultNewLine);
        }
    }


    protected String buildCanonicalName()
    {
        StringBuilder sb = new StringBuilder();
        sb.append(_class.getName());
        sb.append('<');
        sb.append(_referencedType.toCanonical());
        sb.append('>');
        return sb.toString();
    }


    protected String buildCanonicalName()
    {
        StringBuilder sb = new StringBuilder();
        sb.append(_class.getName());
        sb.append('<');
        sb.append(_referencedType.toCanonical());
        return sb.toString();
    }


    public static long skip(InputStream input, long numToSkip) throws IOException {
        long available = numToSkip;
        while (numToSkip > 0) {
            long skipped = input.skip(numToSkip);
            if (skipped == 0) {
                break;
            }
            numToSkip -= skipped;
        }
        if (numToSkip > 0) {
            byte[] skipBuf = new byte[SKIP_BUF_SIZE];
            while (numToSkip > 0) {
                int read = readFully(input, skipBuf, 0,
                                     (int) Math.min(numToSkip, SKIP_BUF_SIZE));
                if (read < 1) {
                    break;
                }
                numToSkip -= read;
            }
        }
        return available - numToSkip;
    }


    public static long skip(InputStream input, long numToSkip) throws IOException {
        long available = numToSkip;
        while (numToSkip > 0) {
            long skipped = input.skip(numToSkip);
            if (skipped == 0) {
                break;
            }
            numToSkip -= skipped;
        }
        return available - numToSkip;
    }


    public static String random(int count, int start, int end, boolean letters, boolean numbers,
                                char[] chars, Random random) {
        if (count == 0) {
            return "";
        } else if (count < 0) {
            throw new IllegalArgumentException("Requested random string length " + count + " is less than 0.");
        }
        if (chars != null && chars.length == 0) {
            throw new IllegalArgumentException("The chars array must not be empty");
        }
        if (start == 0 && end == 0) {
            if (chars != null) {
                end = chars.length;
            } else {
                if (!letters && !numbers) {
                    end = Integer.MAX_VALUE;
                } else {
                    end = 'z' + 1;
                    start = ' ';
                }
            }
        }
        char[] buffer = new char[count];
        int gap = end - start;
        while (count-- != 0) {
            char ch;
            if (chars == null) {
                ch = (char) (random.nextInt(gap) + start);
            } else {
                ch = chars[random.nextInt(gap) + start];
            }
            if (letters && Character.isLetter(ch)
                    || numbers && Character.isDigit(ch)
                    || !letters && !numbers) {
                if(ch >= 56320 && ch <= 57343) {
                    if(count == 0) {
                        count++;
                    } else {
                        buffer[count] = ch;
                        count--;
                        buffer[count] = (char) (55296 + random.nextInt(128));
                    }
                } else if(ch >= 55296 && ch <= 56191) {
                    if(count == 0) {
                        count++;
                    } else {
                        buffer[count] = (char) (56320 + random.nextInt(128));
                        count--;
                        buffer[count] = ch;
                    }
                } else if(ch >= 56192 && ch <= 56319) {
                    count++;
                } else {
                    buffer[count] = ch;
                }
            } else {
                count++;
            }
        }
        return new String(buffer);
    }


    public static String random(int count, int start, int end, boolean letters, boolean numbers,
                                char[] chars, Random random) {
        if (count == 0) {
            return "";
        } else if (count < 0) {
            throw new IllegalArgumentException("Requested random string length " + count + " is less than 0.");
        }
        if (start == 0 && end == 0) {
                if (!letters && !numbers) {
                    end = Integer.MAX_VALUE;
                } else {
                    end = 'z' + 1;
                    start = ' ';
                }
        }
        char[] buffer = new char[count];
        int gap = end - start;
        while (count-- != 0) {
            char ch;
            if (chars == null) {
                ch = (char) (random.nextInt(gap) + start);
            } else {
                ch = chars[random.nextInt(gap) + start];
            }
            if (letters && Character.isLetter(ch)
                    || numbers && Character.isDigit(ch)
                    || !letters && !numbers) {
                if(ch >= 56320 && ch <= 57343) {
                    if(count == 0) {
                        count++;
                    } else {
                        buffer[count] = ch;
                        count--;
                        buffer[count] = (char) (55296 + random.nextInt(128));
                    }
                } else if(ch >= 55296 && ch <= 56191) {
                    if(count == 0) {
                        count++;
                    } else {
                        buffer[count] = (char) (56320 + random.nextInt(128));
                        count--;
                        buffer[count] = ch;
                    }
                } else if(ch >= 56192 && ch <= 56319) {
                    count++;
                } else {
                    buffer[count] = ch;
                }
            } else {
                count++;
            }
        }
        return new String(buffer);
    }


    public static long parseOctal(final byte[] buffer, final int offset, final int length) {
        long    result = 0;
        int     end = offset + length;
        int     start = offset;
        if (length < 2){
            throw new IllegalArgumentException("Length "+length+" must be at least 2");
        }
        if (buffer[start] == 0) {
            return 0L;
        }
        while (start < end){
            if (buffer[start] == ' '){
                start++;
            } else {
                break;
            }
        }
        byte trailer = buffer[end - 1];
        while (start < end && (trailer == 0 || trailer == ' ')) {
            end--;
            trailer = buffer[end - 1];
        }
        for ( ;start < end; start++) {
            final byte currentByte = buffer[start];
            if (currentByte < '0' || currentByte > '7'){
                throw new IllegalArgumentException(
                        exceptionMessage(buffer, offset, length, start, currentByte));
            }
            result = (result << 3) + (currentByte - '0');
        }
        return result;
    }


    public static long parseOctal(final byte[] buffer, final int offset, final int length) {
        long    result = 0;
        int     end = offset + length;
        int     start = offset;
        if (length < 2){
            throw new IllegalArgumentException("Length "+length+" must be at least 2");
        }
        if (buffer[start] == 0) {
            return 0L;
        }
        while (start < end){
            if (buffer[start] == ' '){
                start++;
            } else {
                break;
            }
        }
        byte trailer = buffer[end - 1];
        while (start < end && (trailer == 0 || trailer == ' ')) {
            end--;
            trailer = buffer[end - 1];
        }
        for ( ;start < end; start++) {
            final byte currentByte = buffer[start];
            if (currentByte == 0) {
                break;
            }
            if (currentByte < '0' || currentByte > '7'){
                throw new IllegalArgumentException(
                        exceptionMessage(buffer, offset, length, start, currentByte));
            }
            result = (result << 3) + (currentByte - '0');
        }
        return result;
    }


    private void printAndQuote(final Object object, final CharSequence value, final int offset, final int len,
            final Appendable out, final boolean newRecord) throws IOException {
        boolean quote = false;
        int start = offset;
        int pos = offset;
        final int end = offset + len;
        final char delimChar = getDelimiter();
        final char quoteChar = getQuoteCharacter().charValue();
        QuoteMode quoteModePolicy = getQuoteMode();
        if (quoteModePolicy == null) {
            quoteModePolicy = QuoteMode.MINIMAL;
        }
        switch (quoteModePolicy) {
        case ALL:
            quote = true;
            break;
        case NON_NUMERIC:
            quote = !(object instanceof Number);
            break;
        case NONE:
            printAndEscape(value, offset, len, out);
            return;
        case MINIMAL:
            if (len <= 0) {
                if (newRecord) {
                    quote = true;
                }
            } else {
                char c = value.charAt(pos);
                if (newRecord && (c < 0x20 || c > 0x21 && c < 0x23 || c > 0x2B && c < 0x2D || c > 0x7E)) {
                    quote = true;
                } else if (c <= COMMENT) {
                    quote = true;
                } else {
                    while (pos < end) {
                        c = value.charAt(pos);
                        if (c == LF || c == CR || c == quoteChar || c == delimChar) {
                            quote = true;
                            break;
                        }
                        pos++;
                    }
                    if (!quote) {
                        pos = end - 1;
                        c = value.charAt(pos);
                        if (c <= SP) {
                            quote = true;
                        }
                    }
                }
            }
            if (!quote) {
                out.append(value, start, end);
                return;
            }
            break;
        default:
            throw new IllegalStateException("Unexpected Quote value: " + quoteModePolicy);
        }
        if (!quote) {
            out.append(value, start, end);
            return;
        }
        out.append(quoteChar);
        while (pos < end) {
            final char c = value.charAt(pos);
            if (c == quoteChar) {
                out.append(value, start, pos + 1);
                start = pos;
            }
            pos++;
        }
        out.append(value, start, pos);
        out.append(quoteChar);
    }


    private void printAndQuote(final Object object, final CharSequence value, final int offset, final int len,
            final Appendable out, final boolean newRecord) throws IOException {
        boolean quote = false;
        int start = offset;
        int pos = offset;
        final int end = offset + len;
        final char delimChar = getDelimiter();
        final char quoteChar = getQuoteCharacter().charValue();
        QuoteMode quoteModePolicy = getQuoteMode();
        if (quoteModePolicy == null) {
            quoteModePolicy = QuoteMode.MINIMAL;
        }
        switch (quoteModePolicy) {
        case ALL:
            quote = true;
            break;
        case NON_NUMERIC:
            quote = !(object instanceof Number);
            break;
        case NONE:
            printAndEscape(value, offset, len, out);
            return;
        case MINIMAL:
            if (len <= 0) {
                if (newRecord) {
                    quote = true;
                }
            } else {
                char c = value.charAt(pos);
                if (newRecord && (c < '0' || c > '9' && c < 'A' || c > 'Z' && c < 'a' || c > 'z')) {
                    quote = true;
                } else if (c <= COMMENT) {
                    quote = true;
                } else {
                    while (pos < end) {
                        c = value.charAt(pos);
                        if (c == LF || c == CR || c == quoteChar || c == delimChar) {
                            quote = true;
                            break;
                        }
                        pos++;
                    }
                    if (!quote) {
                        pos = end - 1;
                        c = value.charAt(pos);
                        if (c <= SP) {
                            quote = true;
                        }
                    }
                }
            }
            if (!quote) {
                out.append(value, start, end);
                return;
            }
            break;
        default:
            throw new IllegalStateException("Unexpected Quote value: " + quoteModePolicy);
        }
        if (!quote) {
            out.append(value, start, end);
            return;
        }
        out.append(quoteChar);
        while (pos < end) {
            final char c = value.charAt(pos);
            if (c == quoteChar) {
                out.append(value, start, pos + 1);
                start = pos;
            }
            pos++;
        }
        out.append(value, start, pos);
        out.append(quoteChar);
    }


    public static DateTimeZone forOffsetHoursMinutes(int hoursOffset, int minutesOffset) throws IllegalArgumentException {
        if (hoursOffset == 0 && minutesOffset == 0) {
            return DateTimeZone.UTC;
        }
        if (hoursOffset < -23 || hoursOffset > 23) {
            throw new IllegalArgumentException("Hours out of range: " + hoursOffset);
        }
        if (minutesOffset < -59 || minutesOffset > 59) {
            throw new IllegalArgumentException("Minutes out of range: " + minutesOffset);
        }
        if (hoursOffset > 0 && minutesOffset < 0) {
            throw new IllegalArgumentException("Positive hours must not have negative minutes: " + minutesOffset);
        }
        int offset = 0;
        try {
            int hoursInMinutes = hoursOffset * 60;
            if (hoursInMinutes < 0) {
                minutesOffset = hoursInMinutes - Math.abs(minutesOffset);
            } else {
                minutesOffset = hoursInMinutes + minutesOffset;
            }
            offset = FieldUtils.safeMultiply(minutesOffset, DateTimeConstants.MILLIS_PER_MINUTE);
        } catch (ArithmeticException ex) {
            throw new IllegalArgumentException("Offset is too large");
        }
        return forOffsetMillis(offset);
    }


    public static DateTimeZone forOffsetHoursMinutes(int hoursOffset, int minutesOffset) throws IllegalArgumentException {
        if (hoursOffset == 0 && minutesOffset == 0) {
            return DateTimeZone.UTC;
        }
        if (hoursOffset < -23 || hoursOffset > 23) {
            throw new IllegalArgumentException("Hours out of range: " + hoursOffset);
        }
        if (minutesOffset < 0 || minutesOffset > 59) {
            throw new IllegalArgumentException("Minutes out of range: " + minutesOffset);
        }
        int offset = 0;
        try {
            int hoursInMinutes = hoursOffset * 60;
            if (hoursInMinutes < 0) {
                minutesOffset = hoursInMinutes - minutesOffset;
            } else {
                minutesOffset = hoursInMinutes + minutesOffset;
            }
            offset = FieldUtils.safeMultiply(minutesOffset, DateTimeConstants.MILLIS_PER_MINUTE);
        } catch (ArithmeticException ex) {
            throw new IllegalArgumentException("Offset is too large");
        }
        return forOffsetMillis(offset);
    }


    public ChecksumCalculatingInputStream(final Checksum checksum, final InputStream in) {
        if ( checksum == null ){
            throw new NullPointerException("Parameter checksum must not be null");
        }
        if ( in == null ){
            throw new NullPointerException("Parameter in must not be null");
        }
        this.checksum = checksum;
        this.in = in;
    }


    public ChecksumCalculatingInputStream(final Checksum checksum, final InputStream in) {
        this.checksum = checksum;
        this.in = in;
    }


    public double getNumericalMean() {
        return getSampleSize() * (getNumberOfSuccesses() / (double) getPopulationSize());
    }


    public double getNumericalMean() {
        return (double) (getSampleSize() * getNumberOfSuccesses()) / (double) getPopulationSize();
    }


    public JsonSerializer<?> createContextual(SerializerProvider serializers,
            BeanProperty property) throws JsonMappingException
    {
        if (property == null) {
            return this;
        }
        JsonFormat.Value format = findFormatOverrides(serializers, property, handledType());
        if (format == null) {
            return this;
        }
        JsonFormat.Shape shape = format.getShape();
        if (shape.isNumeric()) {
            return withFormat(Boolean.TRUE, null);
        }
        if (format.hasPattern()) {
            final Locale loc = format.hasLocale()
                            ? format.getLocale()
                            : serializers.getLocale();
            SimpleDateFormat df = new SimpleDateFormat(format.getPattern(), loc);
            TimeZone tz = format.hasTimeZone() ? format.getTimeZone()
                    : serializers.getTimeZone();
            df.setTimeZone(tz);
            return withFormat(Boolean.FALSE, df);
        }
        final boolean hasLocale = format.hasLocale();
        final boolean hasTZ = format.hasTimeZone();
        final boolean asString = (shape == JsonFormat.Shape.STRING);
        if (!hasLocale && !hasTZ && !asString) {
            return this;
        }
        DateFormat df0 = serializers.getConfig().getDateFormat();
        if (df0 instanceof StdDateFormat) {
            StdDateFormat std = (StdDateFormat) df0;
            if (format.hasLocale()) {
                std = std.withLocale(format.getLocale());
            }
            if (format.hasTimeZone()) {
                std = std.withTimeZone(format.getTimeZone());
            }
            return withFormat(Boolean.FALSE, std);
        }
        if (!(df0 instanceof SimpleDateFormat)) {
            serializers.reportMappingProblem(
"Configured `DateFormat` (%s) not a `SimpleDateFormat`; can not configure `Locale` or `TimeZone`",
df0.getClass().getName());
        }
        SimpleDateFormat df = (SimpleDateFormat) df0;
        if (hasLocale) {
            df = new SimpleDateFormat(df.toPattern(), format.getLocale());
        } else {
            df = (SimpleDateFormat) df.clone();
        }
        TimeZone newTz = format.getTimeZone();
        boolean changeTZ = (newTz != null) && !newTz.equals(df.getTimeZone());
        if (changeTZ) {
            df.setTimeZone(newTz);
        }
        return withFormat(Boolean.FALSE, df);
    }


    public JsonSerializer<?> createContextual(SerializerProvider serializers,
            BeanProperty property) throws JsonMappingException
    {
        if (property == null) {
            return this;
        }
        JsonFormat.Value format = findFormatOverrides(serializers, property, handledType());
        if (format == null) {
            return this;
        }
        JsonFormat.Shape shape = format.getShape();
        if (shape.isNumeric()) {
            return withFormat(Boolean.TRUE, null);
        }
                if ((shape == JsonFormat.Shape.STRING) || format.hasPattern()
                                || format.hasLocale() || format.hasTimeZone()) {
                    TimeZone tz = format.getTimeZone();
                    final String pattern = format.hasPattern()
                                    ? format.getPattern()
                                    : StdDateFormat.DATE_FORMAT_STR_ISO8601;
            final Locale loc = format.hasLocale()
                            ? format.getLocale()
                            : serializers.getLocale();
                    SimpleDateFormat df = new SimpleDateFormat(pattern, loc);
                    if (tz == null) {
                        tz = serializers.getTimeZone();
                    }
            df.setTimeZone(tz);
            return withFormat(Boolean.FALSE, df);
        }
        return this;
    }


    public static long parseOctal(final byte[] buffer, final int offset, final int length) {
        long    result = 0;
        int     end = offset + length;
        int     start = offset;
        if (length < 2){
            throw new IllegalArgumentException("Length "+length+" must be at least 2");
        }
        if (buffer[start] == 0) {
            return 0L;
        }
        while (start < end){
            if (buffer[start] == ' '){
                start++;
            } else {
                break;
            }
        }
        byte trailer;
        trailer = buffer[end-1];
        if (trailer == 0 || trailer == ' '){
            end--;
        } else {
            throw new IllegalArgumentException(
                    exceptionMessage(buffer, offset, length, end-1, trailer));
        }
        trailer = buffer[end - 1];
        while (start < end - 1 && (trailer == 0 || trailer == ' ')) {
            end--;
            trailer = buffer[end - 1];
        }
        for ( ;start < end; start++) {
            final byte currentByte = buffer[start];
            if (currentByte < '0' || currentByte > '7'){
                throw new IllegalArgumentException(
                        exceptionMessage(buffer, offset, length, start, currentByte));
            }
            result = (result << 3) + (currentByte - '0');
        }
        return result;
    }


    public static long parseOctal(final byte[] buffer, final int offset, final int length) {
        long    result = 0;
        int     end = offset + length;
        int     start = offset;
        if (length < 2){
            throw new IllegalArgumentException("Length "+length+" must be at least 2");
        }
        if (buffer[start] == 0) {
            return 0L;
        }
        while (start < end){
            if (buffer[start] == ' '){
                start++;
            } else {
                break;
            }
        }
        byte trailer;
        trailer = buffer[end-1];
        if (trailer == 0 || trailer == ' '){
            end--;
        } else {
            throw new IllegalArgumentException(
                    exceptionMessage(buffer, offset, length, end-1, trailer));
        }
        trailer = buffer[end - 1];
        if (trailer == 0 || trailer == ' '){
            end--;
        }
        for ( ;start < end; start++) {
            final byte currentByte = buffer[start];
            if (currentByte < '0' || currentByte > '7'){
                throw new IllegalArgumentException(
                        exceptionMessage(buffer, offset, length, start, currentByte));
            }
            result = (result << 3) + (currentByte - '0');
        }
        return result;
    }


  private void processRequireCall(NodeTraversal t, Node n, Node parent) {
    Node left = n.getFirstChild();
    Node arg = left.getNext();
    if (verifyLastArgumentIsString(t, left, arg)) {
      String ns = arg.getString();
      ProvidedName provided = providedNames.get(ns);
      if (provided == null || !provided.isExplicitlyProvided()) {
        unrecognizedRequires.add(
            new UnrecognizedRequire(n, ns, t.getSourceName()));
      } else {
        JSModule providedModule = provided.explicitModule;
        Preconditions.checkNotNull(providedModule);
        JSModule module = t.getModule();
        if (moduleGraph != null &&
            module != providedModule &&
            !moduleGraph.dependsOn(module, providedModule)) {
          compiler.report(
              t.makeError(n, XMODULE_REQUIRE_ERROR, ns,
                  providedModule.getName(),
                  module.getName()));
        }
      }
      maybeAddToSymbolTable(left);
      maybeAddStringNodeToSymbolTable(arg);
      if (provided != null || requiresLevel.isOn()) {
        parent.detachFromParent();
        compiler.reportCodeChange();
      }
    }
  }


  private void processRequireCall(NodeTraversal t, Node n, Node parent) {
    Node left = n.getFirstChild();
    Node arg = left.getNext();
    if (verifyLastArgumentIsString(t, left, arg)) {
      String ns = arg.getString();
      ProvidedName provided = providedNames.get(ns);
      if (provided == null || !provided.isExplicitlyProvided()) {
        unrecognizedRequires.add(
            new UnrecognizedRequire(n, ns, t.getSourceName()));
      } else {
        JSModule providedModule = provided.explicitModule;
        Preconditions.checkNotNull(providedModule);
        JSModule module = t.getModule();
        if (moduleGraph != null &&
            module != providedModule &&
            !moduleGraph.dependsOn(module, providedModule)) {
          compiler.report(
              t.makeError(n, XMODULE_REQUIRE_ERROR, ns,
                  providedModule.getName(),
                  module.getName()));
        }
      }
      maybeAddToSymbolTable(left);
      maybeAddStringNodeToSymbolTable(arg);
      if (provided != null) {
        parent.detachFromParent();
        compiler.reportCodeChange();
      }
    }
  }


        protected JSType caseTopType(JSType topType) {
          return topType.isAllType() ?
              getNativeType(ARRAY_TYPE) : topType;
        }


        protected JSType caseTopType(JSType topType) {
          return topType;
        }


  public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
    if (n.getType() == Token.FUNCTION) {
      JSDocInfo jsDoc = getFunctionJsDocInfo(n);
      if (jsDoc != null &&
          (jsDoc.isConstructor() ||
           jsDoc.isInterface() ||
           jsDoc.hasThisType() ||
           jsDoc.isOverride())) {
        return false;
      }
      int pType = parent.getType();
      if (!(pType == Token.BLOCK ||
            pType == Token.SCRIPT ||
            pType == Token.NAME ||
            pType == Token.ASSIGN ||
            pType == Token.STRING ||
            pType == Token.NUMBER)) {
        return false;
      }
      Node gramps = parent.getParent();
      if (NodeUtil.isObjectLitKey(parent, gramps)) {
        JSDocInfo maybeLends = gramps.getJSDocInfo();
        if (maybeLends != null &&
            maybeLends.getLendsName() != null &&
            maybeLends.getLendsName().endsWith(".prototype")) {
          return false;
        }
      }
    }
    if (parent != null && parent.getType() == Token.ASSIGN) {
      Node lhs = parent.getFirstChild();
      Node rhs = lhs.getNext();
      if (n == lhs) {
        if (assignLhsChild == null) {
          assignLhsChild = lhs;
        }
      } else {
        if (NodeUtil.isGet(lhs)) {
          if (lhs.getType() == Token.GETPROP &&
              lhs.getLastChild().getString().equals("prototype")) {
            return false;
          }
          Node llhs = lhs.getFirstChild();
          if (llhs.getType() == Token.GETPROP &&
              llhs.getLastChild().getString().equals("prototype")) {
            return false;
          }
        }
      }
    }
    return true;
  }


  public boolean shouldTraverse(NodeTraversal t, Node n, Node parent) {
    if (n.getType() == Token.FUNCTION) {
      JSDocInfo jsDoc = getFunctionJsDocInfo(n);
      if (jsDoc != null &&
          (jsDoc.isConstructor() ||
           jsDoc.isInterface() ||
           jsDoc.hasThisType() ||
           jsDoc.isOverride())) {
        return false;
      }
      int pType = parent.getType();
      if (!(pType == Token.BLOCK ||
            pType == Token.SCRIPT ||
            pType == Token.NAME ||
            pType == Token.ASSIGN ||
            pType == Token.STRING ||
            pType == Token.NUMBER)) {
        return false;
      }
    }
    if (parent != null && parent.getType() == Token.ASSIGN) {
      Node lhs = parent.getFirstChild();
      Node rhs = lhs.getNext();
      if (n == lhs) {
        if (assignLhsChild == null) {
          assignLhsChild = lhs;
        }
      } else {
        if (NodeUtil.isGet(lhs)) {
          if (lhs.getType() == Token.GETPROP &&
              lhs.getLastChild().getString().equals("prototype")) {
            return false;
          }
          Node llhs = lhs.getFirstChild();
          if (llhs.getType() == Token.GETPROP &&
              llhs.getLastChild().getString().equals("prototype")) {
            return false;
          }
        }
      }
    }
    return true;
  }


  private boolean hasExceptionHandler(Node cfgNode) {
    List<DiGraphEdge<Node, Branch>> branchEdges = getCfg().getOutEdges(cfgNode);
    for (DiGraphEdge<Node, Branch> edge : branchEdges) {
      if (edge.getValue() == Branch.ON_EX) {
        return true;
      }
    }
    return false;
  }


  private boolean hasExceptionHandler(Node cfgNode) {
    return false;
  }


    static boolean preserveWhitespace(Node node) {
        if (node != null && node instanceof Element) {
            Element el = (Element) node;
            int i = 0;
            do {
                if (el.tag.preserveWhitespace())
                    return true;
                el = el.parent();
                i++;
            } while (i < 6 && el != null);
        }
        return false;
    }


    static boolean preserveWhitespace(Node node) {
        if (node != null && node instanceof Element) {
            Element el = (Element) node;
                if (el.tag.preserveWhitespace())
                    return true;
                else
                    return el.parent() != null && el.parent().tag.preserveWhitespace();
        }
        return false;
    }


  private Node tryFoldArrayAccess(Node n, Node left, Node right) {
    Node parent = n.getParent();
    if (isAssignmentTarget(n)) {
      return n;
    }
    if (!right.isNumber()) {
      return n;
    }
    double index = right.getDouble();
    int intIndex = (int) index;
    if (intIndex != index) {
      error(INVALID_GETELEM_INDEX_ERROR, right);
      return n;
    }
    if (intIndex < 0) {
      error(INDEX_OUT_OF_BOUNDS_ERROR, right);
      return n;
    }
    Node current = left.getFirstChild();
    Node elem = null;
    for (int i = 0; current != null; i++) {
      if (i != intIndex) {
        if (mayHaveSideEffects(current)) {
          return n;
        }
      } else {
        elem = current;
      }
      current = current.getNext();
    }
    if (elem == null) {
      error(INDEX_OUT_OF_BOUNDS_ERROR, right);
      return n;
    }
    if (elem.isEmpty()) {
      elem = NodeUtil.newUndefinedNode(elem);
    } else {
      left.removeChild(elem);
    }
    n.getParent().replaceChild(n, elem);
    reportCodeChange();
    return elem;
  }


  private Node tryFoldArrayAccess(Node n, Node left, Node right) {
    Node parent = n.getParent();
    if (isAssignmentTarget(n)) {
      return n;
    }
    if (!right.isNumber()) {
      return n;
    }
    double index = right.getDouble();
    int intIndex = (int) index;
    if (intIndex != index) {
      error(INVALID_GETELEM_INDEX_ERROR, right);
      return n;
    }
    if (intIndex < 0) {
      error(INDEX_OUT_OF_BOUNDS_ERROR, right);
      return n;
    }
    Node current = left.getFirstChild();
    Node elem = null;
    for (int i = 0; current != null && i < intIndex; i++) {
        elem = current;
      current = current.getNext();
    }
    if (elem == null) {
      error(INDEX_OUT_OF_BOUNDS_ERROR, right);
      return n;
    }
    if (elem.isEmpty()) {
      elem = NodeUtil.newUndefinedNode(elem);
    } else {
      left.removeChild(elem);
    }
    n.getParent().replaceChild(n, elem);
    reportCodeChange();
    return elem;
  }


  public void visit(NodeTraversal t, Node n, Node parent) {
    JSType childType;
    JSType leftType, rightType;
    Node left, right;
    boolean typeable = true;
    switch (n.getType()) {
      case Token.NAME:
        typeable = visitName(t, n, parent);
        break;
      case Token.LP:
        if (parent.getType() != Token.FUNCTION) {
          ensureTyped(t, n, getJSType(n.getFirstChild()));
        } else {
          typeable = false;
        }
        break;
      case Token.COMMA:
        ensureTyped(t, n, getJSType(n.getLastChild()));
        break;
      case Token.TRUE:
      case Token.FALSE:
        ensureTyped(t, n, BOOLEAN_TYPE);
        break;
      case Token.THIS:
        ensureTyped(t, n, t.getScope().getTypeOfThis());
        break;
      case Token.REF_SPECIAL:
        ensureTyped(t, n);
        break;
      case Token.GET_REF:
        ensureTyped(t, n, getJSType(n.getFirstChild()));
        break;
      case Token.NULL:
        ensureTyped(t, n, NULL_TYPE);
        break;
      case Token.NUMBER:
        ensureTyped(t, n, NUMBER_TYPE);
        break;
      case Token.STRING:
        if (!NodeUtil.isObjectLitKey(n, n.getParent())) {
          ensureTyped(t, n, STRING_TYPE);
        } else {
          typeable = false;
        }
        break;
      case Token.GET:
      case Token.SET:
        break;
      case Token.ARRAYLIT:
        ensureTyped(t, n, ARRAY_TYPE);
        break;
      case Token.REGEXP:
        ensureTyped(t, n, REGEXP_TYPE);
        break;
      case Token.GETPROP:
        visitGetProp(t, n, parent);
        typeable = !(parent.getType() == Token.ASSIGN &&
                     parent.getFirstChild() == n);
        break;
      case Token.GETELEM:
        visitGetElem(t, n);
        typeable = false;
        break;
      case Token.VAR:
        visitVar(t, n);
        typeable = false;
        break;
      case Token.NEW:
        visitNew(t, n);
        typeable = true;
        break;
      case Token.CALL:
        visitCall(t, n);
        typeable = !NodeUtil.isExpressionNode(parent);
        break;
      case Token.RETURN:
        visitReturn(t, n);
        typeable = false;
        break;
      case Token.DEC:
      case Token.INC:
        left = n.getFirstChild();
        validator.expectNumber(
            t, left, getJSType(left), "increment/decrement");
        ensureTyped(t, n, NUMBER_TYPE);
        break;
      case Token.NOT:
        ensureTyped(t, n, BOOLEAN_TYPE);
        break;
      case Token.VOID:
        ensureTyped(t, n, VOID_TYPE);
        break;
      case Token.TYPEOF:
        ensureTyped(t, n, STRING_TYPE);
        break;
      case Token.BITNOT:
        childType = getJSType(n.getFirstChild());
        if (!childType.matchesInt32Context()) {
          report(t, n, BIT_OPERATION, NodeUtil.opToStr(n.getType()),
              childType.toString());
        }
        ensureTyped(t, n, NUMBER_TYPE);
        break;
      case Token.POS:
      case Token.NEG:
        left = n.getFirstChild();
        validator.expectNumber(t, left, getJSType(left), "sign operator");
        ensureTyped(t, n, NUMBER_TYPE);
        break;
      case Token.EQ:
      case Token.NE: {
        leftType = getJSType(n.getFirstChild());
        rightType = getJSType(n.getLastChild());
        JSType leftTypeRestricted = leftType.restrictByNotNullOrUndefined();
        JSType rightTypeRestricted = rightType.restrictByNotNullOrUndefined();
        TernaryValue result =
            leftTypeRestricted.testForEquality(rightTypeRestricted);
        if (result != TernaryValue.UNKNOWN) {
          if (n.getType() == Token.NE) {
            result = result.not();
          }
          report(t, n, DETERMINISTIC_TEST, leftType.toString(),
              rightType.toString(), result.toString());
        }
        ensureTyped(t, n, BOOLEAN_TYPE);
        break;
      }
      case Token.SHEQ:
      case Token.SHNE: {
        leftType = getJSType(n.getFirstChild());
        rightType = getJSType(n.getLastChild());
        JSType leftTypeRestricted = leftType.restrictByNotNullOrUndefined();
        JSType rightTypeRestricted = rightType.restrictByNotNullOrUndefined();
        if (!leftTypeRestricted.canTestForShallowEqualityWith(
                rightTypeRestricted)) {
          report(t, n, DETERMINISTIC_TEST_NO_RESULT, leftType.toString(),
              rightType.toString());
        }
        ensureTyped(t, n, BOOLEAN_TYPE);
        break;
      }
      case Token.LT:
      case Token.LE:
      case Token.GT:
      case Token.GE:
        leftType = getJSType(n.getFirstChild());
        rightType = getJSType(n.getLastChild());
        if (rightType.isNumber()) {
          validator.expectNumber(
              t, n, leftType, "left side of numeric comparison");
        } else if (leftType.isNumber()) {
          validator.expectNumber(
              t, n, rightType, "right side of numeric comparison");
        } else if (leftType.matchesNumberContext() &&
                   rightType.matchesNumberContext()) {
        } else {
          String message = "left side of comparison";
          validator.expectString(t, n, leftType, message);
          validator.expectNotNullOrUndefined(
              t, n, leftType, message, getNativeType(STRING_TYPE));
          message = "right side of comparison";
          validator.expectString(t, n, rightType, message);
          validator.expectNotNullOrUndefined(
              t, n, rightType, message, getNativeType(STRING_TYPE));
        }
        ensureTyped(t, n, BOOLEAN_TYPE);
        break;
      case Token.IN:
        left = n.getFirstChild();
        right = n.getLastChild();
        leftType = getJSType(left);
        rightType = getJSType(right);
        validator.expectObject(t, n, rightType, "'in' requires an object");
        validator.expectString(t, left, leftType, "left side of 'in'");
        ensureTyped(t, n, BOOLEAN_TYPE);
        break;
      case Token.INSTANCEOF:
        left = n.getFirstChild();
        right = n.getLastChild();
        leftType = getJSType(left);
        rightType = getJSType(right).restrictByNotNullOrUndefined();
        validator.expectAnyObject(
            t, left, leftType, "deterministic instanceof yields false");
        validator.expectActualObject(
            t, right, rightType, "instanceof requires an object");
        ensureTyped(t, n, BOOLEAN_TYPE);
        break;
      case Token.ASSIGN:
        visitAssign(t, n);
        typeable = false;
        break;
      case Token.ASSIGN_LSH:
      case Token.ASSIGN_RSH:
      case Token.ASSIGN_URSH:
      case Token.ASSIGN_DIV:
      case Token.ASSIGN_MOD:
      case Token.ASSIGN_BITOR:
      case Token.ASSIGN_BITXOR:
      case Token.ASSIGN_BITAND:
      case Token.ASSIGN_SUB:
      case Token.ASSIGN_ADD:
      case Token.ASSIGN_MUL:
      case Token.LSH:
      case Token.RSH:
      case Token.URSH:
      case Token.DIV:
      case Token.MOD:
      case Token.BITOR:
      case Token.BITXOR:
      case Token.BITAND:
      case Token.SUB:
      case Token.ADD:
      case Token.MUL:
        visitBinaryOperator(n.getType(), t, n);
        break;
      case Token.DELPROP:
        if (!isReference(n.getFirstChild())) {
          report(t, n, BAD_DELETE);
        }
        ensureTyped(t, n, BOOLEAN_TYPE);
        break;
      case Token.CASE:
        JSType switchType = getJSType(parent.getFirstChild());
        JSType caseType = getJSType(n.getFirstChild());
        validator.expectSwitchMatchesCase(t, n, switchType, caseType);
        typeable = false;
        break;
      case Token.WITH: {
        Node child = n.getFirstChild();
        childType = getJSType(child);
        validator.expectObject(
            t, child, childType, "with requires an object");
        typeable = false;
        break;
      }
      case Token.FUNCTION:
        visitFunction(t, n);
        break;
      case Token.LABEL:
      case Token.LABEL_NAME:
      case Token.SWITCH:
      case Token.BREAK:
      case Token.CATCH:
      case Token.TRY:
      case Token.SCRIPT:
      case Token.EXPR_RESULT:
      case Token.BLOCK:
      case Token.EMPTY:
      case Token.DEFAULT:
      case Token.CONTINUE:
      case Token.DEBUGGER:
      case Token.THROW:
        typeable = false;
        break;
      case Token.DO:
      case Token.FOR:
      case Token.IF:
      case Token.WHILE:
        typeable = false;
        break;
      case Token.AND:
      case Token.HOOK:
      case Token.OBJECTLIT:
      case Token.OR:
        if (n.getJSType() != null) {
          ensureTyped(t, n);
        } else {
          if ((n.getType() == Token.OBJECTLIT)
              && (parent.getJSType() instanceof EnumType)) {
            ensureTyped(t, n, parent.getJSType());
          } else {
            ensureTyped(t, n);
          }
        }
        if (n.getType() == Token.OBJECTLIT) {
          for (Node key : n.children()) {
            visitObjLitKey(t, key, n);
          }
        }
        break;
      default:
        report(t, n, UNEXPECTED_TOKEN, Token.name(n.getType()));
        ensureTyped(t, n);
        break;
    }
    typeable = typeable && !inExterns;
    if (typeable) {
      doPercentTypedAccounting(t, n);
    }
    checkNoTypeCheckSection(n, false);
  }


  public void visit(NodeTraversal t, Node n, Node parent) {
    JSType childType;
    JSType leftType, rightType;
    Node left, right;
    boolean typeable = true;
    switch (n.getType()) {
      case Token.NAME:
        typeable = visitName(t, n, parent);
        break;
      case Token.LP:
        if (parent.getType() != Token.FUNCTION) {
          ensureTyped(t, n, getJSType(n.getFirstChild()));
        } else {
          typeable = false;
        }
        break;
      case Token.COMMA:
        ensureTyped(t, n, getJSType(n.getLastChild()));
        break;
      case Token.TRUE:
      case Token.FALSE:
        ensureTyped(t, n, BOOLEAN_TYPE);
        break;
      case Token.THIS:
        ensureTyped(t, n, t.getScope().getTypeOfThis());
        break;
      case Token.REF_SPECIAL:
        ensureTyped(t, n);
        break;
      case Token.GET_REF:
        ensureTyped(t, n, getJSType(n.getFirstChild()));
        break;
      case Token.NULL:
        ensureTyped(t, n, NULL_TYPE);
        break;
      case Token.NUMBER:
        ensureTyped(t, n, NUMBER_TYPE);
        break;
      case Token.STRING:
        if (!NodeUtil.isObjectLitKey(n, n.getParent())) {
          ensureTyped(t, n, STRING_TYPE);
        }
        break;
      case Token.GET:
      case Token.SET:
        break;
      case Token.ARRAYLIT:
        ensureTyped(t, n, ARRAY_TYPE);
        break;
      case Token.REGEXP:
        ensureTyped(t, n, REGEXP_TYPE);
        break;
      case Token.GETPROP:
        visitGetProp(t, n, parent);
        typeable = !(parent.getType() == Token.ASSIGN &&
                     parent.getFirstChild() == n);
        break;
      case Token.GETELEM:
        visitGetElem(t, n);
        typeable = false;
        break;
      case Token.VAR:
        visitVar(t, n);
        typeable = false;
        break;
      case Token.NEW:
        visitNew(t, n);
        typeable = true;
        break;
      case Token.CALL:
        visitCall(t, n);
        typeable = !NodeUtil.isExpressionNode(parent);
        break;
      case Token.RETURN:
        visitReturn(t, n);
        typeable = false;
        break;
      case Token.DEC:
      case Token.INC:
        left = n.getFirstChild();
        validator.expectNumber(
            t, left, getJSType(left), "increment/decrement");
        ensureTyped(t, n, NUMBER_TYPE);
        break;
      case Token.NOT:
        ensureTyped(t, n, BOOLEAN_TYPE);
        break;
      case Token.VOID:
        ensureTyped(t, n, VOID_TYPE);
        break;
      case Token.TYPEOF:
        ensureTyped(t, n, STRING_TYPE);
        break;
      case Token.BITNOT:
        childType = getJSType(n.getFirstChild());
        if (!childType.matchesInt32Context()) {
          report(t, n, BIT_OPERATION, NodeUtil.opToStr(n.getType()),
              childType.toString());
        }
        ensureTyped(t, n, NUMBER_TYPE);
        break;
      case Token.POS:
      case Token.NEG:
        left = n.getFirstChild();
        validator.expectNumber(t, left, getJSType(left), "sign operator");
        ensureTyped(t, n, NUMBER_TYPE);
        break;
      case Token.EQ:
      case Token.NE: {
        leftType = getJSType(n.getFirstChild());
        rightType = getJSType(n.getLastChild());
        JSType leftTypeRestricted = leftType.restrictByNotNullOrUndefined();
        JSType rightTypeRestricted = rightType.restrictByNotNullOrUndefined();
        TernaryValue result =
            leftTypeRestricted.testForEquality(rightTypeRestricted);
        if (result != TernaryValue.UNKNOWN) {
          if (n.getType() == Token.NE) {
            result = result.not();
          }
          report(t, n, DETERMINISTIC_TEST, leftType.toString(),
              rightType.toString(), result.toString());
        }
        ensureTyped(t, n, BOOLEAN_TYPE);
        break;
      }
      case Token.SHEQ:
      case Token.SHNE: {
        leftType = getJSType(n.getFirstChild());
        rightType = getJSType(n.getLastChild());
        JSType leftTypeRestricted = leftType.restrictByNotNullOrUndefined();
        JSType rightTypeRestricted = rightType.restrictByNotNullOrUndefined();
        if (!leftTypeRestricted.canTestForShallowEqualityWith(
                rightTypeRestricted)) {
          report(t, n, DETERMINISTIC_TEST_NO_RESULT, leftType.toString(),
              rightType.toString());
        }
        ensureTyped(t, n, BOOLEAN_TYPE);
        break;
      }
      case Token.LT:
      case Token.LE:
      case Token.GT:
      case Token.GE:
        leftType = getJSType(n.getFirstChild());
        rightType = getJSType(n.getLastChild());
        if (rightType.isNumber()) {
          validator.expectNumber(
              t, n, leftType, "left side of numeric comparison");
        } else if (leftType.isNumber()) {
          validator.expectNumber(
              t, n, rightType, "right side of numeric comparison");
        } else if (leftType.matchesNumberContext() &&
                   rightType.matchesNumberContext()) {
        } else {
          String message = "left side of comparison";
          validator.expectString(t, n, leftType, message);
          validator.expectNotNullOrUndefined(
              t, n, leftType, message, getNativeType(STRING_TYPE));
          message = "right side of comparison";
          validator.expectString(t, n, rightType, message);
          validator.expectNotNullOrUndefined(
              t, n, rightType, message, getNativeType(STRING_TYPE));
        }
        ensureTyped(t, n, BOOLEAN_TYPE);
        break;
      case Token.IN:
        left = n.getFirstChild();
        right = n.getLastChild();
        leftType = getJSType(left);
        rightType = getJSType(right);
        validator.expectObject(t, n, rightType, "'in' requires an object");
        validator.expectString(t, left, leftType, "left side of 'in'");
        ensureTyped(t, n, BOOLEAN_TYPE);
        break;
      case Token.INSTANCEOF:
        left = n.getFirstChild();
        right = n.getLastChild();
        leftType = getJSType(left);
        rightType = getJSType(right).restrictByNotNullOrUndefined();
        validator.expectAnyObject(
            t, left, leftType, "deterministic instanceof yields false");
        validator.expectActualObject(
            t, right, rightType, "instanceof requires an object");
        ensureTyped(t, n, BOOLEAN_TYPE);
        break;
      case Token.ASSIGN:
        visitAssign(t, n);
        typeable = false;
        break;
      case Token.ASSIGN_LSH:
      case Token.ASSIGN_RSH:
      case Token.ASSIGN_URSH:
      case Token.ASSIGN_DIV:
      case Token.ASSIGN_MOD:
      case Token.ASSIGN_BITOR:
      case Token.ASSIGN_BITXOR:
      case Token.ASSIGN_BITAND:
      case Token.ASSIGN_SUB:
      case Token.ASSIGN_ADD:
      case Token.ASSIGN_MUL:
      case Token.LSH:
      case Token.RSH:
      case Token.URSH:
      case Token.DIV:
      case Token.MOD:
      case Token.BITOR:
      case Token.BITXOR:
      case Token.BITAND:
      case Token.SUB:
      case Token.ADD:
      case Token.MUL:
        visitBinaryOperator(n.getType(), t, n);
        break;
      case Token.DELPROP:
        if (!isReference(n.getFirstChild())) {
          report(t, n, BAD_DELETE);
        }
        ensureTyped(t, n, BOOLEAN_TYPE);
        break;
      case Token.CASE:
        JSType switchType = getJSType(parent.getFirstChild());
        JSType caseType = getJSType(n.getFirstChild());
        validator.expectSwitchMatchesCase(t, n, switchType, caseType);
        typeable = false;
        break;
      case Token.WITH: {
        Node child = n.getFirstChild();
        childType = getJSType(child);
        validator.expectObject(
            t, child, childType, "with requires an object");
        typeable = false;
        break;
      }
      case Token.FUNCTION:
        visitFunction(t, n);
        break;
      case Token.LABEL:
      case Token.LABEL_NAME:
      case Token.SWITCH:
      case Token.BREAK:
      case Token.CATCH:
      case Token.TRY:
      case Token.SCRIPT:
      case Token.EXPR_RESULT:
      case Token.BLOCK:
      case Token.EMPTY:
      case Token.DEFAULT:
      case Token.CONTINUE:
      case Token.DEBUGGER:
      case Token.THROW:
        typeable = false;
        break;
      case Token.DO:
      case Token.FOR:
      case Token.IF:
      case Token.WHILE:
        typeable = false;
        break;
      case Token.AND:
      case Token.HOOK:
      case Token.OBJECTLIT:
      case Token.OR:
        if (n.getJSType() != null) {
          ensureTyped(t, n);
        } else {
          if ((n.getType() == Token.OBJECTLIT)
              && (parent.getJSType() instanceof EnumType)) {
            ensureTyped(t, n, parent.getJSType());
          } else {
            ensureTyped(t, n);
          }
        }
        if (n.getType() == Token.OBJECTLIT) {
          for (Node key : n.children()) {
            visitObjLitKey(t, key, n);
          }
        }
        break;
      default:
        report(t, n, UNEXPECTED_TOKEN, Token.name(n.getType()));
        ensureTyped(t, n);
        break;
    }
    typeable = typeable && !inExterns;
    if (typeable) {
      doPercentTypedAccounting(t, n);
    }
    checkNoTypeCheckSection(n, false);
  }


    protected RealPointValuePair getSolution() {
        double[] coefficients = new double[getOriginalNumDecisionVariables()];
        Integer basicRow =
            getBasicRow(getNumObjectiveFunctions() + getOriginalNumDecisionVariables());
        double mostNegative = basicRow == null ? 0 : getEntry(basicRow, getRhsOffset());
        Set<Integer> basicRows = new HashSet<Integer>();
        for (int i = 0; i < coefficients.length; i++) {
            basicRow = getBasicRow(getNumObjectiveFunctions() + i);
            if (basicRows.contains(basicRow)) {
                coefficients[i] = 0;
            } else {
                basicRows.add(basicRow);
                coefficients[i] =
                    (basicRow == null ? 0 : getEntry(basicRow, getRhsOffset())) -
                    (restrictToNonNegative ? 0 : mostNegative);
            }
        }
        return new RealPointValuePair(coefficients, f.getValue(coefficients));
    }


    protected RealPointValuePair getSolution() {
        double[] coefficients = new double[getOriginalNumDecisionVariables()];
        Integer basicRow =
            getBasicRow(getNumObjectiveFunctions() + getOriginalNumDecisionVariables());
        double mostNegative = basicRow == null ? 0 : getEntry(basicRow, getRhsOffset());
        for (int i = 0; i < coefficients.length; i++) {
            basicRow = getBasicRow(getNumObjectiveFunctions() + i);
                coefficients[i] =
                    (basicRow == null ? 0 : getEntry(basicRow, getRhsOffset())) -
                    (restrictToNonNegative ? 0 : mostNegative);
            if (basicRow != null) {
                for (int j = getNumObjectiveFunctions(); j < getNumObjectiveFunctions() + i; j++) {
                    if (tableau.getEntry(basicRow, j) == 1) {
                         coefficients[i] = 0;
                    }
                }
            }
        }
        return new RealPointValuePair(coefficients, f.getValue(coefficients));
    }