describe("Complex number", function()
	local complex = require("math.complex")
	local function assert_almost_same(a, b)
		assert.truthy(math.abs(a.real - b.real) < 1e-6)
		assert.truthy(math.abs(a.imaginary - b.imaginary) < 1e-6)
	end
	it("can be constructed", function()
		assert.same({ real = 33, imaginary = 42 }, complex.new(33, 42))
		assert.same({ real = 33, imaginary = 0 }, complex.new(33))
		assert.same({ real = 0, imaginary = 1 }, complex.i)
	end)
	describe("arithmetic", function()
		it("addition works", function()
			assert.same(complex.new(1, 2) + complex.new(3, 4), complex.new(4, 6))
		end)
		it("subtraction works", function()
			assert.same(complex.new(1, 2) - complex.new(3, 4), complex.new(-2, -2))
		end)
		it("negation works", function()
			assert.same(complex.new(-1, -2), -complex.new(1, 2))
		end)
		it("multiplication works", function()
			assert.same(complex.new(0, 2), complex.new(1, 1) * complex.new(1, 1))
			assert.same(complex.new(1), complex.new(1) * complex.new(1))
			assert.same(complex.new(-1), complex.new(0, 1) * complex.new(0, 1))
		end)
		it("division works", function()
			assert.same(complex.new(1), complex.new(42) / complex.new(42))
			assert.same(complex.new(1), complex.new(0, 42) / complex.new(0, 42))
			assert.same(complex.new(0.5, 0.5), complex.new(1, 1) / complex.new(2))
			assert.same(complex.new(0.5, 0.5), complex.new(1, 1) / complex.new(2))
		end)
		it("integer exponentiation works", function()
			assert.same(complex.new(-1), complex.i ^ 2)
			assert.same(complex.new(-1), complex.i ^ 22)
			assert.same(complex.new(1), complex.i ^ 4)
			assert.same(complex.new(1), complex.i ^ 44)
			assert.same(complex.new(2 ^ 8), complex.new(2) ^ 8)
		end)
		it("sqrt works", function()
			local function assert_equals_ignore_sign(a, b)
				assert(a == b or -a == b)
			end
			for _ = 1, 1e3 do
				local z = complex.new(math.random(-100, 100), math.random(-100, 100))
				local perfect_square = z ^ 2
				assert_equals_ignore_sign(perfect_square, (perfect_square ^ 0.5) ^ 2)
				assert_equals_ignore_sign(perfect_square, (perfect_square:sqrt()) ^ 2)
			end
		end)
		it("conjugation works", function()
			assert.same(complex.new(33, 42), complex.new(33, -42):conjugate())
			for _ = 1, 1e3 do
				local z = complex.new(math.random(), math.random())
				assert_almost_same(complex.new(z.real ^ 2 + z.imaginary ^ 2), z * z:conjugate())
			end
		end)
		it("abs works", function()
			assert.equal(5, complex.new(3, 4):abs())
			assert.equal(4, complex.new(4):abs())
			assert.equal(4, (4 * complex.i):abs())
		end)
	end)
	describe("conversion", function()
		describe("polar coordinates", function()
			it("identity works", function()
				for _ = 1, 1e3 do
					local z = complex.new(math.random(), math.random())
					assert_almost_same(z, complex.from_polar_coordinates(z:to_polar_coordinates()))
				end
			end)
			it("special values work", function()
				assert_almost_same(complex.new(2 ^ -0.5, 2 ^ -0.5), complex.from_polar_coordinates(0.25 * math.pi, 1))
				assert_almost_same(complex.new(2 ^ -0.5, -2 ^ -0.5), complex.from_polar_coordinates(-0.25 * math.pi, 1))
				assert_almost_same(complex.new(42), complex.from_polar_coordinates(0, 42))
				assert_almost_same(complex.new(-42), complex.from_polar_coordinates(math.pi, 42))
				assert_almost_same(complex.new(0, 42), complex.from_polar_coordinates(0.5 * math.pi, 42))
				assert_almost_same(complex.new(0, -42), complex.from_polar_coordinates(1.5 * math.pi, 42))
			end)
		end)
		it("to string works", function()
			assert.equal(tostring(complex.new(1, 2)), 1 .. "+" .. 2 .. "i")
		end)
	end)
	it("can be compared", function()
		assert.truthy(complex.new(1, 1) == complex.new(1, 1))
		assert.truthy(complex.new(1, 1) ~= complex.new(0, 1))
		assert.truthy(complex.new(1, 1) ~= complex.new(1))
		assert.truthy(complex.new(1, 1) ~= complex.new(0))
	end)
end)
