#define _POSIX_C_SOURCE 200809L

#include <arpa/inet.h>
#include <errno.h>
#include <fcntl.h>
#include <inttypes.h>
#include <limits.h>
#include <math.h>
#include <netinet/in.h>
#include <stdint.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <time.h>
#include <unistd.h>

typedef struct {
    int32_t len;
    int32_t *data;
} __glagol_vec_i32;

typedef struct {
    int32_t len;
    int64_t *data;
} __glagol_vec_i64;

typedef struct {
    int32_t len;
    double *data;
} __glagol_vec_f64;

typedef struct {
    int32_t len;
    bool *data;
} __glagol_vec_bool;

typedef struct {
    int32_t len;
    char **data;
} __glagol_vec_string;

static int32_t __glagol_process_argc_value = 0;
static char **__glagol_process_argv_value = NULL;
static bool __glagol_time_base_initialized = false;
static struct timespec __glagol_time_base;

#define __GLAGOL_FILE_HANDLE_LIMIT 1024
static FILE *__glagol_file_handles[__GLAGOL_FILE_HANDLE_LIMIT];

#define __GLAGOL_NET_HANDLE_LIMIT 1024
static int __glagol_net_handles[__GLAGOL_NET_HANDLE_LIMIT];

static int64_t __glagol_result_i32_encode(uint32_t status, int32_t payload) {
    uint64_t encoded = ((uint64_t)status << 32) | (uint32_t)payload;
    return (int64_t)encoded;
}

static int64_t __glagol_result_i32_err(void) {
    return __glagol_result_i32_encode(1, 1);
}

static int32_t __glagol_net_handle_insert(int fd) {
    if (fd < 0 || fd == INT_MAX) {
        return 0;
    }

    for (int32_t handle = 1; handle < __GLAGOL_NET_HANDLE_LIMIT; handle++) {
        if (__glagol_net_handles[handle] == 0) {
            __glagol_net_handles[handle] = fd + 1;
            return handle;
        }
    }
    return 0;
}

static int __glagol_net_handle_get(int32_t handle) {
    if (handle <= 0 || handle >= __GLAGOL_NET_HANDLE_LIMIT) {
        return -1;
    }

    int stored = __glagol_net_handles[handle];
    return stored == 0 ? -1 : stored - 1;
}

static int __glagol_net_handle_take(int32_t handle) {
    int fd = __glagol_net_handle_get(handle);
    if (fd >= 0) {
        __glagol_net_handles[handle] = 0;
    }
    return fd;
}

static bool __glagol_net_connect_port_is_valid(int32_t port) {
    return port > 0 && port <= 65535;
}

static bool __glagol_net_bind_port_is_valid(int32_t port) {
    return port >= 0 && port <= 65535;
}

static struct sockaddr_in __glagol_net_loopback_addr(int32_t port) {
    struct sockaddr_in addr;
    memset(&addr, 0, sizeof(addr));
    addr.sin_family = AF_INET;
    addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
    addr.sin_port = htons((uint16_t)port);
    return addr;
}

void print_i32(int32_t value) {
    printf("%d\n", value);
}

void print_i64(int64_t value) {
    printf("%" PRId64 "\n", value);
}

void print_u32(uint32_t value) {
    printf("%" PRIu32 "\n", value);
}

void print_u64(uint64_t value) {
    printf("%" PRIu64 "\n", value);
}

void print_f64(double value) {
    printf("%.17g\n", value);
}

void print_string(const char *value) {
    puts(value);
}

void print_bool(bool value) {
    puts(value ? "true" : "false");
}

int32_t string_len(const char *value) {
    return (int32_t)strlen(value);
}

void __glagol_io_eprint(const char *value) {
    fputs(value, stderr);
}

char *__glagol_io_read_stdin_result(void) {
    size_t capacity = 1024;
    size_t length = 0;
    char *value = malloc(capacity);
    if (value == NULL) {
        return NULL;
    }

    for (;;) {
        if (length == capacity) {
            if (capacity > SIZE_MAX / 2) {
                free(value);
                return NULL;
            }

            size_t next_capacity = capacity * 2;
            char *next = realloc(value, next_capacity);
            if (next == NULL) {
                free(value);
                return NULL;
            }

            value = next;
            capacity = next_capacity;
        }

        size_t available = capacity - length;
        size_t read_count = fread(value + length, 1, available, stdin);
        length += read_count;

        if (read_count < available) {
            if (ferror(stdin)) {
                free(value);
                return NULL;
            }
            break;
        }
    }

    if (length == capacity) {
        if (capacity == SIZE_MAX) {
            free(value);
            return NULL;
        }

        char *next = realloc(value, capacity + 1);
        if (next == NULL) {
            free(value);
            return NULL;
        }
        value = next;
    }

    value[length] = '\0';
    return value;
}

void __glagol_process_init(int32_t argc, char **argv) {
    __glagol_process_argc_value = argc;
    __glagol_process_argv_value = argv;
}

int32_t __glagol_process_argc(void) {
    return __glagol_process_argc_value;
}

static void __glagol_process_arg_trap(void) {
    fputs("slovo runtime error: process argument index out of bounds\n", stderr);
    exit(1);
}

char *__glagol_process_arg(int32_t index) {
    if (index < 0 || index >= __glagol_process_argc_value || __glagol_process_argv_value == NULL) {
        __glagol_process_arg_trap();
    }
    return __glagol_process_argv_value[index];
}

char *__glagol_process_arg_result(int32_t index) {
    if (index < 0 || index >= __glagol_process_argc_value || __glagol_process_argv_value == NULL) {
        return NULL;
    }
    return __glagol_process_argv_value[index];
}

char *__glagol_env_get(const char *name) {
    char *value = getenv(name);
    return value == NULL ? "" : value;
}

char *__glagol_env_get_result(const char *name) {
    return getenv(name);
}

static char *__glagol_read_remaining_text(FILE *file) {
    size_t capacity = 1024;
    size_t length = 0;
    char *value = malloc(capacity);
    if (value == NULL) {
        return NULL;
    }

    for (;;) {
        if (length == capacity) {
            if (capacity > SIZE_MAX / 2) {
                free(value);
                return NULL;
            }

            size_t next_capacity = capacity * 2;
            char *next = realloc(value, next_capacity);
            if (next == NULL) {
                free(value);
                return NULL;
            }

            value = next;
            capacity = next_capacity;
        }

        size_t available = capacity - length;
        size_t read_count = fread(value + length, 1, available, file);
        length += read_count;

        if (read_count < available) {
            if (ferror(file)) {
                free(value);
                return NULL;
            }
            break;
        }
    }

    if (length == capacity) {
        if (capacity == SIZE_MAX) {
            free(value);
            return NULL;
        }

        char *next = realloc(value, capacity + 1);
        if (next == NULL) {
            free(value);
            return NULL;
        }
        value = next;
    }

    value[length] = '\0';
    return value;
}

static FILE *__glagol_file_handle_get(int32_t handle) {
    if (handle <= 0 || handle >= __GLAGOL_FILE_HANDLE_LIMIT) {
        return NULL;
    }
    return __glagol_file_handles[handle];
}

static int32_t __glagol_file_handle_insert(FILE *file) {
    for (int32_t handle = 1; handle < __GLAGOL_FILE_HANDLE_LIMIT; handle++) {
        if (__glagol_file_handles[handle] == NULL) {
            __glagol_file_handles[handle] = file;
            return handle;
        }
    }
    return 0;
}

static void __glagol_fs_read_trap(void) {
    fputs("slovo runtime error: file read failed\n", stderr);
    exit(1);
}

char *__glagol_fs_read_text(const char *path) {
    FILE *file = fopen(path, "rb");
    if (file == NULL) {
        __glagol_fs_read_trap();
    }

    if (fseek(file, 0, SEEK_END) != 0) {
        fclose(file);
        __glagol_fs_read_trap();
    }

    long length = ftell(file);
    if (length < 0) {
        fclose(file);
        __glagol_fs_read_trap();
    }

    if (fseek(file, 0, SEEK_SET) != 0) {
        fclose(file);
        __glagol_fs_read_trap();
    }

    size_t size = (size_t)length;
    char *value = malloc(size + 1);
    if (value == NULL) {
        fclose(file);
        __glagol_fs_read_trap();
    }

    if (size > 0 && fread(value, 1, size, file) != size) {
        free(value);
        fclose(file);
        __glagol_fs_read_trap();
    }

    if (fclose(file) != 0) {
        free(value);
        __glagol_fs_read_trap();
    }

    value[size] = '\0';
    return value;
}

char *__glagol_fs_read_text_result(const char *path) {
    FILE *file = fopen(path, "rb");
    if (file == NULL) {
        return NULL;
    }

    if (fseek(file, 0, SEEK_END) != 0) {
        fclose(file);
        return NULL;
    }

    long length = ftell(file);
    if (length < 0) {
        fclose(file);
        return NULL;
    }

    if (fseek(file, 0, SEEK_SET) != 0) {
        fclose(file);
        return NULL;
    }

    size_t size = (size_t)length;
    char *value = malloc(size + 1);
    if (value == NULL) {
        fclose(file);
        return NULL;
    }

    if (size > 0 && fread(value, 1, size, file) != size) {
        free(value);
        fclose(file);
        return NULL;
    }

    if (fclose(file) != 0) {
        free(value);
        return NULL;
    }

    value[size] = '\0';
    return value;
}

int32_t __glagol_fs_write_text(const char *path, const char *text) {
    FILE *file = fopen(path, "wb");
    if (file == NULL) {
        return 1;
    }

    size_t len = strlen(text);
    if (len > 0 && fwrite(text, 1, len, file) != len) {
        fclose(file);
        return 1;
    }

    if (fclose(file) != 0) {
        return 1;
    }

    return 0;
}

int32_t __glagol_fs_write_text_result(const char *path, const char *text) {
    return __glagol_fs_write_text(path, text);
}

bool __glagol_fs_exists(const char *path) {
    struct stat info;
    return stat(path, &info) == 0;
}

bool __glagol_fs_is_file(const char *path) {
    struct stat info;
    return stat(path, &info) == 0 && S_ISREG(info.st_mode);
}

bool __glagol_fs_is_dir(const char *path) {
    struct stat info;
    return stat(path, &info) == 0 && S_ISDIR(info.st_mode);
}

int32_t __glagol_fs_remove_file_result(const char *path) {
    return unlink(path) == 0 ? 0 : 1;
}

int32_t __glagol_fs_create_dir_result(const char *path) {
    return mkdir(path, 0777) == 0 ? 0 : 1;
}

int64_t __glagol_fs_open_text_read_result(const char *path) {
    FILE *file = fopen(path, "rb");
    if (file == NULL) {
        return __glagol_result_i32_err();
    }

    int32_t handle = __glagol_file_handle_insert(file);
    if (handle == 0) {
        fclose(file);
        return __glagol_result_i32_err();
    }

    return __glagol_result_i32_encode(0, handle);
}

char *__glagol_fs_read_open_text_result(int32_t handle) {
    FILE *file = __glagol_file_handle_get(handle);
    if (file == NULL) {
        return NULL;
    }
    return __glagol_read_remaining_text(file);
}

int32_t __glagol_fs_close_result(int32_t handle) {
    FILE *file = __glagol_file_handle_get(handle);
    if (file == NULL) {
        return 1;
    }

    __glagol_file_handles[handle] = NULL;
    return fclose(file) == 0 ? 0 : 1;
}

int64_t __glagol_net_tcp_connect_loopback_result(int32_t port) {
    if (!__glagol_net_connect_port_is_valid(port)) {
        return __glagol_result_i32_err();
    }

    int fd = socket(AF_INET, SOCK_STREAM, 0);
    if (fd < 0) {
        return __glagol_result_i32_err();
    }

    struct sockaddr_in addr = __glagol_net_loopback_addr(port);
    if (connect(fd, (struct sockaddr *)&addr, sizeof(addr)) != 0) {
        close(fd);
        return __glagol_result_i32_err();
    }

    int32_t handle = __glagol_net_handle_insert(fd);
    if (handle == 0) {
        close(fd);
        return __glagol_result_i32_err();
    }

    return __glagol_result_i32_encode(0, handle);
}

int64_t __glagol_net_tcp_listen_loopback_result(int32_t port) {
    if (!__glagol_net_bind_port_is_valid(port)) {
        return __glagol_result_i32_err();
    }

    int fd = socket(AF_INET, SOCK_STREAM, 0);
    if (fd < 0) {
        return __glagol_result_i32_err();
    }

    int enabled = 1;
    if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &enabled, sizeof(enabled)) != 0) {
        close(fd);
        return __glagol_result_i32_err();
    }

    struct sockaddr_in addr = __glagol_net_loopback_addr(port);
    if (bind(fd, (struct sockaddr *)&addr, sizeof(addr)) != 0) {
        close(fd);
        return __glagol_result_i32_err();
    }

    if (listen(fd, 1) != 0) {
        close(fd);
        return __glagol_result_i32_err();
    }

    int32_t handle = __glagol_net_handle_insert(fd);
    if (handle == 0) {
        close(fd);
        return __glagol_result_i32_err();
    }

    return __glagol_result_i32_encode(0, handle);
}

int64_t __glagol_net_tcp_bound_port_result(int32_t handle) {
    int fd = __glagol_net_handle_get(handle);
    if (fd < 0) {
        return __glagol_result_i32_err();
    }

    struct sockaddr_in addr;
    socklen_t addr_len = sizeof(addr);
    if (getsockname(fd, (struct sockaddr *)&addr, &addr_len) != 0) {
        return __glagol_result_i32_err();
    }

    return __glagol_result_i32_encode(0, (int32_t)ntohs(addr.sin_port));
}

int64_t __glagol_net_tcp_accept_result(int32_t listener) {
    int fd = __glagol_net_handle_get(listener);
    if (fd < 0) {
        return __glagol_result_i32_err();
    }

    int accepted;
    do {
        accepted = accept(fd, NULL, NULL);
    } while (accepted < 0 && errno == EINTR);

    if (accepted < 0) {
        return __glagol_result_i32_err();
    }

    int32_t handle = __glagol_net_handle_insert(accepted);
    if (handle == 0) {
        close(accepted);
        return __glagol_result_i32_err();
    }

    return __glagol_result_i32_encode(0, handle);
}

char *__glagol_net_tcp_read_all_result(int32_t handle) {
    int fd = __glagol_net_handle_get(handle);
    if (fd < 0) {
        return NULL;
    }

    size_t capacity = 1024;
    size_t length = 0;
    char *value = malloc(capacity);
    if (value == NULL) {
        return NULL;
    }

    for (;;) {
        if (length == capacity) {
            if (capacity > SIZE_MAX / 2) {
                free(value);
                return NULL;
            }

            size_t next_capacity = capacity * 2;
            char *next = realloc(value, next_capacity);
            if (next == NULL) {
                free(value);
                return NULL;
            }

            value = next;
            capacity = next_capacity;
        }

        size_t available = capacity - length;
        if (available > (size_t)SSIZE_MAX) {
            available = (size_t)SSIZE_MAX;
        }

        ssize_t read_count = recv(fd, value + length, available, 0);
        if (read_count < 0) {
            if (errno == EINTR) {
                continue;
            }

            free(value);
            return NULL;
        }

        if (read_count == 0) {
            break;
        }

        length += (size_t)read_count;
    }

    if (length == capacity) {
        if (capacity == SIZE_MAX) {
            free(value);
            return NULL;
        }

        char *next = realloc(value, capacity + 1);
        if (next == NULL) {
            free(value);
            return NULL;
        }
        value = next;
    }

    value[length] = '\0';
    return value;
}

int32_t __glagol_net_tcp_write_text_result(int32_t handle, const char *text) {
    int fd = __glagol_net_handle_get(handle);
    if (fd < 0) {
        return 1;
    }

    size_t length = strlen(text);
    size_t written = 0;
    while (written < length) {
        size_t remaining = length - written;
        if (remaining > (size_t)SSIZE_MAX) {
            remaining = (size_t)SSIZE_MAX;
        }

#ifdef MSG_NOSIGNAL
        ssize_t sent = send(fd, text + written, remaining, MSG_NOSIGNAL);
#else
        ssize_t sent = send(fd, text + written, remaining, 0);
#endif
        if (sent < 0) {
            if (errno == EINTR) {
                continue;
            }
            return 1;
        }

        if (sent == 0) {
            return 1;
        }

        written += (size_t)sent;
    }

    return 0;
}

int32_t __glagol_net_tcp_close_result(int32_t handle) {
    int fd = __glagol_net_handle_take(handle);
    if (fd < 0) {
        return 1;
    }

    return close(fd) == 0 ? 0 : 1;
}

static void __glagol_allocation_trap(void) {
    fputs("slovo runtime error: string allocation failed\n", stderr);
    exit(1);
}

char *__glagol_string_concat(const char *left, const char *right) {
    size_t left_len = strlen(left);
    size_t right_len = strlen(right);

    if (left_len > SIZE_MAX - right_len - 1) {
        __glagol_allocation_trap();
    }

    char *value = malloc(left_len + right_len + 1);
    if (value == NULL) {
        __glagol_allocation_trap();
    }

    memcpy(value, left, left_len);
    memcpy(value + left_len, right, right_len + 1);
    return value;
}

int64_t __glagol_string_byte_at_result(const char *value, int32_t index) {
    if (value == NULL || index < 0) {
        return __glagol_result_i32_err();
    }

    size_t length = strlen(value);
    size_t offset = (size_t)index;
    if (offset >= length) {
        return __glagol_result_i32_err();
    }

    return __glagol_result_i32_encode(0, (int32_t)(unsigned char)value[offset]);
}

char *__glagol_string_slice_result(const char *value, int32_t start, int32_t count) {
    if (value == NULL || start < 0 || count < 0) {
        return NULL;
    }

    size_t length = strlen(value);
    size_t offset = (size_t)start;
    size_t slice_len = (size_t)count;
    if (offset > length || slice_len > length - offset) {
        return NULL;
    }

    char *slice = malloc(slice_len + 1u);
    if (slice == NULL) {
        __glagol_allocation_trap();
    }

    if (slice_len > 0) {
        memcpy(slice, value + offset, slice_len);
    }
    slice[slice_len] = '\0';
    return slice;
}

bool __glagol_string_starts_with(const char *value, const char *prefix) {
    if (value == NULL || prefix == NULL) {
        return false;
    }

    size_t value_len = strlen(value);
    size_t prefix_len = strlen(prefix);
    return prefix_len <= value_len && memcmp(value, prefix, prefix_len) == 0;
}

bool __glagol_string_ends_with(const char *value, const char *suffix) {
    if (value == NULL || suffix == NULL) {
        return false;
    }

    size_t value_len = strlen(value);
    size_t suffix_len = strlen(suffix);
    return suffix_len <= value_len
        && memcmp(value + value_len - suffix_len, suffix, suffix_len) == 0;
}

static char __glagol_json_hex_digit(unsigned char value) {
    return value < 10u ? (char)('0' + value) : (char)('A' + (value - 10u));
}

static void __glagol_json_add_size(size_t *total, size_t amount) {
    if (*total > SIZE_MAX - amount) {
        __glagol_allocation_trap();
    }
    *total += amount;
}

char *__glagol_json_quote_string(const char *text) {
    if (text == NULL) {
        text = "";
    }

    size_t escaped_len = 2u;
    const unsigned char *cursor = (const unsigned char *)text;
    while (*cursor != '\0') {
        switch (*cursor) {
            case '"':
            case '\\':
            case '\b':
            case '\f':
            case '\n':
            case '\r':
            case '\t':
                __glagol_json_add_size(&escaped_len, 2u);
                break;
            default:
                __glagol_json_add_size(&escaped_len, *cursor < 0x20u ? 6u : 1u);
                break;
        }
        cursor++;
    }

    if (escaped_len == SIZE_MAX) {
        __glagol_allocation_trap();
    }

    char *value = malloc(escaped_len + 1u);
    if (value == NULL) {
        __glagol_allocation_trap();
    }

    char *out = value;
    *out++ = '"';
    cursor = (const unsigned char *)text;
    while (*cursor != '\0') {
        unsigned char ch = *cursor;
        switch (ch) {
            case '"':
                *out++ = '\\';
                *out++ = '"';
                break;
            case '\\':
                *out++ = '\\';
                *out++ = '\\';
                break;
            case '\b':
                *out++ = '\\';
                *out++ = 'b';
                break;
            case '\f':
                *out++ = '\\';
                *out++ = 'f';
                break;
            case '\n':
                *out++ = '\\';
                *out++ = 'n';
                break;
            case '\r':
                *out++ = '\\';
                *out++ = 'r';
                break;
            case '\t':
                *out++ = '\\';
                *out++ = 't';
                break;
            default:
                if (ch < 0x20u) {
                    *out++ = '\\';
                    *out++ = 'u';
                    *out++ = '0';
                    *out++ = '0';
                    *out++ = __glagol_json_hex_digit((unsigned char)(ch >> 4u));
                    *out++ = __glagol_json_hex_digit((unsigned char)(ch & 0x0Fu));
                } else {
                    *out++ = (char)ch;
                }
                break;
        }
        cursor++;
    }
    *out++ = '"';
    *out = '\0';
    return value;
}

char *__glagol_json_parse_string_value_result(const char *text) {
    if (text == NULL) {
        return NULL;
    }

    size_t len = strlen(text);
    if (len < 2u || text[0] != '"' || text[len - 1u] != '"') {
        return NULL;
    }

    char *value = malloc(len - 1u);
    if (value == NULL) {
        __glagol_allocation_trap();
    }

    char *out = value;
    const unsigned char *cursor = (const unsigned char *)text + 1u;
    const unsigned char *end = (const unsigned char *)text + len - 1u;
    while (cursor < end) {
        unsigned char ch = *cursor++;
        if (ch < 0x20u || ch >= 0x80u || ch == '"') {
            free(value);
            return NULL;
        }

        if (ch != '\\') {
            *out++ = (char)ch;
            continue;
        }

        if (cursor >= end) {
            free(value);
            return NULL;
        }

        ch = *cursor++;
        switch (ch) {
            case '"':
                *out++ = '"';
                break;
            case '\\':
                *out++ = '\\';
                break;
            case '/':
                *out++ = '/';
                break;
            case 'b':
                *out++ = '\b';
                break;
            case 'f':
                *out++ = '\f';
                break;
            case 'n':
                *out++ = '\n';
                break;
            case 'r':
                *out++ = '\r';
                break;
            case 't':
                *out++ = '\t';
                break;
            default:
                free(value);
                return NULL;
        }
    }

    *out = '\0';
    return value;
}

static char *__glagol_num_u64_to_string_impl(uint64_t magnitude, bool negative) {
    char reversed[20];
    size_t digit_count = 0;

    do {
        reversed[digit_count] = (char)(0x30u + (magnitude % 10u));
        digit_count++;
        magnitude /= 10u;
    } while (magnitude != 0u);

    size_t sign_count = negative ? 1u : 0u;
    char *value = malloc(sign_count + digit_count + 1u);
    if (value == NULL) {
        __glagol_allocation_trap();
    }

    size_t cursor = 0;
    if (negative) {
        value[cursor] = (char)0x2du;
        cursor++;
    }

    for (size_t i = 0; i < digit_count; i++) {
        value[cursor + i] = reversed[digit_count - i - 1u];
    }
    value[cursor + digit_count] = '\0';
    return value;
}

static char *__glagol_num_i64_to_string_impl(int64_t value) {
    bool negative = value < 0;
    uint64_t magnitude = negative
        ? (uint64_t)(-(value + 1)) + 1u
        : (uint64_t)value;
    return __glagol_num_u64_to_string_impl(magnitude, negative);
}

char *__glagol_num_i32_to_string(int32_t value) {
    return __glagol_num_i64_to_string_impl((int64_t)value);
}

char *__glagol_num_u32_to_string(uint32_t value) {
    return __glagol_num_u64_to_string_impl((uint64_t)value, false);
}

char *__glagol_num_i64_to_string(int64_t value) {
    return __glagol_num_i64_to_string_impl(value);
}

char *__glagol_num_u64_to_string(uint64_t value) {
    return __glagol_num_u64_to_string_impl(value, false);
}

char *__glagol_num_f64_to_string(double value) {
    int length = snprintf(NULL, 0, "%.17f", value);
    if (length < 0) {
        __glagol_allocation_trap();
    }

    char *text = malloc((size_t)length + 1u);
    if (text == NULL) {
        __glagol_allocation_trap();
    }

    int written = snprintf(text, (size_t)length + 1u, "%.17f", value);
    if (written != length) {
        free(text);
        __glagol_allocation_trap();
    }

    char *dot = strchr(text, '.');
    if (dot != NULL) {
        char *last = text + strlen(text) - 1u;
        while (last > dot + 1 && *last == '0') {
            *last = '\0';
            last--;
        }
    }
    return text;
}

bool __glagol_string_eq(const char *left, const char *right) {
    return strcmp(left, right) == 0;
}

static int64_t __glagol_string_parse_i32_result_encode(uint32_t status, int32_t payload) {
    uint64_t encoded = ((uint64_t)status << 32) | (uint32_t)payload;
    return (int64_t)encoded;
}

static int64_t __glagol_string_parse_i32_result_err(void) {
    return __glagol_string_parse_i32_result_encode(1, 1);
}

int64_t __glagol_string_parse_i32_result(const char *text) {
    if (text == NULL || text[0] == '\0') {
        return __glagol_string_parse_i32_result_err();
    }

    const unsigned char *cursor = (const unsigned char *)text;
    bool negative = false;
    if (*cursor == '-') {
        negative = true;
        cursor++;
        if (*cursor == '\0') {
            return __glagol_string_parse_i32_result_err();
        }
    }

    int64_t limit = negative ? 2147483648LL : 2147483647LL;
    int64_t value = 0;
    while (*cursor != '\0') {
        unsigned char byte = *cursor;
        if (byte < '0' || byte > '9') {
            return __glagol_string_parse_i32_result_err();
        }

        int64_t digit = (int64_t)(byte - '0');
        if (value > (limit - digit) / 10) {
            return __glagol_string_parse_i32_result_err();
        }

        value = value * 10 + digit;
        cursor++;
    }

    int32_t parsed;
    if (negative) {
        parsed = value == 2147483648LL ? INT32_MIN : (int32_t)-value;
    } else {
        parsed = (int32_t)value;
    }
    return __glagol_string_parse_i32_result_encode(0, parsed);
}

static int64_t __glagol_string_parse_u32_result_err(void) {
    return __glagol_string_parse_i32_result_encode(1, 1);
}

int64_t __glagol_string_parse_u32_result(const char *text) {
    if (text == NULL || text[0] == '\0') {
        return __glagol_string_parse_u32_result_err();
    }

    const unsigned char *cursor = (const unsigned char *)text;
    uint64_t value = 0;
    while (*cursor != '\0') {
        unsigned char byte = *cursor;
        if (byte < '0' || byte > '9') {
            return __glagol_string_parse_u32_result_err();
        }

        uint64_t digit = (uint64_t)(byte - '0');
        if (value > (((uint64_t)UINT32_MAX) - digit) / 10u) {
            return __glagol_string_parse_u32_result_err();
        }

        value = value * 10u + digit;
        cursor++;
    }

    return __glagol_string_parse_i32_result_encode(0, (int32_t)(uint32_t)value);
}

int32_t __glagol_string_parse_i64_result(const char *text, int64_t *out) {
    if (out == NULL || text == NULL || text[0] == '\0') {
        return 1;
    }

    const unsigned char *cursor = (const unsigned char *)text;
    bool negative = false;
    if (*cursor == '-') {
        negative = true;
        cursor++;
        if (*cursor == '\0') {
            return 1;
        }
    }

    uint64_t limit = negative ? ((uint64_t)INT64_MAX + 1u) : (uint64_t)INT64_MAX;
    uint64_t value = 0;
    while (*cursor != '\0') {
        unsigned char byte = *cursor;
        if (byte < '0' || byte > '9') {
            return 1;
        }

        uint64_t digit = (uint64_t)(byte - '0');
        if (value > (limit - digit) / 10u) {
            return 1;
        }

        value = value * 10u + digit;
        cursor++;
    }

    if (negative) {
        *out = value == ((uint64_t)INT64_MAX + 1u) ? INT64_MIN : -(int64_t)value;
    } else {
        *out = (int64_t)value;
    }
    return 0;
}

int32_t __glagol_string_parse_u64_result(const char *text, uint64_t *out) {
    if (out == NULL || text == NULL || text[0] == '\0') {
        return 1;
    }

    const unsigned char *cursor = (const unsigned char *)text;
    uint64_t value = 0;
    while (*cursor != '\0') {
        unsigned char byte = *cursor;
        if (byte < '0' || byte > '9') {
            return 1;
        }

        uint64_t digit = (uint64_t)(byte - '0');
        if (value > (UINT64_MAX - digit) / 10u) {
            return 1;
        }

        value = value * 10u + digit;
        cursor++;
    }

    *out = value;
    return 0;
}

static size_t __glagol_consume_ascii_digits(const unsigned char *text, size_t index) {
    while (text[index] >= '0' && text[index] <= '9') {
        index++;
    }
    return index;
}

static bool __glagol_string_is_ascii_decimal_f64(const char *text) {
    if (text == NULL || text[0] == '\0') {
        return false;
    }

    const unsigned char *bytes = (const unsigned char *)text;
    size_t index = 0;
    if (bytes[index] == '-') {
        index++;
        if (bytes[index] == '\0') {
            return false;
        }
    }

    size_t whole_start = index;
    index = __glagol_consume_ascii_digits(bytes, index);
    size_t whole_digits = index - whole_start;

    if (whole_digits == 0 || bytes[index] != '.') {
        return false;
    }

    index++;
    size_t fractional_start = index;
    index = __glagol_consume_ascii_digits(bytes, index);
    size_t fractional_digits = index - fractional_start;
    if (fractional_digits == 0) {
        return false;
    }

    return bytes[index] == '\0';
}

int32_t __glagol_string_parse_f64_result(const char *text, double *out) {
    if (out == NULL || !__glagol_string_is_ascii_decimal_f64(text)) {
        return 1;
    }

    errno = 0;
    char *end = NULL;
    double value = strtod(text, &end);
    if (errno == ERANGE || end == text || end == NULL || *end != '\0' || !isfinite(value)) {
        return 1;
    }

    *out = value;
    return 0;
}

int32_t __glagol_string_parse_bool_result(const char *text, bool *out) {
    if (out == NULL || text == NULL) {
        return 1;
    }

    if (strcmp(text, "true") == 0) {
        *out = true;
        return 0;
    }

    if (strcmp(text, "false") == 0) {
        *out = false;
        return 0;
    }

    return 1;
}

static bool __glagol_json_is_integer_token(const char *text, bool allow_negative) {
    if (text == NULL || text[0] == '\0') {
        return false;
    }

    const unsigned char *cursor = (const unsigned char *)text;
    if (*cursor == '-') {
        if (!allow_negative) {
            return false;
        }
        cursor++;
        if (*cursor == '\0') {
            return false;
        }
    }

    if (*cursor < '0' || *cursor > '9') {
        return false;
    }

    if (*cursor == '0' && cursor[1] != '\0') {
        return false;
    }

    cursor++;
    while (*cursor != '\0') {
        if (*cursor < '0' || *cursor > '9') {
            return false;
        }
        cursor++;
    }

    return true;
}

static bool __glagol_json_is_number_token(const char *text) {
    if (text == NULL || text[0] == '\0') {
        return false;
    }

    const unsigned char *bytes = (const unsigned char *)text;
    size_t index = 0;
    if (bytes[index] == '-') {
        index++;
        if (bytes[index] == '\0') {
            return false;
        }
    }

    if (bytes[index] == '0') {
        index++;
    } else if (bytes[index] >= '1' && bytes[index] <= '9') {
        index = __glagol_consume_ascii_digits(bytes, index);
    } else {
        return false;
    }

    if (bytes[index] == '.') {
        index++;
        size_t fractional_start = index;
        index = __glagol_consume_ascii_digits(bytes, index);
        if (index == fractional_start) {
            return false;
        }
    }

    if (bytes[index] == 'e' || bytes[index] == 'E') {
        index++;
        if (bytes[index] == '+' || bytes[index] == '-') {
            index++;
        }
        size_t exponent_start = index;
        index = __glagol_consume_ascii_digits(bytes, index);
        if (index == exponent_start) {
            return false;
        }
    }

    return bytes[index] == '\0';
}

int32_t __glagol_json_parse_bool_value_result(const char *text, bool *out) {
    return __glagol_string_parse_bool_result(text, out);
}

int64_t __glagol_json_parse_i32_value_result(const char *text) {
    if (!__glagol_json_is_integer_token(text, true)) {
        return __glagol_string_parse_i32_result_err();
    }
    return __glagol_string_parse_i32_result(text);
}

int64_t __glagol_json_parse_u32_value_result(const char *text) {
    if (!__glagol_json_is_integer_token(text, false)) {
        return __glagol_string_parse_u32_result_err();
    }
    return __glagol_string_parse_u32_result(text);
}

int32_t __glagol_json_parse_i64_value_result(const char *text, int64_t *out) {
    if (!__glagol_json_is_integer_token(text, true)) {
        return 1;
    }
    return __glagol_string_parse_i64_result(text, out);
}

int32_t __glagol_json_parse_u64_value_result(const char *text, uint64_t *out) {
    if (!__glagol_json_is_integer_token(text, false)) {
        return 1;
    }
    return __glagol_string_parse_u64_result(text, out);
}

int32_t __glagol_json_parse_f64_value_result(const char *text, double *out) {
    if (out == NULL || !__glagol_json_is_number_token(text)) {
        return 1;
    }

    errno = 0;
    char *end = NULL;
    double value = strtod(text, &end);
    if (errno == ERANGE || end == text || end == NULL || *end != '\0' || !isfinite(value)) {
        return 1;
    }

    *out = value;
    return 0;
}

static void __glagol_vec_i32_allocation_trap(void) {
    fputs("slovo runtime error: vector allocation failed\n", stderr);
    exit(1);
}

static void __glagol_vec_i32_index_trap(void) {
    fputs("slovo runtime error: vector index out of bounds\n", stderr);
    exit(1);
}

__glagol_vec_i32 *__glagol_vec_i32_empty(void) {
    __glagol_vec_i32 *value = malloc(sizeof(__glagol_vec_i32));
    if (value == NULL) {
        __glagol_vec_i32_allocation_trap();
    }

    value->len = 0;
    value->data = NULL;
    return value;
}

__glagol_vec_i32 *__glagol_vec_i32_append(const __glagol_vec_i32 *source, int32_t element) {
    if (source->len < 0 || source->len == INT32_MAX) {
        __glagol_vec_i32_allocation_trap();
    }

    size_t old_len = (size_t)source->len;
    size_t new_len = old_len + 1;
    if (new_len > SIZE_MAX / sizeof(int32_t)) {
        __glagol_vec_i32_allocation_trap();
    }

    __glagol_vec_i32 *value = malloc(sizeof(__glagol_vec_i32));
    if (value == NULL) {
        __glagol_vec_i32_allocation_trap();
    }

    int32_t *data = malloc(new_len * sizeof(int32_t));
    if (data == NULL) {
        free(value);
        __glagol_vec_i32_allocation_trap();
    }

    if (old_len > 0) {
        memcpy(data, source->data, old_len * sizeof(int32_t));
    }
    data[old_len] = element;

    value->len = (int32_t)new_len;
    value->data = data;
    return value;
}

int32_t __glagol_vec_i32_len(const __glagol_vec_i32 *value) {
    return value->len;
}

int32_t __glagol_vec_i32_index(const __glagol_vec_i32 *value, int32_t index) {
    if (index < 0 || index >= value->len) {
        __glagol_vec_i32_index_trap();
    }
    return value->data[index];
}

bool __glagol_vec_i32_eq(const __glagol_vec_i32 *left, const __glagol_vec_i32 *right) {
    if (left->len != right->len) {
        return false;
    }
    if (left->len == 0) {
        return true;
    }
    return memcmp(left->data, right->data, (size_t)left->len * sizeof(int32_t)) == 0;
}

static void __glagol_vec_i64_allocation_trap(void) {
    fputs("slovo runtime error: vector allocation failed\n", stderr);
    exit(1);
}

static void __glagol_vec_i64_index_trap(void) {
    fputs("slovo runtime error: vector index out of bounds\n", stderr);
    exit(1);
}

__glagol_vec_i64 *__glagol_vec_i64_empty(void) {
    __glagol_vec_i64 *value = malloc(sizeof(__glagol_vec_i64));
    if (value == NULL) {
        __glagol_vec_i64_allocation_trap();
    }

    value->len = 0;
    value->data = NULL;
    return value;
}

__glagol_vec_i64 *__glagol_vec_i64_append(const __glagol_vec_i64 *source, int64_t element) {
    if (source->len < 0 || source->len == INT32_MAX) {
        __glagol_vec_i64_allocation_trap();
    }

    size_t old_len = (size_t)source->len;
    size_t new_len = old_len + 1;
    if (new_len > SIZE_MAX / sizeof(int64_t)) {
        __glagol_vec_i64_allocation_trap();
    }

    __glagol_vec_i64 *value = malloc(sizeof(__glagol_vec_i64));
    if (value == NULL) {
        __glagol_vec_i64_allocation_trap();
    }

    int64_t *data = malloc(new_len * sizeof(int64_t));
    if (data == NULL) {
        free(value);
        __glagol_vec_i64_allocation_trap();
    }

    if (old_len > 0) {
        memcpy(data, source->data, old_len * sizeof(int64_t));
    }
    data[old_len] = element;

    value->len = (int32_t)new_len;
    value->data = data;
    return value;
}

int32_t __glagol_vec_i64_len(const __glagol_vec_i64 *value) {
    return value->len;
}

int64_t __glagol_vec_i64_index(const __glagol_vec_i64 *value, int32_t index) {
    if (index < 0 || index >= value->len) {
        __glagol_vec_i64_index_trap();
    }
    return value->data[index];
}

bool __glagol_vec_i64_eq(const __glagol_vec_i64 *left, const __glagol_vec_i64 *right) {
    if (left->len != right->len) {
        return false;
    }
    if (left->len == 0) {
        return true;
    }
    return memcmp(left->data, right->data, (size_t)left->len * sizeof(int64_t)) == 0;
}

static void __glagol_vec_f64_allocation_trap(void) {
    fputs("slovo runtime error: vector allocation failed\n", stderr);
    exit(1);
}

static void __glagol_vec_f64_index_trap(void) {
    fputs("slovo runtime error: vector index out of bounds\n", stderr);
    exit(1);
}

__glagol_vec_f64 *__glagol_vec_f64_empty(void) {
    __glagol_vec_f64 *value = malloc(sizeof(__glagol_vec_f64));
    if (value == NULL) {
        __glagol_vec_f64_allocation_trap();
    }

    value->len = 0;
    value->data = NULL;
    return value;
}

__glagol_vec_f64 *__glagol_vec_f64_append(const __glagol_vec_f64 *source, double element) {
    if (source->len < 0 || source->len == INT32_MAX) {
        __glagol_vec_f64_allocation_trap();
    }

    size_t old_len = (size_t)source->len;
    size_t new_len = old_len + 1;
    if (new_len > SIZE_MAX / sizeof(double)) {
        __glagol_vec_f64_allocation_trap();
    }

    __glagol_vec_f64 *value = malloc(sizeof(__glagol_vec_f64));
    if (value == NULL) {
        __glagol_vec_f64_allocation_trap();
    }

    double *data = malloc(new_len * sizeof(double));
    if (data == NULL) {
        free(value);
        __glagol_vec_f64_allocation_trap();
    }

    if (old_len > 0) {
        memcpy(data, source->data, old_len * sizeof(double));
    }
    data[old_len] = element;

    value->len = (int32_t)new_len;
    value->data = data;
    return value;
}

int32_t __glagol_vec_f64_len(const __glagol_vec_f64 *value) {
    return value->len;
}

double __glagol_vec_f64_index(const __glagol_vec_f64 *value, int32_t index) {
    if (index < 0 || index >= value->len) {
        __glagol_vec_f64_index_trap();
    }
    return value->data[index];
}

bool __glagol_vec_f64_eq(const __glagol_vec_f64 *left, const __glagol_vec_f64 *right) {
    if (left->len != right->len) {
        return false;
    }
    for (int32_t index = 0; index < left->len; index++) {
        if (left->data[index] != right->data[index]) {
            return false;
        }
    }
    return true;
}

static void __glagol_vec_bool_allocation_trap(void) {
    fputs("slovo runtime error: vector allocation failed\n", stderr);
    exit(1);
}

static void __glagol_vec_bool_index_trap(void) {
    fputs("slovo runtime error: vector index out of bounds\n", stderr);
    exit(1);
}

__glagol_vec_bool *__glagol_vec_bool_empty(void) {
    __glagol_vec_bool *value = malloc(sizeof(__glagol_vec_bool));
    if (value == NULL) {
        __glagol_vec_bool_allocation_trap();
    }

    value->len = 0;
    value->data = NULL;
    return value;
}

__glagol_vec_bool *__glagol_vec_bool_append(const __glagol_vec_bool *source, bool element) {
    if (source->len < 0 || source->len == INT32_MAX) {
        __glagol_vec_bool_allocation_trap();
    }

    size_t old_len = (size_t)source->len;
    size_t new_len = old_len + 1;
    if (new_len > SIZE_MAX / sizeof(bool)) {
        __glagol_vec_bool_allocation_trap();
    }

    __glagol_vec_bool *value = malloc(sizeof(__glagol_vec_bool));
    if (value == NULL) {
        __glagol_vec_bool_allocation_trap();
    }

    bool *data = malloc(new_len * sizeof(bool));
    if (data == NULL) {
        free(value);
        __glagol_vec_bool_allocation_trap();
    }

    if (old_len > 0) {
        memcpy(data, source->data, old_len * sizeof(bool));
    }
    data[old_len] = element;

    value->len = (int32_t)new_len;
    value->data = data;
    return value;
}

int32_t __glagol_vec_bool_len(const __glagol_vec_bool *value) {
    return value->len;
}

bool __glagol_vec_bool_index(const __glagol_vec_bool *value, int32_t index) {
    if (index < 0 || index >= value->len) {
        __glagol_vec_bool_index_trap();
    }
    return value->data[index];
}

bool __glagol_vec_bool_eq(const __glagol_vec_bool *left, const __glagol_vec_bool *right) {
    if (left->len != right->len) {
        return false;
    }
    if (left->len == 0) {
        return true;
    }
    return memcmp(left->data, right->data, (size_t)left->len * sizeof(bool)) == 0;
}

static void __glagol_vec_string_allocation_trap(void) {
    fputs("slovo runtime error: vector allocation failed\n", stderr);
    exit(1);
}

static void __glagol_vec_string_index_trap(void) {
    fputs("slovo runtime error: vector index out of bounds\n", stderr);
    exit(1);
}

__glagol_vec_string *__glagol_vec_string_empty(void) {
    __glagol_vec_string *value = malloc(sizeof(__glagol_vec_string));
    if (value == NULL) {
        __glagol_vec_string_allocation_trap();
    }

    value->len = 0;
    value->data = NULL;
    return value;
}

__glagol_vec_string *__glagol_vec_string_append(
    const __glagol_vec_string *source,
    const char *element
) {
    if (source->len < 0 || source->len == INT32_MAX) {
        __glagol_vec_string_allocation_trap();
    }

    size_t old_len = (size_t)source->len;
    size_t new_len = old_len + 1;
    if (new_len > SIZE_MAX / sizeof(char *)) {
        __glagol_vec_string_allocation_trap();
    }

    __glagol_vec_string *value = malloc(sizeof(__glagol_vec_string));
    if (value == NULL) {
        __glagol_vec_string_allocation_trap();
    }

    char **data = malloc(new_len * sizeof(char *));
    if (data == NULL) {
        free(value);
        __glagol_vec_string_allocation_trap();
    }

    if (old_len > 0) {
        memcpy(data, source->data, old_len * sizeof(char *));
    }
    data[old_len] = (char *)element;

    value->len = (int32_t)new_len;
    value->data = data;
    return value;
}

int32_t __glagol_vec_string_len(const __glagol_vec_string *value) {
    return value->len;
}

char *__glagol_vec_string_index(const __glagol_vec_string *value, int32_t index) {
    if (index < 0 || index >= value->len) {
        __glagol_vec_string_index_trap();
    }
    return value->data[index];
}

bool __glagol_vec_string_eq(const __glagol_vec_string *left, const __glagol_vec_string *right) {
    if (left->len != right->len) {
        return false;
    }
    for (int32_t index = 0; index < left->len; index++) {
        if (strcmp(left->data[index], right->data[index]) != 0) {
            return false;
        }
    }
    return true;
}

static void __glagol_time_monotonic_unavailable_trap(void) {
    fputs("slovo runtime error: monotonic time unavailable\n", stderr);
    exit(1);
}

static void __glagol_time_sleep_negative_trap(void) {
    fputs("slovo runtime error: sleep_ms negative duration\n", stderr);
    exit(1);
}

static void __glagol_time_sleep_failed_trap(void) {
    fputs("slovo runtime error: sleep failed\n", stderr);
    exit(1);
}

int32_t __glagol_time_monotonic_ms(void) {
    struct timespec now;
    if (clock_gettime(CLOCK_MONOTONIC, &now) != 0) {
        __glagol_time_monotonic_unavailable_trap();
    }

    if (!__glagol_time_base_initialized) {
        __glagol_time_base = now;
        __glagol_time_base_initialized = true;
        return 0;
    }

    time_t seconds = now.tv_sec - __glagol_time_base.tv_sec;
    long nanoseconds = now.tv_nsec - __glagol_time_base.tv_nsec;
    if (nanoseconds < 0) {
        seconds -= 1;
        nanoseconds += 1000000000L;
    }
    if (seconds < 0) {
        return 0;
    }
    if (seconds > INT32_MAX / 1000) {
        return INT32_MAX;
    }

    int64_t milliseconds = (int64_t)seconds * 1000 + nanoseconds / 1000000L;
    if (milliseconds > INT32_MAX) {
        return INT32_MAX;
    }
    return (int32_t)milliseconds;
}

void __glagol_time_sleep_ms(int32_t ms) {
    if (ms < 0) {
        __glagol_time_sleep_negative_trap();
    }

    struct timespec remaining;
    remaining.tv_sec = ms / 1000;
    remaining.tv_nsec = (long)(ms % 1000) * 1000000L;

    while (nanosleep(&remaining, &remaining) != 0) {
        if (errno != EINTR) {
            __glagol_time_sleep_failed_trap();
        }
    }
}

static void __glagol_random_i32_unavailable_trap(void) {
    fputs("slovo runtime error: random i32 unavailable\n", stderr);
    exit(1);
}

int32_t __glagol_random_i32(void) {
    int fd = open("/dev/urandom", O_RDONLY);
    if (fd < 0) {
        __glagol_random_i32_unavailable_trap();
    }

    uint32_t value = 0;
    unsigned char *cursor = (unsigned char *)&value;
    size_t remaining = sizeof(value);
    while (remaining > 0) {
        ssize_t n = read(fd, cursor, remaining);
        if (n < 0) {
            if (errno == EINTR) {
                continue;
            }
            close(fd);
            __glagol_random_i32_unavailable_trap();
        }
        if (n == 0) {
            close(fd);
            __glagol_random_i32_unavailable_trap();
        }
        cursor += n;
        remaining -= (size_t)n;
    }

    if (close(fd) != 0) {
        __glagol_random_i32_unavailable_trap();
    }
    return (int32_t)(value & INT32_MAX);
}

void __glagol_array_bounds_trap(void) {
    fputs("slovo runtime error: array index out of bounds\n", stderr);
    exit(1);
}

void __glagol_unwrap_some_trap(void) {
    fputs("slovo runtime error: unwrap_some on none\n", stderr);
    exit(1);
}

void __glagol_unwrap_ok_trap(void) {
    fputs("slovo runtime error: unwrap_ok on err\n", stderr);
    exit(1);
}

void __glagol_unwrap_err_trap(void) {
    fputs("slovo runtime error: unwrap_err on ok\n", stderr);
    exit(1);
}
