/FileExtraction/Java_unseen/dlitz_resin/modules/quercus/src/com/caucho/quercus/env/DoubleValue.java
/*
 * Copyright (c) 1998-2011 Caucho Technology -- all rights reserved
 *
 * This file is part of Resin(R) Open Source
 *
 * Each copy or derived work must preserve the copyright notice and this
 * notice unmodified.
 *
 * Resin Open Source is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * Resin Open Source is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, or any warranty
 * of NON-INFRINGEMENT.  See the GNU General Public License for more
 * details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Resin Open Source; if not, write to the
 *
 *   Free Software Foundation, Inc.
 *   59 Temple Place, Suite 330
 *   Boston, MA 02111-1307  USA
 *
 * @author Scott Ferguson
 */

package com.caucho.quercus.env;

import com.caucho.quercus.marshal.Marshal;
import com.caucho.vfs.WriteStream;

import java.io.IOException;
import java.io.PrintWriter;
import java.io.Serializable;
import java.util.IdentityHashMap;

/**
 * Represents a PHP double value.
 */
public class DoubleValue extends NumberValue
  implements Serializable
{
  public static final DoubleValue ZERO = new DoubleValue(0);

  private final double _value;

  public DoubleValue(double value)
  {
    _value = value;
  }

  public static DoubleValue create(double value)
  {
    return new DoubleValue(value);
  }

  public static Value create(Number value)
  {
    if (value == null) {
      // php/3c2d
      return NullValue.NULL;
    }
    else
      return new DoubleValue(value.doubleValue());
  }

  /**
   * Returns the type.
   */
  public String getType()
  {
    // php/0142

    return "double";
  }

  /**
   * Returns the ValueType.
   */
  @Override
  public ValueType getValueType()
  {
    return ValueType.DOUBLE;
  }

  /**
   * Returns true for a double.
   */
  public boolean isDoubleConvertible()
  {
    return true;
  }

  /**
   * Returns true for integer looking doubles.
   */
  public boolean isLongConvertible()
  {
    return _value == (double)((long) _value);
  }

  /**
   * Returns true for a long-value.
   */
  @Override
  public boolean isLong()
  {
    return false;
  }

  /**
   * Returns true for a double-value.
   */
  @Override
  public boolean isDouble()
  {
    return true;
  }

  /**
   * Returns true for is_numeric
   */
  @Override
  public boolean isNumeric()
  {
    return true;
  }

  /**
   * Returns true for a scalar
   */
  public boolean isScalar()
  {
    return true;
  }

  //
  // marshal cost
  //

  /**
   * Cost to convert to a double
   */
  @Override
  public int toDoubleMarshalCost()
  {
    return Marshal.COST_EQUAL;
  }

  /**
   * Cost to convert to a long
   */
  @Override
  public int toLongMarshalCost()
  {
    return Marshal.COST_NUMERIC_LOSSY;
  }

  /**
   * Cost to convert to an integer
   */
  @Override
  public int toIntegerMarshalCost()
  {
    return Marshal.COST_NUMERIC_LOSSY;
  }

  /**
   * Cost to convert to a short
   */
  @Override
  public int toShortMarshalCost()
  {
    return Marshal.COST_NUMERIC_LOSSY;
  }

  /**
   * Cost to convert to a byte
   */
  @Override
  public int toByteMarshalCost()
  {
    return Marshal.COST_NUMERIC_LOSSY;
  }

  //
  // conversions
  //

  /**
   * Converts to a boolean.
   */
  public boolean toBoolean()
  {
    return _value != 0;
  }

  /**
   * Converts to a long.
   */
  public long toLong()
  {
    if ((_value > (double) Long.MAX_VALUE)
        || (_value < (double) Long.MIN_VALUE)) {
      return 0;
    } else {
      return (long) _value;
    }
  }

  /**
   * Converts to a double.
   */
  public double toDouble()
  {
    return _value;
  }

  /**
   * Converts to a double.
   */
  public DoubleValue toDoubleValue()
  {
    return this;
  }

  /**
   * Converts to a string builder
   */
  @Override
  public StringValue toStringBuilder(Env env)
  {
    return env.createUnicodeBuilder().append(toString());
  }

  /**
   * Converts to a key.
   */
  public Value toKey()
  {
    return LongValue.create((long) _value);
  }

  /**
   * Converts to a java object.
   */
  public Object toJavaObject()
  {
    return new Double(_value);
  }

  /**
   * Negates the value.
   */
  public Value neg()
  {
    return new DoubleValue(- _value);
  }

  /**
   * Returns the value
   */
  public Value pos()
  {
    return this;
  }

  /**
   * Multiplies to the following value.
   */
  public Value add(Value rValue)
  {
    return new DoubleValue(_value + rValue.toDouble());
  }

  /**
   * Multiplies to the following value.
   */
  public Value add(long lValue)
  {
    return new DoubleValue(lValue + _value);
  }

  /**
   * Increment the following value.
   */
  @Override
  public Value addOne()
  {
    return new DoubleValue(_value + 1);
  }

  /**
   * Increment the following value.
   */
  @Override
  public Value subOne()
  {
    double next = _value - 1;
    
    /*
    if (next == (long) next)
      return LongValue.create(next);
    else
    */
    return new DoubleValue(next);
  }

  /**
   * Increment the following value.
   */
  @Override
  public Value preincr()
  {
    return new DoubleValue(_value + 1);
  }

  /**
   * Increment the following value.
   */
  @Override
  public Value predecr()
  {
    return new DoubleValue(_value - 1);
  }

  /**
   * Increment the following value.
   */
  @Override
  public Value postincr()
  {
    return new DoubleValue(_value + 1);
  }

  /**
   * Increment the following value.
   */
  @Override
  public Value postdecr()
  {
    return new DoubleValue(_value - 1);
  }

  /**
   * Increment the following value.
   */
  public Value increment(int incr)
  {
    return new DoubleValue(_value + incr);
  }

  /**
   * Multiplies to the following value.
   */
  public Value mul(Value rValue)
  {
    return new DoubleValue(_value * rValue.toDouble());
  }

  /**
   * Multiplies to the following value.
   */
  public Value mul(long lValue)
  {
    return new DoubleValue(lValue * _value);
  }

  /**
   * Absolute value.
   */
  public Value abs()
  {
    if (_value >= 0)
      return this;
    else
      return DoubleValue.create(- _value);
  }

  /**
   * Returns true for equality
   */
  public boolean eql(Value rValue)
  {
    rValue = rValue.toValue();

    if (! (rValue instanceof DoubleValue))
      return false;

    double rDouble = ((DoubleValue) rValue)._value;

    return _value == rDouble;
  }

  /**
   * Converts to a string.
   * @param env
   */
  public String toString()
  {
    long longValue = (long) _value;

    double abs = _value < 0 ? - _value : _value;
    int exp = (int) Math.log10(abs);

    // php/0c02
    if (longValue == _value && exp < 18)
      return String.valueOf(longValue);

    if (-5 < exp && exp < 18) {
      int digits = 13 - exp;

      if (digits > 13)
        digits = 13;
      else if (digits < 0)
        digits = 0;

      String v = String.format("%." + digits + "f", _value);

      int len = v.length();
      int nonzero = -1;
      boolean dot = false;

      for (len--; len >= 0; len--) {
        int ch = v.charAt(len);

        if (ch == '.')
          dot = true;

        if (ch != '0' && nonzero < 0) {
          if (ch == '.')
            nonzero = len - 1;
          else
            nonzero = len;
        }
      }

      if (dot && nonzero >= 0)
        return v.substring(0, nonzero + 1);
      else
        return v;
    }
    else
      return String.format("%.13E", _value);
  }

  /**
   * Converts to an object.
   */
  public Object toObject()
  {
    return toString();
  }

  /**
   * Prints the value.
   * @param env
   */
  public void print(Env env)
  {
    env.print(toString());
  }

  /**
   * Serializes the value.
   */
  public void serialize(Env env, StringBuilder sb)
  {
    sb.append("d:");
    sb.append(_value);
    sb.append(";");
  }

  /**
   * Exports the value.
   */
  public void varExport(StringBuilder sb)
  {
    sb.append(toString());
  }

  //
  // Java generator code
  //

  /**
   * Generates code to recreate the expression.
   *
   * @param out the writer to the Java source code.
   */
  public void generate(PrintWriter out)
    throws IOException
  {
    if (_value == 0)
      out.print("DoubleValue.ZERO");
    else if (_value == Double.POSITIVE_INFINITY)
      out.print("new DoubleValue(Double.POSITIVE_INFINITY)");
    else if (_value == Double.NEGATIVE_INFINITY)
      out.print("new DoubleValue(Double.NEGATIVE_INFINITY)");
    else
      out.print("new DoubleValue(" + _value + ")");
  }

  /**
   * Returns the hash code
   */
  public int hashCode()
  {
    return (int) (37 + 65521 * _value);
  }

  /**
   * Compare for equality.
   */
  public boolean equals(Object o)
  {
    if (this == o)
      return true;
    else if (! (o instanceof DoubleValue))
      return false;

    DoubleValue value = (DoubleValue) o;

    return _value == value._value;
  }

  public void varDumpImpl(Env env,
                          WriteStream out,
                          int depth,
                          IdentityHashMap<Value, String> valueSet)
    throws IOException
  {
    out.print("float(" + toString() + ")");
  }

  //
  // Java Serialization
  //

  private Object readResolve()
  {
    if (_value == 0)
      return ZERO;
    else
      return this;
  }

}

