library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.numeric_std.ALL;

entity Main is
    generic (
        R : in unsigned (160 downto 0):= "1" & to_unsigned(0,160)
        );
    Port (  clk : in std_logic;
            result : out unsigned (159 downto 0);
            RNG : in unsigned (159 downto 0)                    
          );
end Main;

architecture Behavioral of Main is
    component Testwit is
        Port(   clk : in std_logic;
        testresult : out std_logic;
        witness : in unsigned (5 downto 0);
        tested : in unsigned (159 downto 0);
        pinv : in unsigned (159 downto 0);
        montbport : in unsigned (159 downto 0);
        witran : in unsigned (1 downto 0);
        montmin1 : in unsigned (160 downto 0)
        );
    end component;

    function Mult160 (a : unsigned (159 downto 0); b : unsigned (159 downto 0)) return unsigned is
        variable a1 : unsigned (63 downto 0);
        variable a2 : unsigned (63 downto 0);
        variable a3 : unsigned (31 downto 0);
        variable b1 : unsigned (63 downto 0);
        variable b2 : unsigned (63 downto 0);
        variable b3 : unsigned (31 downto 0);
        variable c11 : unsigned (127 downto 0);
        variable c12 : unsigned (191 downto 0);
        variable c13 : unsigned (223 downto 0);
        variable c21 : unsigned (191 downto 0);
        variable c22 : unsigned (255 downto 0);
        variable c23 : unsigned (287 downto 0);
        variable c31 : unsigned (223 downto 0);
        variable c32 : unsigned (287 downto 0);
        variable c33 : unsigned (320 downto 0);
    begin
        a1:=a (63 downto 0);
        a2:=a (127 downto 64);
        a3:=a (159 downto 128);
        b1:=b (63 downto 0);
        b2:=b (127 downto 64);
        b3:=b (159 downto 128);
        c11:= a1*b1 + to_unsigned(0,128);
        c12:= ((a1*b2)&to_unsigned(0,64) )+ to_unsigned(0,192);
        c13:= ((a1*b3)&to_unsigned(0,128))+ to_unsigned(0,224);
        c22:= ((a2*b2)&to_unsigned(0,128))+ to_unsigned(0,256);
        c21:= ((a2*b1)&to_unsigned(0,64))+ to_unsigned(0,192);
        c23:= ((a2*b3)&to_unsigned(0,192)) + to_unsigned(0,288);
        c31:= ((a3*b1)&to_unsigned(0,128))+ to_unsigned(0,224);
        c32:= ((a3*b2)&to_unsigned(0,192)) + to_unsigned(0,288);
        c33:= ((a3*b3)&to_unsigned(0,256)) + to_unsigned(0,321);
        return c11+c12+c13+c22+c21+c31+c32+c33+c23 + to_unsigned(0,321);
    end function;

    signal fresh : unsigned (159 downto 0);
    signal hold : unsigned (159 downto 0);
    signal testres : std_logic_vector (39 downto 0);
    signal pinv : unsigned (159 downto 0);
    signal montbport : unsigned (159 downto 0);
    signal delay : integer := 4;
    signal witran : unsigned (1 downto 0);
    signal montmin1 : unsigned (160 downto 0);


begin
odd : process (clk)
    variable odder : unsigned (159 downto 0);
    variable c : unsigned (159 downto 0);
    variable d : unsigned (159 downto 0);
    variable e : unsigned (159 downto 0);
    variable var1 : unsigned (320 downto 0);
    variable var2 : unsigned (320 downto 0);
    variable var3 : unsigned (160 downto 0);
begin
if rising_edge(clk) then
    if delay > 3 then
    odder:= '1' & RNG(157 downto 0) & "1";
    fresh<=odder;
    witran<=RNG(159 downto 158);
    c:=odder;
    d:=to_unsigned(1,160);
    var3:=R-odder;
    montbport(159 downto 0) <= var3 (159 downto 0);
    montmin1<=(TO_UNSIGNED(0,161)+odder)+odder-R;
    delay<=3;
    else
        for x in 0 to 52 loop                           -- V jednom clk je provedeno 53 kroků z celkem 159 krokového procesu a proto je rozdělen na 3 opakování
        var1:=Mult160(d, c);
        d(159 downto 0) := var1(159 downto 0);
        var2 := Mult160(c, c);
        c(159 downto 0) := var2(159 downto 0);
        end loop;
        if delay = 1 then
            delay<=4;
            var3:=R-d;
            e(159 downto 0):=var3 (159 downto 0);
            pinv<=e;
            hold<=fresh;
        else
            delay<=delay-1;
        end if;
    end if;
end if;
end process;

forgen : for i in 2 to 41 generate begin

repeated : Testwit port map (clk=>clk,testresult=>testres(i-2),witness=>to_unsigned(i,6),tested=>hold,pinv=>pinv,montbport=>montbport, witran=>witran, montmin1=>montmin1);

end generate;

reg : process (clk)
    variable hold2 : unsigned (159 downto 0);
begin
if rising_edge(clk) then
    if testres = "0000000000000000000000000000000000000000" then
        result <= hold2;
    end if;
    hold2 := hold;
end if;
end process;

end Behavioral;

