//  Unused primitive identifiers
//
//   02  07
//   16
//   22  25
//   34  37
//   44  48  49
//   51  57
//   64  65  69
//   70  73  77  79
//   81  83
//   90  94  96

#include <support.h>

static int command_argc;
static const char **command_argv;

#define static_assert(c) _Static_assert(c, #c)

typedef _Bool bool;

typedef int pid_t;
typedef unsigned long rlim_t;

struct rlimit {
    rlim_t rlim_cur;
    rlim_t rlim_max;
};

typedef unsigned int mode_t;

typedef long off_t;

typedef unsigned long dev_t;
typedef unsigned long ino_t;
typedef unsigned long nlink_t;
typedef unsigned uid_t;
typedef unsigned gid_t;
typedef long blksize_t;
typedef long blkcnt_t;

typedef long time_t;

struct timespec {
    time_t tv_sec;
    long tv_nsec;
};

struct stat {
    dev_t st_dev;
    ino_t st_ino;
    nlink_t st_nlink;

    mode_t st_mode;
    uid_t st_uid;
    gid_t st_gid;
    unsigned pad1;
    dev_t st_rdev;
    off_t st_size;
    blksize_t st_blksize;
    blkcnt_t st_blocks;

    struct timespec st_atim;
    struct timespec st_mtim;
    struct timespec st_ctim;
    long pad2[3];
};

#define false 0
#define true 1

#define RLIMIT_STACK 3
#define RLIM_INFINITY -1

#define O_RDONLY 00
#define O_WRONLY 01
#define O_RDWR 02
#define O_CREAT 0100
#define O_TRUNC 01000
#define O_CLOEXEC 02000000

#define PROT_READ 1
#define PROT_WRITE 2
#define PROT_EXEC 4

#define SYS_read 0
#define SYS_write 1
#define SYS_open 2
#define SYS_close 3
#define SYS_stat 4
#define SYS_fstat 5
#define SYS_mmap 9
#define SYS_munmap 11
#define SYS_getpid 39
#define SYS_execve 59
#define SYS_exit 60
#define SYS_getcwd 79
#define SYS_getrlimit 97
#define SYS_setrlimit 160
#define SYS_exit_group 231
#define SYS_epoll_wait 232
#define SYS_epoll_ctl 233
#define SYS_epoll_create1 291

long syscall0(long n);
long syscall1(long n, long a1);
long syscall2(long n, long a1, long a2);
long syscall3(long n, long a1, long a2, long a3);
long syscall4(long n, long a1, long a2, long a3, long a4);
long syscall5(long n, long a1, long a2, long a3, long a4, long a5);
long syscall6(long n, long a1, long a2, long a3, long a4, long a5, long a6);

__asm__(
    ".text\n"
    "syscall0:\n"
    "   mov %rdi, %rax\n"
    "   syscall\n"
    "   retq\n"
    "syscall1:\n"
    "   mov %rdi, %rax\n"
    "   mov %rsi, %rdi\n"
    "   syscall\n"
    "   retq\n"
    "syscall2:\n"
    "   mov %rdi, %rax\n"
    "   mov %rsi, %rdi\n"
    "   mov %rdx, %rsi\n"
    "   syscall\n"
    "   retq\n"
    "syscall3:\n"
    "   mov %rdi, %rax\n"
    "   mov %rsi, %rdi\n"
    "   mov %rdx, %rsi\n"
    "   mov %rcx, %rdx\n"
    "   syscall\n"
    "   retq\n"
    "syscall4:\n"
    "   mov %rdi, %rax\n"
    "   mov %rsi, %rdi\n"
    "   mov %rdx, %rsi\n"
    "   mov %rcx, %rdx\n"
    "   mov %r8, %r10\n"
    "   syscall\n"
    "   retq\n"
    "syscall5:\n"
    "   mov %rdi, %rax\n"
    "   mov %rsi, %rdi\n"
    "   mov %rdx, %rsi\n"
    "   mov %rcx, %rdx\n"
    "   mov %r8, %r10\n"
    "   mov %r9, %r8\n"
    "   syscall\n"
    "   retq\n"
    "syscall6:\n"
    "   mov %rdi, %rax\n"
    "   mov %rsi, %rdi\n"
    "   mov %rdx, %rsi\n"
    "   mov %rcx, %rdx\n"
    "   mov %r8, %r10\n"
    "   mov %r9, %r8\n"
    "   mov 8(%rsp), %r9\n"
    "   syscall\n"
    "   retq\n"
    ".global _start\n"
    "_start:\n"
    "   xor %rbp, %rbp\n"
    "   mov (%rsp), %rdi\n"
    "   lea 8(%rsp), %rsi\n"
    "   call main\n"
    "   mov %rax, %rdi\n"
    "   mov $60, %rax\n"
    "   syscall\n"
);

uint16_t htole16(uint16_t x) { return x; }
uint32_t htole32(uint32_t x) { return x; }
uint64_t htole64(uint64_t x) { return x; }

uint16_t le16toh(uint16_t x) { return x; }
uint32_t le32toh(uint32_t x) { return x; }
uint64_t le64toh(uint64_t x) { return x; }

int
execve(const char *path, const char **argv, const char **env)
{
    return syscall3(SYS_execve, (long)path, (long)argv, (long)env);
}

_Noreturn void
exit(int status)
{
    syscall1(SYS_exit_group, status);
    for (;;) syscall1(SYS_exit, status);
}

int
getcwd(char *buf, size_t size)
{
    return syscall2(SYS_getcwd, (long)buf, size);
}

pid_t
getpid(void)
{
    return syscall0(SYS_getpid);
}

int
getrlimit(int resource, struct rlimit *rlim)
{
    int r = syscall2(SYS_getrlimit, resource, (long)rlim);
    if (r < 0) return -1;
    return 0;
}

int
setrlimit(int resource, const struct rlimit *rlim)
{
    int r = syscall2(SYS_setrlimit, resource, (long)rlim);
    if (r < 0) return -1;
    return 0;
}

int
read(int fd, void *buf, size_t count)
{
    return syscall3(SYS_read, fd, (long)buf, (long)count);
}

int
write(int fd, const void *buf, size_t count)
{
    return syscall3(SYS_write, fd, (long)buf, (long)count);
}

int
close(int fd)
{
    return syscall1(SYS_close, fd);
}

int
open(const char *filename, int flags, mode_t mode)
{
    return syscall3(SYS_open, (long)filename, flags, mode);
}

int
fstat(int fd, struct stat *stat)
{
    return syscall2(SYS_fstat, fd, (long)stat);
}

long
mmap(void *pref, long size, long prot, long flags, long fd, long offset)
{
    return syscall6(SYS_mmap, (long)pref, size, prot, flags, fd, offset);
}

long
munmap(void *bytes, long size)
{
    return syscall2(SYS_munmap, (long)bytes, size);
}

int
epoll_create1(int flags)
{
    return syscall1(SYS_epoll_create1, flags);
}

int
epoll_ctl(int epoll_fd, int op, int fd, void *event)
{
    return syscall4(SYS_epoll_ctl, epoll_fd, op, fd, (long)event);
}

int
epoll_wait(int epoll_fd, void *events, int num_events, int timeout)
{
    return syscall4(SYS_epoll_wait, epoll_fd, (long)events, num_events, timeout);
}

size_t
strlen(const char *s)
{
    size_t size = 0;
    while (s[size] != '\0') ++size;
    return size;
}

void *
memcpy(void *dest, const void *src, size_t n)
{
    char *d = dest;
    const char *s = src;

    if (n > 0 && d != s) for (size_t i = 0; i < n; i++) d[i] = s[i];

    return dest;
}

void *
memmove(void *dest, const void *src, size_t n)
{
    char *d = dest;
    const char *s = src;

    if (n > 0 && d != s) {
        if (s < d && d < s + n)
            for (size_t i = 1; i <= n; i++) d[n - i] = s[n - i];
        else
            for (size_t i = 0; i < n; i++) d[i] = s[i];
    }

    return dest;
}

void *
memset(void *dest, int c, size_t n)
{
    char *d = dest;
    for (size_t i = 0; i < n; i++) d[i] = c;
    return dest;
}

typedef uint32_t value;

static_assert(_Alignof(value) <= 4);

struct closure {
    void *native_code;
    uint16_t num_params;
    uint16_t env_size;
    value env_items[];
};

struct tuple {
    uint16_t num_items;
    uint16_t layout;
    value items[];
};

struct variant {
    uint16_t label;
    value item;
};

#define CHUNK_NUM_BYTES_MAX 0x3fffffff

#define CHUNK_RO 1
#define CHUNK_RW 2

struct chunk {
    uint32_t header;
    char bytes[];
};

static_assert(_Alignof(struct closure) <= 8);
static_assert(_Alignof(struct tuple) <= 4);
static_assert(_Alignof(struct variant) <= 4);
static_assert(_Alignof(struct chunk) <= 4);

#define TAG_HEAP_CLOSURE 0x1
#define TAG_HEAP_TUPLE 0x3
#define TAG_HEAP_VARIANT 0x5
#define TAG_HEAP_CHUNK 0x7
#define TAG_STATIC_CHUNK_RO 0x9
#define TAG_IMMEDIATE_BOOLEAN 0x0f
#define TAG_IMMEDIATE_TUPLE 0x1f
#define TAG_IMMEDIATE_VARIANT 0x2f
#define TAG_IMMEDIATE_CHUNK 0x3f

#define empty_tuple TAG_IMMEDIATE_TUPLE

#define value_true TAG_IMMEDIATE_BOOLEAN
#define value_false (TAG_IMMEDIATE_BOOLEAN | 0x100)

#define INTEGER_MIN (INT32_MIN / 2)
#define INTEGER_MAX (INT32_MAX / 2)

static_assert(CHUNK_NUM_BYTES_MAX <= INTEGER_MAX);

#define NUM_MEMORY_MAPPINGS 16

static struct {
    void *bytes;
    size_t size;
    int prot;
} memory_mappings[NUM_MEMORY_MAPPINGS];

static void
err_print_line(const char *s)
{
    write(2, s, strlen(s));
    write(2, "\n", 1);
}

static _Noreturn void
halt(void)
{
    exit(1);
}

static _Noreturn void
die(const char *s)
{
    err_print_line(s);
    halt();
}

//  The Heap
//
//  The heap is a single contiguous memory region in which value
//  representation data is stored.
//
//  The size of the heap for a given program is fixed at compile time and
//  is at most pow(2, 30) bytes (1 GiB).
//
//  All objects allocated in the heap have 4-byte, 8-byte, or 16-byte
//  alignment. Each object allocated in the heap has an associated heap
//  identifier, established by heap_alloc. The heap_access function provides
//  the means for obtaining the native address of an object with a given heap
//  identifier.
//
//  A region is established by saving the value of heap_top into a local
//  variable. A region is freed by restoring heap_top to the saved value.

static _Alignas(16) char heap_bytes[256 * 1024 * 1024];
const uint32_t heap_limit = sizeof(heap_bytes) / 4;
uint32_t heap_top = 0;

//  heap_access
//
//  Parameters:
//      id - A heap object identifier produced by heap_alloc.

static void *
heap_access(uint32_t id)
{
    return &heap_bytes[4 * id];
}

//  heap_alloc
//
//  Parameters:
//      align - The address alignment constraint. It must be 1, 2, 4, 8, or 16.
//      size - The number of bytes required, which must be greater than zero.

static uint32_t
heap_alloc(int align, size_t size)
{
    uint32_t c = (align - 1) / 4;
    uint32_t id = (heap_top + c) & ~c;
    if (id > heap_limit || size > 4 * (heap_limit - id))
        die("Failed to allocate memory.");
    heap_top = id + (size + 3) / 4;
    return id;
}

static value
value_make_box(unsigned int tag, uint32_t id)
{
    return (id << 4) | tag;
}

static void *
value_unbox(value x)
{
    return heap_access(x >> 4);
}

static bool
value_has_tag(value x, unsigned int mask, unsigned int tag)
{
    return (x & mask) == tag;
}

static struct closure *
closure_unbox(value closure)
{
    if (!value_has_tag(closure, 0xf, TAG_HEAP_CLOSURE))
        die("Value is not a function.");
    return value_unbox(closure);
}

static struct tuple *
tuple_unbox(value tuple)
{
    if (!value_has_tag(tuple, 0xf, TAG_HEAP_TUPLE))
        die("Value is not a tuple.");
    return value_unbox(tuple);
}

static struct variant *
variant_unbox(value variant)
{
    if (!value_has_tag(variant, 0xf, TAG_HEAP_VARIANT))
        die("Value is not a variant.");
    return value_unbox(variant);
}

static bool
value_is_number(value v)
{
    return (v & 1) == 0;
}

static value
integer_encode(int32_t n)
{
    if (n < INTEGER_MIN || INTEGER_MAX < n)
        die("Number is out of range.");
    return n << 1;
}

static int32_t
integer_decode(value v)
{
    if (!value_is_number(v))
        die("Value is not a number.");
#ifdef __GNUC__
    int32_t n = v; // "implementation-defined" behaviour, according to C11.
    return n >> 1; // "implementation-defined" behaviour, according to C11.
#else
#error "Need validation of implementation-defined behaviour."
#endif
}

static value
boolean_encode(bool b)
{
    return b ? value_true : value_false;
}

static int
global_chunk_decode(value chunk)
{
    if (!value_has_tag(chunk, 0xff, TAG_IMMEDIATE_CHUNK))
        die("Value is not a global chunk.");
    return chunk >> 8;
}

static value
global_chunk_encode(int i)
{
    return (i << 8) | TAG_IMMEDIATE_CHUNK;
}

static uint32_t
chunk_header(int perm, uint32_t num_bytes)
{
    return ((perm == CHUNK_RW) ? 0x80000000 : 0) | num_bytes;
}

static int
chunk_perm(const struct chunk *chunk)
{
    return (chunk->header & 0x80000000) ? CHUNK_RW : CHUNK_RO;
}

static uint32_t
chunk_num_bytes(const struct chunk *chunk)
{
    return (chunk->header & 0x7fffffff);
}

static struct chunk *
chunk_unbox(value chunk)
{
    if (value_has_tag(chunk, 0xf, TAG_STATIC_CHUNK_RO)) {
        return (struct chunk *)&constant_bytes[4 * (chunk >> 4)];
    } else if (value_has_tag(chunk, 0xf, TAG_HEAP_CHUNK)) {
        return value_unbox(chunk);
    }
    die("Value is not a chunk.");
}

static void *
chunk_access(value chunk, int perm, value i_value, unsigned num_bytes)
{
    int64_t i = integer_decode(i_value);
    if (value_has_tag(chunk, 0xff, TAG_IMMEDIATE_CHUNK)) {
        int j = global_chunk_decode(chunk);
        if (memory_mappings[j].size == 0)
            die("Global chunk is not mapped.");
        if (i < 0 || memory_mappings[j].size < i + num_bytes)
            die("Chunk access is out of bounds.");
        if (perm == CHUNK_RW && !(memory_mappings[j].prot & PROT_WRITE))
            die("Chunk is read-only.");
        return memory_mappings[j].bytes + i;
    } else {
        struct chunk *chunk_rep = chunk_unbox(chunk);
        if (i < 0 || chunk_num_bytes(chunk_rep) < i + num_bytes)
            die("Chunk access is out of bounds.");
        if (perm == CHUNK_RW && chunk_perm(chunk_rep) == CHUNK_RO)
            die("Chunk is read-only.");
        return &chunk_rep->bytes[i];
    }
    die("Value is not a chunk.");
}

static value
string_make(uint32_t num_bytes, const char *bytes)
{
    if (CHUNK_NUM_BYTES_MAX < num_bytes)
        die("String is too big.");
    size_t align = _Alignof(struct chunk);
    size_t size = sizeof(struct chunk) + num_bytes;
    uint32_t id = heap_alloc(align, size);
    struct chunk *chunk_rep = heap_access(id);
    chunk_rep->header = chunk_header(CHUNK_RO, num_bytes);
    if (bytes != NULL)
        memmove(chunk_rep->bytes, bytes, num_bytes);
    return value_make_box(TAG_HEAP_CHUNK, id);
}

static struct chunk *
string_unbox(value string)
{
    struct chunk *chunk = chunk_unbox(string);
    if (chunk_perm(chunk) != CHUNK_RO)
        die("Value is not a string.");
    return chunk;
}

static char *
string_bytes(value string)
{
    return string_unbox(string)->bytes;
}

static uint32_t
string_length(value string)
{
    return chunk_num_bytes(string_unbox(string));
}

static const char *
zero_terminated(value s)
{
    uint32_t len = string_length(s);
    char *z = heap_access(heap_alloc(1, len + 1));
    memmove(z, string_bytes(s), len);
    z[len] = '\0';
    return z;
}

#ifdef __GNUC__

//  Implementation-defined behaviour, according to C11.

static int8_t
from_uint8(uint8_t u)
{
    return (int8_t)u;
}

static int16_t
from_uint16(uint16_t u)
{
    return (int16_t)u;
}

static int32_t
from_uint32(uint32_t u)
{
    return (int32_t)u;
}

static int64_t
from_uint64(uint64_t u)
{
    return (int64_t)u;
}

#else
#error "Need validation of implementation-defined behaviour."
#endif

static void
store_uint8(void *bytes, uint8_t u)
{
    memmove(bytes, &u, 1);
}

static void
store_uint16_le(void *bytes, uint16_t u)
{
    u = htole16(u);
    memmove(bytes, &u, 2);
}

static void
store_uint32_le(void *bytes, uint32_t u)
{
    u = htole32(u);
    memmove(bytes, &u, 4);
}

static void
store_uint64_le(void *bytes, uint64_t u)
{
    u = htole64(u);
    memmove(bytes, &u, 8);
}

static void
store_int8(void *bytes, int8_t s)
{
    uint8_t u = s;
    memmove(bytes, &u, 1);
}

static void
store_int16_le(void *bytes, int16_t s)
{
    uint16_t u = s;
    u = htole16(u);
    memmove(bytes, &u, 2);
}

static void
store_int32_le(void *bytes, int32_t s)
{
    uint32_t u = s;
    u = htole32(u);
    memmove(bytes, &u, 4);
}

static void
store_int64_le(void *bytes, int64_t s)
{
    uint64_t u = s;
    u = htole64(u);
    memmove(bytes, &u, 8);
}

static uint8_t
fetch_uint8(const void *bytes)
{
    uint8_t u;
    memmove(&u, bytes, 1);
    return u;
}

static uint16_t
fetch_uint16_le(const void *bytes)
{
    uint16_t u;
    memmove(&u, bytes, 2);
    return le16toh(u);
}

static uint32_t
fetch_uint32_le(const void *bytes)
{
    uint32_t u;
    memmove(&u, bytes, 4);
    return le32toh(u);
}

static uint64_t
fetch_uint64_le(const void *bytes)
{
    uint64_t u;
    memmove(&u, bytes, 8);
    return le64toh(u);
}

static int8_t
fetch_int8(const void *bytes)
{
    uint8_t u;
    memmove(&u, bytes, 1);
    return from_uint8(u);
}

static int16_t
fetch_int16_le(const void *bytes)
{
    uint16_t u;
    memmove(&u, bytes, 2);
    u = le16toh(u);
    return from_uint16(u);
}

static int32_t
fetch_int32_le(const void *bytes)
{
    uint32_t u;
    memmove(&u, bytes, 4);
    u = le32toh(u);
    return from_uint32(u);
}

static int64_t
fetch_int64_le(const void *bytes)
{
    uint64_t u;
    memmove(&u, bytes, 8);
    u = le64toh(u);
    return from_uint64(u);
}

//  s36: init
//
//  heap_num_bytes must be at most pow(2, 30).
//  The alignment of heap_bytes must be at least 16.

void
s36(uint32_t heap_num_bytes, char *heap_bytes, uint32_t stack_limit,
        int argc, const char **argv)
{
    command_argc = argc;
    command_argv = argv;
}

//  s40: prim_command_argc

value
s40(void)
{
    return integer_encode(command_argc);
}

//  s24: prim_command_argv

value
s24(value i_value)
{
    int32_t i = integer_decode(i_value);
    if (i < 0 || i >= command_argc)
        die("Command argument index is out of range.");
    return string_make(strlen(command_argv[i]), command_argv[i]);
}

//  s87: halt
//
//  Halt execution, returing 1 as the exit code of the process.

_Noreturn value
s87(void)
{
    exit(1);
}

//  s52: heap_get_top

uint32_t
s52(void)
{
    return heap_top;
}

//  s15: heap_set_top

void
s15(uint32_t top)
{
    heap_top = top;
}

//  s75: closure_make
//
//  Construct a fresh closure value.
//
//  Parameters:
//      native_code - A C function pointer to a function whose return type is
//          value and which takes one more than num_params arguments, all of
//          type value.
//      num_params - The number of parameters associated with the closure (not
//          the native function).
//      env_size - The number of values to be stored in the closure
//          environment.
//      env_items - If env_size is zero, then env_items may be NULL;
//          otherwise, env_items must be an array containing env_size values.
//          The values provided comprise the environment of the closure.

value
s75(void *native_code, uint16_t num_params, uint16_t env_size,
        const value *env_items)
{
    size_t align = _Alignof(struct closure);
    size_t size = sizeof(struct closure) + env_size * sizeof(value);
    uint32_t id = heap_alloc(align, size);
    struct closure *closure_rep = heap_access(id);
    closure_rep->native_code = native_code;
    closure_rep->num_params = num_params;
    closure_rep->env_size = env_size;
    if (env_size > 0)
        memmove(closure_rep->env_items, env_items, env_size * sizeof(value));
    return value_make_box(TAG_HEAP_CLOSURE, id);
}

//  s62: closure_env_items

const value *
s62(value closure)
{
    const struct closure *closure_rep = closure_unbox(closure);
    return closure_rep->env_items;
}

const void *s35(value closure, uint16_t num_args);
value s78(uint16_t num_items, const value *items);
const value * s33(value tuple, uint16_t num_items);

static value
adapt_closure_1_0(value closure)
{
    return ((value (*)(value, value))s35(closure, 1))(closure, empty_tuple);
}

static value
adapt_closure_1_2(value closure, value x0, value x1)
{
    value x = s78(2, (const value[]){x0, x1});
    return ((value (*)(value, value))s35(closure, 1))(closure, x);
}

static value
adapt_closure_1_3(value closure, value x0, value x1, value x2)
{
    value x = s78(3, (const value[]){x0, x1, x2});
    return ((value (*)(value, value))s35(closure, 1))(closure, x);
}

static value
adapt_closure_1_4(value closure, value x0, value x1, value x2, value x3)
{
    value x = s78(4, (const value[]){x0, x1, x2, x3});
    return ((value (*)(value, value))s35(closure, 1))(closure, x);
}

static value
adapt_closure_0_1(value closure, value x)
{
    if (x != empty_tuple)
        die("Ill-formed function application.");
    return ((value (*)(value))s35(closure, 0))(closure);
}

static value
adapt_closure_2_1(value closure, value x)
{
    const value *items = s33(x, 2);
    value (*f)(value, value, value) = s35(closure, 2);
    return f(closure, items[0], items[1]);
}

static value
adapt_closure_3_1(value closure, value x)
{
    const value *items = s33(x, 3);
    value (*f)(value, value, value, value) = s35(closure, 3);
    return f(closure, items[0], items[1], items[2]);
}

static value
adapt_closure_4_1(value closure, value x)
{
    const value *items = s33(x, 4);
    value (*f)(value, value, value, value, value) = s35(closure, 4);
    return f(closure, items[0], items[1], items[2], items[3]);
}

//  s35: closure_native_code

const void *
s35(value closure, uint16_t num_args)
{
    const struct closure *closure_rep = closure_unbox(closure);
    if (closure_rep->num_params == num_args)
        return closure_rep->native_code;
    if (closure_rep->num_params == 1) {
        switch (num_args) {
        case 0: return adapt_closure_1_0;
        case 2: return adapt_closure_1_2;
        case 3: return adapt_closure_1_3;
        case 4: return adapt_closure_1_4;
        default: die("Limitation: Missing adaptor for function application.");
        }
    }
    if (num_args == 1) {
        switch (closure_rep->num_params) {
        case 0: return adapt_closure_0_1;
        case 2: return adapt_closure_2_1;
        case 3: return adapt_closure_3_1;
        case 4: return adapt_closure_4_1;
        default: die("Limitation: Missing adaptor for function application.");
        }
    }
    die("Ill-formed function application.");
}

//  s27: variant_make_nonempty
//
//  Construct a fresh variant value where the enclosed value is not {}.
//
//  Parameters:
//      label - The label!
//      item - The enclosed value.

value
s27(uint16_t label, value item)
{
    size_t align = _Alignof(struct variant);
    size_t size = sizeof(struct variant);
    uint32_t id = heap_alloc(align, size);
    struct variant *variant_rep = heap_access(id);
    variant_rep->label = label;
    variant_rep->item = item;
    return value_make_box(TAG_HEAP_VARIANT, id);
}

//  s09: variant_label
//
//  The label of a variant value.
//
//  Parameters:
//      variant - The variant!

uint16_t
s09(value variant)
{
    if (value_has_tag(variant, 0xff, TAG_IMMEDIATE_VARIANT))
        return variant >> 8;
    struct variant *variant_rep = variant_unbox(variant);
    return variant_rep->label;
}

//  s06: variant_item
//
//  The value embedded within a variant.
//
//  Parameters:
//      variant - The variant!

value
s06(value variant)
{
    if (value_has_tag(variant, 0xff, TAG_IMMEDIATE_VARIANT))
        return empty_tuple;
    struct variant *variant_rep = variant_unbox(variant);
    return variant_rep->item;
}

//  s30: tuple_make_with_layout

value
s30(uint16_t num_items, const value *items, uint16_t layout)
{
    size_t align = _Alignof(struct tuple);
    size_t size = sizeof(struct tuple) + num_items * sizeof(value);
    uint32_t id = heap_alloc(align, size);
    struct tuple *tuple_rep = heap_access(id);
    tuple_rep->num_items = num_items;
    tuple_rep->layout = layout;
    if (num_items > 0)
        memmove(tuple_rep->items, items, num_items * sizeof(value));
    return value_make_box(TAG_HEAP_TUPLE, id);
}

//  s78: tuple_make_with_no_layout

value
s78(uint16_t num_items, const value *items)
{
    return s30(num_items, items, UINT16_MAX);
}

//  s68: tuple_fetch_at_offset

value
s68(value tuple, uint16_t offset)
{
    const char *error_message = "Ill-formed tuple access.";
    if (tuple == empty_tuple)
        die(error_message);
    struct tuple *tuple_rep = tuple_unbox(tuple);
    if (offset >= tuple_rep->num_items)
        die(error_message);
    return tuple_rep->items[offset];
}

//  s31: tuple_fetch_at_label

value
s31(value tuple, uint16_t label)
{
    const char *error_message = "Ill-formed record access.";
    if (tuple == empty_tuple)
        die(error_message);
    struct tuple *tuple_rep = tuple_unbox(tuple);
    unsigned int layout = tuple_rep->layout;
    if (layout == UINT16_MAX)
        die(error_message);
    for (unsigned int i = layout; record_layouts[i] != UINT16_MAX; i++) {
        if (record_layouts[i] == label)
            return tuple_rep->items[i - layout];
    }
    die(error_message);
}

//  s33: tuple_items

const value *
s33(value tuple, uint16_t num_items)
{
    if (tuple == empty_tuple)
        die("Invalid use of empty tuple.");
    struct tuple *tuple_rep = tuple_unbox(tuple);
    if (tuple_rep->num_items != num_items)
        die("Tuple mismatch.");
    return tuple_rep->items;
}

//  s86: string_make

value
s86(size_t num_bytes, const char *bytes)
{
    return string_make(num_bytes, bytes);
}

//  s89: stuck_cond

_Noreturn value
s89(void)
{
    die("Cond expression has no applicable clause.");
}

//  s88: stuck_switch

_Noreturn value
s88(void)
{
    die("Switch expression has no applicable clause.");
}

//  s53: stuck_match

_Noreturn value
s53(void)
{
    die("Match expression has no applicable clause.");
}

//  s45: prim_heap_top

value
s45(void)
{
    return integer_encode(heap_top);
}

//  s26: prim_die

value
s26(value string)
{
    die(zero_terminated(string));
}

//  s00: prim_exec

static const char *
verified_exec_string(const struct chunk *table, size_t offset)
{
    uint32_t table_size = chunk_num_bytes(table);
    bool found_zero = false;
    for (size_t i = offset; i < table_size; i++) {
        if (table->bytes[i] == 0) {
            found_zero = true;
            break;
        }
    }
    if (!found_zero)
        die("Invalid argument for exec.");
    return &table->bytes[offset];
}

value
s00(value path_offset_value, value arg_offsets_value, value var_offsets_value,
        value table_value)
{
    int32_t path_offset = integer_decode(path_offset_value);
    if (path_offset < 0)
        die("Invalid argument for exec.");
    const struct chunk *arg_offsets = chunk_unbox(arg_offsets_value);
    const struct chunk *var_offsets = chunk_unbox(var_offsets_value);
    const struct chunk *table = chunk_unbox(table_value);
    uint32_t num_args = chunk_num_bytes(arg_offsets) / 4;
    uint32_t num_vars = chunk_num_bytes(var_offsets) / 4;
    size_t args_align = _Alignof(const char *);
    size_t args_size = sizeof(const char *) * (num_args + 1);
    size_t vars_align = _Alignof(const char *);
    size_t vars_size = sizeof(const char *) * (num_vars + 1);
    int r;
    {
        uint32_t top = heap_top;
        const char *path = verified_exec_string(table, path_offset);
        const char **args = heap_access(heap_alloc(args_align, args_size));
        const char **vars = heap_access(heap_alloc(vars_align, vars_size));
        for (uint32_t i = 0; i < num_args; i++) {
            void *offset_bytes =
                chunk_access(arg_offsets_value, CHUNK_RO, integer_encode(4 * i), 4);
            uint32_t offset = fetch_uint32_le(offset_bytes);
            args[i] = verified_exec_string(table, offset);
        }
        args[num_args] = NULL;
        for (uint32_t i = 0; i < num_vars; i++) {
            void *offset_bytes =
                chunk_access(var_offsets_value, CHUNK_RO, integer_encode(4 * i), 4);
            uint32_t offset = fetch_uint32_le(offset_bytes);
            vars[i] = verified_exec_string(table, offset);
        }
        vars[num_vars] = NULL;
        r = execve(path, args, vars);
        heap_top = top;
    }
    return r;
}

//  s05: prim_exit

value
s05(value status_value)
{
    int32_t status = integer_decode(status_value);
    exit(status);
}

//  s85: prim_getcwd

value
s85(void)
{
    //  TODO How to better handle the heap allocation trick used here? (We are
    //  writing into the unallocated region of the heap and subsequently
    //  incorporating the written bytes into the most recently allocated
    //  chunk.)

    size_t align = _Alignof(struct chunk);
    size_t size = sizeof(struct chunk);
    uint32_t id = heap_alloc(align, size);
    struct chunk *chunk_rep = heap_access(id);
    int r = getcwd(chunk_rep->bytes, heap_limit - heap_top);
    if (r < 0)
        die("Failed to get current working directory.");
    size_t pathlen = strlen(chunk_rep->bytes);
    if (pathlen > CHUNK_NUM_BYTES_MAX)
        die("Current working directory path is too long.");
    heap_top += pathlen;
    chunk_rep->header = chunk_header(CHUNK_RO, pathlen);
    return value_make_box(TAG_HEAP_CHUNK, id);
}

//  s66: prim_getpid

value
s66(void)
{
    return integer_encode(getpid());
}

//  s03: prim_open

value
s03(value name, value flags_value, value mode_value)
{
    int32_t flags = integer_decode(flags_value);
    int32_t mode = integer_decode(mode_value);
    int r;
    {
        uint32_t top = heap_top;
        const char *zero_name = zero_terminated(name);
        r = open(zero_name, flags, mode);
        heap_top = top;
    }
    return integer_encode(r);
}

//  s72: prim_close

value
s72(value fd_value)
{
    int32_t fd = integer_decode(fd_value);
    int r = close(fd);
    return integer_encode(r);
}

//  s67: prim_read

value
s67(value fd_value, value chunk, value start_value, value count_value)
{
    int32_t fd = integer_decode(fd_value);
    int32_t count = integer_decode(count_value);
    void *buf = chunk_access(chunk, CHUNK_RW, start_value, count);
    long r = read(fd, buf, count);
    return integer_encode(r);
}

//  s41: prim_write

value
s41(value fd_value, value chunk, value start_value, value count_value)
{
    int32_t fd = integer_decode(fd_value);
    int32_t count = integer_decode(count_value);
    const void *buf = chunk_access(chunk, CHUNK_RO, start_value, count);
    long r = write(fd, buf, count);
    return integer_encode(r);
}

//  s18: prim_print

value
s18(value string)
{
    const char *s = string_bytes(string);
    int r = write(1, s, string_length(string));
    if (r < 0)
        die("Failed to print string.");
    return empty_tuple;
}

//  s20: prim_file_create

value
s20(value name)
{
    int fd;
    {
        uint32_t top = heap_top;
        const char *zero_name = zero_terminated(name);
        int r = open(zero_name, O_WRONLY|O_CREAT|O_TRUNC|O_CLOEXEC, 0644);
        if (r < 0)
            die("Failed to create file.");
        fd = r;
        heap_top = top;
    }
    return integer_encode(fd);
}

//  s23: prim_file_open

value
s23(value name)
{
    int fd;
    {
        uint32_t top = heap_top;
        const char *zero_name = zero_terminated(name);
        int r = open(zero_name, O_RDONLY|O_CLOEXEC, 0);
        if (r < 0)
            die("Failed to open file.");
        fd = r;
        heap_top = top;
    }
    return integer_encode(fd);
}

//  s92: prim_file_close

value
s92(value fd_value)
{
    int32_t fd = integer_decode(fd_value);
    if (fd <= 2)
        die("Attempted to close stdin, stdout, or stderr.");
    int r = close(fd);
    if (r < 0)
        die("Failed to close file.");
    return empty_tuple;
}

//  s28: prim_file_read_all

value
s28(value fd_value)
{
    int fd = integer_decode(fd_value);
    struct stat statbuf;
    int r = fstat(fd, &statbuf);
    if (r < 0)
        die("Failed to determine file size.");
    off_t file_size = statbuf.st_size;
    if (file_size < 0 || file_size > UINT32_MAX - 1)
        die("Failed to read file.");
    value string = string_make(file_size, NULL);
    char *bytes = string_bytes(string);
    r = read(fd, bytes, file_size);
    if (r != file_size)
        die("Failed to read file.");
    return string;
}

//  s12: prim_show_integer

value
s12(value integer)
{
    int32_t n = integer_decode(integer);
    bool is_negative = (n < 0);
    int32_t m = is_negative ? -n : n;
    char text[16];
    int i = 16;
    if (n == 0) {
        --i;
        text[i] = '0';
    } else {
        while (m > 0) {
            --i;
            text[i] = '0' + (m % 10);
            m /= 10;
        }
        if (is_negative) {
            --i;
            text[i] = '-';
        }
    }
    size_t len = 16 - i;
    return string_make(len, &text[i]);
}

//  s93: prim_multiply

value
s93(value a, value b)
{
    int32_t n;
    if (__builtin_mul_overflow(integer_decode(a), integer_decode(b), &n))
        die("Integer is out of range.");
    return integer_encode(n);
}

//  s19: prim_add

value
s19(value a, value b)
{
    int32_t n;
    if (__builtin_add_overflow(integer_decode(a), integer_decode(b), &n))
        die("Integer is out of range.");
    return integer_encode(n);
}

//  s47: prim_subtract

value
s47(value a, value b)
{
    int32_t n;
    if (__builtin_sub_overflow(integer_decode(a), integer_decode(b), &n))
        die("Integer is out of range.");
    return integer_encode(n);
}

//  s84: prim_negate

value
s84(value n)
{
    if (n == (1 << 31))
        die("Integer is out of range.");
    return integer_encode(-integer_decode(n));
}

struct division_result {
    int32_t q;
    int32_t r;
};

static struct division_result
divide(int32_t b, int32_t a)
{
    if (a == 0)
        die("Division by zero.");

    int32_t b_abs = (b < 0) ? -b : b;
    int32_t a_abs = (a < 0) ? -a : a;

    int32_t q = b_abs / a_abs;
    int32_t r = b_abs % a_abs;

    //  Invariant: b_abs = q * a_abs + r
    //  Invariant: 0 <= r < |a|

    if (b < 0) {
        if (r > 0) {
            q++;
            r -= a_abs;
        }
        q = -q;
        r = -r;
    }

    //  Invariant: b = q * a_abs + r
    //  Invariant: 0 <= r < |a|

    if (a < 0)
        q = -q;

    //  Invariant: b = q * a + r
    //  Invariant: 0 <= r < |a|

    return (struct division_result){.q = q, .r = r};
}

//  s91: prim_quotient

value
s91(value b_value, value a_value)
{
    int32_t b = integer_decode(b_value);
    int32_t a = integer_decode(a_value);
    struct division_result d = divide(b, a);
    return integer_encode(d.q);
}

//  s43: prim_remainder

value
s43(value b_value, value a_value)
{
    int32_t b = integer_decode(b_value);
    int32_t a = integer_decode(a_value);
    struct division_result d = divide(b, a);
    return integer_encode(d.r);
}

//  s50: prim_equal

value
s50(value a, value b)
{
    return boolean_encode(integer_decode(a) == integer_decode(b));
}

//  s10: prim_less

value
s10(value a, value b)
{
    return boolean_encode(integer_decode(a) < integer_decode(b));
}

//  s63: prim_less_or_equal

value
s63(value a, value b)
{
    return boolean_encode(integer_decode(a) <= integer_decode(b));
}

//  s61: prim_greater

value
s61(value a, value b)
{
    return boolean_encode(integer_decode(a) > integer_decode(b));
}

//  s55: prim_greater_or_equal

value
s55(value a, value b)
{
    return boolean_encode(integer_decode(a) >= integer_decode(b));
}

//  s95: prim_memory_map

value
s95(value i_value, value size_value, value prot_value, value flags_value,
        value fd_value, value offset_value)
{
    int32_t i = integer_decode(i_value);
    int32_t size = integer_decode(size_value);
    int32_t prot = integer_decode(prot_value);
    int32_t flags = integer_decode(flags_value);
    int32_t fd = integer_decode(fd_value);
    int32_t offset = integer_decode(offset_value);
    if (i < 0 || NUM_MEMORY_MAPPINGS <= i)
        die("Memory mapping index is out of range.");
    if (size == 0)
        die("Zero-sized memory mappings are not allowed.");
    if (memory_mappings[i].size > 0)
        die("Memory mapping is already in use.");
    long r = mmap(NULL, size, prot, flags, fd, offset);
    if (r < 0)
        die("Failed to create memory mapping.");
    memory_mappings[i].bytes = (void *)r;
    memory_mappings[i].size = size;
    memory_mappings[i].prot = prot;
    return empty_tuple;
}

//  s56: prim_memory_unmap

value
s56(value i_value)
{
    int32_t i = integer_decode(i_value);
    if (i < 0 || NUM_MEMORY_MAPPINGS <= i)
        die("Memory mapping index is out of range.");
    if (memory_mappings[i].size == 0)
        return empty_tuple;
    long r = munmap(memory_mappings[i].bytes, memory_mappings[i].size);
    if (r < 0)
        die("Failed to unmap memory mapping.");
    memory_mappings[i].bytes = NULL;
    memory_mappings[i].size = 0;
    memory_mappings[i].prot = 0;
    return empty_tuple;
}

//  s71: prim_epoll_create1

value
s71(value flags_value)
{
    int64_t flags = integer_decode(flags_value);
    int r = epoll_create1(flags);
    return integer_encode(r);
}

//  s60: prim_epoll_ctl

value
s60(value epoll_fd_value, value op_value, value fd_value, value event_chunk)
{
    int64_t epoll_fd = integer_decode(epoll_fd_value);
    int64_t op = integer_decode(op_value);
    int64_t fd = integer_decode(fd_value);
    value start_value = integer_encode(0);
    void *event = chunk_access(event_chunk, CHUNK_RW, start_value, 12);
    int r = epoll_ctl(epoll_fd, op, fd, event);
    return integer_encode(r);
}

//  s97: prim_epoll_wait

value
s97(value epoll_fd_value, value events_chunk, value max_events_value,
        value timeout_value)
{
    int64_t epoll_fd = integer_decode(epoll_fd_value);
    int64_t max_events = integer_decode(max_events_value);
    int64_t timeout = integer_decode(timeout_value);
    value start_value = integer_encode(0);
    unsigned n;
    if (__builtin_mul_overflow(max_events, 12, &n))
        die("Integer is out of range.");
    unsigned events_size = n;
    void *events = chunk_access(events_chunk, CHUNK_RW, start_value, events_size);
    int r = epoll_wait(epoll_fd, events, max_events, timeout);
    return integer_encode(r);
}

//  s04: prim_chunk_global

value
s04(value i_value)
{
    int32_t i = integer_decode(i_value);
    if (i < 0 || NUM_MEMORY_MAPPINGS <= i)
        die("Memory mapping index is out of range.");
    return global_chunk_encode(i);
}

//  s38: prim_chunk_new

value
s38(value num_bytes_value)
{
    int32_t num_bytes = integer_decode(num_bytes_value);
    if (num_bytes > CHUNK_NUM_BYTES_MAX)
        die("Chunk size is too big.");
    size_t align = _Alignof(struct chunk);
    size_t size = sizeof(struct chunk) + num_bytes;
    uint32_t id = heap_alloc(align, size);
    struct chunk *chunk_rep = heap_access(id);
    chunk_rep->header = chunk_header(CHUNK_RW, num_bytes);
    memset(chunk_rep->bytes, 0, num_bytes);
    return value_make_box(TAG_HEAP_CHUNK, id);
}

//  s32: prim_chunk_new_ro

value
s32(value num_bytes_value, value init_command_value)
{
    int32_t num_bytes = integer_decode(num_bytes_value);
    if (num_bytes > CHUNK_NUM_BYTES_MAX)
        die("Chunk size is too big.");
    size_t align = _Alignof(struct chunk);
    size_t size = sizeof(struct chunk) + num_bytes;
    uint32_t id = heap_alloc(align, size);
    struct chunk *chunk_rep = heap_access(id);
    chunk_rep->header = chunk_header(CHUNK_RW, num_bytes);
    memset(chunk_rep->bytes, 0, num_bytes);
    value chunk_value = value_make_box(TAG_HEAP_CHUNK, id);
    {
        uint32_t top = heap_top;
        value (*init_command)(value, value) = s35(init_command_value, 1);
        (void)init_command(init_command_value, chunk_value);
        heap_top = top;
    }
    chunk_rep->header = chunk_header(CHUNK_RO, num_bytes);
    return chunk_value;
}

//  s14: prim_chunk_size

value
s14(value chunk)
{
    if (value_has_tag(chunk, 0xff, TAG_IMMEDIATE_CHUNK)) {
        int i = global_chunk_decode(chunk);
        return integer_encode(memory_mappings[i].size);
    } else {
        const struct chunk *chunk_rep = chunk_unbox(chunk);
        return integer_encode(chunk_num_bytes(chunk_rep));
    }
    die("Value is not a chunk.");
}

//  s82: prim_chunk_store_bytes

value
s82(value d, value d_start_value, value s, value s_start_value, value count_value)
{
    int32_t count = integer_decode(count_value);
    if (count < 0)
        die("Chunk index range is invalid.");
    void *d_bytes = chunk_access(d, CHUNK_RW, d_start_value, count);
    const void *s_bytes = chunk_access(s, CHUNK_RO, s_start_value, count);
    memmove(d_bytes, s_bytes, count);
    return empty_tuple;
}

value
chunk_fetch_bytes(value chunk, value start_value, value count_value, int perm)
{
    int32_t count = integer_decode(count_value);
    if (count < 0)
        die("Chunk index range is invalid.");
    const void *bytes = chunk_access(chunk, CHUNK_RO, start_value, count);
    size_t align = _Alignof(struct chunk);
    size_t size = sizeof(struct chunk) + count;
    uint32_t id = heap_alloc(align, size);
    struct chunk *chunk_rep = heap_access(id);
    chunk_rep->header = chunk_header(perm, count);
    memmove(chunk_rep->bytes, bytes, count);
    return value_make_box(TAG_HEAP_CHUNK, id);
}

//  s11: prim_chunk_fetch_bytes_ro

value
s11(value chunk, value start_value, value count_value)
{
    return chunk_fetch_bytes(chunk, start_value, count_value, CHUNK_RO);
}

//  s39: prim_chunk_fetch_bytes_rw

value
s39(value chunk, value start_value, value count_value)
{
    return chunk_fetch_bytes(chunk, start_value, count_value, CHUNK_RW);
}

//  s42: prim_chunk_store_uint8

value
s42(value chunk, value i_value, value n_value)
{
    void *bytes = chunk_access(chunk, CHUNK_RW, i_value, 1);
    int64_t n = integer_decode(n_value);
    if (n < 0 || UINT8_MAX < n)
        die("Chunk value is out of range.");
    store_uint8(bytes, n);
    return empty_tuple;
}

//  s13: prim_chunk_fetch_uint8

value
s13(value chunk, value i_value)
{
    const void *bytes = chunk_access(chunk, CHUNK_RO, i_value, 1);
    uint8_t u = fetch_uint8(bytes);
    static_assert(UINT8_MAX <= INTEGER_MAX);
    return integer_encode(u);
}

//  s76: prim_chunk_store_int8_le

value
s76(value chunk, value i_value, value n_value)
{
    void *bytes = chunk_access(chunk, CHUNK_RW, i_value, 1);
    int64_t s = integer_decode(n_value);
    if (s < INT8_MIN || INT8_MAX < s)
        die("Number is out of range.");
    store_int8(bytes, s);
    return empty_tuple;
}

//  s46: prim_chunk_fetch_int8_le

value
s46(value chunk, value i_value)
{
    const void *bytes = chunk_access(chunk, CHUNK_RO, i_value, 1);
    int64_t s = fetch_int8(bytes);
    if (s < INTEGER_MIN || INTEGER_MAX < s)
        die("Number is out of range.");
    return integer_encode(s);
}

//  s54: prim_chunk_store_uint16_le

value
s54(value chunk, value i_value, value n_value)
{
    void *bytes = chunk_access(chunk, CHUNK_RW, i_value, 2);
    int64_t n = integer_decode(n_value);
    if (n < 0 || UINT16_MAX < n)
        die("Chunk value is out of range.");
    store_uint16_le(bytes, n);
    return empty_tuple;
}

//  s29: prim_chunk_fetch_uint16_le

value
s29(value chunk, value i_value)
{
    const void *bytes = chunk_access(chunk, CHUNK_RO, i_value, 2);
    uint16_t u = fetch_uint16_le(bytes);
    static_assert(UINT16_MAX <= INTEGER_MAX);
    return integer_encode(u);
}

//  s08: prim_chunk_store_int16_le

value
s08(value chunk, value i_value, value n_value)
{
    void *bytes = chunk_access(chunk, CHUNK_RW, i_value, 2);
    int64_t s = integer_decode(n_value);
    if (s < INT16_MIN || INT16_MAX < s)
        die("Number is out of range.");
    store_int16_le(bytes, s);
    return empty_tuple;
}

//  s98: prim_chunk_fetch_int16_le

value
s98(value chunk, value i_value)
{
    const void *bytes = chunk_access(chunk, CHUNK_RO, i_value, 2);
    int64_t s = fetch_int16_le(bytes);
    if (s < INTEGER_MIN || INTEGER_MAX < s)
        die("Number is out of range.");
    return integer_encode(s);
}

//  s74: prim_chunk_store_uint32_le

value
s74(value chunk, value i_value, value n_value)
{
    void *bytes = chunk_access(chunk, CHUNK_RW, i_value, 4);
    int64_t n = integer_decode(n_value);
    if (n < 0 || UINT32_MAX < n)
        die("Chunk value is out of range.");
    store_uint32_le(bytes, n);
    return empty_tuple;
}

//  s01: prim_chunk_fetch_uint32_le

value
s01(value chunk, value i_value)
{
    const void *bytes = chunk_access(chunk, CHUNK_RO, i_value, 4);
    uint32_t u = fetch_uint32_le(bytes);
    if (u > INTEGER_MAX)
        die("Number is out of range.");
    return integer_encode(u);
}

//  s17: prim_chunk_store_int32_le

value
s17(value chunk, value i_value, value n_value)
{
    void *bytes = chunk_access(chunk, CHUNK_RW, i_value, 4);
    int64_t s = integer_decode(n_value);
    if (s < INT32_MIN || INT32_MAX < s)
        die("Number is out of range.");
    store_int32_le(bytes, s);
    return empty_tuple;
}

//  s59: prim_chunk_fetch_int32_le

value
s59(value chunk, value i_value)
{
    const void *bytes = chunk_access(chunk, CHUNK_RO, i_value, 4);
    int64_t s = fetch_int32_le(bytes);
    if (s < INTEGER_MIN || INTEGER_MAX < s)
        die("Number is out of range.");
    return integer_encode(s);
}

//  s21: prim_chunk_store_uint64_le

value
s21(value chunk, value i_value, value n_value)
{
    void *bytes = chunk_access(chunk, CHUNK_RW, i_value, 8);
    int64_t n = integer_decode(n_value);
    if (n < 0 || UINT64_MAX < n)
        die("Chunk value is out of range.");
    store_uint64_le(bytes, n);
    return empty_tuple;
}

//  s99: prim_chunk_fetch_uint64_le

value
s99(value chunk, value i_value)
{
    const void *bytes = chunk_access(chunk, CHUNK_RO, i_value, 8);
    uint64_t u = fetch_uint64_le(bytes);
    if (u > INTEGER_MAX)
        die("Number is out of range.");
    return integer_encode(u);
}

//  s58: prim_chunk_store_int64_le

value
s58(value chunk, value i_value, value n_value)
{
    void *bytes = chunk_access(chunk, CHUNK_RW, i_value, 8);
    int64_t s = integer_decode(n_value);
    if (s < INT64_MIN || INT64_MAX < s)
        die("Number is out of range.");
    store_int64_le(bytes, s);
    return empty_tuple;
}

//  s80: prim_chunk_fetch_int64_le

value
s80(value chunk, value i_value)
{
    const void *bytes = chunk_access(chunk, CHUNK_RO, i_value, 8);
    int64_t s = fetch_int64_le(bytes);
    if (s < INTEGER_MIN || INTEGER_MAX < s)
        die("Number is out of range.");
    return integer_encode(s);
}

int
main(int argc, const char *argv[])
{
    command_argc = argc;
    command_argv = argv;
    P();
    return 0;
}