require_relative '../../spec_helper'
require 'bigdecimal'

describe "BigDecimal#round" do
  before :each do
    @one   = BigDecimal("1")
    @two   = BigDecimal("2")
    @three = BigDecimal("3")

    @neg_one   = BigDecimal("-1")
    @neg_two   = BigDecimal("-2")
    @neg_three = BigDecimal("-3")

    @p1_50 = BigDecimal("1.50")
    @p1_51 = BigDecimal("1.51")
    @p1_49 = BigDecimal("1.49")
    @n1_50 = BigDecimal("-1.50")
    @n1_51 = BigDecimal("-1.51")
    @n1_49 = BigDecimal("-1.49")

    @p2_50 = BigDecimal("2.50")
    @p2_51 = BigDecimal("2.51")
    @p2_49 = BigDecimal("2.49")
    @n2_50 = BigDecimal("-2.50")
    @n2_51 = BigDecimal("-2.51")
    @n2_49 = BigDecimal("-2.49")
  end

  after :each do
    BigDecimal.mode(BigDecimal::ROUND_MODE, BigDecimal::ROUND_HALF_UP)
  end

  it "uses default rounding method unless given" do
    @p1_50.round(0).should == @two
    @p1_51.round(0).should == @two
    @p1_49.round(0).should == @one
    @n1_50.round(0).should == @neg_two
    @n1_51.round(0).should == @neg_two
    @n1_49.round(0).should == @neg_one

    @p2_50.round(0).should == @three
    @p2_51.round(0).should == @three
    @p2_49.round(0).should == @two
    @n2_50.round(0).should == @neg_three
    @n2_51.round(0).should == @neg_three
    @n2_49.round(0).should == @neg_two

    BigDecimal.mode(BigDecimal::ROUND_MODE, BigDecimal::ROUND_DOWN)

    @p1_50.round(0).should == @one
    @p1_51.round(0).should == @one
    @p1_49.round(0).should == @one
    @n1_50.round(0).should == @neg_one
    @n1_51.round(0).should == @neg_one
    @n1_49.round(0).should == @neg_one

    @p2_50.round(0).should == @two
    @p2_51.round(0).should == @two
    @p2_49.round(0).should == @two
    @n2_50.round(0).should == @neg_two
    @n2_51.round(0).should == @neg_two
    @n2_49.round(0).should == @neg_two
  end

  describe "BigDecimal::ROUND_UP" do
    it "rounds values away from zero" do
      @p1_50.round(0, BigDecimal::ROUND_UP).should == @two
      @p1_51.round(0, BigDecimal::ROUND_UP).should == @two
      @p1_49.round(0, BigDecimal::ROUND_UP).should == @two
      @n1_50.round(0, BigDecimal::ROUND_UP).should == @neg_two
      @n1_51.round(0, BigDecimal::ROUND_UP).should == @neg_two
      @n1_49.round(0, BigDecimal::ROUND_UP).should == @neg_two

      @p2_50.round(0, BigDecimal::ROUND_UP).should == @three
      @p2_51.round(0, BigDecimal::ROUND_UP).should == @three
      @p2_49.round(0, BigDecimal::ROUND_UP).should == @three
      @n2_50.round(0, BigDecimal::ROUND_UP).should == @neg_three
      @n2_51.round(0, BigDecimal::ROUND_UP).should == @neg_three
      @n2_49.round(0, BigDecimal::ROUND_UP).should == @neg_three
    end
  end

  describe "BigDecimal::ROUND_DOWN" do
    it "rounds values towards zero" do
      @p1_50.round(0, BigDecimal::ROUND_DOWN).should == @one
      @p1_51.round(0, BigDecimal::ROUND_DOWN).should == @one
      @p1_49.round(0, BigDecimal::ROUND_DOWN).should == @one
      @n1_50.round(0, BigDecimal::ROUND_DOWN).should == @neg_one
      @n1_51.round(0, BigDecimal::ROUND_DOWN).should == @neg_one
      @n1_49.round(0, BigDecimal::ROUND_DOWN).should == @neg_one

      @p2_50.round(0, BigDecimal::ROUND_DOWN).should == @two
      @p2_51.round(0, BigDecimal::ROUND_DOWN).should == @two
      @p2_49.round(0, BigDecimal::ROUND_DOWN).should == @two
      @n2_50.round(0, BigDecimal::ROUND_DOWN).should == @neg_two
      @n2_51.round(0, BigDecimal::ROUND_DOWN).should == @neg_two
      @n2_49.round(0, BigDecimal::ROUND_DOWN).should == @neg_two
    end
  end

  describe "BigDecimal::ROUND_HALF_UP" do
    it "rounds values >= 5 up, otherwise down" do
      @p1_50.round(0, BigDecimal::ROUND_HALF_UP).should == @two
      @p1_51.round(0, BigDecimal::ROUND_HALF_UP).should == @two
      @p1_49.round(0, BigDecimal::ROUND_HALF_UP).should == @one
      @n1_50.round(0, BigDecimal::ROUND_HALF_UP).should == @neg_two
      @n1_51.round(0, BigDecimal::ROUND_HALF_UP).should == @neg_two
      @n1_49.round(0, BigDecimal::ROUND_HALF_UP).should == @neg_one

      @p2_50.round(0, BigDecimal::ROUND_HALF_UP).should == @three
      @p2_51.round(0, BigDecimal::ROUND_HALF_UP).should == @three
      @p2_49.round(0, BigDecimal::ROUND_HALF_UP).should == @two
      @n2_50.round(0, BigDecimal::ROUND_HALF_UP).should == @neg_three
      @n2_51.round(0, BigDecimal::ROUND_HALF_UP).should == @neg_three
      @n2_49.round(0, BigDecimal::ROUND_HALF_UP).should == @neg_two
    end
  end

  describe "BigDecimal::ROUND_HALF_DOWN" do
    it "rounds values > 5 up, otherwise down" do
      @p1_50.round(0, BigDecimal::ROUND_HALF_DOWN).should == @one
      @p1_51.round(0, BigDecimal::ROUND_HALF_DOWN).should == @two
      @p1_49.round(0, BigDecimal::ROUND_HALF_DOWN).should == @one
      @n1_50.round(0, BigDecimal::ROUND_HALF_DOWN).should == @neg_one
      @n1_51.round(0, BigDecimal::ROUND_HALF_DOWN).should == @neg_two
      @n1_49.round(0, BigDecimal::ROUND_HALF_DOWN).should == @neg_one

      @p2_50.round(0, BigDecimal::ROUND_HALF_DOWN).should == @two
      @p2_51.round(0, BigDecimal::ROUND_HALF_DOWN).should == @three
      @p2_49.round(0, BigDecimal::ROUND_HALF_DOWN).should == @two
      @n2_50.round(0, BigDecimal::ROUND_HALF_DOWN).should == @neg_two
      @n2_51.round(0, BigDecimal::ROUND_HALF_DOWN).should == @neg_three
      @n2_49.round(0, BigDecimal::ROUND_HALF_DOWN).should == @neg_two
    end
  end

  describe "BigDecimal::ROUND_CEILING" do
    it "rounds values towards +infinity" do
      @p1_50.round(0, BigDecimal::ROUND_CEILING).should == @two
      @p1_51.round(0, BigDecimal::ROUND_CEILING).should == @two
      @p1_49.round(0, BigDecimal::ROUND_CEILING).should == @two
      @n1_50.round(0, BigDecimal::ROUND_CEILING).should == @neg_one
      @n1_51.round(0, BigDecimal::ROUND_CEILING).should == @neg_one
      @n1_49.round(0, BigDecimal::ROUND_CEILING).should == @neg_one

      @p2_50.round(0, BigDecimal::ROUND_CEILING).should == @three
      @p2_51.round(0, BigDecimal::ROUND_CEILING).should == @three
      @p2_49.round(0, BigDecimal::ROUND_CEILING).should == @three
      @n2_50.round(0, BigDecimal::ROUND_CEILING).should == @neg_two
      @n2_51.round(0, BigDecimal::ROUND_CEILING).should == @neg_two
      @n2_49.round(0, BigDecimal::ROUND_CEILING).should == @neg_two
    end
  end

  describe "BigDecimal::ROUND_FLOOR" do
    it "rounds values towards -infinity" do
      @p1_50.round(0, BigDecimal::ROUND_FLOOR).should == @one
      @p1_51.round(0, BigDecimal::ROUND_FLOOR).should == @one
      @p1_49.round(0, BigDecimal::ROUND_FLOOR).should == @one
      @n1_50.round(0, BigDecimal::ROUND_FLOOR).should == @neg_two
      @n1_51.round(0, BigDecimal::ROUND_FLOOR).should == @neg_two
      @n1_49.round(0, BigDecimal::ROUND_FLOOR).should == @neg_two

      @p2_50.round(0, BigDecimal::ROUND_FLOOR).should == @two
      @p2_51.round(0, BigDecimal::ROUND_FLOOR).should == @two
      @p2_49.round(0, BigDecimal::ROUND_FLOOR).should == @two
      @n2_50.round(0, BigDecimal::ROUND_FLOOR).should == @neg_three
      @n2_51.round(0, BigDecimal::ROUND_FLOOR).should == @neg_three
      @n2_49.round(0, BigDecimal::ROUND_FLOOR).should == @neg_three
    end
  end

  describe "BigDecimal::ROUND_HALF_EVEN" do
    it "rounds values > 5 up, < 5 down and == 5 towards even neighbor" do
      @p1_50.round(0, BigDecimal::ROUND_HALF_EVEN).should == @two
      @p1_51.round(0, BigDecimal::ROUND_HALF_EVEN).should == @two
      @p1_49.round(0, BigDecimal::ROUND_HALF_EVEN).should == @one
      @n1_50.round(0, BigDecimal::ROUND_HALF_EVEN).should == @neg_two
      @n1_51.round(0, BigDecimal::ROUND_HALF_EVEN).should == @neg_two
      @n1_49.round(0, BigDecimal::ROUND_HALF_EVEN).should == @neg_one

      @p2_50.round(0, BigDecimal::ROUND_HALF_EVEN).should == @two
      @p2_51.round(0, BigDecimal::ROUND_HALF_EVEN).should == @three
      @p2_49.round(0, BigDecimal::ROUND_HALF_EVEN).should == @two
      @n2_50.round(0, BigDecimal::ROUND_HALF_EVEN).should == @neg_two
      @n2_51.round(0, BigDecimal::ROUND_HALF_EVEN).should == @neg_three
      @n2_49.round(0, BigDecimal::ROUND_HALF_EVEN).should == @neg_two
    end
  end

  it 'raise exception, if self is special value' do
    lambda { BigDecimal('NaN').round }.should raise_error(FloatDomainError)
    lambda { BigDecimal('Infinity').round }.should raise_error(FloatDomainError)
    lambda { BigDecimal('-Infinity').round }.should raise_error(FloatDomainError)
  end

  it 'do not raise exception, if self is special value and precision is given' do
    lambda { BigDecimal('NaN').round(2) }.should_not raise_error(FloatDomainError)
    lambda { BigDecimal('Infinity').round(2) }.should_not raise_error(FloatDomainError)
    lambda { BigDecimal('-Infinity').round(2) }.should_not raise_error(FloatDomainError)
  end
end
