#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <time.h>
#include <intrin.h>   // rdtsc

#pragma comment(lib, "threefish512.lib")

// ---- D exports ----
extern void threefish512_block_encrypt_C(
    uint64_t* key, uint64_t* tweak, uint64_t* pt, uint64_t* ct);

extern void encryptCTR_C(uint64_t* pt, uint64_t* ct, size_t len, uint64_t* key, uint64_t nonce);

// ===================
// UTIL
// ===================
static inline uint64_t rdtsc() {
    return __rdtsc();
}

void print_hex(uint8_t* x, int len) {
    for(int i=0;i<len;i++) printf("%02x", x[i]);
    printf("\n");
}

// ===================
// 1) TEST VECTORS
// ===================
void test_threefish512_vectors() {
    uint64_t key[8]   = {0};
    uint64_t tweak[2] = {0,0};
    uint64_t pt[8]    = {0};
    uint64_t ct[8];

    threefish512_block_encrypt_C(key, tweak, pt, ct);

    uint8_t* out = (uint8_t*)ct;

    uint8_t expected[64] = {
        0xb1,0xa2,0xbb,0xc6,0xef,0x60,0x25,0xbc,
        0x40,0xeb,0x38,0x22,0x16,0x1f,0x36,0xe3,
        0x75,0xd1,0xbb,0x0a,0xee,0x31,0x86,0xfb,
        0xd1,0x9e,0x47,0xc5,0xd4,0x79,0x94,0x7b,
        0x7b,0xc2,0xf8,0x58,0x6e,0x35,0xf0,0xcf,
        0xf7,0xe7,0xf0,0x30,0x84,0xb0,0xb7,0xb1,
        0xf1,0xab,0x39,0x61,0xa5,0x80,0xa3,0xe9,
        0x7e,0xb4,0x1e,0xa1,0x4a,0x6d,0x7b,0xbe
    };

    if(memcmp(out, expected, 64) == 0) {
        printf("[OK] Threefish‑512 test vector matches\n");
    } else {
        printf("[FAIL] Threefish‑512 mismatch!\n");
        printf("Got:      "); print_hex(out,64);
        printf("Expected: "); print_hex(expected,64);
    }
}

// ===================
// 2) BENCHMARK CTR
// ===================
void bench_ctr() {
    const size_t SZ = 64 * 1024 * 1024; // 64 MB
    uint8_t* buf = malloc(SZ);
    uint8_t* out = malloc(SZ);

    uint64_t key[8] = {1,2,3,4,5,6,7,8};
    uint64_t nonce = 1;

    memset(buf, 0x42, SZ);

    uint64_t t0 = rdtsc();
    encryptCTR_C((uint64_t*)buf, (uint64_t*)out, SZ, key, nonce);
    uint64_t t1 = rdtsc();

    double cycles = (double)(t1 - t0);
    double bytes  = (double)SZ;
    double cpb = cycles / bytes;

    printf("\nCTR benchmark\n");
    printf("Bytes: %.0f\n", bytes);
    printf("Cycles: %.0f\n", cycles);
    printf("cpb: %.3f\n", cpb);

    double ghz = 1.8; // i7‑8550U base clock
    double mbps = (bytes / 1e6) / (cycles / (ghz * 1e9));
    printf("Throughput: %.0f MB/s\n", mbps);
}

int main() {
    test_threefish512_vectors();
    bench_ctr();
    return 0;
}
