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

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

class LispFloat extends LispNumber
{
    double value;

    LispFloat(int value)
    {
        this.value = (double)value;
    }

    LispFloat(String value)
    {
        Double d = Double.valueOf(value);
        this.value = d.doubleValue();
    }

    LispFloat(double value)
    {
        this.value = value;
    }

    LispObject eval()
    {
        return this;
    }

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


// The next method is something that I am reasonably upset about since
// I believe that Java ought to provide this sort of formatting but at
// present I can not see how and where it does it.
    String trimTo(int n) // trim to n significant figures
    {
        if (n < 1) n = 1;
        else if (n > 16) n = 16; // limit precision to sensible range
        String s = Double.toString(value);
//Jlisp.println("original = " + s);
        int len = s.length();
        char [] s1 = s.toCharArray();
// identify and remove any "-" sign
        boolean neg = false;
        if (s1[0] == '-')
        {   neg = true;
            for (int i=0; i<len-1; i++) s1[i] = s1[i+1];
            len--;
        }
//Jlisp.println("made +ve = " + new String(s1, 0, len));
// Extract an exponent if explicitly present
        int e = -1;
        for (int i=0; i<len; i++)
        {   if (s1[i] == 'E')
            {   e = i;
                break;
            }
        }
        int x = 0;
        if (e != -1)
        {   String exponent = new String(s1, e+1, len-e-1);
            x = Integer.parseInt(exponent);
            len = e;
        }
//Jlisp.println("exponent grabbed = " + new String(s1, 0, len) + " x= " + x);
// Locate the decimal point, remove it, adjust exponent
        e = -1;
        for (int i=0; i<len; i++)
        {   if (s1[i] == '.')
            {   e = i;
                break;
            }
        }
        if (e != -1)
        {   x -= (len - e - 1);
            for (int i=e; i<len-1; i++) s1[i] = s1[i+1];
            len--;
        }
//Jlisp.println("dot grabbed = " + new String(s1, 0, len) + " x= " + x);
// Remove leading zeros (eg if original had been formated 0.0012345
        while (len>0 && s1[0] == '0')
        {   for (int i=0; i<len-1; i++) s1[i] = s1[i+1];
            len--;
        }
//Jlisp.println("no leading zero = " + new String(s1, 0, len) + " x= " + x);
// Now the string s1 is digits of the number and x (an integer) is
// an exponent (based on s being viewed as an integer). len is the
// length of s1.  neg is the sign. Eg an approximation to +pi might have
// ended up as
//   s1 = "314158"
//   len = 6
//   neg = false
//   x = -5
// My next job is to round to n places... If I do not have that many
// present I do not have anything to do!
        if (n < len)
        {   char next = s1[n];       // thing to test against '5'
            x = x + len - n;
            len = n;
// Some people would round by detecting the case that the digits to be
// disarded were exactly 50000.. and in that mid-way case they might
// round to yield an even or odd result. At present I do the simple
// thing and round up on any fraction >= 0.5.
//Jlisp.println("truncated = " + new String(s1, 0, len) + " x= " + x);
            if (next >= '5')         // need to round up?
            {   char w = 'x';
                for (int k=len-1; k >= 0; k--)
                {   w = s1[k];
                    w = (char)(w == '9' ? '0' : w + 1);
                    s1[k] = w;
                    if (w != '0') break;
                }
// If the number being rounded (say to 5 places) had been 999997 then
// rounding converts it to (1)00000. The "1" would in natural cases go
// into s1[-1], but in this case I KNOW that all remaining digits in s1
// are "0" so I can safely put it in s1[0]! I then have to adjust the
// exponent to reflect the shift.
                if (w == '0')
                {   s1[0] = '1';
                    x++;
                }
            }
        }
//Jlisp.println("rounded = " + new String(s1, 0, len) + " x= " + x);
// Now I guess I can trim any trailing zeros
        while (len>0 && s1[len-1] == '0')
        {   len--;
            x++;
        }
//Jlisp.println("no trailing 0 = " + new String(s1, 0, len) + " x= " + x);
// Finally I can try to decide on a format to use (F or E style)
// and reconstruct the result as a string
        if (len == 0) return "0.0";  // easy special case!
        StringBuffer r = new StringBuffer();
        if (neg) r.append("-");
        if (x>n || x<(-len-2)) // use "E" style
        {   r.append(s1[0]);
            r.append(".");
            if (len == 1) r.append("0");
            else r.append(s1, 1, len-1);
            r.append("e");
            r.append(x+len-1);
        }
        else // use "F" style
        {   int left = len;
            if (len+x <= 0) r.append("0");
            else 
            {   if (x > 0)
                {   r.append(s1, 0, len);
                    while (x > 0)
                    {   r.append("0");
                        x--;
                    }
                    left = 0;
                }
                else
                {   r.append(s1, 0, len+x);
                    left -= (len+x);
                }
            }
            r.append(".");
            if (left == 0) r.append("0");
            else 
            {   while (len+x < 0)
                {   r.append("0");
                    len++;
                }
                r.append(s1, len+x, left);
            }
        }
//Jlisp.println("result = " + r.toString());
        return r.toString();
    }

    double doubleValue()
    {
        return value;
    }

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

    public int lisphashCode()
    {
        return (new Double(value)).hashCode();
    }
    
    public int hashCode()
    {
        return (new Double(value)).hashCode();
    }
    
    void scan()
    {
        Object w = new Double(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 Double(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);
            }
	    long rep = Double.doubleToLongBits(value);
	    Jlisp.odump.write(X_DOUBLE);
	    for (int i=0; i<8; i++)
	    {   Jlisp.odump.write((int)(rep >> (56-8*i)));
	    }
	}
    }

    public LispObject negate() throws Exception
    {
        return new LispFloat(-value);
    }

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

    public LispObject add(LispObject a) throws Exception
    {
        return new LispFloat(value + a.doubleValue());
    }

    public LispObject subtract(LispObject a) throws Exception
    {
        return new LispFloat(value - a.doubleValue());
    }

    public LispObject multiply(LispObject a) throws Exception
    {
        return new LispFloat(value * a.doubleValue());
    }

    public LispObject divide(LispObject a) throws Exception
    {
        return new LispFloat(value / a.doubleValue());
    }

    public LispObject remainder(LispObject a) throws Exception
    {
        return new LispFloat(value % a.doubleValue());
    }

    public LispObject expt(LispObject a) throws Exception
    {
// it is possible that I should delect cases where a is an integer
// and raise to a power using some alternative scheme, like repeated
// multiplication.
        return new LispFloat(Math.pow(value, a.doubleValue()));
    }

    public LispObject max(LispObject a) throws Exception
    {
        return (value >= a.doubleValue() ? this : a);
    }

    public LispObject min(LispObject a) throws Exception
    {
        return (value <= a.doubleValue() ? this : a);
    }

    boolean eqn(LispObject a) throws Exception
    {
        return (value == a.doubleValue());
    }

    boolean neqn(LispObject a) throws Exception
    {
        return (value != a.doubleValue());
    }

    boolean ge(LispObject a) throws Exception
    {
        return (value > a.doubleValue());
    }

    boolean geq(LispObject a) throws Exception
    {
        return (value >= a.doubleValue());
    }

    boolean le(LispObject a) throws Exception
    {
        return (value < a.doubleValue());
    }

    boolean leq(LispObject a) throws Exception
    {
        return (value <= a.doubleValue());
    }

    public LispObject add1() throws Exception
    {
        return new LispFloat(value + 1.0);
    }

    public LispObject sub1() throws Exception
    {
        return new LispFloat(value - 1.0);
    }

    public LispObject floor() throws Exception
    {
        BigDecimal w =
            new BigDecimal(value).setScale(0, BigDecimal.ROUND_FLOOR);
        return LispInteger.valueOf(w.toBigInteger());
    }

    public LispObject ceiling() throws Exception
    {
        BigDecimal w =
            new BigDecimal(value).setScale(0, BigDecimal.ROUND_CEILING);
        return LispInteger.valueOf(w.toBigInteger());
    }

    public LispObject round() throws Exception
    {
        BigDecimal w =
            new BigDecimal(value).setScale(0, BigDecimal.ROUND_HALF_EVEN);
        return LispInteger.valueOf(w.toBigInteger());
    }

    public LispObject truncate() throws Exception
    {
        BigDecimal w =
            new BigDecimal(value).setScale(0, BigDecimal.ROUND_DOWN);
        return LispInteger.valueOf(w.toBigInteger());
    }

    public LispObject fix() throws Exception
    {
        BigDecimal w =
            new BigDecimal(value).setScale(0, BigDecimal.ROUND_DOWN);
        return LispInteger.valueOf(w.toBigInteger());
    }

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

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

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

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

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

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

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

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

    LispObject addInteger(LispBigInteger a) throws Exception
    {
        return new LispFloat(a.value.doubleValue() + value);
    }

    LispObject subtractInteger(LispBigInteger a) throws Exception
    {
        return new LispFloat(a.value.doubleValue() - value);
    }

    LispObject multiplyInteger(LispBigInteger a) throws Exception
    {
        return new LispFloat(a.value.doubleValue() * value);
    }

    LispObject divideInteger(LispBigInteger a) throws Exception
    {
        return new LispFloat(a.value.doubleValue() / value);
    }

    LispObject remainderInteger(LispBigInteger a) throws Exception
    {
        return new LispFloat(a.value.doubleValue() % value);
    }

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

    LispObject exptInteger(LispBigInteger a) throws Exception
    {
        return new LispFloat(Math.pow(a.doubleValue(), value));
    }

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

    boolean eqnInteger(LispBigInteger a) throws Exception
    {
        return (a.value.doubleValue() == value);
    }

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

    boolean geInteger(LispBigInteger a) throws Exception
    {
        return (a.value.doubleValue() > value);
    }

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

    boolean leInteger(LispBigInteger a) throws Exception
    {
        return (a.value.doubleValue() < value);
    }

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

    LispObject addSmallInteger(LispSmallInteger a) throws Exception
    {
        return new LispFloat((double)a.value + value);
    }

    LispObject subtractSmallInteger(LispSmallInteger a) throws Exception
    {
        return new LispFloat((double)a.value - value);
    }

    LispObject multiplySmallInteger(LispSmallInteger a) throws Exception
    {
        return new LispFloat((double)a.value * value);
    }

    LispObject divideSmallInteger(LispSmallInteger a) throws Exception
    {
        return new LispFloat((double)a.value / value);
    }

    LispObject remainderSmallInteger(LispSmallInteger a) throws Exception
    {
        return new LispFloat((double)a.value % value);
    }

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

    LispObject exptSmallInteger(LispSmallInteger a) throws Exception
    {
        return new LispFloat(Math.pow(a.doubleValue(), value));
    }

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

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

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

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

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

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

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

}

// end of LispFloat.java
