rational.cpp

Ir a la documentación de este archivo.
00001 // rational.cpp        (c) 2005 adolfo@di-mare.com
00002 
00003 /** \file  rational.cpp
00004     \brief Implementaciones para la clase \c "rational"
00005 
00006     \author Adolfo Di Mare <adolfo@di-mare.com>
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 /// Multiplica \c "*a" por \c "b".
00177 rational & operator *= ( rational & a, const rational & b ) {
00178     a.set( a.num() * b.num(), a.den() * b.den() );
00179     return a;
00180 }  // operator *=
00181 
00182 /// Establece el valor de \c "*this" a partir de la hilera \c "nStr".
00183 /// \pre \c "nStr" debe estar escrita en el formato "[num/den]".
00184 rational& rational::fromString (const char* nStr) {
00185     char ch;  // valor obtenido, caracter por caracter, de "nStr"
00186 
00187     bool es_positivo = true;    // manejo de los signos + y -
00188 
00189     // se brinca todo hasta el primer dígito
00190     do {
00191         ch = *nStr; nStr++;
00192         if (ch == '-') {  // cambia de signo
00193             es_positivo = !es_positivo;
00194         }
00195     } while (!isdigit(ch));
00196 
00197     // se traga el numerador
00198     long num = 0;
00199     while (isdigit(ch)) { // convierte a decimal: izq --> der
00200         num = 10 * num + (ch-'0');
00201         ch = *nStr; nStr++;
00202     }
00203 
00204     // se brinca los blancos después del numerador
00205     while (isspace(ch)) {
00206         ch = *nStr; nStr++;
00207     }
00208 
00209     long den;
00210     if (ch ==']') { // es un número entero
00211         den = 1;
00212     }
00213     else {
00214         do {  // se brinca todo hasta el denominador
00215             ch = *nStr; nStr++;
00216             if (ch == '-') {
00217                 es_positivo = !es_positivo;
00218             }
00219         } while (!isdigit(ch));
00220 
00221         // se traga el denominador
00222         den = 0;
00223         while (isdigit(ch)) {
00224             den = 10 * den + (ch-'0');
00225             ch = *nStr; nStr++;
00226         }
00227         // Ya no importa si aparece o no el ']' del final del número
00228     }
00229 
00230 
00231     // le cambia el signo, si hace falta
00232     if (! es_positivo) {
00233         num = -num;
00234     }
00235     set( num, den );
00236     return *this;
00237 }
00238 
00239 /** Graba el valor de \c "r" en el flujo \c "COUT".
00240     - Graba el valor en el formato [num/den].
00241     - En particular, este es el operador que se invoca
00242       cuando se usa, por ejemplo, este tipo de instrucción:
00243      \code
00244           cout << r << q;
00245      \endcode
00246 */
00247 std::ostream& operator<< (std::ostream &COUT, const rational& r) {
00248     if ( r.m_den == 1 ) { // no hay parte fraccional
00249         return COUT << "[" << r.m_num << "]" ;
00250     } else {
00251         return COUT << "[" << r.m_num << "/" << r.m_den << "]" ;
00252     }
00253 }  // operator <<
00254 
00255 /** Lee del flujo de texto \c "CIN" el valor de \c "r".
00256     \pre
00257     El número rational debe haber sido escrito usando
00258     el formato "[r/den]", aunque es permisible usar
00259     algunos blancos.
00260     - Se termina de leer el valor sólo cuando encuentra \c "]".
00261     - <code> [ -+-+-+-+- 4 / -- -+ -- 32  ] </code> se lee como
00262       <code> [1/8] </code>
00263 */
00264 std::istream& operator >> (std::istream &CIN, rational& r) {
00265     char ch;  // valor leido, letra por letra, de "CIN"
00266 
00267     bool es_positivo = true;    // manejo de los signos + y -
00268 
00269     // se brinca todo hasta el primer dígito
00270     do {
00271         CIN >> ch;
00272         if (ch == '-') {  // cambia de signo
00273             es_positivo = !es_positivo;
00274         }
00275     } while (!isdigit(ch));
00276 
00277     // se traga el numerador
00278     r.m_num = 0;
00279     while (isdigit(ch)) { // convierte a decimal: izq --> der
00280         r.m_num = 10 * r.m_num + (ch-'0');
00281         CIN >> ch;
00282     }
00283 
00284     // se brinca los blancos después del numerador
00285     while (isspace(ch)) {
00286         CIN >> ch;
00287     }
00288 
00289     if (ch ==']') { // es un número entero
00290         r.m_den = 1;
00291     }
00292     else {
00293         do {  // se brinca todo hasta el denominador
00294             CIN >> ch;
00295             if (ch == '-') {
00296                 es_positivo = !es_positivo;
00297             }
00298         } while (!isdigit(ch));
00299 
00300         // se traga el denominador
00301         r.m_den = 0;
00302         while (isdigit(ch)) {
00303             r.m_den = 10 * r.m_den + (ch-'0');
00304             CIN >> ch;
00305         }
00306 
00307         // El programa se duerme si en el flujo de entrada
00308         // NO aparece el caracter delimitador final "]",
00309         // pues la lectura termina hasta encontrar el "]".
00310         while (ch != ']') {
00311             CIN >> ch;
00312         }
00313     }   // corrección: Andrés Arias <e980300@anubis.ecci.ucr.ac.cr>
00314 
00315 
00316     // le cambia el signo, si hace falta
00317     if (! es_positivo) {
00318         r.m_num = -r.m_num;
00319     }
00320 
00321     r.Simplify();
00322     return CIN;
00323 /*
00324     no detecta errores...
00325     [1/0] lo lee y no se queja
00326     [ !#!#!$#@! 3/ aaaa 4  jajaja ] lo lee como 3/4
00327     ... pero no se supone que el usuario cometa errores...
00328 */
00329 
00330 }  // operator >>
00331 
00332 /// \c "x+y".
00333 /// - Calcula y retorna la suma \c "x+y".
00334 rational operator + (const rational &x, const rational &y) {
00335     long res_num, res_den;
00336     res_den = x.m_den * y.m_den;
00337     res_num = x.m_num * y.m_den + x.m_den * y.m_num;
00338 
00339     return rational(res_num, res_den);
00340 }  // operator + ()
00341 
00342 /// \c "x-y".
00343 /// - Calcula y retorna la resta \c "x-y".
00344 rational operator - (const rational &x, const rational &y) {
00345     long res_num, res_den;
00346 
00347     res_den = x.m_den * y.m_den;
00348     res_num = x.m_num * y.m_den - x.m_den * y.m_num;
00349 
00350     return rational(res_num, res_den);
00351 }  // operator - ()
00352 
00353 /// \c "x*y".
00354 /// - Calcula y retorna la multiplicación \c "x*y".
00355 rational operator * (const rational &x, const rational &y) {
00356     long res_num, res_den;
00357 
00358     res_num = x.m_num * y.m_num;
00359     res_den = x.m_den * y.m_den;
00360 
00361     return rational(res_num, res_den);
00362 }  // operator * ()
00363 
00364 /// \c "x/y".
00365 /// - Calcula y retorna la división \c "x/y".
00366 /// \pre <code> y != 0 </code>
00367 rational operator / (const rational &x, const rational &y) {
00368     long res_num, res_den;
00369     if (0 != y.m_num) {
00370         res_num = x.m_num * y.m_den;
00371         res_den = x.m_den * y.m_num;
00372     }
00373     return rational(res_num, res_den);
00374 }  // operator / ()
00375 
00376 CLOSE_namespace(ADH)      // ADH_port.h ==> } // namespace ADH
00377 
00378 // EOF: rational.cpp

Generado el Wed Apr 9 13:16:23 2008 para Prueba de la clase rational: por  doxygen 1.5.4