// Copyright (c) 1996-2002 Brian D. Carlstrom

package bdc.scheme.procedure;

import bdc.scheme.Scheme;
import bdc.scheme.SchemeException;
import bdc.scheme.Stack;
import bdc.scheme.exception.ArgumentTypeException;
import bdc.scheme.expression.ProcedureN;
import bdc.util.Constants;

/**
    (* &hellip;)
*/
public class Times extends ProcedureN
{
    public Object apply0 (Stack stack) throws SchemeException
    {
        return Constants.OneInteger;
    }

    public Object apply1 (Stack stack) throws SchemeException
    {
        Object o1 = stack.array[stack.inUse-1];
        return Scheme.number(o1, this);
    }

    public Object apply2 (Stack stack) throws SchemeException
    {
        Object o1 = stack.array[stack.inUse-1];
        Object o2 = stack.array[stack.inUse-2];
        Number n1 = Scheme.number(o1, this);
        Number n2 = Scheme.number(o2, this);

        if ((n1 instanceof Double) ||
            (n2 instanceof Double))
        {
            return new Double(n1.doubleValue() * n2.doubleValue());
        }
        return Constants.getInteger(n1.intValue() * n2.intValue());
    }

    public Object apply3 (Stack stack) throws SchemeException
    {
        Object o1 = stack.array[stack.inUse-1];
        Object o2 = stack.array[stack.inUse-2];
        Object o3 = stack.array[stack.inUse-3];
        Number n1  = Scheme.number(o1, this);
        Number n2  = Scheme.number(o2, this);
        Number n3  = Scheme.number(o3, this);

        if ((n1 instanceof Double) ||
            (n2 instanceof Double) ||
            (n3 instanceof Double))
        {
            return new Double(n1.doubleValue() *
                              n2.doubleValue() *
                              n3.doubleValue());
        }
        return Constants.getInteger(n1.intValue() *
                            n2.intValue() *
                            n3.intValue());
    }

    public Object apply4 (Stack stack) throws SchemeException
    {
        Object o1 = stack.array[stack.inUse-1];
        Object o2 = stack.array[stack.inUse-2];
        Object o3 = stack.array[stack.inUse-3];
        Object o4 = stack.array[stack.inUse-4];
        Number n1  = Scheme.number(o1, this);
        Number n2  = Scheme.number(o2, this);
        Number n3  = Scheme.number(o3, this);
        Number n4  = Scheme.number(o4, this);

        if ((n1 instanceof Double) ||
            (n2 instanceof Double) ||
            (n3 instanceof Double) ||
            (n4 instanceof Double))
        {
            return new Double(n1.doubleValue() *
                              n2.doubleValue() *
                              n3.doubleValue() *
                              n4.doubleValue());
        }
        return Constants.getInteger(n1.intValue() *
                            n2.intValue() *
                            n3.intValue() *
                            n4.intValue());
    }

    public Object applyN (int n, Stack stack) throws ArgumentTypeException
    {
        boolean integerResult = true;
        int     intResult     = 1;
        double  doubleResult  = 1;

        for (int i = 1; i <= n; i++) {
            Number number = Scheme.number(stack.array[stack.inUse-i], this);

            if (!integerResult) {
                doubleResult *= number.doubleValue();
            }
            else if (number instanceof Integer) {
                intResult    *= number.intValue();
            }
            else {
                integerResult = false;
                doubleResult  = (double)intResult;
                doubleResult *= number.doubleValue();
            }
        }

        if (integerResult) {
            return Constants.getInteger(intResult);
        }
        return new Double(doubleResult);
    }
}
