// ----------------------------------------------------------------------------------------
//
//  common/util/fixedpoint.cpp
//    
//  Bob Cook Development, Robotics Library
//  http://www.bobcookdev.com/rl/
//    
//  This file provides a fixed-point numbers for 8.8 and 16.16 variants.
//
//  Copyright (C) 2008 Bob Cook
//
//      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 Street, Fifth Floor, Boston, MA  02110-1301, USA.
//
// ----------------------------------------------------------------------------------------


#include "fixedpoint.h"

#include "project_defs.h"


// ----------------------------------------------------------------------------------------

#if defined( PRJ_FIXED16DOT16_ENABLE_CONVERT_FROM_STRING )

fixed16dot16 fixed16dot16_from_string( const char* str )
{
	uint16_t i = 0;
	uint16_t d = 0;
	uint16_t s = 1;
	bool     got_pt = false;
	bool     is_negative = false;

	if ( *str == '-' )
	{
		is_negative = true;
		str++;
	}
	else if ( *str == '+' )
	{
		str++;
	}

	while ( *str != '\0' )
	{
		char digit = *str++;

		if ( ( digit >= '0' ) && ( digit <= '9' ) )
		{
			if ( got_pt )
			{
				if ( s < 10000 ) // limit the number of digits, preserves accuracy
				{
					d *= 10;
					d += digit - static_cast< uint8_t >( '0' );
					s *= 10;
				}
			}
			else
			{
				i *= 10;
				i += digit - static_cast< uint8_t >( '0' );
			}
		}
		else if ( digit == '.' )
		{
			if ( got_pt ) break;
			got_pt = true;
		}
		else
		{
			break;
		}
    }

	fixed16dot16 r;

	if ( d != 0 )
	{
		r = fixed16dot16( 1 ) / ( fixed16dot16( s ) / fixed16dot16( d ) );
	}

	r += fixed16dot16( i );

	if ( is_negative )
	{
		r *= fixed16dot16( -1 );
	}

	return r;
}

#endif


// ----------------------------------------------------------------------------------------

#if defined( PRJ_FIXED8DOT8_ENABLE_CONVERT_FROM_STRING )

fixed8dot8 fixed8dot8_from_string( const char* str )
{
	uint8_t i = 0;
	uint8_t d = 0;
	uint8_t s = 1;
	bool    got_pt = false;
	bool    is_negative = false;

	if ( *str == '-' )
	{
		is_negative = true;
		str++;
	}
	else if ( *str == '+' )
	{
		str++;
	}

	while ( *str != '\0' )
	{
		char digit = *str++;

		if ( ( digit >= '0' ) && ( digit <= '9' ) )
		{
			if ( got_pt )
			{
				if ( s < 100 ) // limit the number of digits, preserves accuracy
				{
					d *= 10;
					d += digit - static_cast< uint8_t >( '0' );
					s *= 10;
				}
			}
			else
			{
				i *= 10;
				i += digit - static_cast< uint8_t >( '0' );
			}
		}
		else if ( digit == '.' )
		{
			if ( got_pt ) break;
			got_pt = true;
		}
		else
		{
			break;
		}
    }

	fixed8dot8 r;

	if ( d != 0 )
	{
		r = fixed8dot8( 1 ) / ( fixed8dot8( s ) / fixed8dot8( d ) );
	}

	r += fixed8dot8( i );

	if ( is_negative )
	{
		r *= fixed8dot8( -1 );
	}

	return r;
}

#endif


// ----------------------------------------------------------------------------------------

#if defined( PRJ_FIXED16DOT16_ENABLE_CONVERT_TO_DOUBLE )

double fixed_to_double( const fixed16dot16& num )
{
	double r = static_cast< double >( num.asInteger() );
	r += static_cast< double >( num.asFractional() ) / ( 1 << 16 );
	return r;
}

#endif


// ----------------------------------------------------------------------------------------

#if defined( PRJ_FIXED8DOT8_ENABLE_CONVERT_TO_DOUBLE )

double fixed_to_double( const fixed8dot8& num )
{
	double r = static_cast< double >( num.asInteger() );
	r += static_cast< double >( num.asFractional() ) / ( 1 << 8 );
	return r;
}

#endif


// ----------------------------------------------------------------------------------------
//  end of fixedpoint.cpp
// ----------------------------------------------------------------------------------------

