/******************************************************************************* * Copyright (c) 2009-2013 CWI * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * * * Arnold Lankamp - interfaces and implementation * * Michael Steindorfer - Michael.Steindorfer@cwi.nl - CWI *******************************************************************************/ package org.rascalmpl.value.impl.primitive; import java.math.BigDecimal; import java.math.BigInteger; import org.rascalmpl.value.IBool; import org.rascalmpl.value.IInteger; import org.rascalmpl.value.INumber; import org.rascalmpl.value.IRational; import org.rascalmpl.value.IReal; import org.rascalmpl.value.IValue; import org.rascalmpl.value.type.Type; import org.rascalmpl.value.type.TypeFactory; import org.rascalmpl.value.visitors.IValueVisitor; /** * Implementation for IInteger. * <br /><br /> * Integer values that fall outside the 32-bit range will be store in BigIntegerValue instead. * * @author Arnold Lankamp */ /*package*/ class IntegerValue extends AbstractNumberValue implements IInteger, ICanBecomeABigInteger{ private final static Type INTEGER_TYPE = TypeFactory.getInstance().integerType(); private final static String INTEGER_MAX_STRING = "2147483647"; private final static String NEGATIVE_INTEGER_MAX_STRING = "-2147483648"; private final static int SEVEN_BITS_MASK = 0x0000007f; private final static int FIFTEEN_BITS_MASK = 0x00007fff; private final static int TWENTYTHREE_BITS_MASK = 0x007fffff; public final static IInteger INTEGER_ONE = newInteger(1); protected final int value; /* * TODO: Unify IntegerValue and BigIntegerValue in same java class file. */ /*package*/ static IInteger newInteger(BigInteger value) { if (value.bitLength() > 31) { return new BigIntegerValue(value); } return new IntegerValue(value.intValue()); } /*package*/ static IInteger newInteger(int value) { return new IntegerValue(value); } /*package*/ static IInteger newInteger(String integerValue) { if (integerValue.startsWith("-")) { if (integerValue.length() < 11 || (integerValue.length() == 11 && integerValue.compareTo(NEGATIVE_INTEGER_MAX_STRING) <= 0)) { return new IntegerValue(Integer.parseInt(integerValue)); } return new BigIntegerValue(new BigInteger(integerValue)); } if (integerValue.length() < 10 || (integerValue.length() == 10 && integerValue.compareTo(INTEGER_MAX_STRING) <= 0)) { return new IntegerValue(Integer.parseInt(integerValue)); } return new BigIntegerValue(new BigInteger(integerValue)); } /*package*/ static IInteger newInteger(long value) { if (((value & 0x000000007fffffffL) == value) || ((value & 0xffffffff80000000L) == 0xffffffff80000000L)) { return newInteger((int) value); } else { byte[] valueData = new byte[8]; valueData[0] = (byte) ((value >>> 56) & 0xff); valueData[1] = (byte) ((value >>> 48) & 0xff); valueData[2] = (byte) ((value >>> 40) & 0xff); valueData[3] = (byte) ((value >>> 32) & 0xff); valueData[4] = (byte) ((value >>> 24) & 0xff); valueData[5] = (byte) ((value >>> 16) & 0xff); valueData[6] = (byte) ((value >>> 8) & 0xff); valueData[7] = (byte) (value & 0xff); return newInteger(valueData); } } /*package*/ static IInteger newInteger(byte[] integerData) { if (integerData.length <= 4) { int value = 0; for (int i = integerData.length - 1, j = 0; i >= 0; i--, j++) { value |= ((integerData[i] & 0xff) << (j * 8)); } return new IntegerValue(value); } return new BigIntegerValue(new BigInteger(integerData)); } private IntegerValue(int value){ super(); this.value = value; } @Override public IInteger toInteger() { return this; } @Override public Type getType(){ return INTEGER_TYPE; } @Override public int intValue(){ return value; } @Override public long longValue(){ return value; } @Override public double doubleValue(){ return value; } @Override public IReal toReal(int precision){ return BigDecimalValue.newReal(BigDecimal.valueOf(value)); } @Override public byte[] getTwosComplementRepresentation(){ if((value & SEVEN_BITS_MASK) == value){ byte[] data = new byte[1]; data[0] = (byte) (value & 0x7f); return data; }else if((value & FIFTEEN_BITS_MASK) == value){ byte[] data = new byte[2]; data[0] = (byte) ((value >> 8) & 0x7f); data[1] = (byte) (value & 0xff); return data; }else if((value & TWENTYTHREE_BITS_MASK) == value){ byte[] data = new byte[3]; data[0] = (byte) ((value >> 16) & 0x7f); data[1] = (byte) ((value >> 8) & 0xff); data[2] = (byte) (value & 0xff); return data; } byte[] data = new byte[4]; data[0] = (byte) ((value >> 24) & 0xff); data[1] = (byte) ((value >> 16) & 0xff); data[2] = (byte) ((value >> 8) & 0xff); data[3] = (byte) (value & 0xff); return data; } @Override public BigInteger toBigInteger(){ return new BigInteger(getTwosComplementRepresentation()); } @Override public boolean isEqual(IValue other) { return equals(other); } @Override public IInteger add(IInteger other){ if(value == 0) return other; if(other instanceof BigIntegerValue){ return other.add(this); } int otherIntValue = other.intValue(); if(otherIntValue == 0) return this; int result = value + otherIntValue; if((value < 0) && (otherIntValue < 0) && (result >= 0)){// Overflow -> positive. byte[] intValueData = new byte[5]; intValueData[0] = (byte) 0xff; intValueData[1] = (byte)((result >>> 24) & 0xff); intValueData[2] = (byte)((result >>> 16) & 0xff); intValueData[3] = (byte)((result >>> 8) & 0xff); intValueData[4] = (byte)(result & 0xff); return IntegerValue.newInteger(new BigInteger(intValueData)); }else if((value > 0) && (otherIntValue > 0) && (result < 0)){// Overflow -> negative. byte[] intValueData = new byte[5]; intValueData[0] = 0; intValueData[1] = (byte)((result >>> 24) & 0xff); intValueData[2] = (byte)((result >>> 16) & 0xff); intValueData[3] = (byte)((result >>> 8) & 0xff); intValueData[4] = (byte)(result & 0xff); return IntegerValue.newInteger(new BigInteger(intValueData)); } return IntegerValue.newInteger(result); } @Override public IRational add(IRational other) { return (IRational ) other.add(this); } @Override public IReal add(IReal other) { return (IReal) other.add(this); } @Override public INumber subtract(IReal other) { return toReal(other.precision()).subtract(other); } @Override public IInteger subtract(IInteger other){ if(value == 0) return other.negate(); if(other instanceof BigIntegerValue){ return other.negate().subtract(this.negate()); } int otherIntValue = other.intValue(); if(otherIntValue == 0) return this; int result = value - otherIntValue; if((value < 0) && (otherIntValue > 0) && (result > 0)){// Overflow -> positive. byte[] intValueData = new byte[5]; intValueData[0] = (byte) 0xff; intValueData[1] = (byte)((result >>> 24) & 0xff); intValueData[2] = (byte)((result >>> 16) & 0xff); intValueData[3] = (byte)((result >>> 8) & 0xff); intValueData[4] = (byte)(result & 0xff); return IntegerValue.newInteger(new BigInteger(intValueData)); }else if((value > 0) && (otherIntValue < 0) && (result < 0)){// Overflow -> negative. byte[] intValueData = new byte[5]; intValueData[0] = 0; intValueData[1] = (byte)((result >>> 24) & 0xff); intValueData[2] = (byte)((result >>> 16) & 0xff); intValueData[3] = (byte)((result >>> 8) & 0xff); intValueData[4] = (byte)(result & 0xff); return IntegerValue.newInteger(new BigInteger(intValueData)); } return IntegerValue.newInteger(result); } @Override public IRational subtract(IRational other) { return toRational().subtract(other); } @Override public IInteger multiply(IInteger other){ if(value == 0) return this; if(value == 1) return other; if(other instanceof BigIntegerValue){ return other.multiply(this); } int otherIntValue = other.intValue(); if(otherIntValue == 0) return other; if(otherIntValue == 1) return this; boolean resultIsPositive = ((((value ^ otherIntValue) ^ 0x80000000) & 0x80000000) == 0x80000000); if(resultIsPositive){ int div = Integer.MAX_VALUE / otherIntValue; if((value > 0)){ if(value <= div){ return IntegerValue.newInteger(value * other.intValue()); } }else{ if(value >= div){ return IntegerValue.newInteger(value * other.intValue()); } } }else{ int div = Integer.MIN_VALUE / otherIntValue; if((value > 0)){ if(value <= div){ return IntegerValue.newInteger(value * other.intValue()); } }else{ if(value >= div){ return IntegerValue.newInteger(value * other.intValue()); } } } return IntegerValue.newInteger(toBigInteger().multiply(((ICanBecomeABigInteger) other).toBigInteger())); } @Override public IRational multiply(IRational other) { return (IRational) other.multiply(this); } @Override public IReal multiply(IReal other) { return (IReal) other.multiply(this); } @Override public IInteger divide(IInteger other){ if(value == 0) return this; if(other instanceof BigIntegerValue){ return IntegerValue.newInteger(toBigInteger().divide(((ICanBecomeABigInteger) other).toBigInteger())); } int otherIntValue = other.intValue(); if(otherIntValue == 1) return this; return IntegerValue.newInteger(value / otherIntValue); } @Override public IRational divide(IRational other) { return toRational().divide(other); } @Override public INumber divide(IInteger other, int precision) { return toReal(precision).divide(other, precision); } @Override public INumber divide(IRational other, int precision) { return toReal(precision).divide(other, precision); } @Override public IReal divide(IReal other, int precision) { return toReal(precision).divide(other, precision); } @Override public IInteger mod(IInteger other){ if(other instanceof BigIntegerValue){ if(value < 0){ BigInteger m = ((BigIntegerValue)other).toBigInteger(); // i.e. -1 % m = m + (-1) BigInteger res = m.add(toBigInteger()); return IntegerValue.newInteger(res); } return this; } int otherVal = other.intValue(); int newValue = value % other.intValue(); newValue = newValue >= 0 ? newValue : newValue + otherVal; return IntegerValue.newInteger(newValue); } @Override public IInteger remainder(IInteger other){ if(other instanceof BigIntegerValue){ return this; } return IntegerValue.newInteger(value % other.intValue()); } @Override public IInteger negate(){ if(value == 0) return this; else return IntegerValue.newInteger((~((long) value)) + 1); } @Override public IBool equal(IInteger other){ return BoolValue.getBoolValue(compare(other) == 0); } @Override public IBool equal(IRational other) { return other.equal(this); } @Override public IBool equal(IReal other) { return other.equal(this); } @Override public IBool greater(IInteger other){ return BoolValue.getBoolValue(compare(other) > 0); } @Override public IBool greater(IRational other) { return other.less(this); } @Override public IBool greater(IReal other) { return other.less(this); } @Override public IBool greaterEqual(IInteger other){ return BoolValue.getBoolValue(compare(other) >= 0); } @Override public IBool greaterEqual(IRational other) { return other.lessEqual(this); } @Override public IBool greaterEqual(IReal other) { return BoolValue.getBoolValue(compare(other) >= 0); } @Override public IBool less(IInteger other){ return BoolValue.getBoolValue(compare(other) < 0); } @Override public IBool less(IRational other) { return other.greater(this); } @Override public IBool less(IReal other) { return other.greater(this); } @Override public IBool lessEqual(IInteger other){ return BoolValue.getBoolValue(compare(other) <= 0); } @Override public IBool lessEqual(IRational other) { return other.greaterEqual(this); } @Override public IBool lessEqual(IReal other) { return other.greaterEqual(this); } @Override public int compare(IInteger other){ if(other instanceof BigIntegerValue){ return ((~other.compare(this)) + 1); } if(value > other.intValue()) return 1; if(value < other.intValue()) return -1; return 0; } @Override public int compare(INumber other) { if (isIntegerType(other)) { return compare(other.toInteger()); } else if (isRationalType(other)) { return toRational().compare(other); } else { assert other instanceof IReal; return toReal(((IReal) other).precision()).compare(other); } } @Override public <T, E extends Throwable> T accept(IValueVisitor<T,E> v) throws E{ return v.visitInteger(this); } public int hashCode(){ int h = value ^ 0x85ebca6b; // based on the final Avalanching phase of MurmurHash2 // providing a nice mix of bits even for small numbers. h ^= h >>> 13; h *= 0x5bd1e995; h ^= h >>> 15; return h; } public boolean equals(Object o){ if(o == null) return false; else if(o == this) return true; if(o.getClass() == getClass()){ IntegerValue otherInteger = (IntegerValue) o; return (value == otherInteger.value); } return false; } @Override public String getStringRepresentation(){ return Integer.toString(value); } @Override public int signum() { return value < 0 ? -1 : (value == 0 ? 0 : 1); } @Override public IInteger abs() { return newInteger(Math.abs(value)); } @Override public IRational toRational() { return RationalValue.newRational(this, INTEGER_ONE); } }