#include <stdio.h>

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

//struct Predictor {
  short cxt; 
  short p[256-1]; 

  int P() { return p[cxt-1]; }
  unsigned byte( void ) { unsigned c=cxt; cxt=1; return c; }
  void Predictor() { for( unsigned i=0; i<sizeof(p)/sizeof(p[0]); i++ ) p[i]=hSCALE; byte(); }

  void update( unsigned y ) {
    if( y ) p[cxt-1] += ((SCALE - p[cxt-1]) >> 5), cxt+=cxt+1;
    else    p[cxt-1] -= (         p[cxt-1]  >> 5), cxt+=cxt+0;
  }
//};

//struct Encoder {
//  unsigned  x1, x2;
  unsigned  x, x1, x2, tmp; 
//  Predictor predictor;

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

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

  void encode( FILE* g, unsigned y ) {
    unsigned xmid = x1 + ((x2-x1) >> SCALElog) * P();
    if( y ) x2=xmid; else x1=xmid+1;
    update( y );
    while( ((x1^x2)&0xFF000000)==0 ) putc( x2>>24, g ), x1<<=8, x2=(x2<<8)+0xFF;
  }
//};

//struct Decoder {
//  unsigned  x, x1, x2, tmp; 
//  Predictor predictor;

  void Decoder( FILE* f ) {
    x1 = 0; x2 = 0xFFFFFFFF;
    x = getc(f); (x<<=8)+=getc(f); (x<<=8)+=getc(f); (x<<=8)+=getc(f);
  }

  unsigned decode( FILE* f, unsigned y=0 ) {
    unsigned xmid = x1 + ((x2-x1) >> SCALElog) * P();
    if( x<=xmid ) y=1, x2=xmid; else y=0, x1=xmid+1;
    update(y);
    while( ((x1^x2)&0xFF000000)==0 ) x1<<=8, x2=(x2<<8)+0xFF, x=(x<<8)+getc(f);
    return y;
  }
//};

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

int main( int argc, char** argv ) {
  unsigned 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 );
    Predictor(); Encoder(); 
    for( i=0; i<f_len; i++ ) {
      byte();
      c = getc(f);
      encode( g, c&0x80 ); encode( g, c&0x40 ); encode( g, c&0x20 ); encode( g, c&0x10 );
      encode( g, c&0x08 ); encode( g, c&0x04 ); encode( g, c&0x02 ); encode( g, c&0x01 );
    } flush(g);
  } else {
    fread( &f_len, 1,4, f );
    Predictor(); Decoder(f);
    for( i=0; i<f_len; i++ ) {
      decode(f); decode(f); decode(f); decode(f); decode(f); decode(f); decode(f); decode(f); 
      putc( byte(), g );
    }
  }
  fclose( f );
  fclose( g );
  return 0;
}
