#include <stdio.h>

enum { SCALElog=12, SCALE=1<<SCALElog, hSCALE=SCALE/2 };

struct Encoder {
  int  x1, x2;

  Encoder( void ) { x1 = 0; x2 = 0x7FFFFFFF; }

  void flush( FILE* g ) { putc( x2>>23, g ); }

  void encode( FILE* g, int y ) {
    int xmid = x1 + ((x2-x1) >> SCALElog) * hSCALE;
    if( y ) x2=xmid; else x1=xmid+1;
    while( ((x1^x2)&0x7F800000)==0 ) {
      putc( x2>>23, g );
      x1 = (x1<<8) & 0x7FFFFFFF;
      x2 = ((x2<<8)+0xFF) & 0x7FFFFFFF;
    }
  }
};

struct Decoder {
  int  x, x1, x2, ctx, tmp; 

  Decoder( FILE* f ) {
    x1 = 0; x2 = 0x7FFFFFFF;
    x = getc(f); (x<<=8)+=getc(f); (x<<=8)+=getc(f); (x<<=8)+=getc(f);
    tmp = x&1; x=(x>>1)&0x7FFFFFFF;
    ctx = 0;
  }

  int decode( FILE* f, int y=0 ) {
    int xmid = x1 + ((x2-x1) >> SCALElog) * hSCALE;
    if( x<=xmid ) y=1, x2=xmid; else y=0, x1=xmid+1;
    while( ((x1^x2)&0x7F800000)==0 ) {
      x1 = (x1<<8) & 0x7FFFFFFF;
      x2 = ((x2<<8)+0xFF) & 0x7FFFFFFF;
      tmp = (tmp<<8) + getc(f);
      x  = ((x <<8)+(tmp>>1)) & 0x7FFFFFFF;
      tmp &= 1;
    }
    ctx += ctx + y;
    return y;
  }
};

int flen( FILE* f ) {
  fseek( f, 0, SEEK_END );
  int len = ftell(f);
  fseek( f, 0, SEEK_SET );
  return len;
}

int main( int argc, char** argv ) {
  int i,c,f_len = 0;

  if( argc<4 ) return 1;

  FILE* f = fopen( argv[2], "rb" ); if( f==0 ) return 2;
  FILE* g = fopen( argv[3], "wb" ); if( g==0 ) return 3;
  if( argv[1][0]=='c' ) {
    f_len = flen( f );
    fwrite( &f_len, 1,4, g );
    Encoder e;
    for( i=0; i<f_len; i++ ) {
//      e.predictor.byte();
      c = getc(f);
      e.encode( g, c&0x80 ); e.encode( g, c&0x40 ); e.encode( g, c&0x20 ); e.encode( g, c&0x10 );
      e.encode( g, c&0x08 ); e.encode( g, c&0x04 ); e.encode( g, c&0x02 ); e.encode( g, c&0x01 );
    } e.flush(g);
  } else {
    fread( &f_len, 1,4, f );
    Decoder e(f);
    for( i=0; i<f_len; i++ ) {
      e.decode(f); e.decode(f); e.decode(f); e.decode(f); e.decode(f); e.decode(f); e.decode(f); e.decode(f); 
      putc( e.ctx, g );
    }
  }
  fclose( f );
  fclose( g );
  return 0;
}
