#ifndef KHEAVYHASH_CORE_H
#define KHEAVYHASH_CORE_H 1

/**
 * Minimal C core for the kHeavyHash "heavy" step.
 *
 * This header exposes:
 * - xoshiro256++ PRNG state and generator
 * - 64x64 GF(16) matrix generation with full-rank requirement
 * - Core heavyhash transformation that takes a 32-byte seed and matrix
 *   and produces a 32-byte digest using Keccak SHAKE256 with the
 *   "HeavyHash" customization framing used in the original miner code.
 *
 * Higher-level functions (e.g., taking an 80-byte work order and
 * performing the full kHeavyHash PoW pipeline) are intentionally
 * left for a separate layer so this core stays small, testable,
 * and reusable from both Cython and other C callers.
 */

#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>

#define KHEAVYHASH_MATRIX_SIZE 64
#define KHEAVYHASH_GF16_MODULUS 16
#define KHEAVYHASH_MATRIX_VECTOR_SCALE_SHIFT 10

struct kheavyhash_xoshiro_state {
    uint64_t s[4];
};

/**
 * Generate the next 64-bit value from a xoshiro256++ PRNG state.
 *
 * This matches the algorithm in the original heavyhash_gate.c.
 */
uint64_t kheavyhash_xoshiro_next(struct kheavyhash_xoshiro_state *state);

/**
 * Compute the rank of a 64x64 matrix over GF(16) using Gaussian elimination.
 */
int kheavyhash_compute_rank(const uint16_t matrix[KHEAVYHASH_MATRIX_SIZE][KHEAVYHASH_MATRIX_SIZE]);

/**
 * Convenience predicate to check if a matrix has full rank (64).
 */
bool kheavyhash_is_full_rank(
    const uint16_t matrix[KHEAVYHASH_MATRIX_SIZE][KHEAVYHASH_MATRIX_SIZE]
);

/**
 * Generate a 64x64 GF(16) matrix using the xoshiro256++ PRNG.
 *
 * The function fills the matrix using 4-bit nibbles from successive
 * PRNG outputs and repeats until a full-rank matrix is obtained.
 * The PRNG state is advanced as in the original implementation.
 */
void kheavyhash_generate_matrix(
    uint16_t matrix[KHEAVYHASH_MATRIX_SIZE][KHEAVYHASH_MATRIX_SIZE],
    struct kheavyhash_xoshiro_state *state
);

/**
 * Core "heavy" transformation.
 *
 * Given:
 *  - a full-rank 64x64 GF(16) matrix
 *  - a 32-byte input (seed32)
 *
 * This function:
 *  - builds a 64-element GF(16) vector from the input nibbles
 *  - performs matrix-vector multiplication with right-shift scaling
 *  - recombines the result nibbles into 32 bytes
 *  - XORs with the original 32-byte input
 *
 * The 32-byte XOR result is written to output32.
 * Note: The final cSHAKE256("HeavyHash") hash is performed in kheavyhash_pow()
 * for clarity, so both cSHAKE hashes are visible in the main function.
 */
void kheavyhash_heavyhash(
    const uint16_t matrix[KHEAVYHASH_MATRIX_SIZE][KHEAVYHASH_MATRIX_SIZE],
    const uint8_t *seed32,
    uint8_t *output32
);

/**
 * Complete kHeavyHash Proof-of-Work hash function.
 *
 * This implements the full kHeavyHash algorithm:
 * 1. Hash the 80-byte work order with cSHAKE256("ProofOfWorkHash") → 32 bytes
 * 2. Seed xoshiro256++ PRNG from PrePowHash (first 32 bytes of work_order)
 * 3. Generate a full-rank 64x64 GF(16) matrix
 * 4. Apply the heavyhash transformation to the hash from step 1 (matrix-vector multiply + XOR)
 * 5. Hash the result with cSHAKE256("HeavyHash") → 32 bytes
 *
 * Work order format (80 bytes):
 *  - bytes 0-31:   PrePowHash (32 bytes, used as-is for PRNG seed)
 *  - bytes 32-39:  Timestamp (8 bytes, little-endian)
 *  - bytes 40-71:  Padding (32 bytes, all zeros)
 *  - bytes 72-79:  Nonce (8 bytes, little-endian)
 *
 * Returns:
 *  0 on success, -1 on error (e.g., invalid work_order length)
 *
 * The 32-byte output is written to output32.
 */
int kheavyhash_pow(
    const uint8_t *work_order80,
    uint8_t *output32
);

/**
 * Get the initial hash after ProofOfWorkHash step (for debugging/testing).
 *
 * This exposes the intermediate hash value that the C implementation produces
 * after the "ProofOfWorkHash" step, which uses keccak_nano cshake256.
 *
 * Returns:
 *  0 on success, -1 on error
 *
 * The 32-byte initial hash is written to initial_hash32.
 */
int kheavyhash_get_initial_hash(
    const uint8_t *work_order80,
    uint8_t *initial_hash32
);

/**
 * Get the XOR result after matrix operations (step 3 output).
 *
 * This exposes the intermediate value after:
 * - Matrix-vector multiplication
 * - XOR with original hash
 *
 * Returns:
 *  0 on success, -1 on error
 *
 * The 32-byte XOR result is written to xor_result32.
 */
int kheavyhash_get_xor_result(
    const uint8_t *work_order80,
    uint8_t *xor_result32
);

/**
 * Get the generated matrix (step 2 output) for debugging/testing.
 *
 * This exposes the 64x64 GF(16) matrix that was generated using xoshiro256++
 * seeded from PrePowHash.
 *
 * Returns:
 *  0 on success, -1 on error
 *
 * The matrix is written to matrix_out as a flat array of 64*64 uint16_t values
 * (row-major order: matrix[i][j] = matrix_out[i * 64 + j]).
 */
int kheavyhash_get_matrix(
    const uint8_t *work_order80,
    uint16_t *matrix_out
);

#endif /* KHEAVYHASH_CORE_H */

