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


entity Testwit is
    generic (R : in unsigned (160 downto 0):= "1" & to_unsigned(0,160));
    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 Testwit;

architecture Behavioral of Testwit is
    function smallmod (wit : unsigned (5 downto 0); n : unsigned (159 downto 0); montbf : unsigned (159 downto 0)) return unsigned is
        variable x : unsigned (165 downto 0);
    begin
        x:= wit*montbf + to_unsigned(0, 166);
        for i in 0 to 42 loop
            if x < ("000000" & n) then
                return x(159 downto 0);
            end if;
            x:=x-n + to_unsigned(0, 166);
        end loop;
        return x(159 downto 0);
    end function;
    
    function Mult (a : unsigned (160 downto 0); b : unsigned (160 downto 0)) return unsigned is
        variable a1 : unsigned (63 downto 0);
        variable a2 : unsigned (63 downto 0);
        variable a3 : unsigned (32 downto 0);
        variable b1 : unsigned (63 downto 0);
        variable b2 : unsigned (63 downto 0);
        variable b3 : unsigned (32 downto 0);
        variable c11 : unsigned (127 downto 0);
        variable c12 : unsigned (191 downto 0);
        variable c13 : unsigned (224 downto 0);
        variable c21 : unsigned (191 downto 0);
        variable c22 : unsigned (255 downto 0);
        variable c23 : unsigned (288 downto 0);
        variable c31 : unsigned (224 downto 0);
        variable c32 : unsigned (288 downto 0);
        variable c33 : unsigned (321 downto 0);
    begin
        a1:=a (63 downto 0);
        a2:=a (127 downto 64);
        a3:=a (160 downto 128);
        b1:=b (63 downto 0);
        b2:=b (127 downto 64);
        b3:=b (160 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,225);
        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,289);
        c31:= ((a3*b1)&to_unsigned(0,128))+ to_unsigned(0,225);
        c32:= ((a3*b2)&to_unsigned(0,192)) + to_unsigned(0,289);
        c33:= ((a3*b3)&to_unsigned(0,256)) + to_unsigned(0,322);
        return c11+c12+c13+c22+c21+c31+c32+c33+c23 + to_unsigned(0,322);
    end function;



    function ModMult (a : unsigned (160 downto 0); b : unsigned (160 downto 0)) return unsigned is
        variable a1 : unsigned (63 downto 0);
        variable a2 : unsigned (63 downto 0);
        variable a3 : unsigned (32 downto 0);
        variable b1 : unsigned (63 downto 0);
        variable b2 : unsigned (63 downto 0);
        variable b3 : unsigned (32 downto 0);
        variable c11 : unsigned (160 downto 0);
        variable c12 : unsigned (160 downto 0);
        variable c13 : unsigned (160 downto 0);
        variable c21 : unsigned (160 downto 0);
        variable c22 : unsigned (160 downto 0);
        variable c31 : unsigned (160 downto 0);
        
    begin
        a1:=a (63 downto 0);
        a2:=a (127 downto 64);
        a3:=a (160 downto 128);
        b1:=b (63 downto 0);
        b2:=b (127 downto 64);
        b3:=b (160 downto 128);
        c11:= a1*b1 + to_unsigned(0,161);
        c12:= (((a1*b2)&to_unsigned(0,64) )+ to_unsigned(0,161))mod R;
        c13:= (((a1*b3)&to_unsigned(0,128))+ to_unsigned(0,161))mod R;
        c22:= (((a2*b2)&to_unsigned(0,128))+ to_unsigned(0,161))mod R;
        c21:= (((a2*b1)&to_unsigned(0,64))+ to_unsigned(0,161))mod R;
        c31:= (((a3*b1)&to_unsigned(0,128))+ to_unsigned(0,161))mod R;
        return (c11+c12+c13+c22+c21+c31) mod R;
    end function;

    function MontProd (a : unsigned (160 downto 0); b : unsigned (160 downto 0); pinvold : unsigned (159 downto 0); testedold : unsigned (159 downto 0)) return unsigned is
        variable t : unsigned (321 downto 0);
        variable u : unsigned (162 downto 0);
        variable var1 : unsigned (160 downto 0);
        variable var2 : unsigned (160 downto 0);
        variable var3 : unsigned (321 downto 0);
        variable var4 : unsigned (322 downto 0);
        variable var5 : unsigned (319 downto 0);
    begin
        t:=Mult(a,b);
        var1:=t mod R;
        var2:= ModMult(var1,'0' & pinvold);
        var3:= Mult(var2, '0' & testedold);
        var4:=t+var3 + to_unsigned(0,323);
        u(162 downto 0):=var4 (322 downto 160);
        if u < ("000" & testedold) then
            return u(160 downto 0);
        end if;
            u:=u-testedold;
        return u(160 downto 0);
    end function;

    signal delay : integer := 54;
    signal monta : unsigned (160 downto 0);
    signal testedold : unsigned (159 downto 0);
    signal pinvold : unsigned (159 downto 0);
    signal montbportold : unsigned (159 downto 0);
    signal montmin1old : unsigned (160 downto 0);
    signal bless : std_logic;

begin
    testwit : process (clk)
        variable montbmezi : unsigned (160 downto 0);
        variable ble : std_logic;
    begin
    if rising_edge(clk) and tested(0)/= 'U' then
        if delay > 53 then
            bless<='0';
            montbmezi:='0' & montbport;
            monta<= '0' & smallmod(witness+witran, tested, montbport);
            pinvold<=pinv;
            testedold<=tested;
            montmin1old<=montmin1;
            montbportold<=montbport;
            delay<=53;
            testresult <= '1';
        else                                                                    -- V jednom clk jsou provedeny 3 kroky z celkem 159 krokového procesu a proto je rozdělen na 53 opakování
            montbmezi:= MontProd(montbmezi,montbmezi,pinvold,testedold);
            ble:=bless;
            if testedold(3*delay) = '1' then
                montbmezi:= MontProd(montbmezi,monta,pinvold,testedold);
                ble:='0';
            end if;
            if(montbmezi = montmin1old) then
                ble:='1';
            end if;
            montbmezi:= MontProd(montbmezi,montbmezi,pinvold,testedold);
            if testedold(3*delay-1) = '1' then                                  -- Musel být upraven způsob indexování v testedold, jelikož jsou procházeny 3 jeho bity za clk
                montbmezi:= MontProd(montbmezi,monta,pinvold,testedold);
                ble:='0';
            end if;
            if(montbmezi = montmin1old) then
                ble:='1';
            end if;
            montbmezi:= MontProd(montbmezi,montbmezi,pinvold,testedold);
            if testedold(3*delay-2) = '1' then
                montbmezi:= MontProd(montbmezi,monta,pinvold,testedold);
                ble:='0';
            end if;
            if(montbmezi = montmin1old) then
                ble:='1';
            end if;
            bless<=ble;
            if delay = 1 then
                montbmezi:= MontProd(montbmezi,montbmezi,pinvold,testedold);
                delay<=54;
                if (montbmezi = montbportold OR ble = '1') then
                    testresult <= '0';
                else
                    testresult <= '1';   
                end if;
            else
                delay<=delay-1;
            end if;
        end if;
        
    end if;
    end process;
end Behavioral;