class GasLimitCalculator {

    public BigInteger calculateBlockGasLimit(BigInteger parentGasLimit, BigInteger parentGasUsed, BigInteger minGasLimit, BigInteger targetGasLimit, boolean forceTarget) {
        Constants constants = config.getBlockchainConfig().getCommonConstants();

        BigInteger newGasLimit = parentGasLimit;

        // deltaMax = parentGasLimit / 1024
        // current Eth implementation substracts parentGasLimit / 1024 - 1
        // parent - deltaMax or parent + deltaMax are the limits
        // that should be accepted by consensus rules
        BigInteger deltaMax = parentGasLimit
                .divide(BigInteger.valueOf(constants.getGasLimitBoundDivisor()));

        // TODO: we should assert this before reaching this point
        if (targetGasLimit.compareTo(minGasLimit) < 0) {
            targetGasLimit = minGasLimit;
        }

        // In this case we just try to reach the target gasLimit independently of
        // the gas used
        if (forceTarget) {
            if (targetGasLimit.compareTo(parentGasLimit) < 0) {
                newGasLimit = newGasLimit.subtract(deltaMax);
                if (targetGasLimit.compareTo(newGasLimit) > 0) {
                    newGasLimit = targetGasLimit;
                }
                return newGasLimit;
            } else {
                newGasLimit = newGasLimit.add(deltaMax);
                if (targetGasLimit.compareTo(newGasLimit) < 0) {
                    newGasLimit = targetGasLimit;
                }
                return newGasLimit;
            }
        }

        // contrib = (parentGasUsed * 3 / 2) / 1024
        BigInteger contrib = parentGasUsed.multiply(BigInteger.valueOf(3));
        contrib = contrib.divide(BigInteger.valueOf(2));
        contrib = contrib.divide(BigInteger.valueOf(constants.getGasLimitBoundDivisor()));

        newGasLimit = newGasLimit.subtract(deltaMax);
        newGasLimit = newGasLimit.add(contrib);

        // Gas limit can never be lesser than a certain threshold
       if (newGasLimit.compareTo(minGasLimit) < 0) {
           newGasLimit = minGasLimit;
       }

       if (newGasLimit.compareTo(targetGasLimit) > 0) {
           newGasLimit = targetGasLimit;
       }

       // I've never done enough calculations, but neither of these two should ever happen
       if (newGasLimit.compareTo(parentGasLimit.subtract(deltaMax)) < 0) {
           newGasLimit = parentGasLimit;
       }

       if (newGasLimit.compareTo(parentGasLimit.add(deltaMax)) > 0) {
           newGasLimit = parentGasLimit;
       }

        return newGasLimit;
    }

    @Autowired public  GasLimitCalculator(UscSystemProperties config);

}