/* $Id: arith.cpp,v 1.3 2001/06/03 21:07:36 cfreeze Exp $ */

/*
 *  Compface - 48x48x1 image compression and decompression
 *
 *  Copyright (c) James Ashton - Sydney University - June 1990.
 *
 *  Written 11th November 1989.
 *
 *  Permission is given to distribute these sources, as long as the
 *  copyright messages are not removed, and no monies are exchanged.
 *
 *  No responsibility is taken for any errors on inaccuracies inherent
 *  either to the comments or the code of this program, but if reported
 *  to me, then an attempt will be made to fix them.
 */


#include <config.h>
#include "compface.h"
#include <stdio.h>

void RevPush(Prob * p) {
    if(NumProbs >= PIXELS * 2 - 1)
        longjmp(comp_env, ERR_INTERNAL);
    ProbBuf[NumProbs++] = p;
}

void BigPush(Prob * p) {
    static WORD tmp;

    BigDiv(p->p_range, &tmp);
    BigMul(0);
    BigAdd(tmp + p->p_offset);
}

int BigPop(register Prob * p) {
    static WORD tmp;
    register int i;

    BigDiv(0, &tmp);
    i = 0;
    while((tmp < p->p_offset) || (tmp >= p->p_range + p->p_offset)) {
        p++;
        i++;
    }
    BigMul(p->p_range);
    BigAdd(tmp - p->p_offset);
    return i;
}

#ifdef DEBUG
/* Print a BigInt in HexaDecimal
 */
void BigPrint() {
    register int i, c, count;
    register WORD *w;

    count = 0;
    w = B.b_word + (i = B.b_words);
    while(i--) {
        w--;
        c = *((*w >> 4) + HexDigits);
        putc(c, stderr);
        c = *((*w & 0xf) + HexDigits);
        putc(c, stderr);
        if(++count >= 36) {
            putc('\\', stderr);
            putc('\n', stderr);
            count = 0;
        }
    }
    putc('\n', stderr);
}
#endif

/* Divide B by a storing the result in B and the remainder in the word
 * pointer to by r
 */
void BigDiv(register WORD a, register WORD * r) {
    register int i;
    register WORD *w;
    register COMP c, d;

    a &= WORDMASK;
    if((a == 1) || (B.b_words == 0)) {
        *r = 0;
        return;
    }
    if(a == 0) {       /* treat this as a == WORDCARRY */
        /* and just shift everything right a WORD */
        i = --B.b_words;
        w = B.b_word;
        *r = *w;
        while(i--) {
            *w = *(w + 1);
            w++;
        }
        *w = 0;
        return;
    }
    w = B.b_word + (i = B.b_words);
    c = 0;
    while(i--) {
        c <<= BITSPERWORD;
        c += (COMP) * --w;
        d = c / (COMP) a;
        c = c % (COMP) a;
        *w = (WORD) (d & WORDMASK);
    }
    *r = c;
    if(B.b_word[B.b_words - 1] == 0)
        B.b_words--;
}

/* Multiply a by B storing the result in B
 */
void BigMul(register WORD a) {
    register int i;
    register WORD *w;
    register COMP c;

    a &= WORDMASK;
    if((a == 1) || (B.b_words == 0))
        return;
    if(a == 0) {       /* treat this as a == WORDCARRY */
        /* and just shift everything left a WORD */
        if((i = B.b_words++) >= MAXWORDS - 1)
            longjmp(comp_env, ERR_INTERNAL);
        w = B.b_word + i;
        while(i--) {
            *w = *(w - 1);
            w--;
        }
        *w = 0;
        return;
    }
    i = B.b_words;
    w = B.b_word;
    c = 0;
    while(i--) {
        c += (COMP) * w * (COMP) a;
        *(w++) = (WORD) (c & WORDMASK);
        c >>= BITSPERWORD;
    }
    if(c) {
        if(B.b_words++ >= MAXWORDS)
            longjmp(comp_env, ERR_INTERNAL);
        *w = (COMP) (c & WORDMASK);
    }
}

#if 0
/* Subtract a from B storing the result in B
 */
void BigSub(WORD a) {
    register int i;
    register WORD *w;
    register COMP c;

    a &= WORDMASK;
    if(a == 0)
        return;
    i = 1;
    w = B.b_word;
    c = (COMP) * w - (COMP) a;
    *w = (WORD) (c & WORDMASK);
    while(c & WORDCARRY) {
        if(i >= B.b_words)
            longjmp(comp_env, ERR_INTERNAL);
        c = (COMP) * ++w - 1;
        *w = (WORD) (c & WORDMASK);
        i++;
    }
    if((i == B.b_words) && (*w == 0) && (i > 0))
        B.b_words--;
}
#endif

/* Add to a to B storing the result in B
 */
void BigAdd(WORD a) {
    register int i;
    register WORD *w;
    register COMP c;

    a &= WORDMASK;
    if(a == 0)
        return;
    i = 0;
    w = B.b_word;
    c = a;
    while((i < B.b_words) && c) {
        c += (COMP) * w;
        *w++ = (WORD) (c & WORDMASK);
        c >>= BITSPERWORD;
        i++;
    }
    if((i == B.b_words) && c) {
        if(B.b_words++ >= MAXWORDS)
            longjmp(comp_env, ERR_INTERNAL);
        *w = (COMP) (c & WORDMASK);
    }
}

void BigClear() {
    B.b_words = 0;
}
