/*
 * corru - an esolang that looks like corruption.
 *
 * Rules:
 *  - Every byte value 0..255 is a distinct op (256 total), including space.
 *  - Every string is a valid program. No syntax errors, no runtime errors,
 *    no messages. Bad things become 0 or do nothing.
 *  - There is no decoder. This file is the only spec.
 *
 * Build: cc -O2 -o corru corru.c
 * Run:   ./corru prog.cou | ./corru -e 'CODE' | ./corru -g 64 > prog.cou
 *        -s N sets the step limit (default 1000000, 0 = unlimited)
 *        -t trims one trailing newline from the file (editors add one,
 *           and byte 10 is an op now)
 *        -g N writes N random raw bytes (0..255) to stdout
 *        -c OUT after running, write the (self-corrupted) program to OUT
 *
 * MACHINE: int64 stack, 256 memory cells + pointer, loop stack, an
 * "inverted" flag and a "string" flag. A digit after a digit extends the
 * number (so 12 is twelve); any other op ends it. Binary ops pop b (top),
 * then a, and push a OP b. Popping an empty stack gives 0.
 *
 * DIGITS 0-9  push / extend number
 *
 * lower  a add     b and     c dup      d over    e xor     f shl
 *        g get     h halt if popped 0   i input byte        j skip fwd n
 *        k skip 1 if top==0   l back n if top!=0   m mul    n neg
 *        o print number       p print char          q depth  r random
 *        s store (val addr)   t step count          u abs    v not
 *        w inc     x rot      y push 10  z clear
 * UPPER  A sub     B or      C drop     D swap    E bitnot  F shr
 *        G take    H halt    I input int   J skip back n
 *        K skip 1 if top!=0   L skip fwd n if top==0  M div  N sign
 *        O peek print number  P peek print char  Q pick  R mod
 *        S reverse T seed     U max      V bool    W dec     X rot back
 *        Y push 32 Z swap top with mem[0]
 * SYMBOL ! neq   " string mode   # push length   $ print hex
 *        % &255  & logical and  ' char literal  ( ptr--  ) ptr++
 *        * product-all  + sum-all  , dup top n times  - |a-b|
 *        . newline  / divmod  : ptr=pop  ; push ptr
 *        < lt  = eq  > gt  ? pop, skip 1 if 0  @ push ip
 *        [ loop start  \ skip next byte  ] loop end (counter on top)
 *        ^ pow  _ push 256  ` swap letter case  { mem[ptr]=top
 *        | logical or  } push mem[ptr]  ~ popcount
 *
 * CONTROL (0..31) and SPACE (32)
 *   0 NUL: corrupt. Overwrites the next program byte with 0 and skips it.
 *     The program is rewritten in place, so a loop that comes back to that
 *     spot finds a NUL there, and the rot spreads one byte per pass.
 *   1 push 1     2 push -1    3 UB (below)     4 push 1000
 *   5 push 65536  6 push INT64_MAX               7 push INT64_MIN
 *   (1, 2 and 4..7 never extend a number; a digit after them starts a new one)
 *   3 UB: arms a flag. The NEXT op that runs is undefined: it may become a
 *     random op, truncate the stack, push garbage, skip ahead, scramble the
 *     memory pointer, corrupt a random program byte, do nothing, or run
 *     normally. The choice comes from the seeded RNG (see T), so a given
 *     program, seed and input always do the same thing. The interpreter
 *     itself has no C undefined behavior: UB lives in the language only.
 *   8 ptr=0       9 mem[ptr]++ 10 mem[ptr]--
 *  11 push mem[ptr], then zero it           12 swap top with mem[ptr]
 *  13 print a space char                    14 print whole stack as numbers
 *  15 print whole stack as chars            16 zero all memory
 *  17 dup top two (a b -> a b a b)          18 nip (a b -> b)
 *  19 tuck (a b -> b a b)                   20 min
 *  21 square     22 gcd       23 isqrt      24 factorial (0..20, else 0)
 *  25 fibonacci (0..92, else 0)             26 is prime (0/1)
 *  27 clear loop stack                      28 push loop depth
 *  29 break out of innermost loop           30 continue (jump to its ])
 *  31 sort stack ascending
 *  32 SPACE: separator. Ends a number, does nothing else (so 1 2 is 1, 2).
 * HIGH
 * 127 flush: print stack as chars bottom to top, then clear it
 * 128..191  push 2^(byte-128)   (128 = 1, 191 = 2^63)
 * 192..223  top += byte-192     (immediate add, 0..31)
 * 224..255  top *= byte-224+2   (immediate multiply, 2..33)
 *
 * Not ops: nothing. All 256 byte values do something.
 */
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <time.h>

typedef int64_t i64;
typedef uint64_t u64;

#define STK 4096
#define LST 256

static i64 st[STK];
static int sp;
static i64 mem[256];
static unsigned char ptr;
static int ls[LST];
static int lp;
static u64 rs = 88172645463325252ULL;

static void push(i64 v) { if (sp < STK) st[sp++] = v; }
static i64 pop(void) { return sp > 0 ? st[--sp] : 0; }
static i64 peek(void) { return sp > 0 ? st[sp - 1] : 0; }
static u64 rnd(void) { rs ^= rs << 13; rs ^= rs >> 7; rs ^= rs << 17; return rs; }
static i64 add(i64 a, i64 b) { return (i64)((u64)a + (u64)b); }
static i64 sub(i64 a, i64 b) { return (i64)((u64)a - (u64)b); }
static i64 mul(i64 a, i64 b) { return (i64)((u64)a * (u64)b); }
static i64 dv(i64 a, i64 b) { return b == 0 ? 0 : b == -1 ? sub(0, a) : a / b; }
static i64 md(i64 a, i64 b) { return (b == 0 || b == -1) ? 0 : a % b; }
static i64 pw(i64 a, i64 e) {
    if (e < 0) return 0;
    u64 r = 1, x = (u64)a;
    while (e) { if (e & 1) r *= x; x *= x; e >>= 1; }
    return (i64)r;
}

static i64 gcdv(i64 a, i64 b) {
    u64 x = a < 0 ? 0 - (u64)a : (u64)a, y = b < 0 ? 0 - (u64)b : (u64)b;
    while (y) { u64 t = x % y; x = y; y = t; }
    return (i64)x;
}
static i64 isq(i64 n) {
    if (n <= 0) return 0;
    u64 x = (u64)n, r = 0, bit = 1ULL << 62;
    while (bit > x) bit >>= 2;
    while (bit) {
        if (x >= r + bit) { x -= r + bit; r = (r >> 1) + bit; }
        else r >>= 1;
        bit >>= 2;
    }
    return (i64)r;
}
static i64 fact(i64 n) {
    if (n < 0 || n > 20) return 0;
    i64 r = 1;
    for (i64 i = 2; i <= n; i++) r *= i;
    return r;
}
static i64 fibv(i64 n) {
    if (n < 0 || n > 92) return 0;
    i64 a = 0, b = 1;
    while (n-- > 0) { i64 t = a + b; a = b; b = t; }
    return a;
}
static u64 mulmod(u64 a, u64 b, u64 m) { return (u64)((unsigned __int128)a * b % m); }
static u64 powmod(u64 a, u64 e, u64 m) {
    u64 r = 1; a %= m;
    while (e) { if (e & 1) r = mulmod(r, a, m); a = mulmod(a, a, m); e >>= 1; }
    return r;
}
static i64 isprime(i64 v) {
    static const u64 sm[] = {2,3,5,7,11,13,17,19,23,29,31,37};
    if (v < 2) return 0;
    u64 n = (u64)v;
    for (int i = 0; i < 12; i++) if (n % sm[i] == 0) return n == sm[i];
    u64 d = n - 1; int s = 0;
    while (!(d & 1)) { d >>= 1; s++; }
    for (int i = 0; i < 12; i++) {
        u64 x = powmod(sm[i], d, n);
        if (x == 1 || x == n - 1) continue;
        int ok = 0;
        for (int r = 1; r < s; r++) { x = mulmod(x, x, n); if (x == n - 1) { ok = 1; break; } }
        if (!ok) return 0;
    }
    return 1;
}
static int cmp64(const void *x, const void *y) {
    i64 a = *(const i64 *)x, b = *(const i64 *)y;
    return (a > b) - (a < b);
}
/* bracket table: mt[i] = index of the ] matching the [ at i, else -1.
 * Rebuilt lazily when a write touches a [ or ] (op 0, UB corruption). */
static int *mt;
static int mtdirty = 1;
static void mtbuild(const unsigned char *p, int len) {
    int *stk = malloc(sizeof(int) * (size_t)(len > 0 ? len : 1));
    int top = 0;
    for (int i = 0; i < len; i++) mt[i] = -1;
    if (stk) {
        for (int i = 0; i < len; i++) {
            if (p[i] == '[') stk[top++] = i;
            else if (p[i] == ']' && top > 0) mt[stk[--top]] = i;
        }
        free(stk);
    }
    mtdirty = 0;
}
static int match(const unsigned char *p, int len, int open) {
    if (!mt) return -1;
    if (mtdirty) mtbuild(p, len);
    return (open >= 0 && open < len) ? mt[open] : -1;
}
/* write a program byte, marking the bracket table stale if it matters */
static void wr(unsigned char *p, int at, unsigned char v) {
    if (p[at] == '[' || p[at] == ']' || v == '[' || v == ']') mtdirty = 1;
    p[at] = v;
}
/* read an integer like scanf %lld, but wrapping instead of overflowing */
static i64 readint(void) {
    fflush(stdout); /* show any prompt before blocking on input (WASI stdout is not line-buffered) */
    int ch = getchar();
    while (ch == ' ' || (ch >= 9 && ch <= 13)) ch = getchar();
    int neg = 0;
    if (ch == '-' || ch == '+') { neg = ch == '-'; ch = getchar(); }
    u64 v = 0; int any = 0;
    while (ch >= '0' && ch <= '9') { v = v * 10 + (u64)(ch - '0'); any = 1; ch = getchar(); }
    if (ch != EOF) ungetc(ch, stdin);
    if (!any) return 0;
    return (i64)(neg ? 0 - v : v);
}

static void run(unsigned char *p, int len, u64 limit) {
    int ip = 0, inv = 0, str = 0, lastdig = 0, ub = 0;
    u64 steps = 0;
    while (ip >= 0 && ip < len) {
        if (limit && steps >= limit) break;
        steps++;
        int c = p[ip], next = ip + 1, dig = 0;
        i64 a, b, n;
        if (str && c != '"') { push(c); goto done; }
        if (inv && ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'))) c ^= 0x20;
        if (c == 3) { ub = 1; goto done; }
        if (ub) {
            u64 r = rnd();
            ub = 0;
            switch (r & 7) {
            case 0: c = (int)((r >> 8) & 255);                        /* become another op */
                    if (c == 3) { ub = 1; goto done; }
                    if (inv && ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'))) c ^= 0x20;
                    break;
            case 1: sp = (int)((r >> 8) % (u64)(sp + 1)); break;      /* truncate stack */
            case 2: push((i64)rnd()); break;                          /* garbage first */
            case 3: next = ip + 1 + (int)((r >> 8) % 8); goto done;   /* skip ahead */
            case 4: ptr = (unsigned char)(r >> 8); break;             /* scramble ptr */
            case 5: wr(p, (int)((r >> 8) % (u64)len), (unsigned char)(r >> 16)); goto done;
            case 6: goto done;                                        /* does nothing */
            default: break;                                           /* runs normally */
            }
        }
        if (c >= '0' && c <= '9') {
            dig = 1;
            if (lastdig && sp > 0) st[sp - 1] = add(mul(st[sp - 1], 10), c - '0');
            else push(c - '0');
            goto done;
        }
        if (c >= 128 && c <= 191) { push((i64)((u64)1 << (c - 128))); goto done; }
        if (c >= 192 && c <= 223) { push(add(pop(), c - 192)); goto done; }
        if (c >= 224) { push(mul(pop(), c - 224 + 2)); goto done; }
        switch (c) {
        /* control + space */
        case 0: if (ip + 1 < len) { wr(p, ip + 1, 0); next = ip + 2; } break;
        case 1: push(1); break;
        case 2: push(-1); break;
        case 3: ub = 1; break;
        case 4: push(1000); break;
        case 5: push(65536); break;
        case 6: push(INT64_MAX); break;
        case 7: push(INT64_MIN); break;
        case 8: ptr = 0; break;
        case 9: mem[ptr] = add(mem[ptr], 1); break;
        case 10: mem[ptr] = sub(mem[ptr], 1); break;
        case 11: push(mem[ptr]); mem[ptr] = 0; break;
        case 12: a = pop(); push(mem[ptr]); mem[ptr] = a; break;
        case 13: putchar(' '); break;
        case 14: for (int i = 0; i < sp; i++) { if (i) putchar(' '); printf("%lld", (long long)st[i]); } break;
        case 15: for (int i = 0; i < sp; i++) putchar((int)(st[i] & 255)); break;
        case 16: memset(mem, 0, sizeof mem); break;
        case 17: b = peek(); a = sp > 1 ? st[sp - 2] : 0; push(a); push(b); break;
        case 18: b = pop(); pop(); push(b); break;
        case 19: b = pop(); a = pop(); push(b); push(a); push(b); break;
        case 20: b = pop(); a = pop(); push(a < b ? a : b); break;
        case 21: a = pop(); push(mul(a, a)); break;
        case 22: b = pop(); a = pop(); push(gcdv(a, b)); break;
        case 23: push(isq(pop())); break;
        case 24: push(fact(pop())); break;
        case 25: push(fibv(pop())); break;
        case 26: push(isprime(pop())); break;
        case 27: lp = 0; break;
        case 28: push(lp); break;
        case 29:
            if (lp > 0) {
                int m = match(p, len, ls[lp - 1]);
                if (m >= 0) { if (sp > 0) st[sp - 1] = 1; next = m; }
            }
            break;
        case 30:
            if (lp > 0) { int m = match(p, len, ls[lp - 1]); if (m >= 0) next = m; }
            break;
        case 31: if (sp > 1) qsort(st, sp, sizeof(i64), cmp64); break;
        case 32: break; /* separator: ends a number, nothing else */
        case 127: for (int i = 0; i < sp; i++) putchar((int)(st[i] & 255)); sp = 0; break;
        /* lowercase */
        case 'a': b = pop(); a = pop(); push(add(a, b)); break;
        case 'b': b = pop(); a = pop(); push(a & b); break;
        case 'c': push(peek()); break;
        case 'd': push(sp > 1 ? st[sp - 2] : 0); break;
        case 'e': b = pop(); a = pop(); push(a ^ b); break;
        case 'f': b = pop(); a = pop(); push((i64)((u64)a << (b & 63))); break;
        case 'g': a = pop(); push(mem[a & 255]); break;
        case 'h': if (pop() == 0) return; break;
        case 'i': { fflush(stdout); int ch = getchar(); push(ch == EOF ? 0 : ch); } break;
        case 'j': n = pop(); if (n < 0) n = 0; if (n > len) n = len; next = ip + 1 + (int)n; break;
        case 'k': if (peek() == 0) next = ip + 2; break;
        case 'l': n = pop(); if (peek() != 0 && n > 0) { if (n > ip) n = ip; next = ip - (int)n; } break;
        case 'm': b = pop(); a = pop(); push(mul(a, b)); break;
        case 'n': push(sub(0, pop())); break;
        case 'o': printf("%lld", (long long)pop()); break;
        case 'p': putchar((int)(pop() & 255)); break;
        case 'q': push(sp); break;
        case 'r': push((i64)(rnd() >> 33)); break;
        case 's': a = pop(); b = pop(); mem[a & 255] = b; break;
        case 't': push((i64)steps); break;
        case 'u': a = pop(); push(a < 0 ? sub(0, a) : a); break;
        case 'v': push(pop() == 0); break;
        case 'w': push(add(pop(), 1)); break;
        case 'x': { i64 cc = pop(), bb = pop(), aa = pop(); push(bb); push(cc); push(aa); } break;
        case 'y': push(10); break;
        case 'z': sp = 0; break;
        /* uppercase */
        case 'A': b = pop(); a = pop(); push(sub(a, b)); break;
        case 'B': b = pop(); a = pop(); push(a | b); break;
        case 'C': pop(); break;
        case 'D': b = pop(); a = pop(); push(b); push(a); break;
        case 'E': push(~pop()); break;
        case 'F': b = pop(); a = pop(); push(a >> (b & 63)); break;
        case 'G': a = pop(); push(mem[a & 255]); mem[a & 255] = 0; break;
        case 'H': return;
        case 'I': push(readint()); break;
        case 'J': n = pop(); if (n > 0) { if (n > ip) n = ip; next = ip - (int)n; } break;
        case 'K': if (peek() != 0) next = ip + 2; break;
        case 'L': n = pop(); if (n < 0) n = 0; if (n > len) n = len; if (peek() == 0) next = ip + 1 + (int)n; break;
        case 'M': b = pop(); a = pop(); push(dv(a, b)); break;
        case 'N': a = pop(); push(a > 0 ? 1 : a < 0 ? -1 : 0); break;
        case 'O': printf("%lld", (long long)peek()); break;
        case 'P': putchar((int)(peek() & 255)); break;
        case 'Q': n = pop(); push((n >= 0 && n < sp) ? st[sp - 1 - n] : 0); break;
        case 'R': b = pop(); a = pop(); push(md(a, b)); break;
        case 'S': for (int i = 0, j = sp - 1; i < j; i++, j--) { i64 t = st[i]; st[i] = st[j]; st[j] = t; } break;
        case 'T': rs = (u64)pop() * 2654435761ULL + 88172645463325252ULL; if (!rs) rs = 1; break;
        case 'U': b = pop(); a = pop(); push(a > b ? a : b); break;
        case 'V': push(pop() != 0); break;
        case 'W': push(sub(pop(), 1)); break;
        case 'X': { i64 cc = pop(), bb = pop(), aa = pop(); push(cc); push(aa); push(bb); } break;
        case 'Y': push(32); break;
        case 'Z': a = pop(); push(mem[0]); mem[0] = a; break;
        /* symbols */
        case '!': b = pop(); a = pop(); push(a != b); break;
        case '"': str = !str; break;
        case '#': push(len); break;
        case '$': printf("%llx", (unsigned long long)pop()); break;
        case '%': push(pop() & 255); break;
        case '&': b = pop(); a = pop(); push(a && b); break;
        case '\'': if (ip + 1 < len) { push(p[ip + 1]); next = ip + 2; } break;
        case '(': ptr--; break;
        case ')': ptr++; break;
        case '*': if (sp == 0) push(0); else { i64 r = 1; while (sp) r = mul(r, pop()); push(r); } break;
        case '+': { i64 r = 0; while (sp) r = add(r, pop()); push(r); } break;
        case ',': n = pop(); if (n > 1000) n = 1000; a = peek(); for (; n > 0; n--) push(a); break;
        case '-': b = pop(); a = pop(); { i64 d = sub(a, b); push(d < 0 ? sub(0, d) : d); } break;
        case '.': putchar('\n'); break;
        case '/': b = pop(); a = pop(); push(dv(a, b)); push(md(a, b)); break;
        case ':': ptr = (unsigned char)(pop() & 255); break;
        case ';': push(ptr); break;
        case '<': b = pop(); a = pop(); push(a < b); break;
        case '=': b = pop(); a = pop(); push(a == b); break;
        case '>': b = pop(); a = pop(); push(a > b); break;
        case '?': if (pop() == 0) next = ip + 2; break;
        case '@': push(ip); break;
        case '[': if (lp < LST) ls[lp++] = ip; break;
        case '\\': next = ip + 2; break;
        case ']':
            if (lp > 0) {
                i64 cnt = peek();
                if (cnt > 1) { st[sp - 1] = cnt - 1; next = ls[lp - 1] + 1; }
                else { pop(); lp--; }
            }
            break;
        case '^': b = pop(); a = pop(); push(pw(a, b)); break;
        case '_': push(256); break;
        case '`': inv = !inv; break;
        case '{': mem[ptr] = peek(); break;
        case '|': b = pop(); a = pop(); push(a || b); break;
        case '}': push(mem[ptr]); break;
        case '~': push(__builtin_popcountll((u64)pop())); break;
        default: break; /* spaces, newlines, everything else: nothing */
        }
    done:
        lastdig = dig;
        ip = next;
    }
}

int main(int argc, char **argv) {
    unsigned char *prog = NULL;
    int len = 0;
    u64 limit = 1000000;
    const char *src = NULL, *file = NULL, *outf = NULL;
    int gen = 0, trim = 0;
    for (int i = 1; i < argc; i++) {
        if (!strcmp(argv[i], "-e") && i + 1 < argc) src = argv[++i];
        else if (!strcmp(argv[i], "-s") && i + 1 < argc) limit = strtoull(argv[++i], 0, 10);
        else if (!strcmp(argv[i], "-c") && i + 1 < argc) outf = argv[++i];
        else if (!strcmp(argv[i], "-t")) trim = 1;
        else if (!strcmp(argv[i], "-g") && i + 1 < argc) gen = atoi(argv[++i]);
        else file = argv[i];
    }
    if (gen > 0) {
        FILE *u = fopen("/dev/urandom", "rb");
        srand((unsigned)time(NULL));
        for (int i = 0; i < gen; i++) {
            int b = u ? fgetc(u) : EOF;
            putchar(b == EOF ? (rand() & 255) : b);
        }
        if (u) fclose(u);
        return 0;
    }
    const size_t MAXLEN = (size_t)1 << 30;
    unsigned char *raw = NULL;
    size_t rl = 0;
    if (src) {
        rl = strlen(src);
        if (rl > MAXLEN) rl = MAXLEN;
        raw = malloc(rl + 1);
        if (!raw) return 0;
        memcpy(raw, src, rl);
    } else if (file) {
        FILE *f = fopen(file, "rb");
        if (!f) return 0;
        size_t cap = 4096;
        raw = malloc(cap);
        if (!raw) { fclose(f); return 0; }
        size_t k;
        while (rl < MAXLEN && (k = fread(raw + rl, 1, cap - rl, f)) > 0) {
            rl += k;
            if (rl == cap) {
                unsigned char *t = realloc(raw, cap * 2);
                if (!t) break;
                raw = t; cap *= 2;
            }
        }
        fclose(f);
    } else return 0;
    if (trim && file && rl > 0 && raw[rl - 1] == '\n') { rl--; if (rl > 0 && raw[rl - 1] == '\r') rl--; }
    prog = malloc(rl + 1);
    mt = malloc(sizeof(int) * (rl + 1));
    if (!prog || !mt) { free(raw); free(prog); free(mt); return 0; }
    memcpy(prog, raw, rl);
    len = (int)rl;
    run(prog, len, limit);
    fflush(stdout);
    /* -c out: save the program as it ended up, i.e. corrupted by its own 0 and 3 ops.
       Never overwrites the input file; anything that goes wrong is silently skipped. */
    if (outf && !(file && !strcmp(outf, file))) {
        FILE *o = fopen(outf, "wb");
        if (o) { fwrite(prog, 1, (size_t)len, o); fclose(o); }
    }
    free(raw); free(prog); free(mt);
    return 0;
}
