#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <map>
#include <set>
#include <arpa/inet.h>

using namespace std ;

set<unsigned int> branchseen ;
const int MAXARRAYS = 100000000 ;

void error(const char *s) {
   cerr << s << endl ;
   exit(0) ;
}

int trace = 0 ;
long long clk = 0 ;

void debugit() {
   cout << "[Clock " << clk << "]" << endl << flush ;
}

int mgetchar() {
   while (1) {
      int c = getchar() ;
      if (c == 0)
         debugit() ;
      else
         return c ;
   }
}

unsigned int *pool[32] = {0};

unsigned int *my_malloc(unsigned int size)
{
    int bits = 1;
    int tmp_size = size;
    if (tmp_size > 0) tmp_size -= 1;
    while (tmp_size>>=1){++bits;};

    unsigned int *p = pool[bits];
    if (p){
        pool[bits] = *(unsigned int **)p;
    } else {
        p = (unsigned int*)malloc(4*(1<<bits));
    }
    memset(p, 0, 4*size);
    return p;
}

void my_free(unsigned int *p, unsigned int size)
{
    int bits = 1;
    if (size > 0) size -= 1;
    while (size>>=1){++bits;};

    *(unsigned int **)p = pool[bits];
    pool[bits] = p;
}


int main(int argc, char *argv[]) {
   unsigned int *pc, r[8] = {0} ;
   unsigned int **a = new unsigned int*[MAXARRAYS];
   int *sz = new int[MAXARRAYS] ;
   int all = 1000 ;

   char *filename = "pumped.umz" ;
   if (argc > 1)
      filename = argv[1] ;
   FILE *f = fopen(filename, "r") ;
   if (f == 0) {
      cerr << "No file " << filename << endl ;
      exit(10) ;
   }
   struct stat statbuf ;
   fstat(fileno(f), &statbuf) ;
   size_t fsize = statbuf.st_size ;
   unsigned int *p = my_malloc(fsize) ;
   int len = fread(p, 4, fsize/4, f) ;
   fclose(f) ;
   a[0] = p ;
   for (int i=0; i<len; i++)
      p[i] = ntohl(p[i]) ;

   pc = a[0];
   while (1) {
      unsigned int w = *pc++ ;
#define op (w >> 28)
#define A ((w >> 6) & 7)
#define B ((w >> 3) & 7)
#define C ((w     ) & 7)

      //if (trace) cout << "clk " << clk << " pc " << pc << " op is " << op << " " << A << " " << B << " " << C << " r " << r[0] << " " << r[1] << " " << r[2] << " " << r[3] << " " << r[4] << " " << r[5] << " " << r[6] << " " << r[7] << endl << flush ;
      switch (op) {
case 0: if (r[C]) r[A] = r[B] ; break ;
case 1: r[A] = a[r[B]][r[C]] ; break ;
case 2: a[r[A]][r[B]] = r[C] ; break ;
case 3: r[A] = r[B] + r[C] ; break ;
case 4: r[A] = r[B] * r[C] ; break ;
case 5: r[A] = r[B] / r[C] ; break ;
case 6: r[A] = ~(r[B] & r[C]) ; break ;
case 7: error("! Halting") ; break ;
case 8: while (1) {
           if (all >= MAXARRAYS) {
 cerr << "Wrap@" << clk << " " << flush ;
              all = 1000 ;
           }
           if (a[all] == 0)
              break ;
           all++ ;
        }
        sz[all] = r[C] ;
        a[all] = my_malloc(sz[all]) ;
        //memset(a[all], 0, sizeof(unsigned int)*sz[all]);
        r[B] = all++; break ;
case 9: {
    unsigned int rC = r[C];
    my_free(a[rC], sz[rC]) ;
    a[rC] = 0 ; sz[rC] = 0 ; break ;
 }
case 10: cout << (char)r[C] << flush ; break ;
case 11: r[C] = mgetchar() ; break ;
case 12: {
    unsigned int rB = r[B];
    if (rB != 0) {
        my_free(a[0], sz[0]) ;
        a[0] = my_malloc(sz[rB]) ;
        sz[0] = sz[rB] ;
        memcpy(a[0], a[rB], sz[rB]*sizeof(unsigned int)) ;
    }
 /*
   if (branchseen.find(r[C]) == branchseen.end()) {
      branchseen.insert(r[C]) ;
   // cout << "New branch " << r[C] << endl << flush ;
   }
 */
   /*
        if (r[C] == 1949089)
           exit(0) ;
           else */
           pc = a[0]+r[C] ; break ;
 }
case 13: r[(w>>25)&7] = (w & ((1 << 25) - 1)) ; break ;
default: error("! bad code") ; exit(0) ;
      }
      clk++ ;
   }
}

