//
// 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.*;

class LispBigInteger extends LispInteger
{
    BigInteger value;

    LispBigInteger(int value)
    {
        this.value = BigInteger.valueOf((long)value);
    }

    LispBigInteger(long value)
    {
        this.value = BigInteger.valueOf(value);
    }

    LispBigInteger(BigInteger value)
    {
        this.value = value;
    }

    int intValue() throws Exception
    {
        int n;
        if (value.bitLength() <= 31 &&
            (n = value.intValue()) <= 0x3fffffff &&
            n >= -0x40000000) return n;
        Jlisp.error("Integer is too large", this);
        return 0; // never reached
    }

    BigInteger bigIntValue()
    {
        return value;
    }

    LispObject eval()
    {
        return this;
    }

    String printAs()
    {
        if ((currentFlags & (printBinary | printOctal | printHex)) == 0)   
            return value.toString();
        else if ((currentFlags & printBinary) != 0)
            return value.toString(2);
        else if ((currentFlags & printOctal) != 0)
            return value.toString(8);
        else // if ((currentFlags & printHex) != 0)
            return value.toString(16);
    }

    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 value.doubleValue();
    }

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

    public int lisphashCode()
    {
        return value.hashCode();
    }
    
    public int hashCode()
    {
        return value.hashCode();
    }
    
    void scan()
    {
        if (Jlisp.objects.contains(value)) // seen before?
	{   if (!Jlisp.repeatedObjects.containsKey(value))
	    {   Jlisp.repeatedObjects.put(
	            value,
	            Jlisp.nil); // value is junk at this stage
	    }
	}
	else Jlisp.objects.add(value);
    }
    
    void dump() throws IOException
    {
        Object w = Jlisp.repeatedObjects.get(value);
	if (w != null &&
	    w instanceof Integer) putSharedRef(w); // processed before
	else
	{   if (w != null) // will be used again sometime
	    {   Jlisp.repeatedObjects.put(
	            value,
		    new Integer(Jlisp.sharedIndex++));
		Jlisp.odump.write(X_STORE);
            }
// Now this is the first time I see this integer while writing a dump
// file. I will put out a byte that says "here comes an integer", then
// the number of bytes used to represent it, and finally the set of bytes
// concerned. To save a little space I have variants to cope with the
// various possible sizes of length-code needed.
	    byte [] rep = value.toByteArray();
	    int length = rep.length;
	    putPrefix2(length, X_INTn, X_INT);
	    for (int i=0; i<length; i++)
	        Jlisp.odump.write(rep[i]);
	}
    }

    public LispObject negate() throws Exception
    {
        return valueOf(value.negate());
    }

    public LispObject abs() throws Exception
    {
        if (value.signum() >= 0) return this;
        else return valueOf(value.negate());
    }

    public LispObject msd() throws Exception
    {
        return valueOf(value.bitLength());
    }

    public LispObject lsd() throws Exception
    {
        return valueOf(value.getLowestSetBit());
    }

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

    public LispObject reduceMod() throws Exception
    {
        return valueOf(value.mod(Jlisp.bigModulus));
    }

    public LispObject add1() throws Exception
    {
        return valueOf(value.add(BigInteger.ONE));
    }

    public LispObject sub1() throws Exception
    {
        return valueOf(value.subtract(BigInteger.ONE));
    }

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

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

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

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

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

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

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

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

    public LispObject oddp() throws Exception
    {
        return value.testBit(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(value.doubleValue());
    }

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

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

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

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

    public LispObject onep() throws Exception
    {
        return (value.compareTo(BigInteger.ONE) == 0) ? 
               Jlisp.lispTrue :
               Jlisp.nil;
    }

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

// now versions that know they have 2 integer args

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

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

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

    public LispObject divideInteger(LispBigInteger a) throws Exception
    {
        return valueOf(a.value.divide(value));
    }

    public LispObject remainderInteger(LispBigInteger a) throws Exception
    {
        return valueOf(a.value.remainder(value));
    }

    public LispObject quotientAndRemainderInteger(LispBigInteger a) throws Exception
    {
        BigInteger [] r = a.value.divideAndRemainder(value);
        return new Cons(valueOf(r[0]), valueOf(r[1]));
    }

    public LispObject modInteger(LispBigInteger a) throws Exception
    {
        return valueOf(a.value.mod(value));
    }

    public LispObject exptInteger(LispBigInteger a) throws Exception
    {
        switch (value.signum())
        {
    case -1: return valueOf(0);
    case 0:  return valueOf(1);
    default:
             if (value.bitLength() > 15)
                 return Jlisp.error("integer result would be too large");
             else return valueOf(a.value.pow(value.intValue()));
        }
    }

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

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

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

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

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

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

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

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

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

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

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

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

// Finally versions that mix big and small integers. I will generally cope by
// converting the small integer to a big one.

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

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

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

    LispObject divideSmallInteger(LispSmallInteger a) throws Exception
    {
        return valueOf(BigInteger.valueOf((long)a.value).divide(value));
    }

    LispObject remainderSmallInteger(LispSmallInteger a) throws Exception
    {
        return valueOf(BigInteger.valueOf((long)a.value).remainder(value));
    }

    LispObject quotientAndRemainderSmallInteger(LispSmallInteger a) throws Exception
    {
        BigInteger [] r = BigInteger.valueOf((long)a.value).divideAndRemainder(value);
        return new Cons(valueOf(r[0]), valueOf(r[1]));
    }

    LispObject modSmallInteger(LispSmallInteger a) throws Exception
    {
        return valueOf(BigInteger.valueOf((long)a.value).mod(value));
    }

    LispObject exptSmallInteger(LispSmallInteger a) throws Exception
    {
        switch (value.signum())
        {
    case -1: return valueOf(0);
    case 0:  return valueOf(1);
    default:
             if (value.bitLength() > 15)
                 return Jlisp.error("integer result would be too large");
             else return valueOf(BigInteger.valueOf((long)a.value).pow(value.intValue()));
        }
    }

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

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

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

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

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

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

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

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

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

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

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

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


}

// End of LispBigInteger.java


