rational.cpp

Ir a la documentación de este archivo.
00001 // rational.cpp        (c) 2005 [email protected]
00002 
00003 /** \file  rational.cpp
00004     \brief Implementaciones para la clase \c "rational"
00005 
00006     \author Adolfo Di Mare <[email protected]>
00007     \date   2005
00008 
00009     - Why English names??? ==> http://www.di-mare.com/adolfo/binder/c01.htm#sc04
00010 */
00011 
00012 #include "rational.h"
00013 #include  <cstdlib>
00014 #include  <cctype>     // isdigit()
00015 
00016 OPEN_namespace(ADH)       // ADH_port.h ==> namespace ADH {
00017 USING_namespace(ADH);     // ADH_port.h ==> using namespace ADH;
00018 
00019 /** Verifica la invariante de la clase \c rational.
00020     \par <em>Rep</em> Modelo de la clase:
00021     \code
00022     +---+
00023     | 3 | <==  m_num == numerador del n�mero racional
00024     +---+
00025     |134| <==  m_den == denominador del n�mero racional
00026     +---+
00027     \endcode
00028     - http://www.di-mare.com/adolfo/binder/c03.htm#k1-Rep
00029 
00030     \remark
00031     Libera al programador de implementar el m�todo \c Ok()
00032     - http://www.di-mare.com/adolfo/binder/c04.htm#sc11
00033 */
00034 bool check_ok( const rational& r ) {
00035     if (&r == 0) {
00036         /// - Invariante: ning�n objeto puede estar almacenado en la posici�n nula.
00037         return false;
00038     }
00039 
00040     if ( ! (r.m_den > 0) )  {
00041         /// - Invariante: el denominador debe ser un n�mero positivo.
00042         return false;
00043     }
00044     if (r.m_num == 0) {
00045         if ( r.m_den == 1 ) {
00046             /// - Invariante: el cero debe representarse con denominador igual a "1". 
00047             return true;
00048         }
00049         else {
00050             return false;
00051         }
00052     }
00053     if ( ! ( mcd(r.m_num, r.m_den) == 1 ) ) {
00054         /// - Invariante: el numerador y el denominador deben ser primos relativos.
00055         return false;
00056     }
00057     return true;
00058 } // check_ok()
00059 
00060 /** Verifica la invariante de la clase \c rational.
00061     \remark
00062     Esta implementaci�n nos se le mete al <em>Rep</em>
00063     (casi siempre no es posible implementar una funci�n como �sta).
00064       - http://www.di-mare.com/adolfo/binder/c03.htm#k1-Rep
00065     \remark
00066     Libera al programador de implementar el m�todo \c Ok()
00067     - http://www.di-mare.com/adolfo/binder/c04.htm#sc11
00068 */
00069 bool check_ok_no_Rep( const rational& r ) {
00070     if (&r == 0) {
00071         /// - Invariante: ning�n objeto puede estar almacenado en la posici�n nula.
00072         return false;
00073     }
00074 
00075     if ( ! (r.den() > 0) )  {
00076         /// - Invariante: el denominador debe ser un n�mero positivo.
00077         return false;
00078     }
00079     if (r.num() == 0) {
00080         if ( r.den() == 1 ) {
00081             /// - Invariante: el cero debe representarse con denominador igual a "1".
00082             return true;
00083         }
00084         else {
00085             return false;
00086         }
00087     }
00088     if ( ! ( mcd(r.num(), r.den()) == 1 ) ) {
00089         /// - Invariante: el numerador y el denominador deben ser primos relativos.
00090         return false;
00091     }
00092     return true;
00093 } // check_ok_no_Rep()
00094 
00095 
00096 /** Calcula el M�ximo Com�n Divisor de los n�meros \c "x" y \c "y".
00097     - <code> mcd(x,y) >= 1 </code> siempre.
00098     - MCD <==> GCD: <em> Greatest Common Divisor </em>.
00099 
00100     \pre
00101     <code> (y != 0) </code>
00102 
00103     \remark
00104     Se usa el algoritmo de Euclides para hacer el c�lculo.
00105 
00106     \par Ejemplo:
00107     \code
00108     2*3*5 == mcd( 2*2*2*2 * 3*3 * 5*5, 2*3*5 )
00109        30 == mcd( -3600, -30 )
00110     \endcode
00111 */
00112 long mcd(long x, long y) {
00113     long g = (x < 0 ? -x : x); // trabaja con valores positivos
00114     long r = (y < 0 ? -y : y); // "r" es el resto
00115     long temp;
00116 
00117     do {
00118         temp = r;
00119         r    = g % r;
00120         g    = temp;
00121     } while (0 != r);
00122 
00123     return g;
00124 }  // mcd()
00125 
00126 
00127 /** Simplifica el numerador y el denomidador.
00128     - Transforma el n�mero rational de manera que el numerador y el
00129       denominador sean primos relativos, asegurando adem�s que el
00130       denominador es siempre positivo.
00131     - Si <code>(m_num==0) ==> (m_den==1)</code>.
00132     - Simplifica la fracci�n para que \c m_num y \c m_den sean n�meros
00133       primos relativos ie, <code>mcd(m_num,m_den) == 1</code>.
00134     - Asegura que \c m_den sea un n�mero positivo.
00135     - Restaura la invariante de la clase \c rational.
00136 */
00137 void rational::Simplify() {
00138     if (m_num == 0) {
00139        m_den = 1;
00140     }
00141     long divisor = mcd(m_num, m_den);
00142     if (divisor > 1) {   // ==> (divisor != 0)
00143         m_num /= divisor;
00144         m_den /= divisor;
00145     }
00146     if (m_den < 0) {
00147         m_num = -m_num;
00148         m_den = -m_den;
00149     }
00150 }  // rational::Simplify()
00151 
00152 /// Le suma a \c "*this" el valor de \c "otro".
00153 rational& rational::operator += (const rational& otro) {
00154     m_num  = m_num * otro.m_den + m_den * otro.m_num;
00155     m_den *= otro.m_den;
00156     Simplify();
00157 
00158     return *this;
00159 }  // operator +=
00160 
00161 
00162 /// Le resta a \c "*this" el valor de \c "otro".
00163 rational& rational::operator -= (const rational& otro) {
00164     long oldm_den = m_den;
00165     long oldm_num = m_num;
00166     long d       = otro.m_den;
00167     long n       = otro.m_num;
00168 
00169     m_den *= d;
00170     m_num = oldm_num * d - oldm_den * n;
00171     Simplify();
00172 
00173     return *this;
00174 }  // operator -=
00175 
00176 /// Establece el varlor de \c "*this" a partir de la hilera \c "nStr".
00177 /// \pre \c "nStr" debe estar escrita en el formato "[num/den]".
00178 rational& rational::fromString (const char* nStr) {
00179     char ch;  // valor obtenido, caracter por caracter, de "nStr"
00180 
00181     bool es_positivo = true;    // manejo de los signos + y -
00182 
00183     // se brinca todo hasta el primer d�gito
00184     do {
00185         ch = *nStr; nStr++;
00186         if (ch == '-') {  // cambia de signo
00187             es_positivo = !es_positivo;
00188         }
00189     } while (!isdigit(ch));
00190 
00191     // se traga el numerador
00192     long num = 0;
00193     while (isdigit(ch)) { // convierte a decimal: izq --> der
00194         num = 10 * num + (ch-'0');
00195         ch = *nStr; nStr++;
00196     }
00197 
00198     // se brinca los blancos despu�s del numerador
00199     while (isspace(ch)) {
00200         ch = *nStr; nStr++;
00201     }
00202 
00203     long den;
00204     if (ch ==']') { // es un n�mero entero
00205         den = 1;
00206     }
00207     else {
00208         do {  // se brinca todo hasta el denominador
00209             ch = *nStr; nStr++;
00210             if (ch == '-') {
00211                 es_positivo = !es_positivo;
00212             }
00213         } while (!isdigit(ch));
00214 
00215         // se traga el denominador
00216         den = 0;
00217         while (isdigit(ch)) {
00218             den = 10 * den + (ch-'0');
00219             ch = *nStr; nStr++;
00220         }
00221         // Ya no importa si aparece o no el ']' del final del n�mero
00222     }
00223 
00224 
00225     // le cambia el signo, si hace falta
00226     if (! es_positivo) {
00227         num = -num;
00228     }
00229     set( num, den );
00230     return *this;
00231 }
00232 
00233 /** Graba el valor de \c "r" en el flujo \c "COUT".
00234     - Graba el valor en el formato [num/den].
00235     - En particular, este es el operador que se invoca
00236       cuando se usa, por ejemplo, este tipo de instrucci�n:
00237      \code
00238           cout << r << q;
00239      \endcode
00240 */
00241 ostream& operator<< (ostream &COUT, const rational& r) {
00242     if ( r.m_den == 1 ) { // no hay parte fraccional
00243         return COUT << "[" << r.m_num << "]" ;
00244     } else {
00245         return COUT << "[" << r.m_num << "/" << r.m_den << "]" ;
00246     }
00247 }  // operator <<
00248 
00249 /** Lee del flujo de texto \c "CIN" el valor de \c "r".
00250     \pre
00251     El n�mero rational debe haber sido escrito usando
00252     el formato "[r/den]", aunque es permisible usar
00253     algunos blancos.
00254     - Se termina de leer el valor s�lo cuando encuentra \c "]".
00255     - <code> [ -+-+-+-+- 4 / -- -+ -- 32  ] </code> se lee como
00256       <code> [1/8] </code>
00257 */
00258 istream& operator >> (istream &CIN, rational& r) {
00259     char ch;  // valor leido, letra por letra, de "CIN"
00260 
00261     bool es_positivo = true;    // manejo de los signos + y -
00262 
00263     // se brinca todo hasta el primer d�gito
00264     do {
00265         CIN >> ch;
00266         if (ch == '-') {  // cambia de signo
00267             es_positivo = !es_positivo;
00268         }
00269     } while (!isdigit(ch));
00270 
00271     // se traga el numerador
00272     r.m_num = 0;
00273     while (isdigit(ch)) { // convierte a decimal: izq --> der
00274         r.m_num = 10 * r.m_num + (ch-'0');
00275         CIN >> ch;
00276     }
00277 
00278     // se brinca los blancos despu�s del numerador
00279     while (isspace(ch)) {
00280         CIN >> ch;
00281     }
00282 
00283     if (ch ==']') { // es un n�mero entero
00284         r.m_den = 1;
00285     }
00286     else {
00287         do {  // se brinca todo hasta el denominador
00288             CIN >> ch;
00289             if (ch == '-') {
00290                 es_positivo = !es_positivo;
00291             }
00292         } while (!isdigit(ch));
00293 
00294         // se traga el denominador
00295         r.m_den = 0;
00296         while (isdigit(ch)) {
00297             r.m_den = 10 * r.m_den + (ch-'0');
00298             CIN >> ch;
00299         }
00300 
00301         // El programa se duerme si en el flujo de entrada
00302         // NO aparece el caracter delimitador final "]",
00303         // pues la lectura termina hasta encontrar el "]".
00304         while (ch != ']') {
00305             CIN >> ch;
00306         }
00307     }   // correcci�n: Andr�s Arias <[email protected]>
00308 
00309 
00310     // le cambia el signo, si hace falta
00311     if (! es_positivo) {
00312         r.m_num = -r.m_num;
00313     }
00314 
00315     r.Simplify();
00316     return CIN;
00317 /*
00318     no detecta errores...
00319     [1/0] lo lee y no se queja
00320     [ !#!#!$#@! 3/ aaaa 4  jajaja ] lo lee como 3/4
00321     ... pero no se supone que el usuario cometa errores...
00322 */
00323 
00324 }  // operator >>
00325 
00326 /// \c "x+y".
00327 /// - Calcula y retorna la suma \c "x+y".
00328 rational operator + (const rational &x, const rational &y) {
00329     long res_num, res_den;
00330     res_den = x.m_den * y.m_den;
00331     res_num = x.m_num * y.m_den + x.m_den * y.m_num;
00332 
00333     return rational(res_num, res_den);
00334 }  // operator + ()
00335 
00336 /// \c "x-y".
00337 /// - Calcula y retorna la resta \c "x-y".
00338 rational operator - (const rational &x, const rational &y) {
00339     long res_num, res_den;
00340 
00341     res_den = x.m_den * y.m_den;
00342     res_num = x.m_num * y.m_den - x.m_den * y.m_num;
00343 
00344     return rational(res_num, res_den);
00345 }  // operator - ()
00346 
00347 /// \c "x*y".
00348 /// - Calcula y retorna la multiplicaci�n \c "x*y".
00349 rational operator * (const rational &x, const rational &y) {
00350     long res_num, res_den;
00351 
00352     res_num = x.m_num * y.m_num;
00353     res_den = x.m_den * y.m_den;
00354 
00355     return rational(res_num, res_den);
00356 }  // operator * ()
00357 
00358 /// \c "x/y".
00359 /// - Calcula y retorna la divisi�n \c "x/y".
00360 /// \pre <code> y != 0 </code>
00361 rational operator / (const rational &x, const rational &y) {
00362     long res_num, res_den;
00363     if (0 != y.m_num) {
00364         res_num = x.m_num * y.m_den;
00365         res_den = x.m_den * y.m_num;
00366     }
00367     return rational(res_num, res_den);
00368 }  // operator / ()
00369 
00370 CLOSE_namespace(ADH)      // ADH_port.h ==> } // namespace ADH
00371 
00372 // EOF: rational.cpp

Generado el Thu Sep 6 23:18:36 2007 para Prueba de la clase rational: por  doxygen 1.5.3
Hosted by www.Geocities.ws

1