// Copyright(c) 1996,1997 ObjectSpace, Inc.
// Portions Copyright(c) 1995, 1996 Hewlett-Packard Company.

package COM.objectspace.jgl;

import java.math.BigInteger;
import java.math.BigDecimal;

final class NumberHelper
  {
  static private Class classInteger = new Integer( 0 ).getClass();
  static private Class classLong = new Long( 0 ).getClass();
  static private Class classFloat = new Float( 0 ).getClass();
  static private Class classDouble = new Double( 0 ).getClass();
  static private Class classByte = new Byte( (byte)0 ).getClass();
  static private Class classShort = new Short( (short)0 ).getClass();
  static private Class classBigInteger = new BigInteger( "0" ).getClass();
  static private Class classBigDecimal = new BigDecimal( "0" ).getClass();
  static Class classNumber = new Integer( 0 ).getClass().getSuperclass();

  private NumberHelper()
    {
    }

  static BigDecimal asBigDecimal( Number n )
    {
    return n instanceof java.math.BigDecimal
      ? (BigDecimal)n
      : new BigDecimal( n.toString() );
    }

  static BigInteger asBigInteger( Number n )
    {
    // if not already a BigInteger, first convert to a BigDecimal
    // to avoid checking floating point types
    return n instanceof java.math.BigInteger
      ? (BigInteger)n
      : asBigDecimal( n ).toBigInteger();
    }

  static Number plus( Number n1, Number n2, Class mode )
    {
    // normal subclasses
    if ( mode.equals( classInteger ) )
      return new Integer( n1.intValue() + n2.intValue() );
    if ( mode.equals( classLong ) )
      return new Long(  n1.longValue() + n2.longValue() );
    if ( mode.equals( classFloat ) )
      return new Float(  n1.floatValue() + n2.floatValue() );
    if ( mode.equals( classDouble ) )
      return new Double(  n1.doubleValue() + n2.doubleValue() );
    if ( mode.equals( classByte ) )
      return new Byte(  (byte)( n1.byteValue() + n2.byteValue() ) );
    if ( mode.equals( classShort ) )
      return new Short(  (short)( n1.shortValue() + n2.shortValue() ) );

    // compare as BigIntegers
    if ( mode.equals( classBigInteger ) )
      return asBigInteger( n1 ).add( asBigInteger( n2 ) );

    // compare as BigDecimals
    if ( mode.equals( classBigDecimal ) )
      return asBigDecimal( n1 ).add( asBigDecimal( n2 ) );

    // don't know how to deal with mode
    throw new IllegalArgumentException
      (
      "unknown subclass of java.lang.Number: " + mode.getClass()
      );
    }

  static Number minus( Number n1, Number n2, Class mode )
    {
    // normal subclasses
    if ( mode.equals( classInteger ) )
      return new Integer( n1.intValue() - n2.intValue() );
    if ( mode.equals( classLong ) )
      return new Long(  n1.longValue() - n2.longValue() );
    if ( mode.equals( classFloat ) )
      return new Float(  n1.floatValue() - n2.floatValue() );
    if ( mode.equals( classDouble ) )
      return new Double(  n1.doubleValue() - n2.doubleValue() );
    if ( mode.equals( classByte ) )
      return new Byte(  (byte)( n1.byteValue() - n2.byteValue() ) );
    if ( mode.equals( classShort ) )
      return new Short(  (short)( n1.shortValue() - n2.shortValue() ) );

    // compare as BigIntegers
    if ( mode.equals( classBigInteger ) )
      return asBigInteger( n1 ).subtract( asBigInteger( n2 ) );

    // compare as BigDecimals
    if ( mode.equals( classBigDecimal ) )
      return asBigDecimal( n1 ).subtract( asBigDecimal( n2 ) );

    // don't know how to deal with mode
    throw new IllegalArgumentException
      (
      "unknown subclass of java.lang.Number: " + mode.getClass()
      );
    }

  static Number multiply( Number n1, Number n2, Class mode )
    {
    // normal subclasses
    if ( mode.equals( classInteger ) )
      return new Integer( n1.intValue() * n2.intValue() );
    if ( mode.equals( classLong ) )
      return new Long(  n1.longValue() * n2.longValue() );
    if ( mode.equals( classFloat ) )
      return new Float(  n1.floatValue() * n2.floatValue() );
    if ( mode.equals( classDouble ) )
      return new Double(  n1.doubleValue() * n2.doubleValue() );
    if ( mode.equals( classByte ) )
      return new Byte(  (byte)( n1.byteValue() * n2.byteValue() ) );
    if ( mode.equals( classShort ) )
      return new Short(  (short)( n1.shortValue() * n2.shortValue() ) );

    // compare as BigIntegers
    if ( mode.equals( classBigInteger ) )
      return asBigInteger( n1 ).multiply( asBigInteger( n2 ) );

    // compare as BigDecimals
    if ( mode.equals( classBigDecimal ) )
      return asBigDecimal( n1 ).multiply( asBigDecimal( n2 ) );

    // don't know how to deal with mode
    throw new IllegalArgumentException
      (
      "unknown subclass of java.lang.Number: " + mode.getClass()
      );
    }

  static Number divides( Number n1, Number n2, Class mode, int round_mode )
    {
    // normal subclasses
    if ( mode.equals( classInteger ) )
      return new Integer( n1.intValue() / n2.intValue() );
    if ( mode.equals( classLong ) )
      return new Long(  n1.longValue() / n2.longValue() );
    if ( mode.equals( classFloat ) )
      return new Float(  n1.floatValue() / n2.floatValue() );
    if ( mode.equals( classDouble ) )
      return new Double(  n1.doubleValue() / n2.doubleValue() );
    if ( mode.equals( classByte ) )
      return new Byte(  (byte)( n1.byteValue() / n2.byteValue() ) );
    if ( mode.equals( classShort ) )
      return new Short(  (short)( n1.shortValue() / n2.shortValue() ) );

    // compare as BigIntegers
    if ( mode.equals( classBigInteger ) )
      return asBigInteger( n1 ).divide( asBigInteger( n2 ) );

    // compare as BigDecimals
    if ( mode.equals( classBigDecimal ) )
      return asBigDecimal( n1 ).divide( asBigDecimal( n2 ), round_mode );

    // don't know how to deal with mode
    throw new IllegalArgumentException
      (
      "unknown subclass of java.lang.Number: " + mode.getClass()
      );
    }

  static Number modulus( Number n1, Number n2, Class mode, int round_mode )
    {
    // normal subclasses
    if ( mode.equals( classInteger ) )
      return new Integer( n1.intValue() % n2.intValue() );
    if ( mode.equals( classLong ) )
      return new Long(  n1.longValue() % n2.longValue() );
    if ( mode.equals( classFloat ) )
      return new Float(  n1.floatValue() % n2.floatValue() );
    if ( mode.equals( classDouble ) )
      return new Double(  n1.doubleValue() % n2.doubleValue() );
    if ( mode.equals( classByte ) )
      return new Byte(  (byte)( n1.byteValue() % n2.byteValue() ) );
    if ( mode.equals( classShort ) )
      return new Short(  (short)( n1.shortValue() % n2.shortValue() ) );

    // compare as BigIntegers
    if ( mode.equals( classBigInteger ) )
      return asBigInteger( n1 ).mod( asBigInteger( n2 ) );

    // compare as BigDecimals
    if ( mode.equals( classBigDecimal ) )
      {
      // return rounding difference
      BigDecimal b1 = asBigDecimal( n1 );
      BigDecimal b2 = asBigDecimal( n2 );
      BigDecimal d = b1.divide( b2, round_mode );
      return b1.subtract( d.multiply( b2 ) );
      }

    // don't know how to deal with mode
    throw new IllegalArgumentException
      (
      "unknown subclass of java.lang.Number: " + mode.getClass()
      );
    }

  static int compare( Number n1, Number n2, Class mode )
    {
    // normal subclasses
    if ( mode.equals( classInteger ) )
      return n1.intValue() < n2.intValue()
        ? -1
        : n1.intValue() > n2.intValue()
          ? 1
          : 0;
    if ( mode.equals( classLong ) )
      return n1.longValue() < n2.longValue()
        ? -1
        : n1.longValue() > n2.longValue()
          ? 1
          : 0;
    if ( mode.equals( classFloat ) )
      return n1.floatValue() < n2.floatValue()
        ? -1
        : n1.floatValue() > n2.floatValue()
          ? 1
          : 0;
    if ( mode.equals( classDouble ) )
      return n1.doubleValue() < n2.doubleValue()
        ? -1
        : n1.doubleValue() > n2.doubleValue()
          ? 1
          : 0;
    if ( mode.equals( classByte ) )
      return n1.byteValue() < n2.byteValue()
        ? -1
        : n1.byteValue() > n2.byteValue()
          ? 1
          : 0;
    if ( mode.equals( classShort ) )
      return n1.shortValue() < n2.shortValue()
        ? -1
        : n1.shortValue() > n2.shortValue()
          ? 1
          : 0;

    // compare as BigIntegers
    if ( mode.equals( classBigInteger ) )
      return asBigInteger( n1 ).compareTo( asBigInteger( n2 ) );

    // compare as BigDecimals
    if ( mode.equals( classBigDecimal ) )
      return asBigDecimal( n1 ).compareTo( asBigDecimal( n2 ) );

    // don't know how to deal with mode
    throw new IllegalArgumentException
      (
      "unknown subclass of java.lang.Number: " + mode.getClass()
      );
    }
  }
