diff options
Diffstat (limited to 'src/datablocks/mixednumber.cpp')
-rw-r--r-- | src/datablocks/mixednumber.cpp | 289 |
1 files changed, 289 insertions, 0 deletions
diff --git a/src/datablocks/mixednumber.cpp b/src/datablocks/mixednumber.cpp new file mode 100644 index 0000000..17a9047 --- /dev/null +++ b/src/datablocks/mixednumber.cpp @@ -0,0 +1,289 @@ +/*************************************************************************** +* Copyright (C) 2003 by krecipes.sourceforge.net authors * +* * +* * +* 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. * +***************************************************************************/ +#include "mixednumber.h" + +#include <tqregexp.h> + +#include <tdeglobal.h> +#include <tdelocale.h> +#include <kdebug.h> + +TQString beautify( const TQString &num ) +{ + TQString copy( num ); + copy.remove( TQRegExp( TQString( "(%1){0,1}0+$" ).arg( TQRegExp::escape( TDEGlobal::locale() ->decimalSymbol() ) ) ) ); + return copy; +} + +MixedNumber::MixedNumber() : + m_whole( 0 ), + m_numerator( 0 ), + m_denominator( 1 ), + locale( TDEGlobal::locale() ) +{} + +MixedNumber::MixedNumber( int whole, int numerator, int denominator ) : + m_whole( whole ), + m_numerator( numerator ), + m_denominator( denominator ), + locale( TDEGlobal::locale() ) +{} + +MixedNumber::MixedNumber( double decimal, double precision ) : + locale( TDEGlobal::locale() ) +{ + // find nearest fraction + int intPart = static_cast<int>( decimal ); + decimal -= static_cast<double>( intPart ); + + MixedNumber low( 0, 0, 1 ); // "A" = 0/1 + MixedNumber high( 0, 1, 1 ); // "B" = 1/1 + + for ( int i = 0; i < 100; ++i ) { + double testLow = low.denominator() * decimal - low.numerator(); + double testHigh = high.numerator() - high.denominator() * decimal; + + if ( testHigh < precision * high.denominator() ) + break; // high is answer + if ( testLow < precision * low.denominator() ) { // low is answer + high = low; + break; + } + + if ( i & 1 ) { // odd step: add multiple of low to high + double test = testHigh / testLow; + int count = ( int ) test; // "N" + int num = ( count + 1 ) * low.numerator() + high.numerator(); + int denom = ( count + 1 ) * low.denominator() + high.denominator(); + + if ( ( num > 0x8000 ) || ( denom > 0x10000 ) ) + break; + + high.setNumerator( num - low.numerator() ); // new "A" + high.setDenominator( denom - low.denominator() ); + low.setNumerator( num ); // new "B" + low.setDenominator( denom ); + } + else { // even step: add multiple of high to low + double test = testLow / testHigh; + int count = ( int ) test; // "N" + int num = low.numerator() + ( count + 1 ) * high.numerator(); + int denom = low.denominator() + ( count + 1 ) * high.denominator(); + + if ( ( num > 0x10000 ) || ( denom > 0x10000 ) ) + break; + + low.setNumerator( num - high.numerator() ); // new "A" + low.setDenominator( denom - high.denominator() ); + high.setNumerator( num ); // new "B" + high.setDenominator( denom ); + } + } + + m_numerator = high.numerator(); + m_denominator = high.denominator(); + m_whole = intPart; +} + +MixedNumber::~MixedNumber() +{} + +int MixedNumber::getNumerator( const TQString &input, int space_index, int slash_index, bool *ok ) +{ + return input.mid( space_index + 1, slash_index - space_index - 1 ).toInt( ok ); +} + +int MixedNumber::getDenominator( const TQString &input, int slash_index, bool *ok ) +{ + return input.mid( slash_index + 1, input.length() ).toInt( ok ); +} + +MixedNumber MixedNumber::fromString( const TQString &str, bool *ok, bool locale_aware ) +{ + TQString input = str.stripWhiteSpace(); + if ( input.isEmpty() ) { + if ( ok ) { + *ok = true; + } + return MixedNumber(); + } + + TDELocale *locale = TDEGlobal::locale(); + + bool num_ok; + + int whole; + int numerator; + int denominator; + + int space_index = input.find( " " ); + int slash_index = input.find( "/" ); + + if ( space_index == -1 ) { + if ( slash_index == -1 ) //input contains no fractional part + { + TQString decimal_symbol = ( locale_aware ) ? locale->decimalSymbol() : "."; + if ( input.endsWith( decimal_symbol ) ) + { + if ( ok ) { + *ok = false; + } + return MixedNumber(); + } + + double decimal = ( locale_aware ) ? locale->readNumber( input, &num_ok ) : input.toDouble( &num_ok ); + + if ( !num_ok ) + { + if ( ok ) { + *ok = false; + } + return MixedNumber(); + } + + if ( ok ) + { + *ok = true; + } + return MixedNumber( decimal ); + } + else //input just contains a fraction + { + whole = 0; + + numerator = MixedNumber::getNumerator( input, space_index, slash_index, &num_ok ); + if ( !num_ok ) { + if ( ok ) { + *ok = false; + } + return MixedNumber(); + } + + denominator = MixedNumber::getDenominator( input, slash_index, &num_ok ); + if ( !num_ok || denominator == 0 ) { + if ( ok ) { + *ok = false; + } + return MixedNumber(); + } + + if ( ok ) { + *ok = true; + } + return MixedNumber( whole, numerator, denominator ); + } + if ( ok ) { + *ok = false; + } + return MixedNumber(); + } + + whole = input.mid( 0, space_index ).toInt( &num_ok ); + if ( !num_ok ) { + if ( ok ) { + *ok = false; + } + return MixedNumber(); + } + + numerator = MixedNumber::getNumerator( input, space_index, slash_index, &num_ok ); + if ( !num_ok ) { + if ( ok ) { + *ok = false; + } + return MixedNumber(); + } + + denominator = MixedNumber::getDenominator( input, slash_index, &num_ok ); + if ( !num_ok || denominator == 0 ) { + if ( ok ) { + *ok = false; + } + return MixedNumber(); + } + + if ( ok ) { + *ok = true; + } + return MixedNumber( whole, numerator, denominator ); +} + +TQString MixedNumber::toString( Format format, bool locale_aware ) const +{ + if ( format == DecimalFormat ) { + if ( locale_aware ) + return beautify( locale->formatNumber( toDouble(), 5 ) ); + else + return TQString::number( toDouble() ); + } + + if ( m_numerator == 0 && m_whole == 0 ) + return TQString( "0" ); + + + TQString result; + + if ( m_whole != 0 ) { + result += TQString::number( m_whole ); + if ( m_numerator != 0 ) + result += " "; + } + + if ( m_numerator != 0 ) + result += TQString::number( m_numerator ) + "/" + TQString::number( m_denominator ); + + return result; +} + +bool MixedNumber::operator!=( const MixedNumber &fraction ) +{ + return ( fraction.toDouble() != toDouble() ); +} + +MixedNumber& MixedNumber::operator+=( const MixedNumber &fraction ) +{ + m_numerator = ( m_numerator * fraction.m_denominator ) + ( m_denominator * fraction.m_numerator ); + m_denominator = m_denominator * fraction.m_denominator; + m_whole += fraction.m_whole; + simplify(); + + return *this; +} + +MixedNumber& MixedNumber::operator+=( double d ) +{ + MixedNumber mn(d); + *this += mn; + return *this; +} + +void MixedNumber::simplify() +{ + int divisor = gcd( m_numerator, m_denominator ); + m_numerator /= divisor; + m_denominator /= divisor; +} + +double MixedNumber::toDouble() const +{ + return static_cast<double>( m_whole ) + ( static_cast<double>( m_numerator ) / static_cast<double>( m_denominator ) ); +} + +int MixedNumber::gcd( int n, int m ) +{ + int r; + while ( n != 0 ) { + r = m % n; + m = n; + n = r; + } + + return m; +} |