//
// This file is part of the Jlisp implementation of Standard Lisp
// Copyright \u00a9 (C) Codemist Ltd, 1998-2000.
//


import java.math.*;
import java.io.*;
import java.util.*;

public class LispSmallInteger extends LispInteger
{
    public int value;

// I will keep integers of up to 31 bits as "Small" integers. This means that
// the criterion I will use to notice them will be that as ints they will
// have their top two bits equal. For values in some (much) smaller range
// I will have a pre-allocated table of values and will return one of
// those when needed - thereby avoiding some heap allocation at the expense
// of extra calculation and testing.

    static final int MIN = -100;
    static final int MAX = 1000;
    static LispSmallInteger [] preAllocated = new LispSmallInteger[MAX-MIN+1];

    static void preAllocate()
    {
        for (int i=MIN; i<=MAX; i++)
            preAllocated[i-MIN] = new LispSmallInteger(i);
    }

    LispSmallInteger(int n)
    {
        value = n;
    }

    int intValue()
    {
        return value;
    }

    BigInteger bigIntValue()
    {
        return BigInteger.valueOf((long)value);
    }

    LispObject eval()
    {
        return this;
    }

    String printAs()
    {
        if ((currentFlags & (printBinary | printOctal | printHex)) == 0)   
            return Integer.toString(value);
        else if ((currentFlags & printBinary) != 0)
            return Integer.toBinaryString(value);
        else if ((currentFlags & printOctal) != 0)
            return Integer.toOctalString(value);
        else // if ((currentFlags & printHex) != 0)
            return Integer.toHexString(value);
    }

    void iprint()
    {
        String s = printAs();
        if ((currentFlags & noLineBreak) == 0 &&
            currentOutput.column + s.length() > currentOutput.lineLength)
            currentOutput.println();
        currentOutput.print(s);
    }

    void blankprint()
    {
        String s = printAs();
        if ((currentFlags & noLineBreak) == 0 &&
            currentOutput.column + s.length() >= currentOutput.lineLength)
            currentOutput.println();
        else currentOutput.print(" ");
        currentOutput.print(s);
    }

    double doubleValue()
    {
        return (double)value;
    }

    public boolean lispequals(Object b)
    {
        if (!(b instanceof LispSmallInteger)) return false;
        return value == ((LispSmallInteger)b).value;
    }
    
    public boolean equals(Object b)
    {
        if (!(b instanceof LispSmallInteger)) return false;
        return value == ((LispSmallInteger)b).value;
    }

    public int lisphashCode()
    {
        return value*696969;
    }
    
    public int hashCode()
    {
        return value*696969;
    }
    
    void scan()
    {
        Object w = new Integer(value);
        if (Jlisp.objects.contains(w)) // seen before?
	{   if (!Jlisp.repeatedObjects.containsKey(w))
	    {   Jlisp.repeatedObjects.put(
	            w,
	            Jlisp.nil); // value is junk at this stage
	    }
	}
	else Jlisp.objects.add(w);
    }
    
    void dump() throws IOException
    {
        Object d = new Integer(value);
        Object w = Jlisp.repeatedObjects.get(d);
	if (w != null &&
	    w instanceof Integer) putSharedRef(w); // processed before
	else
	{   if (w != null) // will be used again sometime
	    {   Jlisp.repeatedObjects.put(
	            d,
		    new Integer(Jlisp.sharedIndex++));
		Jlisp.odump.write(X_STORE);
            }
// Note whacky coding with sign bit in bottom bit position. The intent
// of this is that numbers that ar esmall in absolute value will be
// packed into rather few bytes.
	    putPrefix(value == 0x80000000 ? 1 :
                      value < 0 ? 1 + ((-value)<<1) :
                      value<<1,
                      X_FIXNUM);
	}
    }

    public LispObject negate() throws Exception
    {
        if (value == 0xc0000000) return new LispBigInteger(-value);
        return valueOf(-value);
    }

    public LispObject abs() throws Exception
    {
        if (value >= 0) return this;
        else if (value == 0xc0000000) return new LispBigInteger(-value);
        else return valueOf(-value);
    }

    public LispObject add1() throws Exception
    {
        if (value == 0x3fffffff) return new LispBigInteger(0x40000000);
        else return valueOf(value+1);
    }

    public LispObject sub1() throws Exception
    {
        if (value == 0xc0000000) return new LispBigInteger(0xbfffffff);
        return valueOf(value-1);
    }

    public LispObject msd() throws Exception
    {
        int r = 0, w = value;
        if (w < 0) w = -(w + 1);
        while ((w & ~0x3f) != 0)
        {   w = w >>> 6;
            r += 6;
        }
        while (w != 0)
        {   w = w >>> 1;
            r += 1;
        }
        return valueOf(r);
    }

    public LispObject lsd() throws Exception
    {
        int r = 0, w = value;
        if (w == 0) return valueOf(-1);
        while ((w & 0x3f) == 0)
        {   w = w >> 6;
            r += 6;
        }
        while ((w & 0x1) == 0)
        {   w = w >> 1;
            r += 1;
        }
        return valueOf(r);
    }

    LispObject not() throws Exception
    {
        return valueOf(~value);
    }

    LispObject modMinus() throws Exception
    {
        if (value == 0) return this;
        else return valueOf(Jlisp.modulus - value);
    }

    LispObject modRecip() throws Exception
    {
        if (value == 0) 
            return Jlisp.error("attempt to take modular recip of zero");
        int a = Jlisp.modulus, b = value, s = 0, t = 1;
        while (b != 0)
        {   int q = a/b;
            int w = a - q*b; a = b; b = w;
            w = s - q*t; s = t; t = w;
        }
        if (s < 0) s += Jlisp.modulus;
        return valueOf(s);
    }

    LispObject reduceMod() throws Exception
    {
        int r = value % Jlisp.modulus;
        if (r < 0) r += Jlisp.modulus;
        return valueOf(r);
    }

    public LispObject floor() throws Exception
    {
        return this;
    }

    public LispObject ceiling() throws Exception
    {
        return this;
    }

    public LispObject round() throws Exception
    {
        return this;
    }

    LispObject truncate() throws Exception
    {
        return this;
    }

    public LispObject ash(int n) throws Exception
    {
        if (n > 0) return valueOf(BigInteger.valueOf((long)value).shiftLeft(n));
        else if (n < -31)
        {   if (value < 0) return valueOf(-1);
            else return valueOf(0);
        }
        else if (n < 0) return valueOf(value >> (-n));
        else return this;
    }

    public LispObject ash1(int n) throws Exception
    {
        if (n > 0) return valueOf(BigInteger.valueOf((long)value).shiftLeft(n));
        else if (n < -31) return valueOf(0);
        else if (n < 0)
        {   if (value >= 0)
                return valueOf(value >> (-n));
            else return valueOf(-((-value) >> (-n)));
        }
        else return this;
    }

    public LispObject rightshift(int n) throws Exception
    {
        return valueOf(value >> n);
    }

    public LispObject evenp() throws Exception
    {
        return ((value & 1) != 0) ? Jlisp.nil : Jlisp.lispTrue;
    }

    public LispObject oddp() throws Exception
    {
        return ((value & 1) != 0) ? Jlisp.lispTrue : Jlisp.nil;
    }

    public LispObject fix() throws Exception
    {
        return this;
    }

    public LispObject fixp() throws Exception
    {
        return Jlisp.lispTrue;
    }

    public LispObject integerp() throws Exception
    {
        return Jlisp.lispTrue;
    }

    public LispObject jfloat() throws Exception
    {
        return new LispFloat((double)value);
    }

    public LispObject floatp() throws Exception
    {
        return Jlisp.nil;
    }

    public LispObject minusp() throws Exception
    {
        return value < 0 ? Jlisp.lispTrue : Jlisp.nil;
    }

    public LispObject plusp() throws Exception
    {
        return value >= 0 ? Jlisp.lispTrue : Jlisp.nil;
    }

    public LispObject zerop() throws Exception
    {
        return value == 0 ? Jlisp.lispTrue : Jlisp.nil;
    }

    public LispObject onep() throws Exception
    {
        return value == 1 ? Jlisp.lispTrue : Jlisp.nil;
    }

    public LispObject add(LispObject a) throws Exception
    {
        return a.addSmallInteger(this);
    }

    public LispObject subtract(LispObject a) throws Exception
    {
        return a.subtractSmallInteger(this);
    }

    public LispObject multiply(LispObject a) throws Exception
    {
        return a.multiplySmallInteger(this);
    }

    public LispObject expt(LispObject a) throws Exception
    {
        return a.exptSmallInteger(this);
    }

    public LispObject divide(LispObject a) throws Exception
    {
        return a.divideSmallInteger(this);
    }

    public LispObject remainder(LispObject a) throws Exception
    {
        return a.remainderSmallInteger(this);
    }

    public LispObject quotientAndRemainder(LispObject a) throws Exception
    {
        return a.quotientAndRemainderSmallInteger(this);
    }

    public LispObject mod(LispObject a) throws Exception
    {
        return a.modSmallInteger(this);
    }

    public LispObject max(LispObject a) throws Exception
    {
        return a.maxSmallInteger(this);
    }

    public LispObject min(LispObject a) throws Exception
    {
        return a.minSmallInteger(this);
    }

    public LispObject and(LispObject a) throws Exception
    {
        return a.andSmallInteger(this);
    }

    public LispObject or(LispObject a) throws Exception
    {
        return a.orSmallInteger(this);
    }

    LispObject xor(LispObject a) throws Exception
    {
        return a.xorSmallInteger(this);
    }

    LispObject gcd(LispObject a) throws Exception
    {
        return a.gcdSmallInteger(this);
    }

    LispObject modAdd(LispObject a) throws Exception
    {
        return a.modAddSmallInteger(this);
    }

    LispObject modSubtract(LispObject a) throws Exception
    {
        return a.modSubtractSmallInteger(this);
    }

    LispObject modMultiply(LispObject a) throws Exception
    {
        return a.modMultiplySmallInteger(this);
    }

    LispObject modDivide(LispObject a) throws Exception
    {
        return a.modDivideSmallInteger(this);
    }

    LispObject modExpt(int a) throws Exception
    {
        long r = 1;
        long w = value;
        while (a != 0)
        {   if ((a & 1) != 0) r = (r*w) % Jlisp.modulus;
            w = (w*w) % Jlisp.modulus;
            a = a >>> 1;
        }
        if (r < 0) r += Jlisp.modulus;
        return valueOf(r);
    }

    boolean eqn(LispObject a) throws Exception
    {
        return a.eqnSmallInteger(this);
    }

    boolean neqn(LispObject a) throws Exception
    {
        return a.neqnSmallInteger(this);
    }

    boolean ge(LispObject a) throws Exception
    {
        return a.geSmallInteger(this);
    }

    boolean geq(LispObject a) throws Exception
    {
        return a.geqSmallInteger(this);
    }

    boolean le(LispObject a) throws Exception
    {
        return a.leSmallInteger(this);
    }

    boolean leq(LispObject a) throws Exception
    {
        return a.leqSmallInteger(this);
    }

// Now versions that know they have one integer and one small integer.
// I am going to accept that these will be seriously slower than the
// LispSmallInteger cases and to cope with that I will suppose that
// they always return small integers if they can.

    LispObject addInteger(LispBigInteger a) throws Exception
    {
        return valueOf(a.value.add(BigInteger.valueOf((long)value)));
    }

    LispObject subtractInteger(LispBigInteger a) throws Exception
    {
        return valueOf(a.value.subtract(BigInteger.valueOf((long)value)));
    }

    LispObject multiplyInteger(LispBigInteger a) throws Exception
    {
        return valueOf(a.value.multiply(BigInteger.valueOf((long)value)));
    }

    LispObject divideInteger(LispBigInteger a) throws Exception
    {
        if (value == 0) return Jlisp.error("attempt to divide by zero");
        return valueOf(a.value.divide(BigInteger.valueOf((long)value)));
    }

    LispObject remainderInteger(LispBigInteger a) throws Exception
    {
        if (value == 0) return Jlisp.error("attempt to divide by zero");
        return valueOf(a.value.remainder(BigInteger.valueOf((long)value)));
    }

    LispObject quotientAndRemainderInteger(LispBigInteger a) throws Exception
    {
        if (value == 0) return Jlisp.error("attempt to divide by zero");
        BigInteger [] r = a.value.divideAndRemainder(BigInteger.valueOf((long)value));
        return new Cons(valueOf(r[0]), valueOf(r[1]));
    }

    LispObject modInteger(LispBigInteger a) throws Exception
    {
        if (value == 0) return Jlisp.error("attempt to divide by zero");
        return valueOf(a.value.mod(BigInteger.valueOf((long)value)));
    }

    LispObject exptInteger(LispBigInteger a) throws Exception
    {
        if (value < 0) return valueOf(0);
        else if (value == 0) return valueOf(1);
        else if (value >= 0x10000)
            return Jlisp.error("integer result would be too large");
        else return valueOf(a.value.pow(value));
    }

    LispObject maxInteger(LispBigInteger a) throws Exception
    {
        if (a.value.compareTo(BigInteger.valueOf((long)value)) >= 0) return a;
        else return this;
    }

    LispObject minInteger(LispBigInteger a) throws Exception
    {
        if (a.value.compareTo(BigInteger.valueOf((long)value)) <= 0) return a;
        else return this;
    }

    LispObject andInteger(LispBigInteger a) throws Exception
    {
        return valueOf(a.value.and(BigInteger.valueOf((long)value)));
    }

    LispObject orInteger(LispBigInteger a) throws Exception
    {
        return valueOf(a.value.or(BigInteger.valueOf((long)value)));
    }

    LispObject xorInteger(LispBigInteger a) throws Exception
    {
        return valueOf(a.value.xor(BigInteger.valueOf((long)value)));
    }

    LispObject gcdInteger(LispBigInteger a) throws Exception
    {
        return valueOf(a.value.gcd(BigInteger.valueOf((long)value)));
    }

    boolean eqnInteger(LispBigInteger a) throws Exception
    {
        return (a.value.compareTo(BigInteger.valueOf((long)value)) == 0);
    }

    boolean neqnInteger(LispBigInteger a) throws Exception
    {
        return (a.value.compareTo(BigInteger.valueOf((long)value)) != 0);
    }

    boolean geInteger(LispBigInteger a) throws Exception
    {
        return (a.value.compareTo(BigInteger.valueOf((long)value)) > 0);
    }

    boolean geqInteger(LispBigInteger a) throws Exception
    {
        return (a.value.compareTo(BigInteger.valueOf((long)value)) >= 0);
    }

    boolean leInteger(LispBigInteger a) throws Exception
    {
        return (a.value.compareTo(BigInteger.valueOf((long)value)) < 0);
    }

    boolean leqInteger(LispBigInteger a) throws Exception
    {
        return (a.value.compareTo(BigInteger.valueOf((long)value)) <= 0);
    }

// Finally versions that know they have 2 small integer args. Here I need
// to worry about overflow and promote to a LispBigInteger in suitable cases.

    LispObject addSmallInteger(LispSmallInteger a) throws Exception
    {
        return valueOf(a.value + value);
    }

    LispObject subtractSmallInteger(LispSmallInteger a) throws Exception
    {
        return valueOf(a.value - value);
    }

    LispObject multiplySmallInteger(LispSmallInteger a) throws Exception
    {
        return valueOf((long)a.value * (long)value);
    }

    LispObject divideSmallInteger(LispSmallInteger a) throws Exception
    {
        if (value == 0) return Jlisp.error("attempt to divide by zero");
        return valueOf(a.value / value);
    }

    LispObject remainderSmallInteger(LispSmallInteger a) throws Exception
    {
        if (value == 0) return Jlisp.error("attempt to divide by zero");
        return valueOf(a.value % value);
    }

    LispObject quotientAndRemainderSmallInteger(LispSmallInteger a) throws Exception
    {
        if (value == 0) return Jlisp.error("attempt to divide by zero");
        return new Cons(valueOf(a.value / value),
                        valueOf(a.value % value));
    }

    LispObject modSmallInteger(LispSmallInteger a) throws Exception
    {
        if (value == 0) return Jlisp.error("attempt to divide by zero");
        int r = a.value % value;
        if (value > 0)
        {   if (r < 0) r += value;
        }
        else if (r > 0) r += value;
        return valueOf(r);
    }

    LispObject exptSmallInteger(LispSmallInteger a) throws Exception
    {
        if (value < 0) return valueOf(0);
        else if (value == 0) return valueOf(1);
        else if (value >= 0x10000)
                 return Jlisp.error("integer result would be too large");
        else return valueOf(
                        BigInteger.valueOf((long)a.value).pow(value));
    }

    LispObject maxSmallInteger(LispSmallInteger a) throws Exception
    {
        if (a.value >= value) return a;
        else return this;
    }

    LispObject minSmallInteger(LispSmallInteger a) throws Exception
    {
        if (a.value <= value) return a;
        else return this;
    }

    LispObject andSmallInteger(LispSmallInteger a) throws Exception
    {
        return valueOf(a.value & value);
    }

    LispObject orSmallInteger(LispSmallInteger a) throws Exception
    {
        return valueOf(a.value | value);
    }

    LispObject xorSmallInteger(LispSmallInteger a) throws Exception
    {
        return valueOf(a.value ^ value);
    }

    LispObject gcdSmallInteger(LispSmallInteger a) throws Exception
    {
        int p = a.value;
        int q = value;
        if (p < 0) p = -p;
        if (q < 0) q = -q;
        if (p == 0) return valueOf(q);
        if (p < q)
        {   q = q % p;
        }
        while (q != 0)
        {   int r = p % q;
            p = q;
            q = r;
        }
        return valueOf(p);
    }

    LispObject modAddSmallInteger(LispSmallInteger a) throws Exception
    {
        int n = a.value + value;
        if (n >= Jlisp.modulus) n -= Jlisp.modulus;
        return valueOf(n);
    }

    LispObject modSubtractSmallInteger(LispSmallInteger a) throws Exception
    {
        int n = a.value - value;
        if (n < 0) n += Jlisp.modulus;
        return valueOf(n);
    }

    LispObject modMultiplySmallInteger(LispSmallInteger a) throws Exception
    {
        long n = (long)a.value * (long)value;
        return valueOf(n % Jlisp.modulus);
    }

    LispObject modDivideSmallInteger(LispSmallInteger arg) throws Exception
    {
        if (value == 0) 
            return Jlisp.error("attempt to divide by (modular) zero");
        int a = Jlisp.modulus, b = value, s = 0, t = 1;
        while (b != 0)
        {   int q = a/b;
            int w = a - q*b; a = b; b = w;
            w = s - q*t; s = t; t = w;
        }
        if (s < 0) s += Jlisp.modulus;
        return valueOf(((long)s * (long)arg.value) % Jlisp.modulus);
    }

    boolean eqnSmallInteger(LispSmallInteger a) throws Exception
    {
        return (a.value == value);
    }

    boolean neqnSmallInteger(LispSmallInteger a) throws Exception
    {
        return (a.value != value);
    }

    boolean geSmallInteger(LispSmallInteger a) throws Exception
    {
        return (a.value > value);
    }

    boolean geqSmallInteger(LispSmallInteger a) throws Exception
    {
        return (a.value >= value);
    }

    boolean leSmallInteger(LispSmallInteger a) throws Exception
    {
        return (a.value < value);
    }

    boolean leqSmallInteger(LispSmallInteger a) throws Exception
    {
        return (a.value <= value);
    }

}

// End of LispSmallInteger.java


