`timescale 1ns/1ps

module tb_perceptron;

    // Inputs
    reg [7:0] x0;
    reg [7:0] x1;
    reg [7:0] x2;
    reg [7:0] x3;

    // Outputs
    wire y;
    wire [11:0] z;

    // Instantiate the Unit Under Test (UUT)
    perceptron uut (
        .x0(x0), 
        .x1(x1), 
        .x2(x2), 
        .x3(x3), 
        .y(y), 
        .z(z)
    );

    // Test vectors: 45 bits wide
    // {x0[7:0], x1[7:0], x2[7:0], x3[7:0], y, z[11:0]}
    localparam NUM_TESTS = 58;
    reg [44:0] test_vectors [0:NUM_TESTS-1];
    reg [44:0] vector;
    
    integer i;
    integer errors;

    initial begin
        // Initialize Inputs
        x0 = 0;
        x1 = 0;
        x2 = 0;
        x3 = 0;
        errors = 0;
        
        // Test Vectors
        test_vectors[0] = 45'h000000000ffb; // x0=0, x1=0, x2=0, x3=0, y=0, z=-5
        test_vectors[1] = 45'h1ffffffff3f7; // x0=255, x1=255, x2=255, x3=255, y=1, z=1015
        test_vectors[2] = 45'h1fe0000012f8; // x0=255, x1=0, x2=0, x3=0, y=1, z=760
        test_vectors[3] = 45'h001fe0000dfd; // x0=0, x1=255, x2=0, x3=0, y=0, z=-515
        test_vectors[4] = 45'h00001fe013f7; // x0=0, x1=0, x2=255, x3=0, y=1, z=1015
        test_vectors[5] = 45'h0000001feefc; // x0=0, x1=0, x2=0, x3=255, y=0, z=-260
        test_vectors[6] = 45'h004000003000; // x0=2, x1=0, x2=0, x3=1, y=1, z=0
        test_vectors[7] = 45'h004000004fff; // x0=2, x1=0, x2=0, x3=2, y=0, z=-1
        test_vectors[8] = 45'h1032675ff041; // x0=129, x1=147, x2=58, x3=255, y=1, z=65
        test_vectors[9] = 45'h16984802f17c; // x0=180, x1=194, x2=64, x3=23, y=1, z=380
        test_vectors[10] = 45'h0b28dfe3d458; // x0=89, x1=70, x2=255, x3=30, y=1, z=1112
        test_vectors[11] = 45'h005e7f601207; // x0=2, x1=243, x2=251, x3=0, y=1, z=519
        test_vectors[12] = 45'h1e93de187498; // x0=244, x1=158, x2=240, x3=195, y=1, z=1176
        test_vectors[13] = 45'h13df6f37110b; // x0=158, x1=251, x2=121, x3=184, y=1, z=267
        test_vectors[14] = 45'h0a2f68c910c8; // x0=81, x1=123, x2=70, x3=72, y=1, z=200
        test_vectors[15] = 45'h08793e3e5204; // x0=67, x1=201, x2=241, x3=242, y=1, z=516
        test_vectors[16] = 45'h05adbcacf2d5; // x0=45, x1=109, x2=229, x3=103, y=1, z=725
        test_vectors[17] = 45'h06fb7f925248; // x0=55, x1=219, x2=252, x3=146, y=1, z=584
        test_vectors[18] = 45'h0560c1c51080; // x0=43, x1=6, x2=14, x3=40, y=1, z=128
        test_vectors[19] = 45'h1f0184a3d341; // x0=248, x1=12, x2=37, x3=30, y=1, z=833
        test_vectors[20] = 45'h00d5352e30ee; // x0=6, x1=169, x2=169, x3=113, y=1, z=238
        test_vectors[21] = 45'h002a56ba915a; // x0=1, x1=82, x2=181, x3=212, y=1, z=346
        test_vectors[22] = 45'h014947ced007; // x0=10, x1=74, x2=62, x3=118, y=1, z=7
        test_vectors[23] = 45'h0f884da1d291; // x0=124, x1=66, x2=109, x3=14, y=1, z=657
        test_vectors[24] = 45'h04a4c4f9afed; // x0=37, x1=38, x2=39, x3=205, y=0, z=-19
        test_vectors[25] = 45'h12a8f2d512dc; // x0=149, x1=71, x2=150, x3=168, y=1, z=732
        test_vectors[26] = 45'h0b75d9b19258; // x0=91, x1=174, x2=205, x3=140, y=1, z=600
        test_vectors[27] = 45'h0a3bb75d5132; // x0=81, x1=221, x2=186, x3=234, y=1, z=306
        test_vectors[28] = 45'h05503a109233; // x0=42, x1=129, x2=208, x3=132, y=1, z=563
        test_vectors[29] = 45'h15a684d57187; // x0=173, x1=52, x2=38, x3=171, y=1, z=391
        test_vectors[30] = 45'h0f487240332a; // x0=122, x1=67, x2=146, x3=1, y=1, z=810
        test_vectors[31] = 45'h1fd001ac91c5; // x0=254, x1=128, x2=13, x3=100, y=1, z=453
        test_vectors[32] = 45'h084792b971d2; // x0=66, x1=60, x2=149, x3=203, y=1, z=466
        test_vectors[33] = 45'h107f131c3113; // x0=131, x1=248, x2=152, x3=225, y=1, z=275
        test_vectors[34] = 45'h0b203ef31448; // x0=89, x1=1, x2=247, x3=152, y=1, z=1096
        test_vectors[35] = 45'h0eb912ebb1c9; // x0=117, x1=200, x2=151, x3=93, y=1, z=457
        test_vectors[36] = 45'h07ebe8371046; // x0=63, x1=95, x2=65, x3=184, y=1, z=70
        test_vectors[37] = 45'h07fa5c90d21e; // x0=63, x1=210, x2=228, x3=134, y=1, z=542
        test_vectors[38] = 45'h0f6cdf5c53a6; // x0=123, x1=102, x2=250, x3=226, y=1, z=934
        test_vectors[39] = 45'h18e6b0b69346; // x0=199, x1=53, x2=133, x3=180, y=1, z=838
        test_vectors[40] = 45'h1a154426f164; // x0=208, x1=170, x2=33, x3=55, y=1, z=356
        test_vectors[41] = 45'h1d4c55ad543f; // x0=234, x1=98, x2=173, x3=106, y=1, z=1087
        test_vectors[42] = 45'h02adb580f209; // x0=21, x1=109, x2=172, x3=7, y=1, z=521
        test_vectors[43] = 45'h10a0dcbb143a; // x0=133, x1=6, x2=229, x3=216, y=1, z=1082
        test_vectors[44] = 45'h00f88bfdef15; // x0=7, x1=196, x2=95, x3=239, y=0, z=-235
        test_vectors[45] = 45'h149ebc233368; // x0=164, x1=245, x2=225, x3=25, y=1, z=872
        test_vectors[46] = 45'h13b72f8ff1d1; // x0=157, x1=185, x2=124, x3=127, y=1, z=465
        test_vectors[47] = 45'h05dad53790c1; // x0=46, x1=214, x2=169, x3=188, y=1, z=193
        test_vectors[48] = 45'h159ff63f31cc; // x0=172, x1=255, x2=177, x3=249, y=1, z=460
        test_vectors[49] = 45'h0137bb78b143; // x0=9, x1=189, x2=219, x3=197, y=1, z=323
        test_vectors[50] = 45'h185aa4e0912f; // x0=194, x1=213, x2=39, x3=4, y=1, z=303
        test_vectors[51] = 45'h0345661bafd6; // x0=26, x1=43, x2=48, x3=221, y=0, z=-42
        test_vectors[52] = 45'h0435bb29d21a; // x0=33, x1=173, x2=217, x3=78, y=1, z=538
        test_vectors[53] = 45'h0233f73c70f1; // x0=17, x1=159, x2=185, x3=227, y=1, z=241
        test_vectors[54] = 45'h120b53f4d2cd; // x0=144, x1=90, x2=159, x3=166, y=1, z=717
        test_vectors[55] = 45'h00ea3a5db1c9; // x0=7, x1=81, x2=210, x3=237, y=1, z=457
        test_vectors[56] = 45'h09c52a157128; // x0=78, x1=41, x2=80, x3=171, y=1, z=296
        test_vectors[57] = 45'h125572bd71c4; // x0=146, x1=171, x2=149, x3=235, y=1, z=452

        // Wait for global reset to finish
        #100;
        
        $display("Starting Perceptron Testbench...");

        for (i = 0; i < NUM_TESTS; i = i + 1) begin
            vector = test_vectors[i];
            x0 = vector[44:37];
            x1 = vector[36:29];
            x2 = vector[28:21];
            x3 = vector[20:13];
            
            #10; // Wait for combinational output
            
            if (y !== vector[12] || z !== vector[11:0]) begin
                $display("FAIL: Test %0d | Inputs: x0=%d x1=%d x2=%d x3=%d | Expected: y=%d z=%d | Got: y=%d z=%1d", 
                         i, x0, x1, x2, x3, vector[12], $signed(vector[11:0]), y, $signed(z));
                errors = errors + 1;
            end
        end

        // Final Results
        $display("");
        $display("===========================================");
        $display("  Tests Run: %0d", NUM_TESTS);
        $display("===========================================");

        if (errors == 0) begin
            $display("TEST_RESULT: PASS");
        end else begin
            $display("TEST_RESULT: FAIL (%0d errors)", errors);
        end

        $finish;
    end
      
endmodule
