class NUMERIC_TEST_SET

(source code)

description

Numeric experiments

note
	description: "Numeric experiments"

	author: "Finnian Reilly"
	copyright: "Copyright (c) 2001-2022 Finnian Reilly"
	contact: "finnian at eiffel hyphen loop dot com"

	license: "MIT license (See: en.wikipedia.org/wiki/MIT_License)"
	date: "2024-10-14 17:12:10 GMT (Monday 14th October 2024)"
	revision: "24"

class
	NUMERIC_TEST_SET

inherit
	EL_EQA_TEST_SET

	DOUBLE_MATH
		rename
			pi as pi_double
		undefine
			default_create
		end

create
	make

feature {NONE} -- Initialization

	make
		-- initialize `test_table'
		do
			make_named (<<
				["double_string_conversion",	 agent test_double_string_conversion],
				["modulo_indexing",				 agent test_modulo_indexing],
				["ones_complement_arithmetic", agent test_ones_complement_arithmetic],
				["store_integer_in_natural",	 agent test_store_integer_in_natural],
				["truncated_natural_64",		 agent test_truncated_natural_64]
			>>)
		end

feature -- Tests

	test_double_string_conversion
		-- NUMERIC_TEST_SET.test_double_string_conversion
		local
			d1, d2, r64: DOUBLE; r32: REAL
			n: INTEGER
		do
			d1 := 3.3;
			across << "3.3000000000000003", "3.3" >> as str loop
				d2 := str.item.to_double
				if str.cursor_index = 1 then
					assert ("same number", d1 /= d2)
				else
					assert ("same number", d1 = d2)
				end
			end
			n := 1
			across 1 |..| 9 as i loop
				r64 := n.to_double + (1 / i.item); r32 := r64.truncated_to_real

				lio.put_labeled_substitution (i.item.out, "double: %S real: %S", [r64, r32])
				lio.put_new_line
				n := n * 10
			end
		end

	test_modulo_indexing
		local
			i, count: INTEGER
		do
			count := 5
			across -10 |..| 10 as n loop
				i := n.item \\ count
				if i < 0 then
					i := count + i
				end
				assert ("i between 0 and count - 1", 0 <= i and i <= count - 1)
			end
		end

	test_ones_complement_arithmetic
		-- NUMERIC_TEST_SET.test_ones_complement_arithmetic
		local
			i: INTEGER; n, negative_n: NATURAL
		do
			i := -2
			n := i.to_natural_32
			assert ("same as abs", n = 4294967294)

			negative_n := 2
			negative_n := negative_n.bit_not + 1
			assert ("same as 1's complement + 1", negative_n = n)
			negative_n := (negative_n - 1).bit_not
			assert ("is reversed", negative_n = 2)

			-- reverse
			i := n.to_integer_32
			assert ("same as abs", i = -2)

			n := n + 4
			assert ("is 2", n = 2)
		end

	test_store_integer_in_natural
		-- NUMERIC_TEST_SET.test_store_integer_in_natural
		local
			natural_64: NATURAL_64; i: INTEGER
		do
			i := -1
			natural_64 := i.to_natural_64
			i := natural_64.to_integer_32
			assert ("recovered i", i = -1)
		end

	test_truncated_natural_64
		local
			natural_64: NATURAL_64
		do
			natural_64 := 0xFFFFFFFF
			assert ("same string", natural_64.to_natural_16.to_hex_string ~ "FFFF")
		end

feature -- Basic operations

	abstract_increment
		local
			n: INTEGER_16; number: NUMERIC
		do
			n := 1
			number := n
			number := number + number.one
			lio.put_labeled_string ("number", number.out)
		end

	generic_numeric
		local
			edge: EDGE [INTEGER]
		do
			create edge
			lio.put_integer_field ("One", edge.cost)
			lio.put_new_line
		end

	hex_conversion
		local
			n: NATURAL_64
		do
			n := 0xAAAABBBBCCCCDDDD
			lio.put_labeled_string ("to_hex_string", n.to_hex_string)
		end

	iteration_10_to_pow_8
		local
			capacity: INTEGER_64
		do
			from capacity := 10 until capacity >= 100_000_000 loop
				lio.put_labeled_string ("capacity", capacity.out)
				lio.put_new_line
				capacity := capacity * 10
			end
		end

	log_sequence
		local
			n, i: INTEGER
		do
			from n := 1; i := 1 until i > 29 loop
				lio.put_integer_field ("log " + n.out, log10 (n).ceiling.max (1))
				lio.put_new_line
				i := i + 1
				n := n * 2
			end
		end

	negative_integer_32_in_integer_64
			-- is it possible to store 2 negative INTEGER_32's in one INTEGER_64
		local
			n: INTEGER_64
		do
			n := ((10).to_integer_64 |<< 32) | -10
			lio.put_integer_field ("low", n.to_integer_32) -- yes you can
			lio.put_integer_field (" hi", (n |>> 32).to_integer_32) -- yes you can
		end

	pi: DOUBLE
			-- Given that Pi can be estimated using the function 4 * (1 - 1/3 + 1/5 - 1/7 + ..)
			-- with more terms giving greater accuracy, write a function that calculates Pi to
			-- an accuracy of 5 decimal places.
		local
			limit, term, four: DOUBLE; divisor: INTEGER
		do
			lio.enter ("pi")
			four := 4.0; limit := 0.5E-5; divisor := 1
			from term := four until term.abs < limit loop
				Result := Result + term
				four := four.opposite
				divisor := divisor + 2
				term := four / divisor
			end
			lio.put_integer_field ("divisor", divisor)
			lio.put_new_line
			lio.exit
		end

	raku_vs_eiffel
		-- Raku gives correct answer for following
		local
			equation: STRING
		do
			equation :="x = 0.1 + 0.2 - 0.3 => x = "
			lio.put_string (equation)
			lio.put_double (0.1 + 0.2 - 0.3, Void)
			lio.put_new_line

			lio.put_string (equation)
			lio.put_real ({REAL} 0.1 + {REAL} 0.2 - {REAL} 0.3, Void)
			lio.put_new_line

			lio.put_string (equation + ({REAL} 0.01 + {REAL} 0.02 - {REAL} 0.03).out)
			lio.put_new_line
		end

	random_sequence
			--
		local
			random: RANDOM; odd, even: INTEGER; time: TIME
		do
			create time.make_now
			create random.make
			random.set_seed (time.compact_time)
			lio.put_integer_field ("random.seed", random.seed)
			lio.put_new_line
			from  until random.index > 200 loop
				lio.put_integer_field (random.index.out, random.item)
				lio.put_new_line
				if random.item \\ 2 = 0 then
					even := even + 1
				else
					odd := odd + 1
				end
				random.forth
			end
			lio.put_new_line
			lio.put_integer_field ("odd", odd)
			lio.put_new_line
			lio.put_integer_field ("even", even)
			lio.put_new_line
		end

	real_rounding
		local
			r: REAL
		do
			r := ("795").to_real
			lio.put_integer_field ("(r * 100).rounded", (r * 100).rounded)
		end

end