org.evosuite.instrumentation.ErrorConditionChecker.java Source code

Java tutorial

Introduction

Here is the source code for org.evosuite.instrumentation.ErrorConditionChecker.java

Source

/**
 * Copyright (C) 2011,2012 Gordon Fraser, Andrea Arcuri and EvoSuite
 * contributors
 * 
 * This file is part of EvoSuite.
 * 
 * EvoSuite is free software: you can redistribute it and/or modify it under the
 * terms of the GNU Public License as published by the Free Software Foundation,
 * either version 3 of the License, or (at your option) any later version.
 * 
 * EvoSuite is distributed in the hope that it will be useful, but WITHOUT ANY
 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
 * A PARTICULAR PURPOSE. See the GNU Public License for more details.
 * 
 * You should have received a copy of the GNU Public License along with
 * EvoSuite. If not, see <http://www.gnu.org/licenses/>.
 */
/**
 * 
 */
package org.evosuite.instrumentation;

import java.math.BigDecimal;

import org.objectweb.asm.Opcodes;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * <p>
 * ErrorConditionChecker class.
 * </p>
 * 
 * @author fraser
 */
public class ErrorConditionChecker {

    private static final Logger logger = LoggerFactory.getLogger(ErrorConditionChecker.class);

    /**
     * <p>
     * scale
     * </p>
     * 
     * @param value
     *            a float.
     * @return a int.
     */
    public static int scale(float value) {
        return (Integer.MAX_VALUE - 2) * (int) Math.ceil((value / (value + 1.0F)));
    }

    /**
     * <p>
     * scale
     * </p>
     * 
     * @param value
     *            a double.
     * @return a int.
     */
    public static int scale(double value) {
        return (Integer.MAX_VALUE - 2) * (int) Math.ceil((value / (value + 1.0)));
    }

    /**
     * <p>
     * scale
     * </p>
     * 
     * @param value
     *            a long.
     * @return a int.
     */
    public static int scale(long value) {
        return (Integer.MAX_VALUE - 2) * (int) Math.ceil((value / (value + 1.0)));
    }

    public static int scaleTo(double value, int max) {
        return (int) (Math.ceil(max * (1.0 * value / (value + 1.0))));
    }

    /**
     * <p>
     * overflowDistance
     * </p>
     * 
     * @param op1
     *            a int.
     * @param op2
     *            a int.
     * @param opcode
     *            a int.
     * @return a int.
     */
    public static int overflowDistance(int op1, int op2, int opcode) {
        switch (opcode) {

        case Opcodes.IADD:
            int result = overflowDistanceAdd(op1, op2);
            logger.debug("O: {} + {} = {} -> {}", op1, op2, op1 + op2, result);
            return result;

        case Opcodes.ISUB:
            return overflowDistanceSub(op1, op2);

        case Opcodes.IMUL:
            return overflowDistanceMul(op1, op2);

        case Opcodes.IDIV:
            return overflowDistanceDiv(op1, op2);
        }
        return Integer.MAX_VALUE;
    }

    protected final static int HALFWAY = Integer.MAX_VALUE / 2;

    protected static int overflowDistanceAdd(int op1, int op2) {
        int result = op1 + op2;
        if (op1 > 0 && op2 > 0) {
            // result has to be < 0 for overflow
            if (result < 0)
                return result;
            else {
                int retVal = HALFWAY - scaleTo(result, HALFWAY);
                if (retVal != 0)
                    return retVal;
                else
                    return 1;
            }
        } else if (op1 < 0 && op2 < 0) {
            // if both are negative then both need to be increased
            return HALFWAY + scaleTo(Math.abs((long) op1) + Math.abs((long) op2), HALFWAY);
        } else if (op1 >= 0 && op2 < 0) {
            // If only one is negative, then optimize that to be positive
            return HALFWAY + scaleTo(Math.abs(op2), HALFWAY);
        } else if (op1 < 0 && op2 >= 0) {
            // If only one is negative, then optimize that to be positive
            return HALFWAY + scaleTo(Math.abs(op1), HALFWAY);
        } else {
            // At least one of them is zero, and the sum is larger or equals than 0
            return 1 + HALFWAY - scaleTo(result, HALFWAY);
        }
    }

    protected static int underflowDistanceAdd(int op1, int op2) {
        int result = op1 + op2;
        if (op1 <= 0 && op2 <= 0) {
            if (op1 == Integer.MIN_VALUE && op2 == Integer.MIN_VALUE) {
                return Integer.MIN_VALUE;
            } else {
                // result has to be < 0 for overflow
                return result > 0 ? -result : HALFWAY - scaleTo(Math.abs((long) result), HALFWAY) + 1;
            }
        } else if (op1 > 0 && op2 > 0) {
            // if both are positive then both need to be decreased
            return HALFWAY + scaleTo(Math.abs((long) op1) + Math.abs((long) op2), HALFWAY);
        } else if (op1 >= 0 && op2 < 0) {
            return HALFWAY + scaleTo(Math.abs(op1), HALFWAY);
        } else if (op1 < 0 && op2 >= 0) {
            return HALFWAY + scaleTo(Math.abs(op2), HALFWAY);
        } else {
            // Unreachable
            return Integer.MAX_VALUE;
        }
    }

    protected static int overflowDistanceSub(int op1, int op2) {
        int result = op1 - op2;
        if (op1 >= 0 && op2 <= 0) {
            // result has to be < 0 for overflow
            return result < 0 ? result : HALFWAY + 1 - scaleTo(result, HALFWAY);
        } else if (op1 < 0 && op2 > 0) {
            // if both are negative then an overflow will be difficult
            return HALFWAY + scaleTo(Math.abs((long) op1) + Math.abs((long) op2), HALFWAY);
        } else if (op1 >= 0 && op2 > 0) {
            // In this case we can't have an overflow yet
            return HALFWAY + scaleTo(op2, HALFWAY);
        } else if (op1 < 0 && op2 <= 0) {
            return HALFWAY + scaleTo(Math.abs((long) op1), HALFWAY);
        } else {
            // At least one of them is zero, and the sum is larger or equals than 0
            return 1 + HALFWAY - scaleTo(result, HALFWAY);
        }
    }

    protected static int underflowDistanceSub(int op1, int op2) {
        int result = op1 - op2;
        if (op1 <= 0 && op2 >= 0) {
            return result > 0 ? -result : HALFWAY + 1 - scaleTo(result, HALFWAY);
        } else if (op1 > 0 && op2 < 0) {
            return HALFWAY + scaleTo(Math.abs((long) op1) + Math.abs((long) op2), HALFWAY);
        } else if (op1 >= 0 && op2 > 0) {
            // In this case we can't have an overflow yet
            return HALFWAY + scaleTo(op1, HALFWAY);
        } else if (op1 < 0 && op2 <= 0) {
            return HALFWAY + scaleTo(Math.abs((long) op2), HALFWAY);
        } else {
            // Not sure if this can be reached
            return 1 + HALFWAY - scaleTo(result, HALFWAY);
        }
    }

    protected static int overflowDistanceMul(int op1, int op2) {
        int result = op1 * op2;
        if (op1 > 0 && op2 > 0) {
            // result has to be < 0 for overflow
            // the result can be so large that it overflows so much to be positive again
            // so we need to use longs to check this
            long longResult = (long) op1 * (long) (op2);
            if (longResult > Integer.MAX_VALUE) {
                if (result <= 0)
                    return result;
                else
                    return Integer.MIN_VALUE;
            } else {
                int retval = HALFWAY - scaleTo(result, HALFWAY);
                if (retval > 0)
                    return retval;
                else
                    return 1;
            }
            //System.out.println(op1+" * "+op2 +" -> "+result +" -> "+(HALFWAY - scaleTo(result, HALFWAY)));
            //return result <= 0 ? result : HALFWAY - scaleTo(result, HALFWAY);
        } else if (op1 < 0 && op2 < 0) {
            return result <= 0 ? result : HALFWAY - scaleTo(result, HALFWAY);
        } else if (op1 > 0 && op2 < 0) {
            // In this case we can't have an overflow yet
            return HALFWAY + scaleTo(Math.abs(op2), HALFWAY);
        } else if (op1 < 0 && op2 > 0) {
            return HALFWAY + scaleTo(Math.abs(op1), HALFWAY);
        } else {
            // One of them is zero
            return HALFWAY;
        }
    }

    protected static int underflowDistanceMul(int op1, int op2) {
        int result = op1 * op2;
        if (op1 > 0 && op2 < 0) {
            return result >= 0 ? -result : HALFWAY - scaleTo(result, HALFWAY);
        } else if (op1 < 0 && op2 > 0) {
            return result >= 0 ? -result : HALFWAY - scaleTo(result, HALFWAY);
        } else if (op1 > 0 && op2 > 0) {
            return HALFWAY + scaleTo(Math.min(op1, op2), HALFWAY);
        } else if (op1 < 0 && op2 < 0) {
            return HALFWAY + scaleTo(Math.abs(Math.max(op1, op2)), HALFWAY);
        } else {
            // One of them is zero
            return HALFWAY;
        }
    }

    protected static int overflowDistanceDiv(int op1, int op2) {
        if (op1 == Integer.MIN_VALUE && op2 == -1)
            return -1;
        else {
            // If op2 is MAX_VALUE then -1 -op2 will give us an overflow
            if (op2 == Integer.MAX_VALUE)
                return Integer.MAX_VALUE;

            // TODO There may be an overflow here
            return scaleTo(Math.abs(Integer.MIN_VALUE - op1), HALFWAY) + scaleTo(Math.abs(-1 - op2), HALFWAY);
        }
    }

    public static int underflowDistance(int op1, int op2, int opcode) {
        switch (opcode) {

        case Opcodes.IADD:
            int result = underflowDistanceAdd(op1, op2);
            logger.debug("U: {} + {} = {} -> {}", op1, op2, op1 + op2, result);
            return result;

        case Opcodes.ISUB:
            return underflowDistanceSub(op1, op2);

        case Opcodes.IMUL:
            return underflowDistanceMul(op1, op2);

        }
        return Integer.MAX_VALUE;
    }

    public static int overflowDistance(float op1, float op2, int opcode) {
        switch (opcode) {

        case Opcodes.FADD:
            return overflowDistanceAdd(op1, op2);

        case Opcodes.FSUB:
            return overflowDistanceSub(op1, op2);

        case Opcodes.FMUL:
            return overflowDistanceMul(op1, op2);

        case Opcodes.FDIV:
            return overflowDistanceDiv(op1, op2);
        }
        return Integer.MAX_VALUE;
    }

    public static int underflowDistance(float op1, float op2, int opcode) {
        switch (opcode) {

        case Opcodes.FADD:
            return underflowDistanceAdd(op1, op2);

        case Opcodes.FSUB:
            return underflowDistanceSub(op1, op2);

        case Opcodes.FMUL:
            return underflowDistanceMul(op1, op2);

        }
        return Integer.MAX_VALUE;
    }

    protected static int overflowDistanceAdd(float op1, float op2) {
        float result = op1 + op2;
        if (op1 > 0 && op2 > 0) {
            // result has to be < 0 for overflow
            return result == Float.POSITIVE_INFINITY ? -1 : HALFWAY - scaleTo(result, HALFWAY) + 1;

        } else if (op1 < 0 && op2 < 0) {
            // if both are negative then both need to be increased
            return result == Float.NEGATIVE_INFINITY ? Integer.MAX_VALUE
                    : HALFWAY + scaleTo((double) op1 + (double) op2, HALFWAY);
        } else if (op1 >= 0 && op2 < 0) {
            // If only one is negative, then optimize that to be positive
            return HALFWAY + scaleTo(Math.abs(op2), HALFWAY);
        } else if (op1 < 0 && op2 >= 0) {
            // If only one is negative, then optimize that to be positive
            return HALFWAY + scaleTo(Math.abs(op1), HALFWAY);
        } else {
            // At least one of them is zero, and the sum is larger or equals than 0
            return 1 + HALFWAY - scaleTo(result, HALFWAY);
        }
    }

    protected static int underflowDistanceAdd(float op1, float op2) {
        float result = op1 + op2;
        if (op1 <= 0 && op2 <= 0) {
            // result has to be < 0 for overflow
            return result == Float.NEGATIVE_INFINITY ? -1
                    : HALFWAY - scaleTo(Math.abs((double) result), HALFWAY) + 1;
        } else if (op1 > 0 && op2 > 0) {
            // if both are positive then both need to be decreased
            return result == Float.POSITIVE_INFINITY ? Integer.MAX_VALUE
                    : HALFWAY + scaleTo(Math.abs((double) op1) + Math.abs((double) op2), HALFWAY);
        } else if (op1 >= 0 && op2 < 0) {
            return HALFWAY + scaleTo(op1, HALFWAY);
        } else if (op1 < 0 && op2 >= 0) {
            return HALFWAY + scaleTo(op2, HALFWAY);
        } else {
            // Unreachable
            return Integer.MAX_VALUE;
        }
    }

    protected static int overflowDistanceSub(float op1, float op2) {
        float result = op1 - op2;
        if (op1 >= 0 && op2 <= 0) {
            // result has to be < 0 for overflow
            return result == Float.POSITIVE_INFINITY ? -1 : HALFWAY + 1 - scaleTo(result, HALFWAY);
        } else if (op1 < 0 && op2 > 0) {
            // if both are negative then an overflow will be difficult
            return result == Float.NEGATIVE_INFINITY ? Integer.MAX_VALUE
                    : HALFWAY + scaleTo(Math.abs((double) op1) + Math.abs((double) op2), HALFWAY);
        } else if (op1 >= 0 && op2 > 0) {
            // In this case we can't have an overflow yet
            return HALFWAY + scaleTo(op2, HALFWAY);
        } else if (op1 < 0 && op2 <= 0) {
            return HALFWAY + scaleTo(Math.abs((double) op1), HALFWAY);
        } else {
            // At least one of them is zero, and the sum is larger or equals than 0
            return 1 + HALFWAY - scaleTo(result, HALFWAY);
        }
    }

    protected static int underflowDistanceSub(float op1, float op2) {
        float result = op1 - op2;
        if (op1 <= 0 && op2 >= 0) {
            return result == Float.NEGATIVE_INFINITY ? -1 : HALFWAY + 1 - scaleTo(result, HALFWAY);
        } else if (op1 > 0 && op2 < 0) {
            return result == Float.POSITIVE_INFINITY ? Integer.MAX_VALUE
                    : HALFWAY + scaleTo(Math.abs((double) op1) + Math.abs((double) op2), HALFWAY);
        } else if (op1 >= 0 && op2 > 0) {
            // In this case we can't have an overflow yet
            return HALFWAY + scaleTo(Math.abs((double) op1), HALFWAY);
        } else if (op1 < 0 && op2 <= 0) {
            return HALFWAY + scaleTo(Math.abs((double) op2), HALFWAY);
        } else {
            // Not sure if this can be reached
            return 1 + HALFWAY - scaleTo(result, HALFWAY);
        }
    }

    protected static int overflowDistanceMul(float op1, float op2) {
        float result = op1 * op2;
        if (op1 > 0 && op2 > 0) {
            // result has to be < 0 for overflow
            return result == Float.POSITIVE_INFINITY ? -1 : HALFWAY - scaleTo(result, HALFWAY);
        } else if (op1 < 0 && op2 < 0) {
            return result == Float.POSITIVE_INFINITY ? -1 : HALFWAY - scaleTo(result, HALFWAY) + 1;
        } else if (op1 > 0 && op2 < 0) {
            // In this case we can't have an overflow yet
            return result == Float.NEGATIVE_INFINITY ? Integer.MAX_VALUE
                    : HALFWAY + scaleTo(Math.abs(op2), HALFWAY);
        } else if (op1 < 0 && op2 > 0) {
            return result == Float.NEGATIVE_INFINITY ? Integer.MAX_VALUE
                    : HALFWAY + scaleTo(Math.abs(op1), HALFWAY);
        } else {
            // One of them is zero
            return HALFWAY;
        }
    }

    protected static int underflowDistanceMul(float op1, float op2) {
        float result = op1 * op2;
        if (op1 > 0 && op2 < 0) {
            return result == Float.NEGATIVE_INFINITY ? -1 : HALFWAY - scaleTo(result, HALFWAY);
        } else if (op1 < 0 && op2 > 0) {
            return result == Float.NEGATIVE_INFINITY ? -1 : HALFWAY - scaleTo(result, HALFWAY);
        } else if (op1 > 0 && op2 > 0) {
            return result == Float.POSITIVE_INFINITY ? Integer.MAX_VALUE
                    : HALFWAY + scaleTo(Math.min(op1, op2), HALFWAY);
        } else if (op1 < 0 && op2 < 0) {
            return result == Float.POSITIVE_INFINITY ? Integer.MAX_VALUE
                    : HALFWAY + scaleTo(Math.abs(Math.max(op1, op2)), HALFWAY);
        } else {
            // One of them is zero
            return HALFWAY;
        }
    }

    protected static int overflowDistanceDiv(float op1, float op2) {
        if (op1 == -Float.MAX_VALUE && op2 == -1.0)
            return -1;
        else
            // TODO There may be an overflow here
            return scaleTo(Math.abs(-Float.MAX_VALUE - op1), HALFWAY) + scaleTo(Math.abs(-1.0 - op2), HALFWAY);
    }

    public static int overflowDistance(double op1, double op2, int opcode) {
        switch (opcode) {

        case Opcodes.DADD:
            return overflowDistanceAdd(op1, op2);

        case Opcodes.DSUB:
            return overflowDistanceSub(op1, op2);

        case Opcodes.DMUL:
            return overflowDistanceMul(op1, op2);

        case Opcodes.DDIV:
            return overflowDistanceDiv(op1, op2);
        }
        return Integer.MAX_VALUE;
    }

    public static int underflowDistance(double op1, double op2, int opcode) {
        switch (opcode) {

        case Opcodes.DADD:
            return underflowDistanceAdd(op1, op2);

        case Opcodes.DSUB:
            return underflowDistanceSub(op1, op2);

        case Opcodes.DMUL:
            return underflowDistanceMul(op1, op2);

        }
        return Integer.MAX_VALUE;
    }

    protected static int overflowDistanceAdd(double op1, double op2) {
        double result = op1 + op2;
        if (op1 > 0 && op2 > 0) {
            // result has to be < 0 for overflow
            return result == Double.POSITIVE_INFINITY ? -1 : HALFWAY - scaleTo(result, HALFWAY) + 1;

        } else if (op1 < 0 && op2 < 0) {
            return result == Double.NEGATIVE_INFINITY ? Integer.MAX_VALUE : HALFWAY - scaleTo(result, HALFWAY) + 1;
        } else if (op1 >= 0 && op2 < 0) {
            // If only one is negative, then optimize that to be positive
            return HALFWAY + scaleTo(Math.abs(op2), HALFWAY);
        } else if (op1 < 0 && op2 >= 0) {
            // If only one is negative, then optimize that to be positive
            return HALFWAY + scaleTo(Math.abs(op1), HALFWAY);
        } else {
            // At least one of them is zero, and the sum is larger or equals than 0
            return 1 + HALFWAY - scaleTo(result, HALFWAY);
        }
    }

    protected static int underflowDistanceAdd(double op1, double op2) {
        double result = op1 + op2;
        if (op1 <= 0 && op2 <= 0) {
            // result has to be < 0 for overflow
            return result == Double.NEGATIVE_INFINITY ? -1 : HALFWAY - scaleTo(Math.abs(result), HALFWAY) + 1;
        } else if (op1 > 0 && op2 > 0) {
            // if both are positive then both need to be decreased
            return result == Double.POSITIVE_INFINITY ? Integer.MAX_VALUE
                    : HALFWAY + scaleTo(Math.abs(op1) + Math.abs(op2), HALFWAY);
        } else if (op1 >= 0 && op2 < 0) {
            return HALFWAY + scaleTo(op1, HALFWAY);
        } else if (op1 < 0 && op2 >= 0) {
            return HALFWAY + scaleTo(op2, HALFWAY);
        } else {
            // Unreachable
            return Integer.MAX_VALUE;
        }
    }

    protected static int overflowDistanceSub(double op1, double op2) {
        double result = op1 - op2;
        if (op1 >= 0 && op2 <= 0) {
            // result has to be < 0 for overflow
            return result == Double.POSITIVE_INFINITY ? -1 : HALFWAY + 1 - scaleTo(result, HALFWAY);
        } else if (op1 < 0 && op2 > 0) {
            // if both are negative then an overflow will be difficult
            return result == Double.NEGATIVE_INFINITY ? Integer.MAX_VALUE
                    : HALFWAY + scaleTo(Math.abs(op1) + Math.abs(op2), HALFWAY);
        } else if (op1 >= 0 && op2 > 0) {
            // In this case we can't have an overflow yet
            return HALFWAY + scaleTo(op2, HALFWAY);
        } else if (op1 < 0 && op2 <= 0) {
            return HALFWAY + scaleTo(Math.abs(op1), HALFWAY);
        } else {
            // At least one of them is zero, and the sum is larger or equals than 0
            return 1 + HALFWAY - scaleTo(result, HALFWAY);
        }
    }

    protected static int underflowDistanceSub(double op1, double op2) {
        double result = op1 - op2;
        if (op1 <= 0 && op2 >= 0) {
            return result == Double.NEGATIVE_INFINITY ? -1 : HALFWAY + 1 - scaleTo(result, HALFWAY);
        } else if (op1 > 0 && op2 < 0) {
            return result == Double.POSITIVE_INFINITY ? Integer.MAX_VALUE
                    : HALFWAY + scaleTo(Math.abs(op1) + Math.abs(op2), HALFWAY);
        } else if (op1 >= 0 && op2 > 0) {
            // In this case we can't have an overflow yet
            return HALFWAY + scaleTo(Math.abs(op1), HALFWAY);
        } else if (op1 < 0 && op2 <= 0) {
            return HALFWAY + scaleTo(Math.abs(op2), HALFWAY);
        } else {
            // Not sure if this can be reached
            return 1 + HALFWAY - scaleTo(result, HALFWAY);
        }
    }

    protected static int overflowDistanceMul(double op1, double op2) {
        double result = op1 * op2;
        if (op1 > 0 && op2 > 0) {
            // result has to be < 0 for overflow
            return result == Double.POSITIVE_INFINITY ? -1 : HALFWAY - scaleTo(result, HALFWAY);
        } else if (op1 < 0 && op2 < 0) {
            return result == Double.POSITIVE_INFINITY ? -1 : HALFWAY - scaleTo(result, HALFWAY) + 1;
        } else if (op1 > 0 && op2 < 0) {
            // In this case we can't have an overflow yet
            return result == Double.NEGATIVE_INFINITY ? Integer.MAX_VALUE
                    : HALFWAY + scaleTo(Math.abs(op2), HALFWAY);
        } else if (op1 < 0 && op2 > 0) {
            return result == Double.NEGATIVE_INFINITY ? Integer.MAX_VALUE
                    : HALFWAY + scaleTo(Math.abs(op1), HALFWAY);
        } else {
            // One of them is zero
            return HALFWAY;
        }
    }

    protected static int underflowDistanceMul(double op1, double op2) {
        double result = op1 * op2;
        if (op1 > 0 && op2 < 0) {
            return result == Double.NEGATIVE_INFINITY ? -1 : HALFWAY - scaleTo(result, HALFWAY);
        } else if (op1 < 0 && op2 > 0) {
            return result == Double.NEGATIVE_INFINITY ? -1 : HALFWAY - scaleTo(result, HALFWAY);
        } else if (op1 > 0 && op2 > 0) {
            return result == Double.POSITIVE_INFINITY ? Integer.MAX_VALUE
                    : HALFWAY + scaleTo(Math.min(op1, op2), HALFWAY);
        } else if (op1 < 0 && op2 < 0) {
            return result == Double.POSITIVE_INFINITY ? Integer.MAX_VALUE
                    : HALFWAY + scaleTo(Math.abs(Math.max(op1, op2)), HALFWAY);
        } else {
            // One of them is zero
            return HALFWAY;
        }
    }

    protected static int overflowDistanceDiv(double op1, double op2) {
        if (op1 == -Double.MAX_VALUE && op2 == -1.0)
            return -1;
        else
            // TODO There may be an overflow here
            return scaleTo(Math.abs(-Double.MAX_VALUE - op1), HALFWAY) + scaleTo(Math.abs(-1.0 - op2), HALFWAY);
    }

    public static int overflowDistance(long op1, long op2, int opcode) {
        switch (opcode) {

        case Opcodes.LADD:
            return overflowDistanceAdd(op1, op2);

        case Opcodes.LSUB:
            return overflowDistanceSub(op1, op2);

        case Opcodes.LMUL:
            return overflowDistanceMul(op1, op2);

        case Opcodes.LDIV:
            return overflowDistanceDiv(op1, op2);
        }
        return Integer.MAX_VALUE;
    }

    public static int underflowDistance(long op1, long op2, int opcode) {
        switch (opcode) {

        case Opcodes.LADD:
            return underflowDistanceAdd(op1, op2);

        case Opcodes.LSUB:
            return underflowDistanceSub(op1, op2);

        case Opcodes.LMUL:
            return underflowDistanceMul(op1, op2);

        }
        return Integer.MAX_VALUE;
    }

    protected static int overflowDistanceAdd(long op1, long op2) {
        long result = op1 + op2;
        if (op1 > 0 && op2 > 0) {
            // result has to be < 0 for overflow
            return result < 0 ? -scaleTo(Math.abs(result), HALFWAY) : HALFWAY - scaleTo(result, HALFWAY) + 1;

        } else if (op1 < 0 && op2 < 0) {
            return result > 0 ? Integer.MAX_VALUE : HALFWAY - scaleTo(result, HALFWAY) + 1;
        } else if (op1 >= 0 && op2 < 0) {
            // If only one is negative, then optimize that to be positive
            return HALFWAY + scaleTo(Math.abs(op2), HALFWAY);
        } else if (op1 < 0 && op2 >= 0) {
            // If only one is negative, then optimize that to be positive
            return HALFWAY + scaleTo(Math.abs(op1), HALFWAY);
        } else {
            // At least one of them is zero, and the sum is larger or equals than 0
            return 1 + HALFWAY - scaleTo(result, HALFWAY);
        }
    }

    protected static int underflowDistanceAdd(long op1, long op2) {
        long result = op1 + op2;
        if (op1 <= 0L && op2 <= 0L) {
            // result has to be < 0 for underflow
            if (result > 0) {
                int retval = -scaleTo(result, HALFWAY);
                if (retval < 0)
                    return retval;
                else
                    return -1;
            } else if (result == 0L) {
                if (op1 != 0 && op2 != 0) {
                    return -1;
                } else {
                    return HALFWAY - scaleTo(Math.abs(result), HALFWAY);
                }
            } else {
                int intResult = HALFWAY - scaleTo(Math.abs(result), HALFWAY);
                if (intResult == 0)
                    return 1;
                else
                    return intResult;
            }
        } else if (op1 > 0 && op2 > 0) {
            // if both are positive then both need to be decreased
            return result < 0 ? Integer.MAX_VALUE : HALFWAY + scaleTo(Math.abs(op1) + Math.abs(op2), HALFWAY);
        } else if (op1 >= 0 && op2 < 0) {
            return HALFWAY + scaleTo(op1, HALFWAY);
        } else if (op1 < 0 && op2 >= 0) {
            return HALFWAY + scaleTo(op2, HALFWAY);
        } else {
            // Unreachable
            return Integer.MAX_VALUE;
        }
    }

    protected static int overflowDistanceSub(long op1, long op2) {
        long result = op1 - op2;
        if (op1 >= 0 && op2 <= 0) {
            // result has to be < 0 for overflow
            return result < 0 ? -scaleTo(Math.abs(result), HALFWAY) : HALFWAY + 1 - scaleTo(result, HALFWAY);
        } else if (op1 < 0 && op2 > 0) {
            // if both are negative then an overflow will be difficult
            return result > 0 ? Integer.MAX_VALUE : HALFWAY + scaleTo(Math.abs(op1) + Math.abs(op2), HALFWAY);
        } else if (op1 >= 0 && op2 > 0) {
            // In this case we can't have an overflow yet
            return HALFWAY + scaleTo(op2, HALFWAY);
        } else if (op1 < 0 && op2 <= 0) {
            return HALFWAY + scaleTo(Math.abs(op1), HALFWAY);
        } else {
            // At least one of them is zero, and the sum is larger or equals than 0
            return 1 + HALFWAY - scaleTo(result, HALFWAY);
        }
    }

    protected static int underflowDistanceSub(long op1, long op2) {
        long result = op1 - op2;
        if (op1 <= 0 && op2 >= 0) {
            return result > 0 ? -scaleTo(result, HALFWAY) : HALFWAY + 1 - scaleTo(result, HALFWAY);
        } else if (op1 > 0 && op2 < 0) {
            return result < 0 ? Integer.MAX_VALUE : HALFWAY + scaleTo(Math.abs(op1) + Math.abs(op2), HALFWAY);
        } else if (op1 >= 0 && op2 > 0) {
            // In this case we can't have an overflow yet
            return HALFWAY + scaleTo(Math.abs(op1), HALFWAY);
        } else if (op1 < 0 && op2 <= 0) {
            return 1 + HALFWAY + scaleTo(Math.abs(op2), HALFWAY);
        } else {
            // Not sure if this can be reached
            return 1 + HALFWAY - scaleTo(result, HALFWAY);
        }
    }

    protected static int overflowDistanceMul(long op1, long op2) {
        long result = op1 * op2;
        if ((op1 > 0 && op2 > 0) || (op1 < 0 && op2 < 0)) {
            BigDecimal bigDecimal = new BigDecimal(op1).multiply(new BigDecimal(op2));
            BigDecimal maxResult = new BigDecimal(Long.MAX_VALUE);

            if (bigDecimal.compareTo(maxResult) > 0) {
                int intResult = -scaleTo(Math.abs(result), HALFWAY);
                if (result <= 0)
                    return intResult;
                else
                    return Integer.MIN_VALUE;
            } else {
                int retval = HALFWAY - scaleTo(result, HALFWAY);
                if (retval > 0)
                    return retval;
                else
                    return 1;
            }
            // result has to be < 0 for overflow
        } else if (op1 > 0 && op2 < 0) {
            // In this case we can't have an overflow yet
            return result > 0 ? Integer.MAX_VALUE : HALFWAY + scaleTo(Math.abs(op2), HALFWAY);
        } else if (op1 < 0 && op2 > 0) {
            return result > 0 ? Integer.MAX_VALUE : HALFWAY + scaleTo(Math.abs(op1), HALFWAY);
        } else {
            // One of them is zero
            return HALFWAY;
        }
    }

    protected static int underflowDistanceMul(long op1, long op2) {
        long result = op1 * op2;
        BigDecimal bigDecimal = new BigDecimal(op1).multiply(new BigDecimal(op2));
        BigDecimal minResult = new BigDecimal(Long.MIN_VALUE);

        if (bigDecimal.compareTo(minResult) < 0) {
            int intResult = -scaleTo(Math.abs(result), HALFWAY);
            if (result <= 0)
                return intResult;
            else
                return Integer.MIN_VALUE;
        } else {
            int retval = HALFWAY - scaleTo(result, HALFWAY);
            if (retval > 0)
                return retval;
            else
                return 1;
        }
    }

    protected static int overflowDistanceDiv(long op1, long op2) {
        if (op1 == Long.MIN_VALUE && op2 == -1L)
            return -1;
        else
            // TODO There may be an overflow here
            return scaleTo(Math.abs(Long.MIN_VALUE - op1), HALFWAY) + scaleTo(Math.abs(-1L - op2), HALFWAY);
    }

}