/*************************************************************************** * 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 #include #include #include 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( decimal ); decimal -= static_cast( 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( m_whole ) + ( static_cast( m_numerator ) / static_cast( m_denominator ) ); } int MixedNumber::gcd( int n, int m ) { int r; while ( n != 0 ) { r = m % n; m = n; n = r; } return m; }