require 'test/unit'
require 'solution'

class TestPoly < Test::Unit::TestCase
  def test_equality_and_construction
    assert Poly.new([3, 4, 1]) == Poly.new([3, 4, 1, 0, 0, 0, 0])
    assert Poly.new([3, 4, 1]) == Poly.new({0 => 3, 2 => 1, 1 => 4})
    assert Poly.new([3, 4, 1]) != Poly.new([1, 3])
    assert Poly.new([3, 4, 1]) != Poly.new([3, 1, 1])
    assert Poly.new([0]) == Poly.new({0 => 0})
    assert Poly.new([]) == Poly.new({})
    assert Poly.new([]) == Poly.new({0 => 0})
    assert Poly.new([]) == Poly.new
  end

	def test_add
		assert_equal Poly.new([1, 2]), Poly.new([1, 1])  + Poly.new([0, 1])
		assert_equal Poly.new([12, 1]), Poly.new([1, 1])  + 11
    assert_equal Poly.new([12, 1]), 11 + Poly.new([1, 1])
    assert_equal Poly.new([-1]), Poly.new([0]) + Poly.new([-1])
    assert_equal Poly.new([-1, 3, 7]), Poly.new([0]) + Poly.new([-1, 3, 7])
	end

  def test_sub
		assert_equal Poly.new([1]), Poly.new([1, 1])  - Poly.new([0, 1])
    assert_equal Poly.new([1]), Poly.new([0]) - Poly.new([-1])
    assert_equal Poly.new([1, -3, -7]), Poly.new([0]) - Poly.new([-1, 3, 7])
		assert_equal Poly.new([-10, 1]), Poly.new([1, 1])  - 11
	end

	def test_brackets
		poly = Poly.new [1,2]
		assert_equal 1, poly[0]
		assert_equal 2, poly[1]
		assert_equal 0, poly[2]
    assert_equal 0, poly[6]
    poly[2] += -3
    poly[6] = 12
    assert_equal -3, poly[2]
    assert_equal 12, poly[6]
	end

	def test_multiplication
		assert_equal Poly.new([6, 15, 8, 4]), Poly.new([6, 3, 2]) * Poly.new([1, 2])
		assert_equal Poly.new([12, 6, 4]), Poly.new([6, 3, 2]) * 2
    assert_equal Poly.new([4, -12, 9]), Poly.new([2, -3]) * Poly.new([2, -3])
	end

	def test_power
		assert_equal Poly.new([1, 3, 3, 1]), Poly.new([1, 1]) ** 3
		assert_equal Poly.new([1, 4, 6, 4, 1]), Poly.new([1, 1]) ** 4
    assert_equal Poly.new([1, 1]), Poly.new([1, 1]) ** 1
    assert_equal Poly.new([1]), Poly.new([1, 1]) ** 0
    assert_equal Poly.new([4, -12, 9]), Poly.new([2, -3]) ** 2
	end

	def test_derivative
		assert_equal Poly.new([]), ~Poly.new([11])
    assert_equal Poly.new([]), ~Poly.new
		assert_equal Poly.new([3, 4]), ~Poly.new([6, 3, 2])
    assert_equal Poly.new([3, -4]), ~Poly.new([6, 3, -2])
	end

	def test_to_s
		assert_equal '5x^3+2x+1', Poly.new([1, 2, 0, 5]).to_s
    assert_equal '5x^3+x+1', Poly.new([1, 1, 0, 5]).to_s
#    assert_equal '5x^3-x+1', Poly.new([1, 1, 0, 5]).to_s
		assert_equal '-5x^3-2x-1', Poly.new([-1, -2, 0, -5]).to_s
    assert_equal '7', Poly.new([7]).to_s
    assert_equal '-7', Poly.new([-7, 0]).to_s
    assert_equal '', Poly.new.to_s
    assert_equal '', Poly.new({2 => 0}).to_s
	end

  def test_find
    o = Poly.new([1, 0, 5])
    assert_equal nil, o.find { |power, coeff| false }
    assert_equal nil, o.find(nil) { |power, coeff| false }
    assert_equal "larodi", o.find(lambda { "larodi" }) { |power, coeff| false }
    assert_equal nil, o.find { |power, coeff| power == 1 or coeff == 0 }
    assert_equal [0, 1], o.find { |power, coeff| power == 0 }
    assert_equal [2, 5], o.find { |power, coeff| power == 2 }
  end

	def test_include
		assert Poly.new([1, 0, 5]).include?([0, 1])
    assert Poly.new([1, 0, 5]).include?([2, 5])
    assert !Poly.new([1, 0, 5]).include?([2, 0])
    assert !Poly.new([1, 0, 5]).include?([0, 0])
    assert Poly.new.include?([0, 0])
    assert Poly.new([0, 0, 5]).include?([0, 0])
    assert Poly.new([1, 0, 5]).include?([4, 0])
	end

  def test_sort_by
    assert_equal [[0, 1], [2, 5]], Poly.new([1, 0, 5]).sort_by { |power, coeff| power }
    assert_equal [[2, 5], [0, 1]], Poly.new([1, 0, 5]).sort_by { |power, coeff| -power }
    assert_equal [[2, 5], [0, 1]], Poly.new([1, 0, 5]).sort_by { |power, coeff| -coeff }
    assert_equal [], Poly.new.sort_by { |power, coeff| -coeff }
    assert_equal [[0, -3]], Poly.new([-3]).sort_by { |power, coeff| -coeff }
  end

  # Community tests

 	def test_hashability_community
		pol = Poly.new( [1,0,2,3] )
		assert_equal 2, pol[2]
		assert_equal 0, pol[100]
		pol[0] = 3
		assert_equal 3, pol[0]
	end

	def test_comparison_community
		pol1 = Poly.new( [1,0,2,3] )
		pol2 = Poly.new( {0=>1, 2=>2, 3=>3} )
		assert pol1 == pol2
		assert pol1 != Poly.new([])
		assert Poly.new([]) == Poly.new([0,0,0])
	end

	def test_opeartions_community
		p1 = Poly.new( {0=>1, 2=>2} )
		p2 = Poly.new( {1=>1, 2=>3} )
		p3 = Poly.new( {0=>1, 2=>5, 1=>1} ) # p1 + p2
		p4 = Poly.new( {0=>5, 2=>5, 1=>1} ) # p3 + 4
		p5 = Poly.new( {0=>6, 2=>7, 1=>1} ) # p1 + p4
		assert_equal p3, p1+p2
		assert_equal p3, (p3+4)-4
		assert_equal p4, p3+4
    assert_equal p3, p4-4
		assert_equal p5, p1+p4
		assert_equal p4, p5-p1
		assert_equal Poly.new( {1=>2, 2=>6} ), p2 * 2
		assert_equal Poly.new( {0=>-1, 2=>-2} ), p1 * -1
		assert_equal Poly.new( {} ), Poly.new( {} ) * p1
		assert_equal Poly.new( {} ), p1 * Poly.new( {} )
		assert_equal Poly.new( {} ), p1 * 0
		assert_equal Poly.new( {4=>6,3=>2,2=>3,1=>1} ), p1 * p2
		assert_equal p2 * p1, p1 * p2
		
		assert_equal Poly.new( {4=>4, 2=>4,0=>1} ), p1**2
		assert_equal Poly.new( {0=>1} ), p1**0
		
 		assert_equal Poly.new( [] ), ~Poly.new([1])
		assert_equal Poly.new( [0,6] ), ~Poly.new([1,0,3])
		assert_equal Poly.new( [6] ), ~Poly.new([0,6])
	end

  # got he idea? That was a case in my first poly :/
  # mitio: nb! this is a very important test!
	def test_misc_community
		a = {1=>5, 2=>3}
		pol = Poly.new( a )
		pol[100] = 5
		assert_equal( {1=>5, 2=>3}, a )
    # just another way to test this
    pol = Poly.new({0 => 1, 2 => 5, 1 => 1 })
    pol_str = pol.to_s
    pol + 17 # no-op
    assert_equal pol.to_s, pol_str
	end

	def test_to_string_community
		assert_equal "2x^2+1", Poly.new( {0=>1, 2=>2} ).to_s
		assert_equal "-2x^2+1", Poly.new( {0=>1, 2=>-2} ).to_s
		assert_equal "-6x^4+2x^3-3x^2+x", Poly.new( {4=>-6,3=>2,2=>-3,1=>1} ).to_s
	end

  def test_include_community
		assert Poly.new([1, 0, 5]).include?( [0, 1] )
		assert Poly.new([1, 0, 5]).include?( [101, 0] )
		assert ! ( Poly.new([1, 0, 5]).include? [1, 1] )
	end

	def test_map_sortby_find_community
		pol = Poly.new( {4=>-6,3=>2,2=>-3,1=>1} )
		assert_equal [-2,-1,2,5], pol.map{ |k,v| k+v }.sort
		assert_equal nil, pol.find{ |exp,coef| coef == 42 }
		assert_equal [4,-6], pol.find{ |exp,coef| exp==4 && coef==-6 }
		assert_equal [ [4,-6], [2,-3], [1,1], [3,2] ], pol.sort_by{ |exp,coef| coef }
	end

end

