/* Delta.c -- Delta converter
   2021-02-09 : Igor Pavlov : Public domain */

#include "Precomp.h"

#include "Delta.h"

void Delta_Init(Byte *state)
{
	unsigned i;
	for (i = 0; i < DELTA_STATE_SIZE; i++)
		state[i] = 0;
}


void Delta_Encode(Byte *state, unsigned delta, Byte *data, SizeT size)
{
	Byte temp[DELTA_STATE_SIZE];

	if (size == 0)
		return;

	{
		unsigned i = 0;
		do
			temp[i] = state[i];
		while (++i != delta);
	}

	if (size <= delta)
	{
		unsigned i = 0, k;
		do
		{
			Byte b = *data;
			*data++ = (Byte)(b - temp[i]);
			temp[i] = b;
		}
		while (++i != size);

		k = 0;

		do
		{
			if (i == delta)
				i = 0;
			state[k] = temp[i++];
		}
		while (++k != delta);

		return;
	}

	{
		Byte *p = data + size - delta;
		{
			unsigned i = 0;
			do
				state[i] = *p++;
			while (++i != delta);
		}
		{
			const Byte *lim = data + delta;
			ptrdiff_t dif = -(ptrdiff_t)delta;

			if (((ptrdiff_t)size + dif) & 1)
			{
				--p;  *p = (Byte)(*p - p[dif]);
			}

			while (p != lim)
			{
				--p;  *p = (Byte)(*p - p[dif]);
				--p;  *p = (Byte)(*p - p[dif]);
			}

			dif = -dif;

			do
			{
				--p;  *p = (Byte)(*p - temp[--dif]);
			}
			while (dif != 0);
		}
	}
}


void Delta_Decode(Byte *state, unsigned delta, Byte *data, SizeT size)
{
	unsigned i;
	const Byte *lim;

	if (size == 0)
		return;

	i = 0;
	lim = data + size;

	if (size <= delta)
	{
		do
			*data = (Byte)(*data + state[i++]);
		while (++data != lim);

		for (; delta != i; state++, delta--)
			*state = state[i];
		data -= i;
	}
	else
	{
		/*
#define B(n) b ## n
#define I(n) Byte B(n) = state[n];
#define U(n) { B(n) = (Byte)((B(n)) + *data++); data[-1] = (B(n)); }
#define F(n) if (data != lim) { U(n) }

if (delta == 1)
{
I(0)
if ((lim - data) & 1) { U(0) }
while (data != lim) { U(0) U(0) }
data -= 1;
}
else if (delta == 2)
{
I(0) I(1)
lim -= 1; while (data < lim) { U(0) U(1) }
lim += 1; F(0)
data -= 2;
}
else if (delta == 3)
{
I(0) I(1) I(2)
lim -= 2; while (data < lim) { U(0) U(1) U(2) }
lim += 2; F(0) F(1)
data -= 3;
}
else if (delta == 4)
{
I(0) I(1) I(2) I(3)
lim -= 3; while (data < lim) { U(0) U(1) U(2) U(3) }
lim += 3; F(0) F(1) F(2)
data -= 4;
}
else
*/
{
	do
	{
		*data = (Byte)(*data + state[i++]);
		data++;
	}
	while (i != delta);

	{
		ptrdiff_t dif = -(ptrdiff_t)delta;
		do
			*data = (Byte)(*data + data[dif]);
		while (++data != lim);
		data += dif;
	}
}
}

do
*state++ = *data;
while (++data != lim);
}
