/* XDC Module Headers */
extern "C" {
#include <xdc/std.h>
#include <xdc/runtime/System.h>

/* BIOS Module Headers */
#include <ti/sysbios/BIOS.h>

#include <ti/drivers/Board.h>
}

/**
 * SWITCH       = 0
 * IF           = 1
 */
#define COMPARISON_MODE         0
/**
 * Number of elements to query for
 */
#define NO_OF_ELEMENTS          2

using namespace std;

/**
 * comparison - 10000 times invokes:
 *
 * No of cycles (size) - M4F (TI CC1312):
 *             -------------------------------------
 *             |     SWITCH      |       IF        |
 *             -------------------------------------
 *             |       GNU Linaro 9.2.1 - -O3      |
 * -------------------------------------------------
 * 2 elements  |    280,008      |    280,008      |
 *             |    (27873)      |    (27873)      |
 * -------------------------------------------------
 * 20 elements |    660,117      |    973,134      |
 *             |    (28241)      |    (28273)      |
 * -------------------------------------------------
 *             |     TI v20.2.1.LTS - -O4 -mf4     |
 * -------------------------------------------------
 * 2 elements  |    525,020      |    360,008      |
 *             |    (21234)      |    (20898)      |
 * -------------------------------------------------
 * 20 elements |    683,818      |   1,097,848     |
 *             |    (21250)      |    (21358)      |
 * -------------------------------------------------
 */

class Neighbor {
public:
    uint16_t read_val1 = 1;

    uint32_t read1() const {
        return read_val1;
    }
    uint32_t write1(const uint32_t n) {
        read_val1 = n;
        return 0;
    }
};

class Meta {
public:
    uint16_t read_val1 = 2;

    uint32_t read1() const {
        return read_val1;
    }
    uint32_t write1(const uint32_t n) {
        read_val1 = n;
        return 0;
    }
};

struct Abc1 {
    Neighbor field1;
    Meta field2;
    Neighbor field3;
    Meta field4;
    Neighbor field5;
    Meta field6;
    Neighbor field7;
    Meta field8;
    Neighbor field9;
    Meta field10;
    Neighbor field11;
    Meta field12;
    Neighbor field13;
    Meta field14;
    Neighbor field15;
    Meta field16;
    Neighbor field17;
    Meta field18;
    Neighbor field19;
    Meta field20;

#if COMPARISON_MODE == 0
    uint32_t read1(const uint32_t index) const {
        switch (index) {
        case 0:
            return field1.read1();
        case 1:
            return field2.read1();
        case 2:
            return field3.read1();
        case 3:
            return field4.read1();
        case 4:
            return field5.read1();
        case 5:
            return field6.read1();
        case 6:
            return field7.read1();
        case 7:
            return field8.read1();
        case 8:
            return field9.read1();
        case 9:
            return field10.read1();
        case 10:
            return field11.read1();
        case 11:
            return field12.read1();
        case 12:
            return field13.read1();
        case 13:
            return field14.read1();
        case 14:
            return field15.read1();
        case 15:
            return field16.read1();
        case 16:
            return field17.read1();
        case 17:
            return field18.read1();
        case 18:
            return field19.read1();
        case 19:
            return field20.read1();
        }
        return 0;
    }
    uint32_t write1(const uint32_t index, const uint32_t n) {
        switch (index) {
        case 0:
            return field1.write1(n);
        case 1:
            return field2.write1(n);
        case 2:
            return field3.write1(n);
        case 3:
            return field4.write1(n);
        case 4:
            return field5.write1(n);
        case 5:
            return field6.write1(n);
        case 6:
            return field7.write1(n);
        case 7:
            return field8.write1(n);
        case 8:
            return field9.write1(n);
        case 9:
            return field10.write1(n);
        case 10:
            return field11.write1(n);
        case 11:
            return field12.write1(n);
        case 12:
            return field13.write1(n);
        case 13:
            return field14.write1(n);
        case 14:
            return field15.write1(n);
        case 15:
            return field16.write1(n);
        case 16:
            return field17.write1(n);
        case 17:
            return field18.write1(n);
        case 18:
            return field19.write1(n);
        case 19:
            return field20.write1(n);
        }
        return 0;
    }
#elif COMPARISON_MODE == 1
    uint32_t read1(const uint32_t index) const {
        if (index == 0) {
            return field1.read1();
        }
        else if (index == 1) {
            return field2.read1();
        }
        else if (index == 2) {
            return field3.read1();
        }
        else if (index == 3) {
            return field4.read1();
        }
        else if (index == 4) {
            return field5.read1();
        }
        else if (index == 5) {
            return field6.read1();
        }
        else if (index == 6) {
            return field7.read1();
        }
        else if (index == 7) {
            return field8.read1();
        }
        else if (index == 8) {
            return field9.read1();
        }
        else if (index == 9) {
            return field10.read1();
        }
        else if (index == 10) {
            return field11.read1();
        }
        else if (index == 11) {
            return field12.read1();
        }
        else if (index == 12) {
            return field13.read1();
        }
        else if (index == 13) {
            return field14.read1();
        }
        else if (index == 14) {
            return field15.read1();
        }
        else if (index == 15) {
            return field16.read1();
        }
        else if (index == 16) {
            return field17.read1();
        }
        else if (index == 17) {
            return field18.read1();
        }
        else if (index == 18) {
            return field19.read1();
        }
        else if (index == 19) {
            return field20.read1();
        }
        return 0;
    }
    uint32_t write1(const uint32_t index, const uint32_t n) {
        if (index == 0) {
            return field1.write1(n);
        }
        else if (index == 1) {
            return field2.write1(n);
        }
        else if (index == 2) {
            return field3.write1(n);
        }
        else if (index == 3) {
            return field4.write1(n);
        }
        else if (index == 4) {
            return field5.write1(n);
        }
        else if (index == 5) {
            return field6.write1(n);
        }
        else if (index == 6) {
            return field7.write1(n);
        }
        else if (index == 7) {
            return field8.write1(n);
        }
        else if (index == 8) {
            return field9.write1(n);
        }
        else if (index == 9) {
            return field10.write1(n);
        }
        else if (index == 10) {
            return field11.write1(n);
        }
        else if (index == 11) {
            return field12.write1(n);
        }
        else if (index == 12) {
            return field13.write1(n);
        }
        else if (index == 13) {
            return field14.write1(n);
        }
        else if (index == 14) {
            return field15.write1(n);
        }
        else if (index == 15) {
            return field16.write1(n);
        }
        else if (index == 16) {
            return field17.write1(n);
        }
        else if (index == 17) {
            return field18.write1(n);
        }
        else if (index == 18) {
            return field19.write1(n);
        }
        else if (index == 19) {
            return field20.write1(n);
        }
        return 0;
    }
#endif
};

/*
 *  ======== main ========
 */
int main() {
    /* Call driver init functions */
    Board_init();

    Abc1 abc1;

    System_printf("CPP: %d\n", __cplusplus);

    volatile uint32_t no = 0;
    for (volatile uint32_t j = 0; j < 10000; j++) {
        // j%10 - change to desire number of elements
        no += abc1.read1(j % NO_OF_ELEMENTS);
        abc1.write1(j % NO_OF_ELEMENTS, no);
    }
    System_printf("result: %d\n", no);

    /*
     *  normal BIOS programs, would call BIOS_start() to enable interrupts
     *  and start the scheduler and kick BIOS into gear.  But, this program
     *  is a simple sanity test and calls BIOS_exit() instead.
     */
    BIOS_exit(0); /* terminates program and dumps SysMin output */
    return (0);
}
