/*
 * Range coder
 * Copyright (c) 2004 Michael Niedermayer <michaelni@gmx.at>
 *
 * This program 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.
 *
 * This program 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 this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
 */

/**
 * @file rangecoder.c
 * Range coder.
 * based upon
 *    "Range encoding: an algorithm for removing redundancy from a digitised
 *                     message.
 *     G. N. N. Martin                  Presented in March 1979 to the Video &
 *                                      Data Recording Conference,
 *     IBM UK Scientific Center         held in Southampton July 24-27 1979."
 */

#include "common.h"

void xlsc_init_range_encoder( xlsc_rac_t *c, uint8_t *buf, int buf_size )
{
    c->bytestream_start =
    c->bytestream = buf;
    c->bytestream_end = buf + buf_size;

    c->low = 0;
    c->range = 0xFF00;
    c->outstanding_count = 0;
    c->outstanding_byte = -1;
}

void xlsc_init_range_decoder( xlsc_rac_t *c, const uint8_t *buf, int buf_size )
{
    /* cast to avoid compiler warning */
    xlsc_init_range_encoder( c, (uint8_t *) buf, buf_size );

    c->low = (*c->bytestream++)<<8;
    c->low += *c->bytestream++;
}

void xlsc_build_rac_states( xlsc_rac_t *c, int factor, int max_p )
{
    const int64_t one = 1LL<<32;
    int64_t p;
    int last_p8, p8, i;

    memset( c->transition, 0, sizeof(c->transition) );

    last_p8 = 0;
    p = one/2;
    for( i=0; i<128; i++ )
    {
        p8 = (256*p + one/2) >> 32;
        if( p8 <= last_p8 ) p8 = last_p8+1;
        if( last_p8 && last_p8<256 && p8<= max_p )
            c->transition[1][last_p8] = p8;

        p += ((one-p)*factor + one/2) >> 32;
        last_p8 = p8;
    }

    for( i=256-max_p; i<= max_p; i++ )
    {
        if( c->transition[1][i] )
            continue;

        p = (i*one + 128) >> 8;
        p += ((one-p)*factor + one/2) >> 32;
        p8 = (256*p + one/2) >> 32;
        if(p8 <= i) p8 = i+1;
        if(p8 > max_p) p8 = max_p;
        c->transition[1][i] = p8;
    }

    for( i=1; i<255; i++ )
        c->transition[0][i] = 256-c->transition[1][256-i];
}

/**
 * @return the number of bytes written
 */
int xlsc_rac_terminate( xlsc_rac_t *c )
{
    c->range = 0xFF;
    c->low += 0xFF;
    renorm_encoder(c);
    c->range = 0xFF;
    renorm_encoder(c);

    assert(c->low   == 0);
    assert(c->range >= 0x100);

    return c->bytestream - c->bytestream_start;
}
