summaryrefslogtreecommitdiffstats
path: root/src/datablocks/mixednumber.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/datablocks/mixednumber.cpp')
-rw-r--r--src/datablocks/mixednumber.cpp289
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;
+}