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

  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.

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

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

  scfx_rep.cpp - 

  Original Author: Robert Graulich, Synopsys, Inc.
                   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: scfx_rep.cpp,v $
// Revision 1.4  2011/08/24 22:05:43  acg
//  Torsten Maehne: initialization changes to remove warnings.
//
// Revision 1.3  2011/08/15 16:43:24  acg
//  Torsten Maehne: changes to remove unused argument warnings.
//
// Revision 1.2  2009/02/28 00:26:20  acg
//  Andy Goodrich: bug fixes.
//
// Revision 1.2  2008/11/06 17:22:47  acg
//  Andy Goodrich: bug fixes for 2.2.1.
//
// Revision 1.1.1.1  2006/12/15 20:31:36  acg
// SystemC 2.2
//
// Revision 1.3  2006/01/13 18:53:58  acg
// Andy Goodrich: added $Log command so that CVS comments are reproduced in
// the source.
//

#include "sysc/utils/sc_machine.h"
#include "sysc/datatypes/fx/scfx_rep.h"

#include "sysc/datatypes/fx/scfx_ieee.h"
#include "sysc/datatypes/fx/scfx_pow10.h"
#include "sysc/datatypes/fx/scfx_utils.h"

#include "sysc/datatypes/bit/sc_bv_base.h"

#include <ctype.h>
#include <math.h>


namespace sc_dt
{

// ----------------------------------------------------------------------------
//  some utilities
// ----------------------------------------------------------------------------

static scfx_pow10 pow10_fx;

static const int mantissa0_size = SCFX_IEEE_DOUBLE_M_SIZE - bits_in_int;

static inline
int
n_word( int x )
{
    return ( x + bits_in_word - 1 ) / bits_in_word;
}


// ----------------------------------------------------------------------------
//  CONSTRUCTORS
// ----------------------------------------------------------------------------

scfx_rep::scfx_rep()
: m_mant( min_mant ), m_wp(), m_sign(), m_state(), m_msw(), m_lsw(), 
  m_r_flag( false )
{
    set_zero();
}

scfx_rep::scfx_rep( int a )
: m_mant( min_mant ), m_wp(), m_sign(), m_state(), m_msw(), m_lsw(), 
  m_r_flag( false )
{
    if( a != 0 )
    {
        m_mant.clear();
	m_wp = m_msw = m_lsw = 2;
	m_state = normal;
	if( a > 0 )
	{
	    m_mant[2] = a;
	    m_sign = 1;
	}
	else
	{
	    m_mant[2] = -a;
	    m_sign = -1;
	}
    }
    else
        set_zero();
}

scfx_rep::scfx_rep( unsigned int a )
: m_mant( min_mant ), m_wp(), m_sign(), m_state(), m_msw(), m_lsw(), 
  m_r_flag( false )
{
    if( a != 0 )
    {
        m_mant.clear();
	m_wp = m_msw = m_lsw = 2;
	m_state = normal;
	m_mant[2] = a;
	m_sign = 1;
    }
    else
        set_zero();
}

scfx_rep::scfx_rep( long a )
: m_mant( min_mant ), m_wp(), m_sign(), m_state(), m_msw(), m_lsw(), 
  m_r_flag( false )
{
    if( a != 0 )
    {
        m_mant.clear();
	m_state = normal;
        if ( a > 0 )
        {
	    m_sign = 1;
        }
        else
        {
            a = -a;
            m_sign = -1;
        } 
#       if defined(SC_LONG_64)
            m_wp = 1;
            m_mant[1] = static_cast<word>( a );
            m_mant[2] = static_cast<word>( a >> bits_in_word );
	    find_sw();
#       else
            m_wp = 2;
            m_msw = 2;
            m_lsw = 2;
            m_mant[2] = a;
#       endif

    }
    else
        set_zero();
}

scfx_rep::scfx_rep( unsigned long a )
: m_mant( min_mant ), m_wp(), m_sign(), m_state(), m_msw(), m_lsw(), 
  m_r_flag( false )
{
    if( a != 0 )
    {
        m_mant.clear();
	m_wp = m_msw = m_lsw = 2;
	m_state = normal;
#       if defined(SC_LONG_64)
	    m_wp = 1;
	    m_mant[1] = static_cast<word>( a );
	    m_mant[2] = static_cast<word>( a >> bits_in_word );
	    find_sw();
#       else
	    m_wp = 2;
	    m_msw = 2;
	    m_lsw = 2;
	    m_mant[2] = a;
#	endif
	m_sign = 1;
    }
    else
        set_zero();
}

scfx_rep::scfx_rep( double a )
: m_mant( min_mant ), m_wp( 0 ), m_sign(), m_state( normal ), m_msw( 0 ), 
  m_lsw( 0 ), m_r_flag( false )
{
    m_mant.clear();

    scfx_ieee_double id( a );

    m_sign = id.negative() ? -1 : 1;

    if( id.is_nan() )
        m_state = not_a_number;
    else if( id.is_inf() )
        m_state = infinity;
    else if( id.is_subnormal() )
    {
	m_mant[0] = id.mantissa1();
	m_mant[1] = id.mantissa0();
	normalize( id.exponent() + 1 - SCFX_IEEE_DOUBLE_M_SIZE );
    }
    else if( id.is_normal() )
    {
	m_mant[0] = id.mantissa1();
	m_mant[1] = id.mantissa0() | ( 1 << mantissa0_size );
	normalize( id.exponent() - SCFX_IEEE_DOUBLE_M_SIZE );
    }
}

scfx_rep::scfx_rep( int64 a )
: m_mant( min_mant ), m_wp(), m_sign(), m_state(), m_msw(), m_lsw(), 
  m_r_flag( false )
{
    if( a != 0 )
    {
        m_mant.clear();
	m_wp = 1;
	m_state = normal;
	if( a > 0 )
	{
	    m_mant[1] = static_cast<word>( a );
	    m_mant[2] = static_cast<word>( a >> bits_in_word );
	    m_sign = 1;
	}
	else
	{
	    m_mant[1] = static_cast<word>( -a );
	    m_mant[2] = static_cast<word>( (-a) >> bits_in_word );
	    m_sign = -1;
	}
	find_sw();
    }
    else
        set_zero();
}

scfx_rep::scfx_rep( uint64 a )
: m_mant( min_mant ), m_wp(), m_sign(), m_state(), m_msw(), m_lsw(), 
  m_r_flag( false )
{
    if( a != 0 )
    {
        m_mant.clear();
	m_wp = 1;
	m_state = normal;
	m_mant[1] = static_cast<word>( a );
	m_mant[2] = static_cast<word>( a >> bits_in_word );
	m_sign = 1;
	find_sw();
    }
    else
        set_zero();
}

scfx_rep::scfx_rep( const sc_signed& a )
: m_mant( min_mant ), m_wp(), m_sign(), m_state(), m_msw(), m_lsw(), 
  m_r_flag( false )
{
    if( a.iszero() )
	set_zero();
    else
    {
	int words = n_word( a.length() );
	if( words > size() )
	    resize_to( words );
	m_mant.clear();
	m_wp = 0;
	m_state = normal;
	if( a.sign() )
	{
	    sc_signed a2 = -a;
	    for( int i = 0; i < a2.length(); ++ i )
	    {
		if( a2[i] )
		{
		    scfx_index x = calc_indices( i );
		    m_mant[x.wi()] |= 1 << x.bi();
		}
	    }
	    m_sign = -1;
	}
	else
	{
	    for( int i = 0; i < a.length(); ++ i )
	    {
		if( a[i] )
		{
		    scfx_index x = calc_indices( i );
		    m_mant[x.wi()] |= 1 << x.bi();
		}
	    }
	    m_sign = 1;
	}
	find_sw();
    }
}

scfx_rep::scfx_rep( const sc_unsigned& a )
: m_mant( min_mant ), m_wp(), m_sign(), m_state(), m_msw(), m_lsw(), 
  m_r_flag( false )
{
    if( a.iszero() )
	set_zero();
    else
    {
	int words = n_word( a.length() );
	if( words > size() )
	    resize_to( words );
	m_mant.clear();
	m_wp = 0;
	m_state = normal;
	for( int i = 0; i < a.length(); ++ i )
	{
	    if( a[i] )
	    {
		scfx_index x = calc_indices( i );
		m_mant[x.wi()] |= 1 << x.bi();
	    }
	}
	m_sign = 1;
	find_sw();
    }
}


// copy constructor

scfx_rep::scfx_rep( const scfx_rep& a )
: m_mant( a.m_mant ), m_wp( a.m_wp ), m_sign( a.m_sign ), m_state( a.m_state ),
  m_msw( a.m_msw ), m_lsw( a.m_lsw ), m_r_flag( false )
{}


// ----------------------------------------------------------------------------
//  OPERATORS : new, delete
//
//  Memory management for class scfx_rep.
// ----------------------------------------------------------------------------

union scfx_rep_node
{
    char           data[sizeof( scfx_rep )];
    scfx_rep_node* next;
};


static scfx_rep_node* list = 0;


void*
scfx_rep::operator new( std::size_t size )
{
    const int ALLOC_SIZE = 1024;

    if( size != sizeof( scfx_rep ) )
	return ::operator new( size );

    if( ! list )
    {
	list = new scfx_rep_node[ALLOC_SIZE];
	for( int i = 0; i < ALLOC_SIZE - 1; i ++ )
	    list[i].next = list + i + 1;
	list[ALLOC_SIZE - 1].next = 0;
    }

    scfx_rep* ptr = reinterpret_cast<scfx_rep*>( list->data );
    list = list->next;

    return ptr;
}


void scfx_rep::operator delete( void* ptr, std::size_t size )
{
    if( size != sizeof( scfx_rep ) )
    {
	::operator delete( ptr );
	return;
    }

    scfx_rep_node* node = static_cast<scfx_rep_node*>( ptr );
    node->next = list;
    list = node;
}


// ----------------------------------------------------------------------------
//  METHOD : from_string
//
//  Convert from character string to sc_fxrep.
// ----------------------------------------------------------------------------

#define SCFX_FAIL_IF_(cnd)                                                    \
{                                                                             \
    if( ( cnd ) )                                                             \
    {                                                                         \
        m_state = not_a_number;                                               \
	m_mant.clear(); /* to avoid Purify UMRs during assignment */          \
        return;                                                               \
    }                                                                         \
}


void
scfx_rep::from_string( const char* s, int cte_wl )
{
    SCFX_FAIL_IF_( s == 0 || *s == 0 );

    scfx_string s2;
    s2 += s;
    s2 += '\0';

    bool sign_char;
    m_sign = scfx_parse_sign( s, sign_char );

    sc_numrep numrep = scfx_parse_prefix( s );

    int base = 0;

    switch( numrep )
    {
	case SC_DEC:
	{
	    base = 10;
	    if( scfx_is_nan( s ) )
	    {   // special case: NaN
		m_state = not_a_number;
		m_mant.clear(); /* to avoid Purify UMRs during assignment */
		return;
	    }
	    if( scfx_is_inf( s ) )
	    {   // special case: Infinity
		m_state = infinity;
		m_mant.clear(); /* to avoid Purify UMRs during assignment */
		return;
	    }
	    break;
	}
	case SC_BIN:
	case SC_BIN_US:
	{
	    SCFX_FAIL_IF_( sign_char );
	    base = 2;
	    break;
	}
	
	case SC_BIN_SM:
	{
	    base = 2;
	    break;
	}
	case SC_OCT:
	case SC_OCT_US:
	{
	    SCFX_FAIL_IF_( sign_char );
	    base = 8;
	    break;
	}
	case SC_OCT_SM:
	{
	    base = 8;
	    break;
	}
	case SC_HEX:
	case SC_HEX_US:
	{
	    SCFX_FAIL_IF_( sign_char );
	    base = 16;
	    break;
	}
	case SC_HEX_SM:
	{
	    base = 16;
	    break;
	}
	case SC_CSD:
	{
	    SCFX_FAIL_IF_( sign_char );
	    base = 2;
	    scfx_csd2tc( s2 );
	    s = (const char*) s2 + 4;
	    numrep = SC_BIN;
	    break;
	}
        default:;
    }

    //
    // find end of mantissa and count the digits and points
    //

    const char *end = s;
    bool based_point = false;
    int int_digits = 0;
    int frac_digits = 0;

    while( *end )
    {
	if( scfx_exp_start( end ) )
	    break;
	
	if( *end == '.' )
	{
	    SCFX_FAIL_IF_( based_point );
	    based_point = true;
	}
	else
	{
	    SCFX_FAIL_IF_( ! scfx_is_digit( *end, numrep ) );
	    if( based_point )
		frac_digits ++;
	    else
		int_digits ++;
	}

	++ end;
    }

    SCFX_FAIL_IF_( int_digits == 0 && frac_digits == 0 );

    // [ exponent ]
    
    int exponent = 0;

    if( *end )
    {
	for( const char *e = end + 2; *e; ++ e )
	    SCFX_FAIL_IF_( ! scfx_is_digit( *e, SC_DEC ) );
	exponent = atoi( end + 1 );
    }

    //
    // check if the mantissa is negative
    //

    bool mant_is_neg = false;

    switch( numrep )
    {
	case SC_BIN:
	case SC_OCT:
	case SC_HEX:
	{
	    const char* p = s;
	    if( *p == '.' )
		++ p;

	    mant_is_neg = ( scfx_to_digit( *p, numrep ) >= ( base >> 1 ) );

	    break;
	}
	default:
	    ;
    }

    //
    // convert the mantissa
    //

    switch( base )
    {
        case 2:
	{
	    int bit_offset = exponent % bits_in_word;
	    int word_offset = exponent / bits_in_word;

	    int_digits += bit_offset;
	    frac_digits -= bit_offset;

	    int words = n_word( int_digits ) + n_word( frac_digits );
	    if( words > size() )
		resize_to( words );
	    m_mant.clear();

	    int j = n_word( frac_digits ) * bits_in_word + int_digits - 1;
	    
	    for( ; s < end; s ++ )
	    {
		switch( *s )
		{
		    case '1':
		        set_bin( j );
		    case '0':
			j --;
		    case '.':
			break;
		    default:
			SCFX_FAIL_IF_( true );  // should not happen
		}
	    }

	    m_wp = n_word( frac_digits ) - word_offset;
	    break;
	}
        case 8:
	{
	    exponent *= 3;
	    int_digits *= 3;
	    frac_digits *= 3;

	    int bit_offset = exponent % bits_in_word;
	    int word_offset = exponent / bits_in_word;

	    int_digits += bit_offset;
	    frac_digits -= bit_offset;

	    int words = n_word( int_digits ) + n_word( frac_digits );
	    if( words > size() )
		resize_to( words );
	    m_mant.clear();

	    int j = n_word( frac_digits ) * bits_in_word + int_digits - 3;
	    
	    for( ; s < end; s ++ )
	    {
		switch( *s )
		{
		    case '7': case '6': case '5': case '4':
		    case '3': case '2': case '1':
		        set_oct( j, *s - '0' );
		    case '0':
			j -= 3;
		    case '.':
			break;
		    default:
			SCFX_FAIL_IF_( true );  // should not happen
		}
	    }

	    m_wp = n_word( frac_digits ) - word_offset;
	    break;
	}
        case 10:
	{
	    word carry, temp;
	    int length = int_digits + frac_digits;
	    resize_to( sc_max( min_mant, n_word( 4 * length ) ) );

	    m_mant.clear();
	    m_msw = m_lsw = 0;
	    
	    for( ; s < end; s ++ )
	    {
		switch( *s )
		{
		    case '9': case '8': case '7': case '6': case '5':
		    case '4': case '3': case '2': case '1': case '0':
		        multiply_by_ten();
			carry = *s - '0';
			for ( int i = 0; carry && i < m_mant.size(); i++ )
			{
			    temp = m_mant[i];
                            temp += carry;			    
			    carry = temp < m_mant[i];
			    m_mant[i] = temp;
			}
		    case '.':
			break;
		    default:
			SCFX_FAIL_IF_( true );  // should not happen
		}
	    }
	    
	    m_wp = 0;
	    find_sw();

	    int denominator = frac_digits - exponent;
	    
	    if( denominator )
	    {
		scfx_rep frac_num = pow10_fx( denominator );
		scfx_rep* temp_num =
		    div_scfx_rep( const_cast<const scfx_rep&>( *this ),
				   frac_num, cte_wl );
		*this = *temp_num;
		delete temp_num;
	    }

	    break;
	}
        case 16:
	{
	    exponent *= 4;
	    int_digits *= 4;
	    frac_digits *= 4;

	    int bit_offset = exponent % bits_in_word;
	    int word_offset = exponent / bits_in_word;

	    int_digits += bit_offset;
	    frac_digits -= bit_offset;

	    int words = n_word( int_digits ) + n_word( frac_digits );
	    if( words > size() )
		resize_to( words );
	    m_mant.clear();

	    int j = n_word( frac_digits ) * bits_in_word + int_digits - 4;
	    
	    for( ; s < end; s ++ )
	    {
		switch( *s )
		{
		    case 'f': case 'e': case 'd': case 'c': case 'b': case 'a':
		       set_hex( j, *s - 'a' + 10 );
		       j -= 4;
		       break;
		    case 'F': case 'E': case 'D': case 'C': case 'B': case 'A':
		       set_hex( j, *s - 'A' + 10 );
		       j -= 4;
		       break;
		    case '9': case '8': case '7': case '6': case '5':
		    case '4': case '3': case '2': case '1':
		       set_hex( j, *s - '0' );
		    case '0':
		       j -= 4;
		    case '.':
		       break;
		   default:
		       SCFX_FAIL_IF_( true );  // should not happen
		}
	    }

	    m_wp = n_word( frac_digits ) - word_offset;
	    break;
	}
    }

    m_state = normal;
    find_sw();

    //
    // two's complement of mantissa if it is negative
    //

    if( mant_is_neg )
    {
	m_mant[m_msw] |=  -1 << scfx_find_msb( m_mant[m_msw] );
	for( int i = m_msw + 1; i < m_mant.size(); ++ i )
	    m_mant[i] = static_cast<word>( -1 );
	complement( m_mant, m_mant, m_mant.size() );
	inc( m_mant );
	m_sign *= -1;
	find_sw();
    }
}


#undef SCFX_FAIL_IF_


// ----------------------------------------------------------------------------
//  METHOD : to_double
//
//  Convert from scfx_rep to double.
// ----------------------------------------------------------------------------

double
scfx_rep::to_double() const 
{
    scfx_ieee_double id;

    // handle special cases

    if( is_nan() )
    {
        id.set_nan();
	return id;
    }

    if( is_inf() )
    {
        id.set_inf();
	id.negative( m_sign < 0 );
	return id;
    }

    if( is_zero() )
    {
	id = 0.;
	id.negative( m_sign < 0 );
	return id;
    }

    int msb = scfx_find_msb( m_mant[m_msw] );

    int exp = (m_msw - m_wp) * bits_in_word + msb;

    if( exp > SCFX_IEEE_DOUBLE_E_MAX )
    {
	id.set_inf();
	id.negative( m_sign < 0 );
	return id;
    }

    if( exp < SCFX_IEEE_DOUBLE_E_MIN
	- static_cast<int>( SCFX_IEEE_DOUBLE_M_SIZE ) )
    {
	id = 0.;
	return id;
    }
 
    int shift = mantissa0_size - msb;

    unsigned int m0;
    unsigned int m1 = 0;
    unsigned int guard = 0;

    if( shift == 0 )
    {
        m0 = m_mant[m_msw] & ~( 1 << mantissa0_size );
	if( m_msw > m_lsw )
	{
	    m1 = m_mant[m_msw - 1];
	    if( m_msw - 1 > m_lsw )
	        guard = m_mant[m_msw - 2] >> ( bits_in_word - 1 );
	}
    }
    else if( shift < 0 )
    {
	m0 = ( m_mant[m_msw] >> -shift ) & ~( 1 << mantissa0_size );
	m1 = m_mant[m_msw] << ( bits_in_word + shift );
	if( m_msw > m_lsw )
	{
	    m1 |= m_mant[m_msw - 1] >> -shift;
	    guard = ( m_mant[m_msw - 1] >> ( -shift - 1 ) ) & 1;
	}
    }
    else
    {
	m0 = ( m_mant[m_msw] << shift ) & ~( 1 << mantissa0_size );
	if( m_msw > m_lsw )
	{
	    m0 |= m_mant[m_msw - 1] >> ( bits_in_word - shift );
	    m1 = m_mant[m_msw - 1] << shift;
	    if( m_msw - 1 > m_lsw )
	    {
	        m1 |= m_mant[m_msw - 2] >> ( bits_in_word - shift );
		guard = ( m_mant[m_msw - 2] >> (bits_in_word - shift - 1) )
                      & 1;
	    }      
	}
    }

    if( exp < SCFX_IEEE_DOUBLE_E_MIN )
    {
	m0 |= ( 1 << mantissa0_size );

	int subnormal_shift = SCFX_IEEE_DOUBLE_E_MIN - exp;

	if( subnormal_shift < bits_in_word )
	{
	    m1 = m1 >> subnormal_shift
	       | m0 << ( bits_in_word - subnormal_shift );
	    m0 = m0 >> subnormal_shift;
	}
	else
	{
	    m1 = m0 >> ( subnormal_shift - bits_in_word );
	    m0 = 0;
	}

	guard = 0;

	exp = SCFX_IEEE_DOUBLE_E_MIN - 1;
    }

    id.mantissa0( m0 );
    id.mantissa1( m1 );
    id.exponent( exp );
    id.negative( m_sign < 0 );

    double result = id;

    if( guard != 0 )
        result += m_sign * scfx_pow2( exp - SCFX_IEEE_DOUBLE_M_SIZE );

    return result;
}


// ----------------------------------------------------------------------------
//  METHOD : to_string
//
//  Convert from scfx_rep to character string.
// ----------------------------------------------------------------------------

void
print_dec( scfx_string& s, const scfx_rep& num, int w_prefix, sc_fmt fmt )
{
    if( num.is_neg() )
	s += '-';

    if( w_prefix == 1 ) {
	scfx_print_prefix( s, SC_DEC );
    }

    if( num.is_zero() )
    {
	s += '0';
	return;
    }

    // split 'num' into its integer and fractional part

    scfx_rep int_part  = num;
    scfx_rep frac_part = num;

    int i;
    
    for( i = int_part.m_lsw; i <= int_part.m_msw && i < int_part.m_wp; i ++ )
	int_part.m_mant[i] = 0;
    int_part.find_sw();
    if( int_part.m_wp < int_part.m_lsw )
	int_part.resize_to( int_part.size() - int_part.m_wp, -1 );
    
    for( i = frac_part.m_msw;
	 i >= frac_part.m_lsw && i >= frac_part.m_wp;
	 i -- )
	frac_part.m_mant[i] = 0;
    frac_part.find_sw();
    if( frac_part.m_msw == frac_part.size() - 1 )
	frac_part.resize_to( frac_part.size() + 1, 1 );

    // print integer part

    int int_digits = 0;
    int int_zeros  = 0;
    
    if( ! int_part.is_zero() )
    {
	double int_wl = ( int_part.m_msw - int_part.m_wp ) * bits_in_word
	              + scfx_find_msb( int_part.m_mant[int_part.m_msw] ) + 1;
	int_digits = (int) ceil( int_wl * log10( 2. ) );

	int len = s.length();
	s.append( int_digits );

	bool zero_digits = ( frac_part.is_zero() && fmt != SC_F );

	for( i = int_digits + len - 1; i >= len; i-- )
	{
	    unsigned int remainder = int_part.divide_by_ten();
	    s[i] = static_cast<char>( '0' + remainder );
	    
	    if( zero_digits )
	    {
		if( remainder == 0 )
		    int_zeros ++;
		else
		    zero_digits = false;
	    }
	}

	// discard trailing zeros from int_part
	s.discard( int_zeros );

	if( s[len] == '0' )
	{
	    // int_digits was overestimated by one
	    s.remove( len );
	    -- int_digits;
	}
    }

    // print fractional part

    int frac_digits = 0;
    int frac_zeros  = 0;

    if( ! frac_part.is_zero() )
    {
	s += '.';

	bool zero_digits = ( int_digits == 0 && fmt != SC_F );

	double frac_wl = ( frac_part.m_wp - frac_part.m_msw ) * bits_in_word
	               - scfx_find_msb( frac_part.m_mant[frac_part.m_msw] )
                       - 1;
	frac_zeros = (int) floor( frac_wl * log10( 2. ) );

	scfx_rep temp;
	sc_dt::multiply( temp, frac_part, pow10_fx( frac_zeros ) );
	frac_part = temp;
	if( frac_part.m_msw == frac_part.size() - 1 )
	    frac_part.resize_to( frac_part.size() + 1, 1 );
	
	frac_digits = frac_zeros;
	if( ! zero_digits )
	{
	    for( i = 0; i < frac_zeros; i ++ )
		s += '0';
	    frac_zeros = 0;
	}

	while( ! frac_part.is_zero() )
	{
	    frac_part.multiply_by_ten();
	    int n = frac_part.m_mant[frac_part.m_msw + 1];
	
	    if( zero_digits )
	    {
		if( n == 0 )
		    frac_zeros ++;
		else
		    zero_digits = false;
	    }
	
	    if( ! zero_digits )
		s += static_cast<char>( '0' + n );

	    frac_part.m_mant[frac_part.m_msw + 1] = 0;
	    frac_digits ++;
	}
    }

    // print exponent
    
    if( fmt != SC_F )
    {
        if( frac_digits == 0 )
	    scfx_print_exp( s, int_zeros );
	else if( int_digits == 0 )
	    scfx_print_exp( s, - frac_zeros );
    }
}

void
print_other( scfx_string& s, const scfx_rep& a, sc_numrep numrep, int w_prefix,
	     sc_fmt fmt, const scfx_params* params )
{
    scfx_rep b = a;

    sc_numrep numrep2 = numrep;

    bool numrep_is_sm = ( numrep == SC_BIN_SM ||
			  numrep == SC_OCT_SM ||
			  numrep == SC_HEX_SM );

    if( numrep_is_sm )
    {
	if( b.is_neg() )
	{
	    s += '-';
	    b = *neg_scfx_rep( a );
	}
	switch( numrep )
	{
	    case SC_BIN_SM:
		numrep2 = SC_BIN_US;
		break;
	    case SC_OCT_SM:
		numrep2 = SC_OCT_US;
		break;
	    case SC_HEX_SM:
		numrep2 = SC_HEX_US;
		break;
	    default:
		;
	}
    }
    
    if( w_prefix != 0 ) {
	scfx_print_prefix( s, numrep );
    }

    numrep = numrep2;

    int msb, lsb;

    if( params != 0 )
    {
	msb = params->iwl() - 1;
	lsb = params->iwl() - params->wl();

	if( params->enc() == SC_TC_ &&
	    ( numrep == SC_BIN_US ||
	      numrep == SC_OCT_US ||
	      numrep == SC_HEX_US ) &&
	    ! numrep_is_sm &&
	    params->wl() > 1 )
	    -- msb;
	else if( params->enc() == SC_US_ &&
	    ( numrep == SC_BIN ||
	      numrep == SC_OCT ||
	      numrep == SC_HEX ||
	      numrep == SC_CSD ) )
	    ++ msb;
    }
    else
    {
	if( b.is_zero() )
	{
	    msb = 0;
	    lsb = 0;
	}
	else
	{
	    msb = ( b.m_msw - b.m_wp ) * bits_in_word
		+ scfx_find_msb( b.m_mant[ b.m_msw ] ) + 1;
	    while( b.get_bit( msb ) == b.get_bit( msb - 1 ) )
		-- msb;

	    if( numrep == SC_BIN_US ||
		numrep == SC_OCT_US ||
		numrep == SC_HEX_US )
		-- msb;

	    lsb = ( b.m_lsw - b.m_wp ) * bits_in_word
		+ scfx_find_lsb( b.m_mant[ b.m_lsw ] );
	}
    }

    int step;

    switch( numrep )
    {
	case SC_BIN:
	case SC_BIN_US:
	case SC_CSD:
	    step = 1;
	   break;
	case SC_OCT:
	case SC_OCT_US:
	    step = 3;
	    break;
	case SC_HEX:
	case SC_HEX_US:
	    step = 4;
	    break;
	default:
	    step = 0;
    }

    msb = (int) ceil( double( msb + 1 ) / step ) * step - 1;

    lsb = (int) floor( double( lsb ) / step ) * step;

    if( msb < 0 )
    {
	s += '.';
	if( fmt == SC_F )
	{
	    int sign = ( b.is_neg() ) ? ( 1 << step ) - 1 : 0;
	    for( int i = ( msb + 1 ) / step; i < 0; i ++ )
	    {
		if( sign < 10 )
		    s += static_cast<char>( sign + '0' );
		else
		    s += static_cast<char>( sign + 'a' - 10 );
	    }
	}
    }

    int i = msb;
    while( i >= lsb )
    {
        int value = 0;
        for( int j = step - 1; j >= 0; -- j )
	{
            value += static_cast<int>( b.get_bit( i ) ) << j;
            -- i;
        }
        if( value < 10 )
            s += static_cast<char>( value + '0' );
	else
            s += static_cast<char>( value + 'a' - 10 );
	if( i == -1 )
	    s += '.';
    }

    if( lsb > 0 && fmt == SC_F )
    {
	for( int i = lsb / step; i > 0; i -- )
	    s += '0';
    }

    if( s[s.length() - 1] == '.' )
	s.discard( 1 );

    if( fmt != SC_F )
    {
	if( msb < 0 )
	    scfx_print_exp( s, ( msb + 1 ) / step );
	else if( lsb > 0 )
	    scfx_print_exp( s, lsb / step );
    }

    if( numrep == SC_CSD )
	scfx_tc2csd( s, w_prefix );
}

const char*
scfx_rep::to_string( sc_numrep numrep, int w_prefix,
		     sc_fmt fmt, const scfx_params* params ) const
{
    static scfx_string s;

    s.clear();

    if( is_nan() )
        scfx_print_nan( s );
    else if( is_inf() )
        scfx_print_inf( s, is_neg() );
    else if( is_neg() && ! is_zero() &&
	     ( numrep == SC_BIN_US ||
	       numrep == SC_OCT_US ||
	       numrep == SC_HEX_US ) )
        s += "negative";
    else if( numrep == SC_DEC || numrep == SC_NOBASE )
        sc_dt::print_dec( s, *this, w_prefix, fmt );
    else
        sc_dt::print_other( s, *this, numrep, w_prefix, fmt, params );

    return s;
}


// ----------------------------------------------------------------------------
//  ADD
//
//  add two mantissas of the same size
//  result has the same size
//  returns carry of operation
// ----------------------------------------------------------------------------

static inline
int
add_mants( int size, scfx_mant& result,
	   const scfx_mant& a, const scfx_mant& b )
{
    unsigned int carry = 0;

    int index = 0;

    do
    {
        word x = a[index];
	word y = b[index];

	y += carry;
	carry = y < carry;
	y += x;
	carry += y < x;
	result[index] = y;
    }
    while( ++ index < size );

    return ( carry ? 1 : 0 );
}


static inline
int
sub_mants( int size, scfx_mant& result,
	   const scfx_mant& a, const scfx_mant& b )
{
    unsigned carry = 0;

    int index = 0;

    do
    {
	word x = a[index];
	word y = b[index];

	y += carry;
	carry = y < carry;
	y = x - y;
	carry += y > x;
	result[index] = y;
    }
    while( ++ index < size );

    return ( carry ? 1 : 0 );
}


scfx_rep*
add_scfx_rep( const scfx_rep& lhs, const scfx_rep& rhs, int max_wl )
{
    scfx_rep& result = *new scfx_rep;

    //
    // check for special cases
    //

    if( lhs.is_nan() || rhs.is_nan()
    ||  ( lhs.is_inf() && rhs.is_inf() && lhs.m_sign != rhs.m_sign ) )
    {
	result.set_nan();
	return &result;
    }

    if( lhs.is_inf() )
    {
	result.set_inf( lhs.m_sign );
	return &result;
    }

    if( rhs.is_inf() )
    {
	result.set_inf( rhs.m_sign );
	return &result;
    }

    //
    // align operands if needed
    //

    scfx_mant_ref lhs_mant;
    scfx_mant_ref rhs_mant;

    int len_mant = lhs.size();
    int new_wp = lhs.m_wp;

    align( lhs, rhs, new_wp, len_mant, lhs_mant, rhs_mant );

    //
    // size the result mantissa
    //

    result.resize_to( len_mant );
    result.m_wp = new_wp;

    //
    // do it
    //

    if( lhs.m_sign == rhs.m_sign )
    {
	add_mants( len_mant, result.m_mant, lhs_mant, rhs_mant );
	result.m_sign = lhs.m_sign;
    }
    else
    {
	int cmp = compare_abs( lhs, rhs );

	if( cmp == 1 )
	{
	    sub_mants( len_mant, result.m_mant, lhs_mant, rhs_mant );
	    result.m_sign = lhs.m_sign;
	}
	else if ( cmp == -1 )
	{
	    sub_mants( len_mant, result.m_mant, rhs_mant, lhs_mant );
	    result.m_sign = rhs.m_sign;
	}
	else
	{
	    result.m_mant.clear();
	    result.m_sign = 1;
	}
    }

    result.find_sw();
    result.round( max_wl );

    return &result;
}


// ----------------------------------------------------------------------------
//  SUB
//
//  sub two word's of the same size
//  result has the same size
//  returns carry of operation
// ----------------------------------------------------------------------------

static inline
int
sub_with_index(       scfx_mant& a, int a_msw, int /*a_lsw*/,
		const scfx_mant& b, int b_msw, int b_lsw )
{
    unsigned carry = 0;

    int size    = b_msw - b_lsw;
    int a_index = a_msw - size;
    int b_index = b_msw - size;

    do
    {
	word x = a[a_index];
	word y = b[b_index];

	y += carry;
	carry = y < carry;
	y = x - y;
	carry += y > x;
	a[a_index] = y;

	a_index ++;
	b_index ++;
    }
    while( size -- );

    if( carry )
    {
        // special case: a[a_msw + 1 ] == 1
        a[a_msw + 1] = 0;
    }

    return ( carry ? 1 : 0 );
}


scfx_rep*
sub_scfx_rep( const scfx_rep& lhs, const scfx_rep& rhs, int max_wl )
{
    scfx_rep& result = *new scfx_rep;

    //
    // check for special cases
    //

    if( lhs.is_nan() || rhs.is_nan()
    ||  ( lhs.is_inf() && rhs.is_inf() && lhs.m_sign == rhs.m_sign ) )
    {
	result.set_nan();
	return &result;
    }

    if( lhs.is_inf() )
    {
	result.set_inf( lhs.m_sign );
	return &result;
    }

    if( rhs.is_inf() )
    {
	result.set_inf( -1 * rhs.m_sign );
	return &result;
    }

    //
    // align operands if needed
    //

    scfx_mant_ref lhs_mant;
    scfx_mant_ref rhs_mant;

    int len_mant = lhs.size();
    int new_wp = lhs.m_wp;

    align( lhs, rhs, new_wp, len_mant, lhs_mant, rhs_mant );

    //
    // size the result mantissa
    //

    result.resize_to( len_mant );
    result.m_wp = new_wp;

    //
    // do it
    //

    if( lhs.m_sign != rhs.m_sign )
    {
	add_mants( len_mant, result.m_mant, lhs_mant, rhs_mant );
	result.m_sign = lhs.m_sign;
    }
    else
    {
	int cmp = compare_abs( lhs, rhs );

	if( cmp == 1 )
	{
	    sub_mants( len_mant, result.m_mant, lhs_mant, rhs_mant );
	    result.m_sign = lhs.m_sign;
	}
	else if ( cmp == -1 )
	{
	    sub_mants( len_mant, result.m_mant, rhs_mant, lhs_mant );
	    result.m_sign = -rhs.m_sign;
	} else {
	    result.m_mant.clear();
	    result.m_sign = 1;
	}
    }

    result.find_sw();
    result.round( max_wl );

    return &result;
}


// ----------------------------------------------------------------------------
//  MUL
// ----------------------------------------------------------------------------

union word_short
{
    word l;
    struct
    {
#if defined( SC_BIG_ENDIAN )
        half_word u;
        half_word l;
#elif defined( SC_LITTLE_ENDIAN )
        half_word l;
        half_word u;
#endif
    } s;
};


#if defined( SC_BIG_ENDIAN )
static const int half_word_incr = -1;
#elif defined( SC_LITTLE_ENDIAN )
static const int half_word_incr = 1;
#endif


void
multiply( scfx_rep& result, const scfx_rep& lhs, const scfx_rep& rhs,
	  int max_wl )
{
    //
    // check for special cases
    //

    if( lhs.is_nan() || rhs.is_nan()
    ||  (lhs.is_inf() && rhs.is_zero())
    ||  (lhs.is_zero() && rhs.is_inf()) )
    {
	result.set_nan();
	return;
    }

    if( lhs.is_inf() || rhs.is_inf() )
    {
	result.set_inf( lhs.m_sign * rhs.m_sign );
	return;
    }
    
    if( lhs.is_zero() || rhs.is_zero() ) {
	result.set_zero( lhs.m_sign * rhs.m_sign );
	return;
    }

    //
    // do it
    //

    int len_lhs = lhs.m_msw - lhs.m_lsw + 1;
    int len_rhs = rhs.m_msw - rhs.m_lsw + 1;

    int new_size = sc_max( min_mant, len_lhs + len_rhs );
    int new_wp   = ( lhs.m_wp - lhs.m_lsw ) + ( rhs.m_wp - rhs.m_lsw );
    int new_sign = lhs.m_sign * rhs.m_sign;

    result.resize_to( new_size );
    result.m_mant.clear();
    result.m_wp    = new_wp;
    result.m_sign  = new_sign;
    result.m_state = scfx_rep::normal;

    half_word *s1 = lhs.m_mant.half_addr( lhs.m_lsw );
    half_word *s2 = rhs.m_mant.half_addr( rhs.m_lsw );

    half_word *t = result.m_mant.half_addr();

    len_lhs <<= 1;
    len_rhs <<= 1;

    int i1, i2;

    for( i1 = 0; i1 * half_word_incr < len_lhs; i1 += half_word_incr )
    {
	register word_short ls;
	ls.l = 0;

	half_word v1 = s1[i1];

	for( i2  = 0; i2 * half_word_incr < len_rhs; i2 += half_word_incr )
	{
	    ls.l  += v1 * s2[i2];
	    ls.s.l = ls.s.u + ( ( t[i2] += ls.s.l ) < ls.s.l );
	    ls.s.u = 0;
	}

	t[i2] = ls.s.l;
	t += half_word_incr;
    }

    result.find_sw();
    result.round( max_wl );
}


// ----------------------------------------------------------------------------
//  DIV
// ----------------------------------------------------------------------------

scfx_rep*
div_scfx_rep( const scfx_rep& lhs, const scfx_rep& rhs, int div_wl )
{
    scfx_rep& result = *new scfx_rep;

    //
    // check for special cases
    //

    if( lhs.is_nan() || rhs.is_nan() || (lhs.is_inf() && rhs.is_inf()) ||
	(lhs.is_zero() && rhs.is_zero()) )
    {
	result.set_nan();
	return &result;
    }

    if( lhs.is_inf() || rhs.is_zero() )
    {
	result.set_inf( lhs.m_sign * rhs.m_sign );
	return &result;
    }

    if( lhs.is_zero() || rhs.is_inf() )
    {
	result.set_zero( lhs.m_sign * rhs.m_sign );
	return &result;
    }

    //
    // do it
    //

    // compute one bit more for rounding
    div_wl ++;

    result.resize_to( sc_max( n_word( div_wl ) + 1, min_mant ) );
    result.m_mant.clear();
    result.m_sign = lhs.m_sign * rhs.m_sign;

    int msb_lhs = scfx_find_msb( lhs.m_mant[lhs.m_msw] )
	        + ( lhs.m_msw - lhs.m_wp ) * bits_in_word;
    int msb_rhs = scfx_find_msb( rhs.m_mant[rhs.m_msw] )
	        + ( rhs.m_msw - rhs.m_wp ) * bits_in_word;

    int msb_res = msb_lhs - msb_rhs;
    int to_shift = -msb_res % bits_in_word;
    int result_index;

    int c = ( msb_res % bits_in_word >= 0 ) ? 1 : 0;

    result_index = (result.size() - c) * bits_in_word + msb_res % bits_in_word;
    result.m_wp   = (result.size() - c) - msb_res / bits_in_word;

    scfx_rep remainder = lhs;

    // align msb from remainder to msb from rhs
    remainder.lshift( to_shift );

    // make sure msw( remainder ) < size - 1
    if( remainder.m_msw == remainder.size() - 1 )
	remainder.resize_to( remainder.size() + 1, 1 );

    // make sure msw( remainder ) >= msw( rhs )!
    int msw_diff = rhs.m_msw - remainder.m_msw;
    if (msw_diff > 0)
	remainder.resize_to( remainder.size() + msw_diff, -1 );

    int counter;

    for( counter = div_wl; counter && ! remainder.is_zero(); counter -- )
    {
	if( compare_msw_ff( rhs, remainder ) <= 0 )
	{
	    result.set_bin( result_index );
	    sub_with_index( remainder.m_mant, remainder.m_msw, remainder.m_lsw,
			    rhs.m_mant, rhs.m_msw, rhs.m_lsw );
	}
	result_index --;
	remainder.shift_left( 1 );
        remainder.m_lsw = remainder.find_lsw();
    }

    // perform convergent rounding, if needed
    if( counter == 0 )
    {
	int index = result_index + 1 - result.m_wp * bits_in_word;

	scfx_index x = result.calc_indices( index );
	scfx_index x1 = result.calc_indices( index + 1 );

	if( result.o_bit_at( x ) && result.o_bit_at( x1 ) )
	    result.q_incr( x );

	result.m_r_flag = true;
    }

    result.find_sw();

    return &result;
}


// ----------------------------------------------------------------------------
//  destructive shift mantissa to the left
// ----------------------------------------------------------------------------

void
scfx_rep::lshift( int n )
{
    if( n == 0 )
        return;

    if( n < 0 )
    {
        rshift( -n );
	return;
    }

    if( is_normal() )
    {
        int shift_bits  = n % bits_in_word;
	int shift_words = n / bits_in_word;

	// resize if needed
	if( m_msw == size() - 1 &&
	    scfx_find_msb( m_mant[m_msw] ) >= bits_in_word - shift_bits )
	    resize_to( size() + 1, 1 );

	// do it
	m_wp -= shift_words;
	shift_left( shift_bits );
	find_sw();
    }
}


// ----------------------------------------------------------------------------
//  destructive shift mantissa to the right
// ----------------------------------------------------------------------------

void
scfx_rep::rshift( int n )
{
    if( n == 0 )
        return;

    if( n < 0 )
    {
        lshift( -n );
	return;
    }

    if( is_normal() )
    {
        int shift_bits  = n % bits_in_word;
	int shift_words = n / bits_in_word;

	// resize if needed
	if( m_lsw == 0 && scfx_find_lsb( m_mant[m_lsw] ) < shift_bits )
	    resize_to( size() + 1, -1 );

	// do it
	m_wp += shift_words;
	shift_right( shift_bits );
	find_sw();
    }
}


// ----------------------------------------------------------------------------
//  FRIEND FUNCTION : compare_abs
//
//  Compares the absolute values of two scfx_reps, excluding the special cases.
// ----------------------------------------------------------------------------

int
compare_abs( const scfx_rep& a, const scfx_rep& b )
{
    // check for zero

    word a_word = a.m_mant[a.m_msw];
    word b_word = b.m_mant[b.m_msw];
  
    if( a_word == 0 || b_word == 0 )
    {
	if( a_word != 0 )
	    return 1;
	if( b_word != 0 )
	    return -1;
	return 0;
    }

    // compare msw index

    int a_msw = a.m_msw - a.m_wp;
    int b_msw = b.m_msw - b.m_wp;

    if( a_msw > b_msw )
	return 1;

    if( a_msw < b_msw )
	return -1;

    // compare content

    int a_i = a.m_msw;
    int b_i = b.m_msw;

    while( a_i >= a.m_lsw && b_i >= b.m_lsw )
    {
	a_word = a.m_mant[a_i];
	b_word = b.m_mant[b_i];
	if( a_word > b_word )
	    return 1;
	if( a_word < b_word )
	    return -1;
	-- a_i;
	-- b_i;
    }

    bool a_zero = true;
    while( a_i >= a.m_lsw )
    {
	a_zero = a_zero && ( a.m_mant[a_i] == 0 );
	-- a_i;
    }
  
    bool b_zero = true;
    while( b_i >= b.m_lsw )
    {
	b_zero = b_zero && ( b.m_mant[b_i] == 0 );
	-- b_i;
    }

    // assertion: a_zero || b_zero == true

    if( ! a_zero && b_zero )
	return 1;

    if( a_zero && ! b_zero )
	return -1;

    return 0;
}


// ----------------------------------------------------------------------------
//  FRIEND FUNCTION : cmp_scfx_rep
//
//  Compares the values of two scfx_reps, including the special cases.
// ----------------------------------------------------------------------------

int
cmp_scfx_rep( const scfx_rep& a, const scfx_rep& b )
{
    // handle special cases

    if( a.is_nan() || b.is_nan() )
    {
#if 0
	if( a.is_nan() && b.is_nan() )
	{
	    return 0;
	}
#endif
	return 2;
    }

    if( a.is_inf() || b.is_inf() )
    {
	if( a.is_inf() )
	{
	    if( ! a.is_neg() )
	    {
		if( b.is_inf() && ! b.is_neg() )
		{
		    return 0;
		}
		else
		{
		    return 1;
		}
	    }
	    else
	    {
		if( b.is_inf() && b.is_neg() )
		{
		    return 0;
		}
		else
		{
		    return -1;
		}
	    }
	}
	if( b.is_inf() )
	{
	    if( ! b.is_neg() )
	    {
		return -1;
	    }
	    else
	    {
		return 1;
	    }
	}
    }
  
    if( a.is_zero() && b.is_zero() )
    {
	return 0;
    }

    // compare sign

    if( a.m_sign != b.m_sign )
    {
	return a.m_sign;
    }

    return ( a.m_sign * compare_abs( a, b ) );
}


// ----------------------------------------------------------------------------
//  PRIVATE METHOD : quantization
//
//  Performs destructive quantization.
// ----------------------------------------------------------------------------

void
scfx_rep::quantization( const scfx_params& params, bool& q_flag )
{
    scfx_index x = calc_indices( params.iwl() - params.wl() );

    if( x.wi() < 0 )
        return;

    if( x.wi() >= size() )
        resize_to( x.wi() + 1, 1 );

    bool qb = q_bit( x );
    bool qz = q_zero( x );

    q_flag = ( qb || ! qz );

    if( q_flag )
    {
        switch( params.q_mode() )
	{
            case SC_TRN:			// truncation
	    {
	        if( is_neg() )
		    q_incr( x );
		break;
	    }
            case SC_RND:			// rounding to plus infinity
	    {
	        if( ! is_neg() )
		{
		    if( qb )
			q_incr( x );
		}
		else
		{
		    if( qb && ! qz )
			q_incr( x );
		}
		break;
	    }
            case SC_TRN_ZERO:			// truncation to zero
	    {
	        break;
	    }
            case SC_RND_INF:			// rounding to infinity
	    {
	        if( qb )
		    q_incr( x );
		break;
	    }
            case SC_RND_CONV:			// convergent rounding
	    {
		if( (qb && ! qz) || (qb && qz && q_odd( x )) )
		    q_incr( x );
		break;
	    }
            case SC_RND_ZERO:			// rounding to zero
	    {
		if( qb && ! qz )
		    q_incr( x );
		break;
	    }
            case SC_RND_MIN_INF:		// rounding to minus infinity
	    {
		if( ! is_neg() )
		{
		    if( qb && ! qz )
			q_incr( x );
		}
		else
		{
		    if( qb )
			q_incr( x );
		}
		break;
	    }
            default:
	        ;
	}
	q_clear( x );
	
	find_sw();
    }
}


// ----------------------------------------------------------------------------
//  PRIVATE METHOD : overflow
//
//  Performs destructive overflow handling.
// ----------------------------------------------------------------------------

void
scfx_rep::overflow( const scfx_params& params, bool& o_flag )
{
    scfx_index x = calc_indices( params.iwl() - 1 );

    if( x.wi() >= size() )
        resize_to( x.wi() + 1, 1 );

    if( x.wi() < 0 )
    {
        resize_to( size() - x.wi(), -1 );
        x.wi( 0 );
    }

    bool zero_left = o_zero_left( x );
    bool bit_at = o_bit_at( x );
    bool zero_right = o_zero_right( x );

    bool under = false;
    bool over = false;

    sc_enc enc = params.enc();

    if( enc == SC_TC_ )
    {
	if( is_neg() )
	{
	    if( params.o_mode() == SC_SAT_SYM )
		under = ( ! zero_left || bit_at );
	    else
		under = (! zero_left || (zero_left && bit_at && ! zero_right));
	}
	else
	    over = ( ! zero_left || bit_at );
    }
    else
    {
	if( is_neg() )
	    under = ( ! is_zero() );
        else
	    over = ( ! zero_left );
    }

    o_flag = ( under || over );

    if( o_flag )
    {
	scfx_index x2 = calc_indices( params.iwl() - params.wl() );

	if( x2.wi() < 0 )
	{
	    resize_to( size() - x2.wi(), -1 );
	    x.wi( x.wi() - x2.wi() );
	    x2.wi( 0 );
	}

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

		if( n_bits == 0 )
		{
		    // wrap-around all 'wl' bits
		    toggle_tc();
		    o_extend( x, enc );
		    toggle_tc();
		}
		else if( n_bits < params.wl() )
		{
		    scfx_index x3 = calc_indices( params.iwl() - 1 - n_bits );

		    // wrap-around least significant 'wl - n_bits' bits;
		    // saturate most significant 'n_bits' bits
		    toggle_tc();
		    o_set( x, x3, enc, under );
		    o_extend( x, enc );
		    toggle_tc();
		}
		else
		{
		    // saturate all 'wl' bits
		    if( under )
			o_set_low( x, enc );
		    else
			o_set_high( x, x2, enc );
		}
		break;
	    }
            case SC_SAT:			// saturation
	    {
		if( under )
		    o_set_low( x, enc );
		else
		    o_set_high( x, x2, enc );
		break;
	    }
            case SC_SAT_SYM:			// symmetrical saturation
	    {
		if( under )
		{
		    if( enc == SC_TC_ )
			o_set_high( x, x2, SC_TC_, -1 );
		    else
			o_set_low( x, SC_US_ );
		}
		else
		    o_set_high( x, x2, enc );
		break;
	    }
            case SC_SAT_ZERO:			// saturation to zero
	    {
		set_zero();
		break;
	    }
            case SC_WRAP_SM:			// sign magnitude wrap-around
	    {
		SC_ERROR_IF_( enc == SC_US_,
			      sc_core::SC_ID_WRAP_SM_NOT_DEFINED_ );

		int n_bits = params.n_bits();

		if( n_bits == 0 )
		{
		    scfx_index x4 = calc_indices( params.iwl() );

		    if( x4.wi() >= size() )
			resize_to( x4.wi() + 1, 1 );

		    toggle_tc();
		    if( o_bit_at( x4 ) != o_bit_at( x ) )
			o_invert( x2 );
		    o_extend( x, SC_TC_ );
		    toggle_tc();
		}
		else if( n_bits == 1 )
		{
		    toggle_tc();
		    if( is_neg() != o_bit_at( x ) )
			o_invert( x2 );
		    o_extend( x, SC_TC_ );
		    toggle_tc();
		}
		else if( n_bits < params.wl() )
		{
		    scfx_index x3 = calc_indices( params.iwl() - 1 - n_bits );
		    scfx_index x4 = calc_indices( params.iwl() - n_bits );

		    // wrap-around least significant 'wl - n_bits' bits;
		    // saturate most significant 'n_bits' bits
		    toggle_tc();
		    if( is_neg() == o_bit_at( x4 ) )
			o_invert( x2 );
		    o_set( x, x3, SC_TC_, under );
		    o_extend( x, SC_TC_ );
		    toggle_tc();
		}
		else
		{
		    if( under )
			o_set_low( x, SC_TC_ );
		    else
			o_set_high( x, x2, SC_TC_ );
		}
		break;
	    }
            default:
	        ;
	}

	find_sw();
    }
}


// ----------------------------------------------------------------------------
//  PUBLIC METHOD : cast
//
//  Performs a destructive cast operation on a scfx_rep.
// ----------------------------------------------------------------------------

void
scfx_rep::cast( const scfx_params& params, bool& q_flag, bool& o_flag )
{
    q_flag = false;
    o_flag = false;

    // check for special cases
    
    if( is_zero() )
    {
	if( is_neg() )
	    m_sign = 1;
	return;
    }

    // perform casting

    quantization( params, q_flag );
    overflow( params, o_flag );

    // check for special case: -0

    if( is_zero() && is_neg() )
	m_sign = 1;
}


// ----------------------------------------------------------------------------
//  make sure, the two mantissas are aligned
// ----------------------------------------------------------------------------

void
align( const scfx_rep& lhs, const scfx_rep& rhs, int& new_wp,
       int& len_mant, scfx_mant_ref& lhs_mant, scfx_mant_ref& rhs_mant )
{
    bool need_lhs = true;
    bool need_rhs = true;

    if( lhs.m_wp != rhs.m_wp || lhs.size() != rhs.size() )
    {
	int lower_bound_lhs = lhs.m_lsw - lhs.m_wp;
	int upper_bound_lhs = lhs.m_msw - lhs.m_wp;
	int lower_bound_rhs = rhs.m_lsw - rhs.m_wp;
	int upper_bound_rhs = rhs.m_msw - rhs.m_wp;

	int lower_bound = sc_min( lower_bound_lhs, lower_bound_rhs );
	int upper_bound = sc_max( upper_bound_lhs, upper_bound_rhs );

	new_wp   = -lower_bound;
	len_mant = sc_max( min_mant, upper_bound - lower_bound + 1 );

	if( new_wp != lhs.m_wp || len_mant != lhs.size() )
	{
	    lhs_mant = lhs.resize( len_mant, new_wp );
	    need_lhs = false;
	}

	if( new_wp != rhs.m_wp || len_mant != rhs.size() )
        {
	    rhs_mant = rhs.resize( len_mant, new_wp );
	    need_rhs = false;
	}
    }

    if( need_lhs )
    {
	lhs_mant = lhs.m_mant;
    }

    if( need_rhs )
    {
	rhs_mant = rhs.m_mant;
    }
}


// ----------------------------------------------------------------------------
//  compare two mantissas
// ----------------------------------------------------------------------------

int
compare_msw_ff( const scfx_rep& lhs, const scfx_rep& rhs )
{
    // special case: rhs.m_mant[rhs.m_msw + 1] == 1
    if( rhs.m_msw < rhs.size() - 1 && rhs.m_mant[rhs.m_msw + 1 ] != 0 )
    {
	return -1;
    }

    int lhs_size = lhs.m_msw - lhs.m_lsw + 1;
    int rhs_size = rhs.m_msw - rhs.m_lsw + 1;

    int size = sc_min( lhs_size, rhs_size );

    int lhs_index = lhs.m_msw;
    int rhs_index = rhs.m_msw;

    int i;

    for( i = 0;
	 i < size && lhs.m_mant[lhs_index] == rhs.m_mant[rhs_index];
	 i ++ )
    {
	lhs_index --;
	rhs_index --;
    }

    if( i == size )
    {
	if( lhs_size == rhs_size )
	{
	    return 0;
	}

	if( lhs_size < rhs_size )
	{
	    return -1;
	}
	else
	{
	    return 1;
	}
  }

  if( lhs.m_mant[lhs_index] < rhs.m_mant[rhs_index] )
  {
      return -1;
  } else {
      return 1;
  }
}


// ----------------------------------------------------------------------------
//  divide the mantissa by ten
// ----------------------------------------------------------------------------

unsigned int
scfx_rep::divide_by_ten()
{
#if defined( SC_BIG_ENDIAN )
    half_word* hw = (half_word*) &m_mant[m_msw];
#elif defined( SC_LITTLE_ENDIAN )
    half_word* hw = ( (half_word*) &m_mant[m_msw] ) + 1;
#endif

    unsigned int remainder = 0;

    word_short ls;
    ls.l = 0;

#if defined( SC_BIG_ENDIAN )
    for( int i = 0, end = ( m_msw - m_wp + 1 ) * 2; i < end; i ++ )
#elif defined( SC_LITTLE_ENDIAN )
    for( int i = 0, end = -( m_msw - m_wp + 1 ) * 2; i > end; i -- )
#endif
    {
	ls.s.u = static_cast<half_word>( remainder );
	ls.s.l = hw[i];
	remainder = ls.l % 10;
	ls.l /= 10;
	hw[i] = ls.s.l;
    }

    return remainder;
}


// ----------------------------------------------------------------------------
//  multiply the mantissa by ten
// ----------------------------------------------------------------------------

void
scfx_rep::multiply_by_ten()
{
    int size = m_mant.size() + 1;

    scfx_mant mant8( size );
    scfx_mant mant2( size );

    size --;

    mant8[size] = (m_mant[size - 1] >> (bits_in_word - 3));
    mant2[size] = (m_mant[size - 1] >> (bits_in_word - 1));

    while( -- size )
    {
	mant8[size] = ( m_mant[size] << 3 ) |
	              ( m_mant[size - 1] >> ( bits_in_word - 3 ) );
	mant2[size] = ( m_mant[size] << 1 ) |
	              ( m_mant[size - 1] >> ( bits_in_word - 1 ) );
    }

    mant8[0] = ( m_mant[0] << 3 );
    mant2[0] = ( m_mant[0] << 1 );

    add_mants( m_mant.size(), m_mant, mant8, mant2 );

#if 0
    for( int i = size() - 1; i > 0; i -- )
    {
	m_mant[i] = ( m_mant[i] << 3 ) |
                    ( m_mant[i-1] >> ( bits_in_word - 3 ) )
	          + ( m_mant[i] << 1 ) |
                    ( m_mant[i-1] >> ( bits_in_word - 1 ) );
    }
    m_mant[0] = ( m_mant[0] << 3 ) + ( m_mant[0] << 1 );
#endif
}


// ----------------------------------------------------------------------------
//  normalize
// ----------------------------------------------------------------------------

void
scfx_rep::normalize( int exponent )
{
    int shift = exponent % bits_in_word;
    if( shift < 0 )
    {
	shift += bits_in_word;
    }

    if( shift )
    {
	shift_left( shift );
    }

    find_sw();

    m_wp = (shift - exponent) / bits_in_word;
}


// ----------------------------------------------------------------------------
//  return a new mantissa that is aligned and resized
// ----------------------------------------------------------------------------

scfx_mant*
scfx_rep::resize( int new_size, int new_wp ) const
{
    scfx_mant *result = new scfx_mant( new_size );

    result->clear();

    int shift = new_wp - m_wp;

    for( int j = m_lsw; j <= m_msw; j ++ )
    {
	(*result)[j+shift] = m_mant[j];
    }

    return result;
}


// ----------------------------------------------------------------------------
//  set a single bit
// ----------------------------------------------------------------------------

void
scfx_rep::set_bin( int i )
{
    m_mant[i >> 5] |= 1 << ( i & 31 );
}


// ----------------------------------------------------------------------------
//  set three bits
// ----------------------------------------------------------------------------

void
scfx_rep::set_oct( int i, int n )
{
    if( n & 1 )
    {
	m_mant[i >> 5] |= 1 << ( i & 31 );
    }
    i ++;
    if( n & 2 )
    {
	m_mant[i >> 5] |= 1 << ( i & 31 );
    }
    i ++;
    if( n & 4 )
    {
	m_mant[i >> 5] |= 1 << ( i & 31 );
    }
}


// ----------------------------------------------------------------------------
//  set four bits
// ----------------------------------------------------------------------------

void
scfx_rep::set_hex( int i, int n )
{
    if( n & 1 )
    {
	m_mant[i >> 5] |= 1 << ( i & 31 );
    }
    i ++;
    if( n & 2 )
    {
	m_mant[i >> 5] |= 1 << ( i & 31 );
    }
    i ++;
    if( n & 4 )
    {
	m_mant[i >> 5] |= 1 << ( i & 31 );
    }
    i ++;
    if( n & 8 )
    {
	m_mant[i >> 5] |= 1 << ( i & 31 );
    }
}


// ----------------------------------------------------------------------------
//  PRIVATE METHOD : shift_left
//
//  Shifts a scfx_rep to the left by a MAXIMUM of bits_in_word - 1 bits.
// ----------------------------------------------------------------------------

void
scfx_rep::shift_left( int n )
{
    if( n != 0 )
    {
	int shift_left  = n;
	int shift_right = bits_in_word - n;

	SC_ASSERT_( !(m_mant[size()-1] >> shift_right),
		    "shift_left overflow" );

	for( int i = size() - 1; i > 0; i -- )
	{
	    m_mant[i] = ( m_mant[i] << shift_left ) |
		       ( m_mant[i-1] >> shift_right );
	}
	m_mant[0] <<= shift_left;
    }
}


// ----------------------------------------------------------------------------
//  PRIVATE METHOD : shift_right
//
//  Shifts a scfx_rep to the right by a MAXIMUM of bits_in_word - 1 bits.
// ----------------------------------------------------------------------------

void
scfx_rep::shift_right( int n )
{
    if( n != 0 )
    {
	int shift_left  = bits_in_word - n;
	int shift_right = n;

	SC_ASSERT_( !(m_mant[0] << shift_left), "shift_right overflow" );

	for( int i = 0; i < size() - 1; i ++ )
	{
	    m_mant[i] = ( m_mant[i] >> shift_right ) |
		       ( m_mant[i+1] << shift_left );
	}
	m_mant[size()-1] >>= shift_right;
    }
}


// ----------------------------------------------------------------------------
//  METHOD : get_bit
//
//  Tests a bit, in two's complement.
// ----------------------------------------------------------------------------

bool
scfx_rep::get_bit( int i ) const
{
    if( ! is_normal() )
	return false;

    scfx_index x = calc_indices( i );

    if( x.wi() >= size() )
	return is_neg();

    if( x.wi() < 0 )
	return false;

    const_cast<scfx_rep*>( this )->toggle_tc();

    bool result = ( m_mant[x.wi()] & ( 1 << x.bi() ) ) != 0;

    const_cast<scfx_rep*>( this )->toggle_tc();

    return result;
}


// ----------------------------------------------------------------------------
//  METHOD : set
//
//  Sets a bit, in two's complement, between iwl-1 and -fwl.
// ----------------------------------------------------------------------------

bool
scfx_rep::set( int i, const scfx_params& params )
{
    if( ! is_normal() )
	return false;

    scfx_index x = calc_indices( i );

    if( x.wi() >= size() )
    {
	if( is_neg() )
	    return true;
	else
	    resize_to( x.wi() + 1, 1 );
    }
    else if( x.wi() < 0 )
    {
	resize_to( size() - x.wi(), -1 );
	x.wi( 0 );
    }

    toggle_tc();

    m_mant[x.wi()] |= 1 << x.bi();

    if( i == params.iwl() - 1 )
        o_extend( x, params.enc() );  // sign extension

    toggle_tc();

    find_sw();

    return true;
}


// ----------------------------------------------------------------------------
//  METHOD : clear
//
//  Clears a bit, in two's complement, between iwl-1 and -fwl.
// ----------------------------------------------------------------------------

bool
scfx_rep::clear( int i, const scfx_params& params )
{
    if( ! is_normal() )
	return false;

    scfx_index x = calc_indices( i );

    if( x.wi() >= size() )
    {
	if( ! is_neg() )
	    return true;
	else
	    resize_to( x.wi() + 1, 1 );
    }
    else if( x.wi() < 0 )
	return true;

    toggle_tc();

    m_mant[x.wi()] &= ~( 1 << x.bi() );

    if( i == params.iwl() - 1 )
        o_extend( x, params.enc() );  // sign extension

    toggle_tc();

    find_sw();

    return true;
}


// ----------------------------------------------------------------------------
//  METHOD : get_slice
// ----------------------------------------------------------------------------

bool
scfx_rep::get_slice( int i, int j, const scfx_params&,
		     sc_bv_base& bv ) const
{
    if( is_nan() || is_inf() )
	return false;

    // get the bits

    int l = j;
    for( int k = 0; k < bv.length(); ++ k )
    {
	bv[k] = get_bit( l );

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

    return true;
}

bool
scfx_rep::set_slice( int i, int j, const scfx_params& params,
		     const sc_bv_base& bv )
{
    if( is_nan() || is_inf() )
        return false;

    // set the bits

    int l = j;
    for( int k = 0; k < bv.length(); ++ k )
    {
	if( bv[k].to_bool() )
	    set( l, params );
	else
	    clear( l, params );

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

    return true;
}


// ----------------------------------------------------------------------------
//  METHOD : print
// ----------------------------------------------------------------------------

void
scfx_rep::print( ::std::ostream& os ) const
{
    os << to_string( SC_DEC, -1, SC_E );
}


// ----------------------------------------------------------------------------
//  METHOD : dump
// ----------------------------------------------------------------------------

void
scfx_rep::dump( ::std::ostream& os ) const
{
    os << "scfx_rep" << ::std::endl;
    os << "(" << ::std::endl;

    os << "mant  =" << ::std::endl;
    for( int i = size() - 1; i >= 0; i -- )
    {
	char buf[BUFSIZ];
	std::sprintf( buf, " %d: %10u (%8x)", i, (int) m_mant[i], (int) m_mant[i] );
	os << buf << ::std::endl;
    }

    os << "wp    = " << m_wp << ::std::endl;
    os << "sign  = " << m_sign << ::std::endl;

    os << "state = ";
    switch( m_state )
    {
        case normal:
	    os << "normal";
	    break;
        case infinity:
	    os << "infinity";
	    break;
        case not_a_number:
	    os << "not_a_number";
	    break;
        default:
	    os << "unknown";
    }
    os << ::std::endl;

    os << "msw   = " << m_msw << ::std::endl;
    os << "lsw   = " << m_lsw << ::std::endl;

    os << ")" << ::std::endl;
}


// ----------------------------------------------------------------------------
//  METHOD : get_type
// ----------------------------------------------------------------------------

void
scfx_rep::get_type( int& wl, int& iwl, sc_enc& enc ) const
{
    if( is_nan() || is_inf() )
    {
        wl  = 0;
        iwl = 0;
        enc = SC_TC_;
        return;
    }

    if( is_zero() )
    {
        wl  = 1;
        iwl = 1;
        enc = SC_US_;
        return;
    }

    int msb = ( m_msw - m_wp ) * bits_in_word
            + scfx_find_msb( m_mant[ m_msw ] ) + 1;
    while( get_bit( msb ) == get_bit( msb - 1 ) )
    {
        -- msb;
    }

    int lsb = ( m_lsw - m_wp ) * bits_in_word
            + scfx_find_lsb( m_mant[ m_lsw ] );

    if( is_neg() )
    {
        wl  = msb - lsb + 1;
        iwl = msb + 1;
        enc = SC_TC_;
    }
    else
    {
        wl  = msb - lsb;
        iwl = msb;
        enc = SC_US_;
    }
}


// ----------------------------------------------------------------------------
//  PRIVATE METHOD : round
//
//  Performs convergent rounding (rounding to even) as in floating-point.
// ----------------------------------------------------------------------------

void
scfx_rep::round( int wl )
{
    // check for special cases
    
    if( is_nan() || is_inf() || is_zero() )
	return;

    // estimate effective wordlength and compare

    int wl_effective;

    wl_effective = ( m_msw - m_lsw + 1 ) * bits_in_word;
    if( wl_effective <= wl )
	return;

    // calculate effective wordlength and compare

    int msb = scfx_find_msb( m_mant[m_msw] );
    int lsb = scfx_find_lsb( m_mant[m_lsw] );

    wl_effective = ( m_msw * bits_in_word + msb ) -
	           ( m_lsw * bits_in_word + lsb ) + 1;
    if( wl_effective <= wl )
	return;

    // perform rounding

    int wi = m_msw - ( wl - 1 ) / bits_in_word;
    int bi =  msb - ( wl - 1 ) % bits_in_word;
    if( bi < 0 )
    {
	-- wi;
	bi += bits_in_word;
    }

    scfx_index x( wi, bi );

    if( (q_bit( x ) && ! q_zero( x )) ||
	(q_bit( x ) && q_zero( x ) && q_odd( x )) )
	q_incr( x );
    q_clear( x );

    find_sw();

    m_r_flag = true;
}

} // namespace sc_dt


// Taf!
