/* 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);
}