import core.stdc.stdio;
import core.stdc.stdlib;
import core.stdc.string;
import core.stdc.time;
import std.string: toStringz;
struct Predictor
{
int cxt = 1;
int ct[512][2];
pure int p() const
{
return 4096 * (ct[cxt][1] + 1) / (ct[cxt][0] + ct[cxt][1] + 2);
}
void update(int y)
{
if (++ct[cxt][y] > 65534)
{
ct[cxt][0] >>= 1;
ct[cxt][1] >>= 1;
}
if ((cxt += cxt + y) >= 512)
cxt = 1;
}
}
enum Mode
{
COMPRESS = 0,
DECOMPRESS
}
struct Encoder
{
private:
Predictor predictor;
Mode mode = void;
FILE* archive = void;
uint x1, x2 = 0xffffffff;
uint x;
public:
this(Mode m, FILE* f)
{
mode = m;
archive = f;
if (mode == Mode.DECOMPRESS)
{
foreach (i; 0 .. 4)
{
int c = getc(archive);
if (c == EOF) c = '\0';
x = (x << 8) + (c&0xff);
}
}
}
void encode(int y)
{
const uint xmid = x1 + ((x2-x1) >> 12) * predictor.p();
assert(xmid >= x1 && xmid < x2);
if (y) x2 = xmid;
else x1 = xmid + 1;
predictor.update(y);
while (((x1 ^ x2) & 0xff000000) == 0)
{
putc(x2 >> 24, archive);
x1 <<= 8;
x2 = (x2 << 8) + 255;
}
}
int decode()
{
const uint xmid = x1 + ((x2-x1) >> 12) * predictor.p();
assert(xmid >= x1 && xmid < x2);
int y = 0;
if (x <= xmid)
{
y = 1;
x2 = xmid;
}
else x1 = xmid + 1;
predictor.update(y);
while (((x1 ^ x2)&0xff000000) == 0)
{
x1 <<= 8;
x2 = (x2 << 8) + 255;
int c = getc(archive);
if (c == EOF) c = 0;
x = (x << 8) + c;
}
return y;
}
void flush()
{
if (mode == Mode.COMPRESS)
{
while (((x1 ^ x2)&0xff000000) == 0)
{
putc(x2 >> 24, archive);
x1 <<= 8;
x2 = (x2 << 8) + 255;
}
putc(x2 >> 24, archive);
}
}
}
int main(string[] args)
{
if (args.length != 4 || (args[1][0] != 'c' && args[1][0] != 'd'))
{
printf(
"To compress: fpaq0 c input output\n"
"To decompress: fpaq0 d input output\n"
);
return 1;
}
clock_t start = clock();
FILE* fin = fopen(args[2].toStringz(), "rb");
if (!fin)
{
perror(args[2].toStringz());
return 1;
}
FILE *fout = fopen(args[3].toStringz(), "wb");
if (!fout)
{
perror(args[3].toStringz());
return 1;
}
if (args[1][0] == 'c')
{
int c;
Encoder e = Encoder(Mode.COMPRESS, fout);
while ((c = getc(fin)) != EOF)
{
e.encode(0);
for (int i = 7; i >= 0; --i)
e.encode((c >> i)&1);
}
e.encode(1);
e.flush();
}
else
{
Encoder e = Encoder(Mode.DECOMPRESS, fin);
while (!e.decode())
{
int c = 1;
while (c < 256)
c += c + e.decode();
putc(c - 256, fout);
}
}
printf(
"%s (%ld bytes) -> %s (%ld bytes) in %1.2f s.\n",
args[2].toStringz(), ftell(fin), args[3].toStringz(), ftell(fout),
(cast(double)clock() - start) / CLOCKS_PER_SEC
);
return 0;
}