/* simple.c -- the annotated simple example program for the UCL library

   This file is part of the UCL real-time data compression library.

   Copyright (C) 1996-2000 Markus Franz Xaver Johannes Oberhumer

   The UCL library is free software; you can redistribute it and/or
   modify it under the terms of the GNU General Public License as
   published by the Free Software Foundation; either version 2 of
   the License, or (at your option) any later version.

   The UCL library is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with the UCL library; see the file COPYING.
   If not, write to the Free Software Foundation, Inc.,
   59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.

   Markus F.X.J. Oberhumer
   markus.oberhumer@jk.uni-linz.ac.at
 */


/*************************************************************************
 This program shows the basic usage of the UCL library.
 We will compress a block of data and decompress again.
**************************************************************************/

#include <stdio.h>

#include <ucl/ucl.h>

#include "../src/ucl_conf.h"
#include "../src/getbit.h"


int gs(int i)
{
  static int v0=0, v1=0;

  if (i==-1)
    fprintf(stderr, "\nSTATS: (0,1)=(%d,%d)=(%f,%f) (jr:%d/i%d, jp:%d)\n",
	    v0, v1, (double)v0/(double)(v0+v1), (double)v1/(double)(v0+v1),
	    v0*7+v1*12, v1*7+v0*12, (v0+v1)*10);
  else
    {
      if (i==0)
	v0++;
      else
	v1++;
    }

  return i;
}


void adam_decompress(ucl_byte *src,
		     ucl_uint  src_len,
		     ucl_byte *dst        /*, ucl_uint *dst_len*/ )
{
  /*
#define getbit(bb) \
  (bb*=2,bb&0xff ? (bb>>8)&1 : ((bb=(src[ilen++]*2)|1)>>8)&1)
  */

  /*
#define getbit(bb) \
  (bb&0x7f ? \
   ((bb*=2)>>8)&1 : \
   ((bb=(src[ilen++]*2)|1)>>8)&1)
  */

#define getbit(bb) \
  ( ( ((bb) = (bb)&0x7f ? (bb)*2 : (src[ilen++]*2)|1) >> 8) & 1)

  ucl_uint bb = 0;
  ucl_uint ilen = 0, olen = 0, last_m_off = 1;
        
  for (;;)
    {
      ucl_uint m_off;
      ucl_uint m_len;

      while (/*gs*/(getbit(bb)) != 0)
	{
	  /* note: short runs, ~0-9 bytes */
	  dst[olen] = src[ilen];
	  ilen += 1;
	  olen += 1;
	}

      m_off = 1;

      do
	{
	  m_off *= 2;

	  if (m_off > 0xFFFF) /* if the shift just carried over 16 bits...! */
	    {
	      fprintf(stderr, "TERM%x ", m_off);
	      goto done;
	    }

	  m_off |= (getbit(bb));
	}
      while ((getbit(bb)) == 0);


      if ((m_off == 2))
	{
	  m_off = last_m_off;
	}
      else
	{
	  m_off -= 3;
	  m_off *= 256;
	  m_off |= src[ilen];

	  /*
            if (m_off == 0xffffffff)
	    {
	    fprintf(stderr, "TERMINATOR ");
	    break;
	    }
	  */

	  ilen += 1;
	  m_off += 1;
	  last_m_off = m_off;
	}

      m_len = (getbit(bb));
      m_len *= 2;
      m_len |= (getbit(bb));
      if ((m_len == 0))
	{
	  m_len = 1;

	  do
	    {
	      m_len *= 2;
	      m_len |= (getbit(bb));
	    }
	  while (!(getbit(bb)));

	  m_len += 2;
	}

      if ((m_off > 0x0d00))
	{
	  m_len += 1;
	}

      {
	ucl_byte *m_pos;

	m_pos = dst;
	m_pos += olen;
	m_pos -= m_off;

	dst[olen] = *m_pos;
	m_pos += 1;
	olen += 1;

	/*fprintf(stderr, "%d'", m_len); */
	/*gs(m_len == 1);*/
	  
	do
	  {
	    dst[olen] = *m_pos;
	    m_pos += 1;
	    olen += 1;
	    m_len -= 1;
	  }
	while (m_len > 0);
      }
    }
  /* *dst_len = olen; */

 done:
  /*
    gs(-1);
  */
}



/* We want to compress the data block at `in' with length `IN_LEN' to
 * the block at `out'. Because the input block may be incompressible,
 * we must provide a little more output space in case that compression
 * is not possible.
 */

#if 0

#ifndef IN_LEN
/*#define IN_LEN      (49920L) */
#define IN_LEN      (6912L)
/*#define IN_LEN      (128*1024L) */
#endif
#define OUT_LEN     (IN_LEN + IN_LEN / 8 + 256)

#endif

/*************************************************************************

**************************************************************************/

#include "lutil.h"

int main(int argc, char *argv[])
{
    int r;
    ucl_byte *in;
    ucl_byte *out;
    ucl_uint in_len;
    ucl_uint out_len;
    ucl_uint IN_LEN;
    ucl_uint OUT_LEN;
    ucl_uint new_len;
    int level = 10;                  /* compression level (1-10) */

#if defined(__EMX__)
    _response(&argc,&argv);
    _wildcard(&argc,&argv);
#endif

    if (argc < 0 && argv == NULL)   /* avoid warning about unused args */
        return 0;

    printf("\nUCL real-time data compression library (v%s, %s).\n",
            ucl_version_string(), ucl_version_date());
    printf("Copyright (C) 1996,1997,1998,1999,2000 Markus Franz Xaver Johannes Oberhumer\n\n");

/*
 * Step 1: initialize the UCL library
 */
    if (ucl_init() != UCL_E_OK)
    {
        printf("ucl_init() failed !!!\n");
        return 4;
    }

    if (argc != 2)
      {
        printf("need single filename parameter.\n");
        return 7;
      }

/*
 * Step 2: allocate blocks
 */
    {
      struct stat stat_buf;

      if (stat(argv[1], &stat_buf))
	{
	  printf("file not found: %s\n", argv[1]);
	  return 8;
	}
      
      IN_LEN = stat_buf.st_size;
      OUT_LEN = (IN_LEN + IN_LEN / 8 + 256);
    }

#define MAXSLOP 8192

    in = ucl_malloc(IN_LEN+MAXSLOP);
    out = ucl_malloc(OUT_LEN+MAXSLOP);
    if (in == NULL || out == NULL)
    {
        printf("out of memory\n");
        return 3;
    }

/*
 * Step 3: prepare the input block that will get compressed.
 *         We just fill it with zeros in this example program,
 *         but you would use your real-world data here.
 */
    in_len = IN_LEN;
    ucl_memset(in,'!',in_len);

    {
      FILE *fp;

      fp = fopen(argv[1], "rb");
      fread(in, 1, in_len, fp);
      fclose(fp);
    }

/*
 * Step 4: compress from `in' to `out' with UCL NRV2B
 */
    r = ucl_nrv2b_99_compress(in,in_len,out,&out_len,NULL,level,NULL,NULL);
    if (r == UCL_E_OK)
      printf("compressed %lu bytes into %lu bytes\n",
	     (long) in_len, (long) out_len);
    else
      {
        /* this should NEVER happen */
        printf("internal error - compression failed: %d\n", r);
        return 2;
      }
    /* check for an incompressible block */
    if (out_len >= in_len)
      {
        printf("This block contains uncompressible data. (may crash now)\n");
	/*        return 0; */
      }
    
    {
      FILE *fp;
      
      fp = fopen("COMPR", "wb");
      fwrite(out, 1, out_len, fp);
      fclose(fp);
    }

/*
 * Step 5: decompress again, now going from `out' to `in'
 */
    {
      int slop;

      ucl_memset(in,'?',in_len);

      for (slop = 0; slop <= MAXSLOP; slop++)
	{
	  int offset = slop+(in_len - out_len);

	  memcpy(&in[offset], out, out_len);
	  new_len = in_len;
	  r = ucl_nrv2b_test_overlap_8(in,offset,out_len,&new_len,NULL);

	  /* fprintf(stderr, "(%d,%c,%d/%d/%d)", in[offset], in[0],
	     offset, out_len, new_len); */

	  if (r == UCL_E_OK && new_len == in_len)
	    {
	      printf("decompressed %lu bytes back into %lu bytes,\n"
		     "minimal nonoverlap slop is %d\n",
		     (long) out_len, (long) in_len, slop);
#if 0
	      ucl_nrv2b_decompress_8(&in[offset],out_len,in,&new_len,NULL);
#else
	      adam_decompress(out,out_len,in);
#endif

	      break;
	    }

	  if (slop == MAXSLOP)
	    {
	      printf("no luck with in-place decompression at any slop-value\n"
		     " up to %d (error %d)\n", MAXSLOP, r);
	      return 1;
	    }
	}
    }

    {
      FILE *fp;
      char *val = malloc(in_len);
      ucl_memset(val,'!',in_len);

      fp = fopen(argv[1], "rb");
      fread(val, 1, in_len, fp);
      fclose(fp);

      if (memcmp(val, in, in_len))
	printf("UGH, bad verification.\n\007");
    }

    {
      FILE *fp;

      fp = fopen("CYC", "wb");
      fwrite(in, 1, in_len, fp);
      fclose(fp);
    }

    ucl_free(out);
    ucl_free(in);
    printf("Simple compression test passed.\n");
    return 0;
}

/*
vi:ts=4:et
*/

