/*
 *  rpf.c:      Conversion of float to reduced precision format values
 *
 *  Written by:     Stefan Frank
 *          Richard Krampfl
 *          Ullrich Hafner
 *
 *  This file is part of FIASCO («F»ractal «I»mage «A»nd «S»equence «CO»dec)
 *  Copyright (C) 1994-2000 Ullrich Hafner <hafner@bigfoot.de>
 */

/*
 *  $Date: 2000/06/14 20:49:37 $
 *  $Author: hafner $
 *  $Revision: 5.1 $
 *  $State: Exp $
 */

#include "pm_config.h"
#include "config.h"
#include "mallocvar.h"

#include "types.h"
#include "macros.h"
#include "error.h"

#include "misc.h"
#include "rpf.h"

int const RPF_ZERO = -1;

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

                   private code

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


typedef struct {
    double fraction;
    int    exponent;
}  FracExp;



static FracExp
fracExpFromDouble(double const x) {

    FracExp retval;

    retval.fraction = frexp(x, &retval.exponent);

    return retval;
}



int
rtob (real_t        const f,
      const rpf_t * const rpfP)
/*
 *  Convert real number 'f' into fixed point format.
 *  The real number in [-'range'; +'range'] is scaled to [-1 ; +1].
 *  Sign and the first 'precision' - 1 bits of the mantissa are
 *  packed into one integer.
 *
 *  Return value:
 *  real value in reduced precision format
 */
{
    /*
     *  Extract mantissa (23 Bits), exponent (8 Bits) and sign (1 Bit)
     */

    double const normalized = f / rpfP->range;
        /* 'f' scaled to [-1,+1] */
    FracExp const fracExp = fracExpFromDouble(normalized);
    unsigned int const signedMantissa =
        (unsigned int) (fracExp.fraction * (1<<23));

    unsigned int mantissa;
    unsigned int sign;  /* 0 for positive; 1 for negative */

    if (signedMantissa < 0) {
        mantissa = -signedMantissa;
        sign = 1;
    } else {
        mantissa = +signedMantissa;
        sign = 0;
    }

    /*
     *  Generate reduced precision mantissa.
     */
    if (fracExp.exponent > 0)
        mantissa <<= fracExp.exponent;
    else
        mantissa >>= -fracExp.exponent;

    mantissa >>= (23 - rpfP->mantissa_bits - 1);

    mantissa +=  1;          /* Round last bit. */
    mantissa >>= 1;

    if (mantissa == 0)           /* close to zero */
        return RPF_ZERO;
    else if (mantissa >= (1U << rpfP->mantissa_bits)) /* overflow */
        return sign;
    else
        return ((mantissa & ((1U << rpfP->mantissa_bits) - 1)) << 1) | sign;
}



float
btor (int           const binary,
      const rpf_t * const rpfP)
/*
 *  Convert value 'binary' in reduced precision format to a real value.
 *  For more information refer to function rtob() above.
 *
 *  Return value:
 *  converted value
 */
{
    unsigned int mantissa;
    float sign;
    float f;

    if (binary == RPF_ZERO)
        return 0;

    if (binary < 0 || binary >= 1 << (rpfP->mantissa_bits + 1))
        error ("Reduced precision format: value %d out of range.", binary);

    /*
     *  Restore IEEE float format:
     *  mantissa (23 Bits), exponent (8 Bits) and sign (1 Bit)
     */

    sign       = (binary & 0x1) == 0 ? 1.0 : -1.0;
    mantissa   = (binary & ((0x1 << (rpfP->mantissa_bits + 1)) - 1)) >> 1;
    mantissa <<= (23 - rpfP->mantissa_bits);

    if (mantissa == 0)
        f = sign;
    else
        f =  sign * (float) mantissa / 8388608;

    return f * rpfP->range;       /* expand [ -1 ; +1 ] to
                                     [ -range ; +range ] */
}




rpf_t *
alloc_rpf (unsigned           const mantissa,
           fiasco_rpf_range_e const range)
/*
 *  Reduced precision format constructor.
 *  Allocate memory for the rpf_t structure.
 *  Number of mantissa bits is given by `mantissa'.
 *  The range of the real values is in the interval [-`range', +`range'].
 *  In case of invalid parameters, a structure with default values is
 *  returned.
 *
 *  Return value
 *  pointer to the new rpf structure
 */
{
    rpf_t * rpfP;

    MALLOCVAR(rpfP);

    if (mantissa < 2) {
        warning (_("Size of RPF mantissa has to be in the interval [2,8]. "
                   "Using minimum value 2.\n"));
        rpfP->mantissa_bits = 2;
    } else if (mantissa > 8) {
        warning (_("Size of RPF mantissa has to be in the interval [2,8]. "
                   "Using maximum value 8.\n"));
        rpfP->mantissa_bits = 2;
    } else
        rpfP->mantissa_bits = mantissa;

    switch (range) {
    case FIASCO_RPF_RANGE_0_75:
        rpfP->range   = 0.75;
        rpfP->range_e = range;
        break;
    case FIASCO_RPF_RANGE_1_50:
        rpfP->range   = 1.50;
        rpfP->range_e = range;
        break;
    case FIASCO_RPF_RANGE_2_00:
        rpfP->range   = 2.00;
        rpfP->range_e = range;
        break;
    case FIASCO_RPF_RANGE_1_00:
        rpfP->range   = 1.00;
        rpfP->range_e = range;
        break;
    default:
        warning (_("Invalid RPF range specified. Using default value 1.0."));
        rpfP->range   = 1.00;
        rpfP->range_e = FIASCO_RPF_RANGE_1_00;
        break;
    }
    return rpfP;
}

