library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;

-- return a + b * ( c - d ) / e
-- [0 to 255] + [-80 to 79] * ( [0 to 255] - [-80 to 79]) / [-80 to 79]

entity core_math is
	port (
		signal clk : in std_logic;
		signal reset : in std_logic;
		signal a, c, d : in unsigned (8 downto 0) := (others => '0');
		signal b, e : in signed (8 downto 0) := (others => '0');
		signal result : out unsigned (8 downto 0) := (others => '0');
		signal done : out std_logic := '0' -- active high
	);
end core_math;

architecture math of core_math is
    signal done_sig : std_logic := '0';
	signal div_reset : std_logic := '0';
	signal div_done : std_logic := '0';
	signal div_result : signed (17 downto 0) := (others => '0');
	signal numerator_wire : signed (17 downto 0) := (others => '0');
	constant INFINITE : unsigned (8 downto 0) := "011111111";
	constant INFINITE_INT : integer := 255;
begin
	done <= done_sig;
	compute_core_math : process(clk)
		variable cMinusD : signed (8 downto 0) := (others => '0');
		variable numerator : signed (17 downto 0) := (others => '0');
	begin
		if rising_edge(clk) then
			if (reset = '1') then
				result <= "000000000";
				if e = "000000000" then
					done_sig <= '1';
					result <= INFINITE;
				else
					done_sig <= '0';
					cMinusD := signed(c) - signed(d);
					numerator := cMinusD * b;
					numerator_wire <= numerator;
					div_reset <= '1';
				end if;
			else
				div_reset <= '0';
				if div_done = '1' and done_sig = '0' then
					result <= INFINITE;
					if div_result(17) = '0' and (div_result(16) = '1' or div_result(15) = '1') then
					else
						-- If we move backwards and the slope is negative,
						-- or if we move forwards and the slope is positive, good.
						-- In other words, rays can't reverse.
						if div_result = "000000000000000000" or (div_result(17) = '1' and b(8) = '1') or (div_result(17) = '0' and b(8) = '0') then
							numerator := signed("000000000" & a) + div_result;
							if numerator(17) = '1' or numerator(16) = '1' or numerator(15) = '1'
								or numerator(14) = '1' or numerator(13) = '1' or numerator(12) = '1'
								or numerator(11) = '1' or numerator(10) = '1' or numerator(9) = '1'
								or numerator(8) = '1' then
							else
								result <= unsigned(numerator(8 downto 0));
							end if;
						end if;
					end if;
					done_sig <= '1';
				end if;
			end if;
		end if;
	end process;
    
	Divider: entity work.divider
	port map(
		clk => clk,
		reset => div_reset,
		numerator => numerator_wire,
		denominator => e,
		result => div_result,
		done => div_done
	);
end math;