# Numeric objects represent numbers in opal. Unlike ruby, this class is
# used to represent both floats and integers, and there is currently no
# class representing bignums. Numeric values may only be made using
# their literal representations:
#
#     1       # => 1
#     2.0     # => 2
#     0.05    # => 0.05
#
# Implementation details
# ----------------------
#
# Opal numbers are toll-free bridged to native javascript numbers so
# that anywhere a ruby number is expected, a native javascript number
# may be used in its place. Javascript only has a single class/prototype
# to represent numbers, meaning that all integers and floats contain the
# same prototype. For this reason, Opal follows suit and implements all
# numbers as a direct instance of {Numeric}.
#
# Floats and Integers could be truley represented using wrappers, but
# this would **dramatically** increase performance overhead at the very
# core parts of the opal runtime. This overhead is too great, and the
# benefits would be too few.
#
# Ruby compatibility
# ------------------
#
# As discussed, {Numeric} is the only class used to represent numbers in
# opal. Most of the useful methods from `Fixnum`, `Integer` and `Float`
# are implemented on this class.
#
# It is also important to note that there is no distinguishment between
# floats and integers, so that, `1` and `1.0` are exactly equal.
#
# Custom subclasses of Numeric may be used so that a numeric literal is
# passed in. This differs from the ruby approach of number inheritance,
# but does allow for certain circumstances where a subclass of number
# might be useful. Opal does not try to implement `Integer` or `Float`
# for these purposes, but it is easily done. This approach will still
# not allow for literals to be used to make these subclass instances.
class Numeric

  def +(other)
    `return self + other;`
  end

  def -(other)
    `return self - other;`
  end

  def *(other)
    `return self * other;`
  end

  def /(other)
    `return self / other;`
  end

  def ==(other)
    `return self.valueOf() === other.valueOf();`
  end

  def <(other)
    `return self < other;`
  end

  def <=(other)
    `return self <= other;`
  end

  def >(other)
    `return self > other;`
  end

  def >=(other)
    `return self >= other;`
  end

  # Returns `self` modulo `other`. See `divmod` for more information.
  #
  # @param [Numeric] other number to use for module
  # @return [Numeric] result
  def %(other)
    `return self % other;`
  end

  def modulo(other)
    `return self % other;`
  end

  # Bitwise AND.
  #
  # @param [Numeric] other numeric to AND with
  # @return [Numeric]
  def &(num2)
    `return self & num2;`
  end

  # Raises `self` to the power of `other`.
  #
  # @param [Numeric] other number to raise to
  # @return [Numeric]
  def **(other)
    `return Math.pow(self, other);`
  end

  # Shift `self` left by `count` positions.
  #
  # @param [Numeric] count number to shift
  # @return [Numeric]
  def <<(count)
    `return self << count;`
  end

  # Shifts 'self' right by `count` positions.
  #
  # @param [Numeric] count number to shift
  # @return [Numeric]
  def >>(count)
    `return self >> count;`
  end

  # Comparison - Returns '-1', '0', '1' or nil depending on whether `self` is
  # less than, equal to or greater than `other`.
  #
  # @param [Numeric] other number to compare with
  # @return [Number, nil]
  def <=>(other)
    `if (typeof other != 'number') return nil;
    else if (self < other) return -1;
    else if (self > other) return 1;
    return 0;`
  end

  # Bitwise EXCLUSIVE OR.
  #
  # @param [Numeric] other number to XOR with
  # @return [Numeric]
  def ^(other)
    `return self ^ other;`
  end

  # Returns the absolute value of `self`.
  #
  # @example
  #
  #     -1234.abs
  #     # => 1234
  #     1234.abs
  #     # => 1234
  #
  # @return [Numeric]
  def abs
    `return Math.abs(self);`
  end

  def magnitude
    `return Math.abs(self);`
  end

  # Returns `true` if self is even, `false` otherwise.
  #
  # @return [true, false]
  def even?
    `return (self % 2 == 0);`
  end

  # Returns `true` if self is odd, `false` otherwise.
  #
  # @return [true, false]
  def odd?
    `return (self % 2 == 0) ? false : true;`
  end

  # Returns the number equal to `self` + 1.
  #
  # @example
  #
  #     1.next
  #     # => 2
  #     (-1).next
  #     # => 0
  #
  # @return [Numeric]
  def succ
    `return self + 1;`
  end

  def next
    `return self + 1;`
  end

  # Returns the number equal to `self` - 1
  #
  # @example
  #
  #     1.pred
  #     # => 0
  #     (-1).pred
  #     # => -2
  #
  # @return [Numeric]
  def pred
    `return self - 1;`
  end

  # Iterates the block, passing integer values from `self` upto and including
  # `finish`.
  #
  # @example
  #
  #     5.upto(10) { |i| puts i }
  #     # => 5
  #     # => 6
  #     # => 7
  #     # => 8
  #     # => 9
  #     # => 10
  #
  # @param [Numeric] finish where to stop iteration
  # @return [Numeric] returns the receiver
  def upto(finish)
    `for (var i = self; i <= finish; i++) {
      #{yield `i`};
    }

    return self;`
  end

  # Iterates the block, passing decreasing values from `self` downto and
  # including `finish`.
  #
  # @example
  #
  #     5.downto(1) { |x| puts x }
  #     # => 5
  #     # => 4
  #     # => 3
  #     # => 2
  #     # => 1
  #
  # @param [Numeric] finish where to stop iteration
  # @return [Numeric] returns the receiver
  def downto(finish)
    `for (var i = self; i >= finish; i--) {
      #{yield `i`};
    }

    return self;`
  end

  # Iterates the block `self` number of times, passing values in the range 0 to
  # `self` - 1.
  #
  # @example
  #
  #     5.times { |x| puts x }
  #     # => 0
  #     # => 1
  #     # => 2
  #     # => 3
  #     # => 4
  #
  # @return [Numeric] returns the receiver
  def times
    raise "no block given" unless block_given?
    `for (var i = 0; i < self; i++) {
      #{yield `i`};
    }

    return self;`
  end

  # Bitwise OR.
  #
  # @param [Numeric] other number to OR with
  # @return [Numeric]
  def |(other)
    `return self | other;`
  end

  # Returns `true` if `self` is zero, `false` otherwise.
  #
  # @return [true, false]
  def zero?
    `return self == 0;`
  end

  # Returns the receiver if it is not zero, `nil` otherwise
  #
  # @return [Numeric, nil]
  def nonzero?
    `return self == 0 ? nil : self;`
  end

  # One's complement: returns a number where each bit is flipped.
  #
  # @return [Numeric]
  def ~
    `return ~self;`
  end

  # Returns the smallest integer greater than or equal to `num`.
  #
  # @example
  #
  #     1.ceil        # => 1
  #     1.2.ceil      # => 2
  #     (-1.2).ceil   # => -1
  #     (-1.0).ceil   # => -1
  #
  # @return [Numeric]
  def ceil
    `return Math.ceil(self);`
  end

  # Returns the largest integer less than or equal to `self`.
  #
  # @example
  #
  #     1.floor       # => 1
  #     (-1).floor    # => -1
  #
  # @return [Numeric]
  def floor
    `return Math.floor(self);`
  end

  # Returns `true` if self is an ineteger, `false` otherwise.
  #
  # @return [true, false]
  def integer?
    `return self % 1 == 0;`
  end

  def inspect
    `return self.toString();`
  end

  def to_s
    `return self.toString();`
  end

  def to_i
    `return parseInt(self);`
  end

  def self.allocate
    raise RuntimeError, "cannot instantiate instance of Numeric class"
  end
end