#ifndef PROTOTYPE1_SRC_CORE_UTIL_H
#define PROTOTYPE1_SRC_CORE_UTIL_H

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#include <assert.h>
#include <limits.h>
#include <inttypes.h>
#include "int_primitives.h"

// Gonna change it
#define NORETURN __attribute((noreturn))
#define NODISCARD __attribute((warn_unused_result))

typedef const char* CSTR;

typedef enum {
    Option_Some, Option_None
} Option_variant;

typedef enum {
    Result_Ok, Result_Err
} Result_variant;

NORETURN
void abortf(const char* format, ...) {
    va_list args;
    va_start(args, format);
    vfprintf(stderr, format, args);
    va_end(args);
    abort();
}

void* safe_malloc(size_t n) {
    void* res = malloc(n);
    if (!res)
        abortf("allocation failure");
    return res;
}

void* safe_calloc(size_t nmemb, size_t size) {
    void* res = calloc(nmemb, size);
    if (!res)
        abortf("allocation failure");
    return res;
}

void* safe_realloc(void* ptr, size_t n) {
    void* res = realloc(ptr, n);
    if (!res && n)
        abortf("allocation failure");
    return res;
}

#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))

/* I assume that new_length > old_capacity */
size_t Vec_get_new_capacity(size_t old_capacity, size_t new_length) {
    if (!old_capacity)
        return new_length;
    while (old_capacity < new_length)
        old_capacity *= 2;
    return old_capacity;
}

float pow2f(float x) {
    return x * x;
}

bool U64_is_2pow(U64 n){
    return n > 0 && ((n & (n - 1)) == 0);
}

U8 U64_2pow_log(U64 n){
    U8 log = 0;
    while (n > 1){
        n >>= 1;
        log++;
    }
    return log;
}

typedef struct {
    U32 width;
    U32 height;
} SizeOfRectangleU32;

typedef struct{
    U64 key;
    U64 value;
} KVPU64ToU64;

#define check(expr) if (!(expr)) { abortf("Assertion failed at %s : %d : " #expr "\n", __FILE__, __LINE__); }

#endif
