Skip to content

Instantly share code, notes, and snippets.

@danielcristofani
Last active July 26, 2020 04:23
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save danielcristofani/1cf726de066ad2bd7ef781c062c45df3 to your computer and use it in GitHub Desktop.
Save danielcristofani/1cf726de066ad2bd7ef781c062c45df3 to your computer and use it in GitHub Desktop.
Rui interpreter
/* drui.c -- Rui interpreter
Daniel Cristofani
7/2020 */
/* A reasonably fast interpreter for the Rui programming language, as described at https://esolangs.org/wiki/Rui
This uses the GMP bignum library, so it will need to be compiled with something like
gcc drui.c -O2 -o drui -lgmp
(oddly, the -lgmp does apparently need to go at the end).
Options:
-s suppress "Reading: <value>" confirmation from 'r' command
-d send copious state data to stderr to help with debugging Rui programs (upwards of a gigabyte per minute).
-c scan data structures once per tick for corruption due to bugs in this interpreter, and report a few stats.
-n omit newline when outputting numbers with 'w' command
Options c and d both slow this interpreter dramatically (couple orders of magnitude?).
Note: this interpreter supports ASCII i/o with the 'R' and 'W' commands as well as numeric i/o with the 'r' and 'w' commands.
It does not execute oldest threads first, each tick; it may merge threads created at different times for combined execution, if they're otherwise identical. Apart from speed, it is careful to behave as if it executed each thread separately once per tick, in an order of its choosing, beginning the tick after the thread was spawned. Like other implementations, it does spend one tick executing a ':' command.
This interpreter keeps a table of threads, indexed by current position in the source program, value, and time of next execution (current tick or next tick). Threads that are identical in these respects are merged, and executed as one when possible. Threads are also kept in lists sorted by value, and in a single list of all threads. Threads with values >= VALCAP are stored at VALCAP in the table and the value list array; any that are not linked directly from the table, but indirectly through nextbigval due to another thread group of different value at the same position, are reported as "collisions" if -c is on, but shouldn't hurt correctness. */
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>
#include <sys/stat.h>
#include <gmp.h>
#define VALCAP 1024
struct group{
int pos;
int valindex;
mpz_t value;
mpz_t count;
struct group *nextgroup;
struct group *prevgroup;
struct group *nextbyval;
struct group *prevbyval;
struct group *nextbigval;
struct group *prevbigval;
char when;
};
struct inst{
mpz_t value;
int location;
int next;
char command;
};
FILE *progfile;
struct stat info;
char *filename;
char *option;
char *source;
char *buffer, *bufptr;
char debug, checkdata, suppress, nonewline;
int c, w, x, y, z, instct, charct, linect;
struct inst *prog;
int *linestarts;
char thistick;
#define nexttick (1-thistick)
unsigned long tickcount=0, length;
struct group *(*table)[VALCAP+1][2];
struct group *grouplist;
struct group *vallist[VALCAP+1];
struct group *curgroup, *nextgroup;
struct group *agroup;
mpz_t tempval;
mp_bitcnt_t tempbitcount;
void check(void){
char tableokay=1,valsokay=1,grouplistokay=1, consistent=1;
int listgroups=0, valuegroups=0, tablegroups=0;
int tpindex, tvindex, teindex, vindex;
int tzerocount=0, vzerocount=0;
int collisions=0, valcapgroups=0;
struct group *temp;
mpz_t listthreads, valuethreads, tablethreads;
mpz_init(listthreads);
mpz_init(valuethreads);
mpz_init(tablethreads);
for (tpindex=0;tpindex<=instct;tpindex++){
for(teindex=0;teindex<2;teindex++){
for(tvindex=0;tvindex<VALCAP;tvindex++){
temp=table[tpindex][tvindex][teindex];
if(temp==0){
tzerocount++;
} else {
tablegroups++;
if (temp->when!=teindex || temp->valindex!=tvindex||temp->pos!=tpindex){
fprintf(stderr,"mismatch: tpindex=%d, temp->pos=%d, "
"tvindex=%d, temp->valindex=%d, teindex=%d, temp->when=%d.\n",
tpindex,temp->pos,tvindex,temp->valindex,teindex,temp->when);
tableokay=0;
}
if (temp->valindex<0 ||temp->valindex>VALCAP||
(temp->valindex==VALCAP&&(mpz_cmp_ui(temp->value,VALCAP)<0))||
(temp->valindex<VALCAP&&(mpz_cmp_ui(temp->value,VALCAP)>=0))||
mpz_cmp_ui(temp->value,0)<0||mpz_cmp_ui(temp->count,1)<0){
gmp_fprintf(stderr,"value problem! temp->pos=%d, temp->valindex=%d, "
"temp->value=%Zd, temp->count=%Zd, temp->when=%d.\n",
temp->pos,temp->valindex,temp->value,temp->count,temp->when);
tableokay=0;
}
mpz_add(tablethreads, tablethreads, temp->count);
}
}
temp=table[tpindex][tvindex][teindex];
if(temp==0){
tzerocount++;
} else {
if(temp->prevbigval){
fprintf(stderr,"GLORM!!\n");
tableokay=0;
}
for(;temp;temp=temp->nextbigval){
valcapgroups++;
mpz_add(tablethreads,tablethreads,temp->count);
tablegroups++;
if (temp->nextbigval){
collisions++;
if (temp->nextbigval->prevbigval!=temp){
fprintf(stderr,"SMOFT!\n");
tableokay=0;
}
}
if (temp->when!=teindex || temp->valindex!=tvindex||temp->pos!=tpindex){
fprintf(stderr,"mismatch: tpindex=%d, temp->pos=%d, "
"tvindex=%d, temp->valindex=%d, teindex=%d, temp->when=%d.\n",
tpindex,temp->pos,tvindex,temp->valindex,teindex,temp->when);
tableokay=0;
}
if (temp->valindex<0 ||temp->valindex>VALCAP||
(temp->valindex==VALCAP&&(mpz_cmp_ui(temp->value,VALCAP)<0))||
(temp->valindex<VALCAP&&(mpz_cmp_ui(temp->value,VALCAP)>=0))||
mpz_cmp_ui(temp->value,0)<0||mpz_cmp_ui(temp->count,1)<0){
gmp_fprintf(stderr,"value problem! temp->pos=%d, temp->valindex=%d, "
"temp->value=%Zd, temp->count=%Zd, temp->when=%d.\n",
temp->pos,temp->valindex,temp->value,temp->count,temp->when);
tableokay=0;
}
}
}
}
}
for(vindex=0;vindex<=VALCAP;vindex++){
temp=vallist[vindex];
if(temp==0){
vzerocount++;
} else {
if(temp->prevbyval){
fprintf(stderr,"SNESHT!!\n");
valsokay=0;
}
for(;temp;temp=temp->nextbyval){
mpz_add(valuethreads,valuethreads,temp->count);
valuegroups++;
if (temp->nextbyval&&temp->nextbyval->prevbyval!=temp){
fprintf(stderr,"SLARMENT!\n");
valsokay=0;
}
if (temp->valindex<0 ||temp->valindex>VALCAP||
(temp->valindex==VALCAP&&(mpz_cmp_ui(temp->value,VALCAP)<0))||
(temp->valindex<VALCAP&&(mpz_cmp_ui(temp->value,VALCAP)>=0))||
mpz_cmp_ui(temp->value,0)<0||mpz_cmp_ui(temp->count,1)<0||
vindex!=temp->valindex){
gmp_fprintf(stderr,"value list problem! vindex=%d,temp->pos=%d, temp->valindex=%d, "
"temp->value=%Zd, temp->count=%Zd, temp->when=%d.\n",
vindex, temp->pos,temp->valindex,temp->value,temp->count,temp->when);
valsokay=0;
}
}
}
}
temp=grouplist;
if(temp!=0){
if(temp->prevgroup){
fprintf(stderr,"SMAMLER!!\n");
grouplistokay=0;
}
for(;temp;temp=temp->nextgroup){
mpz_add(listthreads,listthreads,temp->count);
listgroups++;
if (temp->nextgroup&&temp->nextgroup->prevgroup!=temp){
fprintf(stderr,"SPORNISH!!\n");
grouplistokay=0;
}
if (temp->valindex<0 ||temp->valindex>VALCAP||
(temp->valindex==VALCAP&&(mpz_cmp_ui(temp->value,VALCAP)<0))||
(temp->valindex<VALCAP&&(mpz_cmp_ui(temp->value,VALCAP)>=0))||
mpz_cmp_ui(temp->value,0)<0||mpz_cmp_ui(temp->count,1)<0){
gmp_fprintf(stderr,"group list problem! temp->pos=%d, temp->valindex=%d, "
"temp->value=%Zd, temp->count=%Zd, temp->when=%d.\n",
temp->pos,temp->valindex,temp->value,temp->count,temp->when);
grouplistokay=0;
}
}
}
if(tablegroups!=listgroups||listgroups!=valuegroups||
mpz_cmp(tablethreads,listthreads)||mpz_cmp(listthreads, valuethreads)){
gmp_fprintf(stderr,"Consistency problems! listthreads=%Zd, valuethreads=%Zd, tablethreads=%Zd, "
"listgroups=%d, valuegroups=%d, tablegroups=%d.\n",listthreads,valuethreads,tablethreads,
listgroups,valuegroups,tablegroups);
consistent=0;
}
fprintf(stderr, "Tick %lu: ", tickcount);
if(tableokay&&grouplistokay&&valsokay&&consistent){
gmp_fprintf(stderr, "no problems. %d groups, %Zd threads, %d collisions, %d groups >=%d.\n",
tablegroups, tablethreads, collisions, valcapgroups, VALCAP);
}else{
fprintf(stderr, "data structures corrupted (see above).\n");
}
}
struct group *talloc(void){
struct group *newgroup;
if (!(newgroup=calloc(1, sizeof(struct group)))){
fprintf(stderr, "Out of memory!\n");
exit(1);
}
return newgroup;
}
struct group *findtarget(struct group *group){
struct group *target;
if(group->valindex<VALCAP){
if(target=table[group->pos][group->valindex][group->when]){
return target;
}else{
return 0;
}
} else {
for(target=table[group->pos][group->valindex][group->when];target;target=target->nextbigval){
if(!mpz_cmp(target->value, group->value)){
return target;
}
}
return 0;
}
}
void tfree(struct group *group){
mpz_clear(group->value);
mpz_clear(group->count);
free(group);
}
void remfromvallist(struct group *group){
if(group->prevbyval)
group->prevbyval->nextbyval=group->nextbyval;
else{
vallist[group->valindex]=group->nextbyval;
}
if(group->nextbyval)
group->nextbyval->prevbyval=group->prevbyval;
}
void rem_from_main_list(struct group *group){
if(group->prevgroup)
group->prevgroup->nextgroup=group->nextgroup;
else{
grouplist=group->nextgroup;
}
if(group->nextgroup)
group->nextgroup->prevgroup=group->prevgroup;
}
void yankforpos(struct group *group){
if(group->valindex==VALCAP){
if(group->prevbigval)
group->prevbigval->nextbigval=group->nextbigval;
else{
table[group->pos][VALCAP][group->when]=group->nextbigval;
}
if(group->nextbigval)
group->nextbigval->prevbigval=group->prevbigval;
}else{
table[group->pos][group->valindex][group->when]=0;
}
}
void yankforval(struct group *group){
yankforpos(group);
remfromvallist(group);
}
void addtotable(struct group *group){
if(group->valindex<VALCAP){
table[group->pos][group->valindex][group->when]=group;
} else {
group->nextbigval=table[group->pos][group->valindex][group->when];
table[group->pos][group->valindex][group->when]=group;
group->prevbigval=0;
if (group->nextbigval){
group->nextbigval->prevbigval=group;
}
}
}
void addtovallist(struct group *group){
if(group->nextbyval=vallist[group->valindex])
group->nextbyval->prevbyval=group;
vallist[group->valindex]=group;
group->prevbyval=0;
}
void addtogroupliststart(struct group *group){
if(group->nextgroup=grouplist)
group->nextgroup->prevgroup=group;
grouplist=group;
group->prevgroup=0;
}
void destroygroup(struct group *group){
yankforval(group);
rem_from_main_list(group);
tfree(group);
}
void fixvalindex(struct group *group){
if(mpz_cmp_ui(group->value, VALCAP)>=0){
group->valindex=VALCAP;
}else{
group->valindex=mpz_get_ui(group->value);
}
}
void inforpos(struct group *group){
struct group *target;
if(target=findtarget(group)){
mpz_add(target->count, target->count, group->count);
remfromvallist(group);
rem_from_main_list(group);
tfree(group);
} else {
addtotable(group);
}
}
void inforval(struct group *group){
struct group *target;
fixvalindex(group);
if(target=findtarget(group)){
mpz_add(target->count, target->count, group->count);
rem_from_main_list(group);
tfree(group);
} else {
addtotable(group);
addtovallist(group);
}
}
void advancepos(struct group *group){
group->pos=prog[group->pos].next;
group->when=nexttick;
}
void step(struct group *group){
yankforpos(group);
advancepos(group);
inforpos(group);
}
void addtogrouplistbefore(struct group *new, struct group *old){
if (new->prevgroup=old->prevgroup){//not a typo
new->prevgroup->nextgroup=new;
} else {
grouplist=new;
}
old->prevgroup=new;
new->nextgroup=old;
}
struct group *splitandyankforval(struct group *group){
struct group *scion;
if(mpz_cmp_ui(group->count, 1)>0){
mpz_sub_ui(group->count, group->count, 1);
scion=talloc();
mpz_init(scion->value);
fixvalindex(scion);
mpz_init_set_ui(scion->count, 1);
scion->pos=group->pos;
scion->when=group->when;
addtogrouplistbefore(scion, group);
return scion;
} else {
yankforval(group);
return group;
}
}
void splitfirstthread(struct group *group){
struct group *first;
mpz_sub_ui(group->count, group->count, 1);
first=talloc();
mpz_init_set(first->value, group->value);
mpz_init_set_ui(first->count, 1);
first->pos=group->pos;
first->when=group->when;
addtogrouplistbefore(first, group);
}
void splitandinsertfirstthread(struct group *group){
struct group *first;
mpz_sub_ui(group->count, group->count, 1);
first=talloc();
mpz_init_set(first->value, group->value);
mpz_init_set_ui(first->count, 1);
first->pos=group->pos;
first->when=group->when;
addtogrouplistbefore(first, group);
advancepos(first);
inforval(first);
}
void killandcount(mpz_t result, mpz_t value){
struct group *this, *next;
if(mpz_cmp_ui(value, VALCAP)>=0){
for(this=vallist[VALCAP];this;this=next){
next=this->nextbyval;
if(!mpz_cmp(this->value, value)){
mpz_add(result, result, this->count);
destroygroup(this);
}
}
}else{
for(this=vallist[mpz_get_ui(value)];this;this=next){
next=this->nextbyval;
if(mpz_cmp(this->value, value)){
fprintf(stderr,"Another loony error!\n"), exit(1);
}
mpz_add(result, result, this->count);
destroygroup(this);
}
}
}
int main(int argc, char **argv){
for(argv++;*argv;argv++){
if(**argv=='-'){
for(option=*argv+1;*option;option++){
switch (*option){
case 'c': checkdata=1; break;
case 'd': debug=1; break;
case 's': suppress=1; break;
case 'n': nonewline=1; break;
default: fprintf(stderr,"Unknown option %c ignored.\n",*option); break;
}
}
} else if (!filename){
filename=*argv;
} else {
fprintf(stderr, "Too many filenames. %s will be ignored.\n", *argv);
}
}
if(!filename){
fprintf(stderr, "I need a filename.\n");
exit(1);
}
if (!(progfile=fopen(filename, "r"))){
fprintf(stderr, "can't open the file \"%s\".\n", filename);
exit(1);
}
stat(filename, &info);
source = calloc(info.st_size+1, 1);
if (fread(source, 1, info.st_size, progfile)!=info.st_size)
fprintf(stderr, "problem reading %s.\n",argv[1]), exit(1);
fclose(progfile);
for(x=0;x<info.st_size;){
switch(source[x]){
case '0': case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9':
source[y++]=source[x++];
break;
case '\n':
linect++;
source[y++]=source[x++];
break;
case 'r': case 'w': case '!': case '.': case 'R': case 'W':
case '$': case '~': case '+': case '*': case ':': case '=':
case '-':
source[y++]=source[x++];
instct++;
break;
case ' ': case '\t':
x++;
break;
case '#':
while(source[x]!='\n') x++;
break;
default:
fprintf(stderr, "Unexpected character \'%c\' at position %d!\n", source[x], x);
exit(1);
break;
}
}
prog = calloc(instct+1, sizeof(struct inst));
for(x=0;x<instct;x++){
mpz_init(prog[x].value);
}
linestarts=calloc(linect+2, sizeof(int));
w=2;
for(x=0;x<y;){
switch (source[x]){
case 'r': case 'w': case '!': case '.': case '$': case '~': case 'R': case 'W':
prog[z++].command=source[x++];
break;
case '+': case '*': case ':':
prog[z].command=source[x++];
if(!isdigit(source[x])){
fprintf(stderr, "Missing argument at position %d!\n", x);
exit(1);
}
sscanf(source+x, "%d%n", &(prog[z++].location), &charct);
x+=charct;
break;
case '=': case '-':
prog[z].command=source[x++];
if(!isdigit(source[x])){
fprintf(stderr, "Missing argument at position %d!\n", x);
exit(1);
}
gmp_sscanf(source+x, "%Zd%n", (prog[z++].value), &charct);
x+=charct;
break;
case '\n':
linestarts[w++]=z;
x++;
break;
}
}
if(z!=instct){
fprintf(stderr, "Weird error 1!\n");
exit(1);
}
prog[instct].command = ' ';
if(debug){
fprintf(stderr, "Array offsets of line starts:\nLine #:");
for(x=1;x<linect+2;x++){
fprintf(stderr, "%4d", x);
}
fprintf(stderr,"\npos = :");
for(x=1;x<linect+2;x++){
fprintf(stderr, "%4d", linestarts[x]);
}
fprintf(stderr,"\n\nParsed program:\npos cmd val locn next\n");
}
for(x=0;x<z;x++){
if(prog[x].location>linect+1){
fprintf(stderr, "Line %d does not exist!\n", prog[x].location);
exit(1);
}
prog[x].location=linestarts[prog[x].location];
if(prog[x].command==':'){
prog[x].next=prog[x].location;
}else{
prog[x].next=x+1;
}
if(debug){
gmp_fprintf(stderr, "%3d %c %10Zd %4d %4d\n", x, prog[x].command, prog[x].value,
prog[x].location, prog[x].next);
}
}
if (!(table=calloc((VALCAP+1)*(instct+1)*2, sizeof(struct group *))))
fprintf(stderr, "Couldn't allocate pointer table!\n"), exit(1);
mpz_init(tempval);
grouplist=talloc();
grouplist->pos=0;
grouplist->valindex=0;
mpz_init(grouplist->value);
mpz_init_set_ui(grouplist->count, 1);
grouplist->when=thistick;
table[0][0][thistick]=grouplist;
vallist[0]=grouplist;
while(grouplist){
if(debug){
fprintf(stderr, "\nTick %lu (thistick=%d, nexttick=%d):-------------------------------------------\n",
tickcount,thistick,nexttick);
}
for(curgroup=grouplist;curgroup;curgroup=nextgroup){
if(debug){
gmp_fprintf(stderr, "Group [pos%d;val%Zd;ct%Zd;when%d] executing %c command.\n", curgroup->pos,
curgroup->value, curgroup->count, curgroup->when, prog[curgroup->pos].command);
}
nextgroup=curgroup->nextgroup;
switch(prog[curgroup->pos].command){
case '=':
yankforval(curgroup);
mpz_set(curgroup->value, prog[curgroup->pos].value);
advancepos(curgroup);
inforval(curgroup);
break;
case '+':
if(agroup=table[prog[curgroup->pos].location][0][nexttick]){
mpz_add(agroup->count, agroup->count, curgroup->count);
}else{
agroup=talloc();
agroup->valindex=0;
mpz_init_set_ui(agroup->value, 0);
mpz_init_set(agroup->count, curgroup->count);
agroup->when=nexttick;
agroup->pos=prog[curgroup->pos].location;
addtogroupliststart(agroup);
inforval(agroup);
}
step(curgroup);
break;
case '*':
if(mpz_cmp_ui(curgroup->value,0)>0){
if(agroup=table[prog[curgroup->pos].location][0][nexttick]){
mpz_addmul(agroup->count, curgroup->value, curgroup->count);
}else{
agroup=talloc();
agroup->valindex=0;
mpz_init_set_ui(agroup->value, 0);
mpz_init_set(agroup->count, curgroup->count);
mpz_mul(agroup->count, agroup->count, curgroup->value);
agroup->when=nexttick;
agroup->pos=prog[curgroup->pos].location;
addtogroupliststart(agroup);
inforval(agroup);
}
}
step(curgroup);
break;
case '-':
yankforval(curgroup);
if(!mpz_cmp(prog[curgroup->pos].value,curgroup->value)){//
mpz_sub_ui(curgroup->count, curgroup->count, 1); //using as temporary
mpz_set(curgroup->value, curgroup->count);
mpz_set_ui(curgroup->count, 1);
} else {
mpz_set_ui(curgroup->value, 0);
}
killandcount(curgroup->value,prog[curgroup->pos].value);
if(mpz_cmp_ui(curgroup->count,1)){ //handle behavior of other threads in group:
fixvalindex(curgroup);
if(mpz_cmp_ui(prog[curgroup->pos].value, 0)){ //target nonzero.
if(curgroup->valindex){ //first thread value nonzero
if(mpz_cmp(prog[curgroup->pos].value,curgroup->value)){//first one doesn't get killed by later ones
splitandinsertfirstthread(curgroup);
mpz_set_ui(curgroup->value, 0); //later threads 0 because they kill nothing
} else {//first gets killed by later ones
if(curgroup->valindex==1){//special case where only last thread survives
mpz_set_ui(curgroup->count, 1);
}else {
mpz_sub_ui(curgroup->count, curgroup->count, 1);
mpz_set_ui(curgroup->value, 1); //second thread kills first thread
if(mpz_cmp_ui(curgroup->count, 1)){ //if third thread exists...
splitandinsertfirstthread(curgroup);
mpz_set_ui(curgroup->value, 0); //it and later ones don't kill second or anything.
}
}
}
} else {
//value zero, target nonzero, no threads in group will kill anything. No further action needed.
}
} else { //target is zero
switch(curgroup->valindex){
default:
splitandinsertfirstthread(curgroup);
case 0: //note fallthrough from default.
if(mpz_cmp_ui(curgroup->count, 1)){
if(mpz_odd_p(curgroup->count)){
mpz_set_ui(curgroup->value, 0);
splitandinsertfirstthread(curgroup);
}
mpz_divexact_ui (curgroup->count, curgroup->count, 2);
mpz_set_ui(curgroup->value, 1);
} else {
mpz_set_ui(curgroup->value, 0);
}
break;
case 1:
if(mpz_even_p(curgroup->count)){
mpz_set_ui(curgroup->value, 0);
splitandinsertfirstthread(curgroup);
mpz_set_ui(curgroup->value, 1);
}
mpz_add_ui(curgroup->count, curgroup->count, 1);
mpz_divexact_ui(curgroup->count, curgroup->count, 2);
break;
}
}
}
advancepos(curgroup);
inforval(curgroup);
nextgroup=curgroup->nextgroup;//in case we killed the other one!
break;
case 'r':
curgroup=splitandyankforval(curgroup);
gmp_scanf("%Zd",curgroup->value);
if(mpz_cmp_ui(curgroup->value, 0)<0){
mpz_set_ui(curgroup->value, 0);
fprintf(stderr, "Warning: negative numbers disallowed. Value set to 0.\n");
} else if (!suppress)
gmp_printf("Reading: %Zd\n",curgroup->value);
advancepos(curgroup);
inforval(curgroup);
nextgroup=curgroup->nextgroup;
break;
case 'w':
mpz_set(prog[curgroup->pos].value,curgroup->count);
while(mpz_cmp_ui(prog[curgroup->pos].value,0)>0){
gmp_printf(nonewline?"%Zd":"%Zd\n", curgroup->value);
mpz_sub_ui(prog[curgroup->pos].value, prog[curgroup->pos].value, 1);
}
fflush(stdout);
step(curgroup);
break;
case 'R':
curgroup=splitandyankforval(curgroup);
if((c=getchar())!=EOF){
mpz_set_ui(curgroup->value, c);
}
advancepos(curgroup);
inforval(curgroup);
nextgroup=curgroup->nextgroup;
break;
case 'W':
length = (mpz_sizeinbase (curgroup->value, 16)+1)/2;
if (!(buffer=calloc(length+1, sizeof(char)))){
fprintf(stderr, "Out of memory!\n");
exit(1);
}
bufptr=buffer+length;
mpz_set(tempval, curgroup->value);
if(mpz_cmp_ui(tempval, 0)>0){
while(mpz_cmp_ui(tempval, 0)>0){
*--bufptr=mpz_fdiv_q_ui (tempval, tempval, 256);
}
} else {
bufptr--;
}
if(bufptr != buffer){
fprintf(stderr, "Mismatch! bufptr=%p, buffer=%p.\n", bufptr, buffer);
exit(1);
}
mpz_set(prog[curgroup->pos].value,curgroup->count);
while(mpz_cmp_ui(prog[curgroup->pos].value,0)>0){
for(bufptr=buffer;bufptr<buffer+length;bufptr++){
putchar(*bufptr);
}
mpz_sub_ui(prog[curgroup->pos].value, prog[curgroup->pos].value, 1);
}
fflush(stdout);
free(buffer);
step(curgroup);
break;
case '!': case ' ':
destroygroup(curgroup);
break;
case '.': case ':':
step(curgroup);
break;
case '$':
if (mpz_cmp_ui(curgroup->value, 0)){
yankforval(curgroup);
mpz_set(tempval, curgroup->value);
if(mpz_fits_ulong_p(curgroup->count)){
tempbitcount=mpz_get_ui(curgroup->count);
mpz_mul_2exp(curgroup->value, curgroup->value, tempbitcount);
mpz_sub(curgroup->value, curgroup->value, tempval);
} else {
gmp_fprintf(stderr, "Value %Zd*2^%Zd-%Zd too big for material universe.\n",
curgroup->value, curgroup->count, curgroup->value);
exit(1);
}
for(agroup=grouplist;agroup;agroup=agroup->nextgroup){
if (agroup!=curgroup){
yankforval(agroup);
mpz_add(agroup->value, agroup->value, curgroup->value);
}
}
advancepos(curgroup);
while(mpz_cmp_ui(curgroup->count, 1)){
splitfirstthread(curgroup);
mpz_sub(curgroup->value, curgroup->value, tempval);
mpz_mul_2exp(tempval, tempval, 1);
}
for(agroup=grouplist;agroup;agroup=agroup->nextgroup){
inforval(agroup);
}
nextgroup=curgroup->nextgroup;
} else {
step(curgroup);
}
break;
case '~':
if(mpz_cmp_ui(curgroup->value,0)){
agroup=splitandyankforval(curgroup);
mpz_set(agroup->value, curgroup->value);
curgroup=agroup;
for(agroup=grouplist;agroup;agroup=agroup->nextgroup){
if (agroup!=curgroup){
yankforval(agroup);
if(mpz_cmp(agroup->value, curgroup->value)>=0){
mpz_sub(agroup->value, agroup->value, curgroup->value);
} else {
mpz_set_ui(agroup->value, 0);
}
}
}
advancepos(curgroup);
for(agroup=grouplist;agroup;agroup=agroup->nextgroup){
inforval(agroup);
}
nextgroup=curgroup->nextgroup;
} else {
step(curgroup);
}
break;
default:
fprintf(stderr,"Very weird error! Command %c\n",prog[curgroup->pos].command), exit(1);
break;
}
if(debug){
for(agroup=grouplist;agroup;agroup=agroup->nextgroup){
gmp_fprintf(stderr, "[pos%d;val%Zd;ct%Zd;when%d] ", agroup->pos, agroup->value,
agroup->count, agroup->when);
}
fprintf(stderr, "\n");
}
}
thistick=nexttick;
tickcount++;
if(checkdata){
check();
fflush(stderr);
}
}
exit(0);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment