/*************************************************************************
* Name:        bcltest.c
* Author:      Marcus Geelnard
* Description: Basic Compression Library tester.
* $Id: bcltest.c,v 1.1 2004/12/07 21:24:27 marcus256 Exp $
*
* This program can be used to test any of the compression algorithms in
* the Basic Compression Library. The program reads a file, which is
* compressed and decompressed and compared in order to detect any errors
* in the compression and/or decompression routines.
*
*-------------------------------------------------------------------------
* Copyright (c) 2003-2004 Marcus Geelnard
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
*    claim that you wrote the original software. If you use this software
*    in a product, an acknowledgment in the product documentation would
*    be appreciated but is not required.
*
* 2. Altered source versions must be plainly marked as such, and must not
*    be misrepresented as being the original software.
*
* 3. This notice may not be removed or altered from any source
*    distribution.
*
* Marcus Geelnard
* marcus.geelnard at home.se
*************************************************************************/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "rle.h"
#include "huffman.h"
#include "rice.h"
#include "lz.h"



/*************************************************************************
* GetFileSize()
*************************************************************************/

long GetFileSize( FILE *f )
{
    long pos, size;

    pos = ftell( f );
    fseek( f, 0, SEEK_END );
    size = ftell( f );
    fseek( f, pos, SEEK_SET );

    return size;
}


/*************************************************************************
* TestFile()
*************************************************************************/

int TestFile( char *name, int algo )
{
    unsigned int  insize, outsize, bufsize, *work, k, err_count;
    unsigned char *in, *out, *buf;
    FILE          *f;

    printf( "Testing %s...", name );

    /* Open input file */
    f = fopen( name, "rb" );
    if( !f )
    {
	    printf( "unable to open!\n" );
	    return 0;
    }

    /* Get input size */
    insize = GetFileSize( f );
    if( insize < 1 )
    {
	    printf( "empty file!\n" );
	    fclose( f );
	    return 0;
    }

    /* Worst case output buffer size */
    bufsize = (insize*104+50)/100 + 384;

    /* Allocate memory */
    in = (unsigned char *) malloc( insize + 2*bufsize );
    if( !in )
    {
	    printf( "out of memory!\n" );
	    fclose( f );
	    return 0;
    }

    /* Pointers to compression buffer and output memory */
    buf = &in[ insize ];
    out = &buf[ bufsize ];

    /* Read and close input file */
    fread( in, 1, insize, f );
    fclose( f );

    /* Compress and decompress */
    switch( algo )
    {
        case 1:
            outsize = RLE_Compress( in, buf, insize );
            RLE_Uncompress( buf, out, outsize );
            break;
        case 2:
            outsize = Huffman_Compress( in, buf, insize );
            Huffman_Uncompress( buf, out, outsize, insize );
            break;
        case 3:
            outsize = Rice_Compress( in, buf, insize, RICE_FMT_UINT8 );
            Rice_Uncompress( buf, out, outsize, insize, RICE_FMT_UINT8 );
            break;
        case 4:
            outsize = Rice_Compress( in, buf, insize, RICE_FMT_UINT16 );
            Rice_Uncompress( buf, out, outsize, insize, RICE_FMT_UINT16 );
            break;
        case 5:
            outsize = Rice_Compress( in, buf, insize, RICE_FMT_UINT32 );
            Rice_Uncompress( buf, out, outsize, insize, RICE_FMT_UINT32 );
            break;
        case 6:
            outsize = Rice_Compress( in, buf, insize, RICE_FMT_INT8 );
            Rice_Uncompress( buf, out, outsize, insize, RICE_FMT_INT8 );
            break;
        case 7:
            outsize = Rice_Compress( in, buf, insize, RICE_FMT_INT16 );
            Rice_Uncompress( buf, out, outsize, insize, RICE_FMT_INT16 );
            break;
        case 8:
            outsize = Rice_Compress( in, buf, insize, RICE_FMT_INT32 );
            Rice_Uncompress( buf, out, outsize, insize, RICE_FMT_INT32 );
            break;
        case 9:
            work = malloc( sizeof(unsigned int) * (65536+insize) );
            if( work )
            {
                outsize = LZ_CompressFast( in, buf, insize, work );
                free( work );
            }
            else
            {
                outsize = LZ_Compress( in, buf, insize );
            }
            LZ_Uncompress( buf, out, outsize );
            break;
        default:
            /* Should never happen... */
            outsize = 0;
    }

    /* Show compression result */
    printf( "\n  Compression: %d/%d bytes (%.1f%%)", outsize, insize,
            100*(float)outsize/(float)insize );

    /* Compare input / output data */
    err_count = 0;
    for( k = 0; k < insize; ++ k )
    {
	    if( in[ k ] != out[ k ] )
	    {
		    if( err_count == 0 ) printf( "\n" );
		    if( err_count == 30 ) printf( "    ...\n" );
		    else if( err_count < 30 )
		    {
		        printf( "    %d: %d != %d\n", k, out[ k ], in[ k ] );
	    	}
		    ++ err_count;
	    }
    }
    if( err_count == 0 )
      	printf( " - OK!\n" );
    else
    {
      	printf( "    *******************************\n" );
      	printf( "    ERROR: %d faulty bytes\n", err_count );
      	printf( "    *******************************\n" );
    }

    /* Free all memory */
    free( in );

    return (err_count == 0);
}


/*************************************************************************
* Help()
*************************************************************************/

void Help( char *prgname )
{
    printf( "Usage: %s algo file\n\n", prgname );
    printf( "algo can be one of the following:\n" );
    printf( "  rle     RLE Compression\n" );
    printf( "  lz      LZ77 Compression\n" );
    printf( "  huff    Huffman compression\n" );
    printf( "  rice8   Rice compresison of 8-bit data\n" );
    printf( "  rice16  Rice compresison of 16-bit data\n" );
    printf( "  rice32  Rice compresison of 32-bit data\n" );
    printf( "  rice8s  Rice compresison of 8-bit signed data\n" );
    printf( "  rice16s Rice compresison of 16-bit signed data\n" );
    printf( "  rice32s Rice compresison of 32-bit signed data\n\n" );
    printf( "file is the name of a file to be tested.\n" );
}


/*************************************************************************
* main()
*************************************************************************/

int main( int argc, char **argv )
{
    int     algo;
    char    *filename;

    /* Check arguments */
    if( argc != 3 )
    {
        Help( argv[ 0 ] );
        return 0;
    }

    /* Get algo */
    algo = 0;
    if( strcmp( argv[1], "rle" ) == 0 )     algo = 1;
    if( strcmp( argv[1], "huff" ) == 0 )    algo = 2;
    if( strcmp( argv[1], "rice8" ) == 0 )   algo = 3;
    if( strcmp( argv[1], "rice16" ) == 0 )  algo = 4;
    if( strcmp( argv[1], "rice32" ) == 0 )  algo = 5;
    if( strcmp( argv[1], "rice8s" ) == 0 )  algo = 6;
    if( strcmp( argv[1], "rice16s" ) == 0 ) algo = 7;
    if( strcmp( argv[1], "rice32s" ) == 0 ) algo = 8;
    if( strcmp( argv[1], "lz" ) == 0 )      algo = 9;
    if( !algo )
    {
        Help( argv[ 0 ] );
        return 0;
    }

    /* Get file name */
    filename = argv[2];

    /* Test file */
    TestFile( filename, algo );

    return 0;
}
