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

  Licensed to Accellera Systems Initiative Inc. (Accellera) under one or
  more contributor license agreements.  See the NOTICE file distributed
  with this work for additional information regarding copyright ownership.
  Accellera licenses this file to you under the Apache License, Version 2.0
  (the "License"); you may not use this file except in compliance with the
  License.  You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

  Unless required by applicable law or agreed to in writing, software
  distributed under the License is distributed on an "AS IS" BASIS,
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
  implied.  See the License for the specific language governing
  permissions and limitations under the License.

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

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

  sc_fxnum.cpp - 

  Original Author: Martin Janssen, Synopsys, Inc.

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

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

  MODIFICATION LOG - modifiers, enter your name, affiliation, date and
  changes you are making here.

      Name, Affiliation, Date:
  Description of Modification:

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


// $Log: sc_fxnum.cpp,v $
// Revision 1.3  2011/01/19 18:57:40  acg
//  Andy Goodrich: changes for IEEE_1666_2011.
//
// Revision 1.2  2010/12/07 20:09:08  acg
// Andy Goodrich: Philipp Hartmann's constructor disambiguation fix
//
// Revision 1.1.1.1  2006/12/15 20:20:04  acg
// SystemC 2.3
//
// Revision 1.3  2006/01/13 18:53:57  acg
// Andy Goodrich: added $Log command so that CVS comments are reproduced in
// the source.
//

#include <cmath>

#include "sysc/datatypes/fx/sc_fxnum.h"


namespace sc_dt
{

// ----------------------------------------------------------------------------
//  CLASS : sc_fxnum_bitref / sc_fxnum_bitref_r
//
//  Proxy class for bit-selection in class sc_fxnum, behaves like sc_bit.
// ----------------------------------------------------------------------------

bool
sc_fxnum_bitref_r::get() const
{
    return m_num.get_bit( m_idx );
}

void
sc_fxnum_bitref::set( bool high )
{
    m_num.set_bit( m_idx, high );
}


// print or dump content

void
sc_fxnum_bitref_r::print( ::std::ostream& os ) const
{
    os << get();
}

void
sc_fxnum_bitref::scan( ::std::istream& is )
{
    bool b;
    is >> b;
    *this = b;
}

void
sc_fxnum_bitref_r::dump( ::std::ostream& os ) const
{
    os << "sc_fxnum_bitref" << ::std::endl;
    os << "(" << ::std::endl;
    os << "num = ";
    m_num.dump( os );
    os << "idx = " << m_idx << ::std::endl;
    os << ")" << ::std::endl;
}


// ----------------------------------------------------------------------------
//  CLASS : sc_fxnum_fast_bitref / sc_fxnum_fast_bitref_r
//
//  Proxy class for bit-selection in class sc_fxnum_fast, behaves like sc_bit.
// ----------------------------------------------------------------------------

bool
sc_fxnum_fast_bitref_r::get() const
{
    return m_num.get_bit( m_idx );
}

void
sc_fxnum_fast_bitref::set( bool high )
{
    m_num.set_bit( m_idx, high );
}


// print or dump content

void
sc_fxnum_fast_bitref_r::print( ::std::ostream& os ) const
{
    os << get();
}

void
sc_fxnum_fast_bitref::scan( ::std::istream& is )
{
    bool b;
    is >> b;
    *this = b;
}

void
sc_fxnum_fast_bitref_r::dump( ::std::ostream& os ) const
{
    os << "sc_fxnum_fast_bitref" << ::std::endl;
    os << "(" << ::std::endl;
    os << "num = ";
    m_num.dump( os );
    os << "idx = " << m_idx << ::std::endl;
    os << ")" << ::std::endl;
}


// ----------------------------------------------------------------------------
//  CLASS : sc_fxnum_subref
//
//  Proxy class for part-selection in class sc_fxnum,
//  behaves like sc_bv_base.
// ----------------------------------------------------------------------------

bool
sc_fxnum_subref_r::get() const
{
    return m_num.get_slice( m_from, m_to, m_bv );
}

bool
sc_fxnum_subref::set()
{
    return m_num.set_slice( m_from, m_to, m_bv );
}


// print or dump content

void
sc_fxnum_subref_r::print( ::std::ostream& os ) const
{
    get();
    m_bv.print( os );
}

void
sc_fxnum_subref::scan( ::std::istream& is )
{
    m_bv.scan( is );
    set();
}

void
sc_fxnum_subref_r::dump( ::std::ostream& os ) const
{
    os << "sc_fxnum_subref" << ::std::endl;
    os << "(" << ::std::endl;
    os << "num  = ";
    m_num.dump( os );
    os << "from = " << m_from << ::std::endl;
    os << "to   = " << m_to << ::std::endl;
    os << ")" << ::std::endl;
}


// ----------------------------------------------------------------------------
//  CLASS : sc_fxnum_fast_subref / sc_fxnum_fast_subref_r
//
//  Proxy class for part-selection in class sc_fxnum_fast,
//  behaves like sc_bv_base.
// ----------------------------------------------------------------------------

bool
sc_fxnum_fast_subref_r::get() const
{
    return m_num.get_slice( m_from, m_to, m_bv );
}

bool
sc_fxnum_fast_subref::set()
{
    return m_num.set_slice( m_from, m_to, m_bv );
}


// print or dump content

void
sc_fxnum_fast_subref_r::print( ::std::ostream& os ) const
{
    get();
    m_bv.print( os );
}

void
sc_fxnum_fast_subref::scan( ::std::istream& is )
{
    m_bv.scan( is );
    set();
}

void
sc_fxnum_fast_subref_r::dump( ::std::ostream& os ) const
{
    os << "sc_fxnum_fast_subref" << ::std::endl;
    os << "(" << ::std::endl;
    os << "num  = ";
    m_num.dump( os );
    os << "from = " << m_from << ::std::endl;
    os << "to   = " << m_to << ::std::endl;
    os << ")" << ::std::endl;
}


// ----------------------------------------------------------------------------
//  CLASS : sc_fxnum
//
//  Base class for the fixed-point types; arbitrary precision.
// ----------------------------------------------------------------------------

// explicit conversion to character string

const std::string
sc_fxnum::to_string() const
{
    return std::string( m_rep->to_string( SC_DEC, -1, SC_F, &m_params ) );
}

const std::string
sc_fxnum::to_string( sc_numrep numrep ) const
{
    return std::string( m_rep->to_string( numrep, -1, SC_F, &m_params ) );
}

const std::string
sc_fxnum::to_string( sc_numrep numrep, bool w_prefix ) const
{
    return std::string( m_rep->to_string( numrep, (w_prefix ? 1 : 0),
					SC_F, &m_params ) );
}

const std::string
sc_fxnum::to_string( sc_fmt fmt ) const
{
    return std::string( m_rep->to_string( SC_DEC, -1, fmt, &m_params ) );
}

const std::string
sc_fxnum::to_string( sc_numrep numrep, sc_fmt fmt ) const
{
    return std::string( m_rep->to_string( numrep, -1, fmt, &m_params ) );
}

const std::string
sc_fxnum::to_string( sc_numrep numrep, bool w_prefix, sc_fmt fmt ) const
{
    return std::string( m_rep->to_string( numrep, (w_prefix ? 1 : 0),
					fmt, &m_params ) );
}


const std::string
sc_fxnum::to_dec() const
{
    return std::string( m_rep->to_string( SC_DEC, -1, SC_F, &m_params ) );
}

const std::string
sc_fxnum::to_bin() const
{
    return std::string( m_rep->to_string( SC_BIN, -1, SC_F, &m_params ) );
}

const std::string
sc_fxnum::to_oct() const
{
    return std::string( m_rep->to_string( SC_OCT, -1, SC_F, &m_params ) );
}

const std::string
sc_fxnum::to_hex() const
{
    return std::string( m_rep->to_string( SC_HEX, -1, SC_F, &m_params ) );
}


// print or dump content

void
sc_fxnum::print( ::std::ostream& os ) const
{
    os << m_rep->to_string( SC_DEC, -1, SC_F, &m_params );
}

void
sc_fxnum::scan( ::std::istream& is )
{
    std::string s;
    is >> s;
    *this = s.c_str();
}

void
sc_fxnum::dump( ::std::ostream& os ) const
{
    os << "sc_fxnum" << ::std::endl;
    os << "(" << ::std::endl;
    os << "rep      = ";
    m_rep->dump( os );
    os << "params   = ";
    m_params.dump( os );
    os << "q_flag   = " << m_q_flag << ::std::endl;
    os << "o_flag   = " << m_o_flag << ::std::endl;
    // TO BE COMPLETED
    // os << "observer = ";
    // if( m_observer != 0 )
    //     m_observer->dump( os );
    // else
    //     os << "0" << ::std::endl;
    os << ")" << ::std::endl;
}


sc_fxnum_observer*
sc_fxnum::lock_observer() const
{
    SC_ASSERT_( m_observer != 0, "lock observer failed" );
    sc_fxnum_observer* tmp = m_observer;
    m_observer = 0;
    return tmp;
}

void
sc_fxnum::unlock_observer( sc_fxnum_observer* observer_ ) const
{
    SC_ASSERT_( observer_ != 0, "unlock observer failed" );
    m_observer = observer_;
}


// ----------------------------------------------------------------------------
//  CLASS : sc_fxnum_fast
//
//  Base class for the fixed-point types; limited precision.
// ----------------------------------------------------------------------------

static
void
quantization( double& c, const scfx_params& params, bool& q_flag )
{
    int fwl = params.wl() - params.iwl();
    double scale = scfx_pow2( fwl );
    double val = scale * c;
    double int_part;
    double frac_part = modf( val, &int_part );

    q_flag = ( frac_part != 0.0 );

    if( q_flag )
    {
        val = int_part;

	switch( params.q_mode() )
	{
            case SC_TRN:			// truncation
	    {
	        if( c < 0.0 )
		    val -= 1.0;
		break;
	    }
            case SC_RND:			// rounding to plus infinity
	    {
		if( frac_part >= 0.5 )
		    val += 1.0;
		else if( frac_part < -0.5 )
		    val -= 1.0;
		break;
	    }
            case SC_TRN_ZERO:			// truncation to zero
	    {
	        break;
	    }
            case SC_RND_INF:			// rounding to infinity
	    {
		if( frac_part >= 0.5 )
		    val += 1.0;
		else if( frac_part <= -0.5 )
		    val -= 1.0;
		break;
	    }
            case SC_RND_CONV:			// convergent rounding
	    {
		if( frac_part > 0.5 ||
		    ( frac_part == 0.5 && fmod( int_part, 2.0 ) != 0.0 ) )
		    val += 1.0;
		else if( frac_part < -0.5 ||
			 ( frac_part == -0.5 && fmod( int_part, 2.0 ) != 0.0 ) )
		    val -= 1.0;
		break;
	    }
            case SC_RND_ZERO:			// rounding to zero
	    {
		if( frac_part > 0.5 )
		    val += 1.0;
		else if( frac_part < -0.5 )
		    val -= 1.0;
		break;
	    }
            case SC_RND_MIN_INF:		// rounding to minus infinity
	    {
		if( frac_part > 0.5 )
		    val += 1.0;
		else if( frac_part <= -0.5 )
		    val -= 1.0;
		break;
	    }
            default:
	        ;
	}
    }

    val /= scale;
    c = val;
}

static
void
overflow( double& c, const scfx_params& params, bool& o_flag )
{
    int iwl = params.iwl();
    int fwl = params.wl() - iwl;
    double full_circle = scfx_pow2( iwl );
    double resolution = scfx_pow2( -fwl );
    double low, high;
    if( params.enc() == SC_TC_ )
    {
	high = full_circle / 2.0 - resolution;
	if( params.o_mode() == SC_SAT_SYM )
	    low = - high;
	else
	    low = - full_circle / 2.0;
    }
    else
    {
	low = 0.0;
	high = full_circle - resolution;
    }
    double val = c;
    sc_fxval_fast c2(c);

    bool under = ( val < low );
    bool over = ( val > high );

    o_flag = ( under || over );

    if( o_flag )
    {
        switch( params.o_mode() )
	{
            case SC_WRAP:			// wrap-around
	    {
		int n_bits = params.n_bits();

	        if( n_bits == 0 )
		{
		    // wrap-around all 'wl' bits
		    val -= floor( val / full_circle ) * full_circle;
		    if( val > high )
			val -= full_circle;
		}
		else if( n_bits < params.wl() )
		{
		    double X = scfx_pow2( iwl - n_bits );

		    // wrap-around least significant 'wl - n_bits' bits
		    val -= floor( val / X ) * X;
		    if( val > ( X - resolution ) )
			val -= X;
		    
		    // saturate most significant 'n_bits' bits
		    if( under )
		        val += low;
		    else
		    {
		        if( params.enc() == SC_TC_ )
			    val += full_circle / 2.0 - X;
			else
			    val += full_circle - X;
		    }
		}
		else
		{
		    // saturate all 'wl' bits
		    if( under )
			val = low;
		    else
			val = high;
		}
		break;
	    }
            case SC_SAT:			// saturation
            case SC_SAT_SYM:			// symmetrical saturation
	    {
	        if( under )
		    val = low;
		else
		    val = high;
		break;
	    }
            case SC_SAT_ZERO:			// saturation to zero
	    {
	        val = 0.0;
		break;
	    }
            case SC_WRAP_SM:			// sign magnitude wrap-around
	    {
		SC_ERROR_IF_( params.enc() == SC_US_,
			      sc_core::SC_ID_WRAP_SM_NOT_DEFINED_ );
	
		int n_bits = params.n_bits();

		if( n_bits == 0 )
		{
		    // invert conditionally
		    if( c2.get_bit( iwl ) != c2.get_bit( iwl - 1 ) )
			val = -val - resolution;

		    // wrap-around all 'wl' bits
		    val -= floor( val / full_circle ) * full_circle;
		    if( val > high )
			val -= full_circle;
		}
		else if( n_bits == 1 )
		{
		    // invert conditionally
		    if( c2.is_neg() != c2.get_bit( iwl - 1 ) )
			val = -val - resolution;

		    // wrap-around all 'wl' bits
		    val -= floor( val / full_circle ) * full_circle;
		    if( val > high )
			val -= full_circle;
		}
		else if( n_bits < params.wl() )
		{
		    // invert conditionally
		    if( c2.is_neg() == c2.get_bit( iwl - n_bits ) )
			val = -val - resolution;
		    
		    double X = scfx_pow2( iwl - n_bits );

		    // wrap-around least significant 'wl - n_bits' bits
		    val -= floor( val / X ) * X;
		    if( val > ( X - resolution ) )
			val -= X;

		    // saturate most significant 'n_bits' bits
		    if( under )
		        val += low;
		    else
			val += full_circle / 2.0 - X;
		} else {
		    // saturate all 'wl' bits
		    if( under )
			val = low;
		    else
			val = high;
		}
	        break;
	    }
            default:
	        ;
	}

	c = val;
    }
}


void
sc_fxnum_fast::cast()
{
    scfx_ieee_double id( m_val );
    SC_ERROR_IF_( id.is_nan() || id.is_inf(), sc_core::SC_ID_INVALID_FX_VALUE_);

    if( m_params.cast_switch() == SC_ON )
    {
        m_q_flag = false;
	m_o_flag = false;

	// check for special cases

	if( id.is_zero() )
	{
	    if( id.negative() != 0 )
	        m_val = -m_val;
	    return;
	}

	// perform casting

	sc_dt::quantization( m_val, m_params, m_q_flag );
	sc_dt::overflow( m_val, m_params, m_o_flag );

	// check for special case: -0

	id = m_val;
	if( id.is_zero() && id.negative() != 0 ) {
	    m_val = -m_val;
	}

	// check for special case: NaN of Inf

	if( id.is_nan() || id.is_inf() ) {
	    m_val = 0.0;
	}
    }
}


// defined in sc_fxval.cpp;
extern
const char*
to_string( const scfx_ieee_double&,
	   sc_numrep,
	   int,
	   sc_fmt,
	   const scfx_params* = 0 );


// explicit conversion to character string

const std::string
sc_fxnum_fast::to_string() const
{
    return std::string( sc_dt::to_string( m_val, SC_DEC, -1, SC_F, &m_params ) );
}

const std::string
sc_fxnum_fast::to_string( sc_numrep numrep ) const
{
    return std::string( sc_dt::to_string( m_val, numrep, -1, SC_F, &m_params ) );
}

const std::string
sc_fxnum_fast::to_string( sc_numrep numrep, bool w_prefix ) const
{
    return std::string( sc_dt::to_string( m_val, numrep, (w_prefix ? 1 : 0),
					SC_F, &m_params ) );
}

const std::string
sc_fxnum_fast::to_string( sc_fmt fmt ) const
{
    return std::string( sc_dt::to_string( m_val, SC_DEC, -1, fmt, &m_params ) );
}

const std::string
sc_fxnum_fast::to_string( sc_numrep numrep, sc_fmt fmt ) const
{
    return std::string( sc_dt::to_string( m_val, numrep, -1, fmt, &m_params ) );
}

const std::string
sc_fxnum_fast::to_string( sc_numrep numrep, bool w_prefix, sc_fmt fmt ) const
{
    return std::string( sc_dt::to_string( m_val, numrep, (w_prefix ? 1 : 0),
					fmt, &m_params ) );
}


const std::string
sc_fxnum_fast::to_dec() const
{
    return std::string( sc_dt::to_string( m_val, SC_DEC, -1, SC_F, &m_params ) );
}

const std::string
sc_fxnum_fast::to_bin() const
{
    return std::string( sc_dt::to_string( m_val, SC_BIN, -1, SC_F, &m_params ) );
}

const std::string
sc_fxnum_fast::to_oct() const
{
    return std::string( sc_dt::to_string( m_val, SC_OCT, -1, SC_F, &m_params ) );
}

const std::string
sc_fxnum_fast::to_hex() const
{
    return std::string( sc_dt::to_string( m_val, SC_HEX, -1, SC_F, &m_params ) );
}


// print or dump content

void
sc_fxnum_fast::print( ::std::ostream& os ) const
{
    os << sc_dt::to_string( m_val, SC_DEC, -1, SC_F, &m_params );
}

void
sc_fxnum_fast::scan( ::std::istream& is )
{
    std::string s;
    is >> s;
    *this = s.c_str();
}

void
sc_fxnum_fast::dump( ::std::ostream& os ) const
{
    os << "sc_fxnum_fast" << ::std::endl;
    os << "(" << ::std::endl;
    os << "val      = " << m_val << ::std::endl;
    os << "params   = ";
    m_params.dump( os );
    os << "q_flag   = " << m_q_flag << ::std::endl;
    os << "o_flag   = " << m_o_flag << ::std::endl;
    // TO BE COMPLETED
    // os << "observer = ";
    // if( m_observer != 0 )
    //     m_observer->dump( os );
    // else
    //     os << "0" << ::std::endl;
    os << ")" << ::std::endl;
}


// internal use only;
bool
sc_fxnum_fast::get_bit( int i ) const
{
    scfx_ieee_double id( m_val );
    if( id.is_zero() || id.is_nan() || id.is_inf() )
        return false;

    // convert to two's complement

    unsigned int m0 = id.mantissa0();
    unsigned int m1 = id.mantissa1();

    if( id.is_normal() )
        m0 += 1U << 20;

    if( id.negative() != 0 )
    {
	m0 = ~ m0;
	m1 = ~ m1;
	unsigned int tmp = m1;
	m1 += 1U;
	if( m1 <= tmp )
	    m0 += 1U;
    }

    // get the right bit

    int j = i - id.exponent();
    if( ( j += 20 ) >= 32 )
        return ( ( m0 & 1U << 31 ) != 0 );
    else if( j >= 0 )
        return ( ( m0 & 1U << j ) != 0 );
    else if( ( j += 32 ) >= 0 )
        return ( ( m1 & 1U << j ) != 0 );
    else
        return false;
}


bool
sc_fxnum_fast::set_bit( int i, bool high )
{
    scfx_ieee_double id( m_val );
    if( id.is_nan() || id.is_inf() )
        return false;

    if( high )
    {
	if( get_bit( i ) )
	    return true;

	if( m_params.enc() == SC_TC_ && i == m_params.iwl() - 1 )
	    m_val -= scfx_pow2( i );
	else
	    m_val += scfx_pow2( i );
    }
    else
    {
	if( ! get_bit( i ) )
	    return true;

	if( m_params.enc() == SC_TC_ && i == m_params.iwl() - 1 )
	    m_val += scfx_pow2( i );
	else
	    m_val -= scfx_pow2( i );
    }

    return true;
}


bool
sc_fxnum_fast::get_slice( int i, int j, sc_bv_base& bv ) const
{
    scfx_ieee_double id( m_val );
    if( id.is_nan() || id.is_inf() )
	return false;

    // convert to two's complement

    unsigned int m0 = id.mantissa0();
    unsigned int m1 = id.mantissa1();

    if( id.is_normal() )
        m0 += 1U << 20;

    if( id.negative() != 0 )
    {
	m0 = ~ m0;
	m1 = ~ m1;
	unsigned int tmp = m1;
	m1 += 1U;
	if( m1 <= tmp )
	    m0 += 1U;
    }

    // get the bits

    int l = j;
    for( int k = 0; k < bv.length(); ++ k )
    {
	bool b = false;

        int n = l - id.exponent();
        if( ( n += 20 ) >= 32 )
	    b = ( ( m0 & 1U << 31 ) != 0 );
	else if( n >= 0 )
	    b = ( ( m0 & 1U << n ) != 0 );
	else if( ( n += 32 ) >= 0 )
	    b = ( ( m1 & 1U << n ) != 0 );

	bv[k] = b;

	if( i >= j )
	    ++ l;
	else
	    -- l;
    }

    return true;
}

bool
sc_fxnum_fast::set_slice( int i, int j, const sc_bv_base& bv )
{
    scfx_ieee_double id( m_val );
    if( id.is_nan() || id.is_inf() )
        return false;

    // set the bits

    int l = j;
    for( int k = 0; k < bv.length(); ++ k )
    {
	if( bv[k].to_bool() )
	{
	    if( ! get_bit( l ) )
	    {
		if( m_params.enc() == SC_TC_ && l == m_params.iwl() - 1 )
		    m_val -= scfx_pow2( l );
		else
		    m_val += scfx_pow2( l );
	    }
	}
	else
	{
	    if( get_bit( l ) )
	    {
		if( m_params.enc() == SC_TC_ && l == m_params.iwl() - 1 )
		    m_val += scfx_pow2( l );
		else
		    m_val -= scfx_pow2( l );
	    }
	}


	if( i >= j )
	    ++ l;
	else
	    -- l;
    }

    return true;
}


sc_fxnum_fast_observer*
sc_fxnum_fast::lock_observer() const
{
    SC_ASSERT_( m_observer != 0, "lock observer failed" );
    sc_fxnum_fast_observer* tmp = m_observer;
    m_observer = 0;
    return tmp;
}

void
sc_fxnum_fast::unlock_observer( sc_fxnum_fast_observer* observer_ ) const
{
    SC_ASSERT_( observer_ != 0, "unlock observer failed" );
    m_observer = observer_;
}

} // namespace sc_dt


// Taf!
