#include <stdio.h>
#include <stdlib.h>
#include <math.h>

struct Element {
    int value;
    int flag;
};

void sum_if_flagged_v1(struct Element *elements, int size, int *sum) {
    asm volatile(
        "beqz %[size], end%=\n\t"
        "start%=:"

        "lw a1, 4(%[elements])\n\t"
        "beqz a1, loopend%=\n\t"
        "lw a0, 0(%[elements])\n\t"
        "add %[sum], %[sum], a0\n\t"

        "loopend%=:"
        "addi %[size], %[size], -1\n\t"
        "addi %[elements], %[elements], 8\n\t"
        "bnez %[size], start%=\n"
        "end%=:"
        : [sum] "+r" (*sum), [elements] "+r" (elements), [size] "+r" (size)
        : 
        : "a0", "a1"
    );
}
void sum_if_flagged_v2(struct Element *elements, int size, int *sum) {
    asm volatile(
        "beqz %[size], end%=\n\t"
        "start%=:"

        "lw a0, 0(%[elements])\n\t"
        "lw a1, 4(%[elements])\n\t"
        "czero.eqz a2, a0, a1\n\t"
        "add %[sum], %[sum], a2\n\t"

        "loopend%=:"
        "addi %[size], %[size], -1\n\t"
        "addi %[elements], %[elements], 8\n\t"
        "bnez %[size], start%=\n"
        "end%=:"
        : [sum] "+r" (*sum), [elements] "+r" (elements), [size] "+r" (size)
        : 
        : "a0", "a1", "a2"
    );
}

#define STR(x) #x
#define XSTR(s) STR(s)

#ifdef __clang__
#define asm __asm__
#endif

void sum_if_flagged_v3(struct Element *elements, int size, int *sum) {
    asm volatile(
        "beqz %[size], end%=\n\t"
        "start%=:"

        "lw a1, 4(%[elements])\n\t"
        "bms problematicbranch%=,branchfree%=," XSTR(BMS_MODIFIER) "\n"
        "problematicbranch%=:\n\t"
        "beqz a1, loopendtop%=\n\t"
        "lw a0, 0(%[elements])\n\t"
        "add %[sum], %[sum], a0\n\t"

        "loopendtop%=:"
        "addi %[size], %[size], -1\n\t"
        "addi %[elements], %[elements], 8\n\t"
        "bnez %[size], start%=\n"
        "j end%=\n"

        "branchfree%=:\n\t"
        "lw a0, 0(%[elements])\n\t"
        "czero.eqz a2, a0, a1\n\t"
        "add %[sum], %[sum], a2\n\t"
        "bjr a2, problematicbranch%=, loopendtop%=\n"
        
        "loopend%=:"
        "addi %[size], %[size], -1\n\t"
        "addi %[elements], %[elements], 8\n\t"
        "bnez %[size], start%=\n"
        "end%=:"
        : [sum] "+r" (*sum), [elements] "+r" (elements), [size] "+r" (size)
        : 
        : "a0", "a1", "a2"
    );
}


int main() {
    struct Element *elements = (struct Element *)malloc(ELEMENTS * sizeof(struct Element));

    printf("Elements,Random Seed,Bms Modifier,Variant,Random Variant,Percentage,Sum,Cycles\n");
    
    for (int percentage = 0; percentage <= 100; percentage += 1) {
        for (int variant = 0; variant <= 2; variant++) {
            srand(RANDOM_SEED);
            for (int i = 0; i < ELEMENTS; i++) {
                int pred = i % 2;
                if (RANDOM_VARIANT == 0) {
                    if (i < percentage * ELEMENTS / 100) {
                        pred = rand() % 2;
                    }
                } else {
                    if (rand() % 100 < percentage) {
                        pred = rand() % 2;
                    }
                }
                elements[i] = (Element) {i, pred};
            }

            void (*sum_if_flagged)(struct Element *, int, int *) = NULL;
            switch (variant) {
                case 0:
                    sum_if_flagged = sum_if_flagged_v1;
                    break;
                case 1:
                    sum_if_flagged = sum_if_flagged_v2;
                    break;
                case 2:
                    sum_if_flagged = sum_if_flagged_v3;
                    break;
            }

            #if defined(ONLYBMS) && ONLYBMS == 1
            if (variant != 2) {
                continue;
            }
            #endif

            printf("%d,%d,%d,%d,%d,%d%%", ELEMENTS, RANDOM_SEED, BMS_MODIFIER, variant, RANDOM_VARIANT, percentage);

            int sum = 0;

            long long cyclesStart;
            asm volatile("csrr %0, cycle" : "=r" (cyclesStart));

            sum_if_flagged(elements, ELEMENTS, &sum);

            long long cyclesEnd;
            asm volatile("csrr %0, cycle" : "=r" (cyclesEnd));

            printf(",%d,%lld\n", sum, cyclesEnd - cyclesStart);
        }
    }
    return 0;
}