Página principal | Lista de namespace | Jerarquía de la clase | Lista de componentes | Lista de archivos | Miembros de las clases | Archivos de los miembros

rational.h

Ir a la documentación de este archivo.
00001 // rational.h   (c) 2005 [email protected]
00002 
00003 /** \file  rational.h
00004     \brief Declara el tipo \c "rational".
00005     - La clase \c rational implementa las operaciones aritméticas
00006       principales para números rationales.
00007 
00008     - <code> [1/3] == [2/6] ==  ...   [9/27] == ... </code>
00009     - <code> [1/3]  * [2/6] / [3/9] - [9/27] </code>
00010 
00011     - Permite usar racionales en cualquier sitio en donde se puedan
00012       usar valores numéricos.
00013 
00014     \author Adolfo Di Mare <[email protected]>
00015     \date   2005
00016 */
00017 
00018 
00019 #ifndef rational_h
00020 #define rational_h ///< Evita la inclusión múltiple
00021 
00022 #include "ADH_port.h"
00023 #include "decimal.h"
00024 #include <iostream>
00025 using namespace std;
00026 
00027 /**  La clase \c rational implementa las operaciones aritméticas
00028      principales para números rationales.
00029      - <code> [1/3] == [2/6] ==  ...   [9/27] == ... </code>
00030      - <code> [1/3]  * [2/6] / [3/9] - [9/27] </code>
00031 */
00032 template <class INT>
00033 class rational {
00034 private:
00035     INT m_num; ///< Numerador
00036     INT m_den; ///< Denominador
00037 
00038     void Simplify();
00039 
00040 public:
00041     // constructores
00042     rational() : m_num(0), m_den(1) { }  ///< Constructor de vector
00043     rational(INT num) : m_num(num), m_den(1) { } ///< Constructor a partir de un valor entero
00044     rational(INT num, INT den)
00045         : m_num(num), m_den(den) { Simplify(); } ///< Constructor a partir de un valor quedbrado
00046     rational(const rational& o)       /// Constructor de copia
00047         { m_num = o.m_num, m_den = o.m_den; }
00048     ~rational() { }      ///< Destructor
00049 
00050     void set(INT num=0, INT den=1);  // Le cambia el valor a \c "*this"
00051 
00052     INT num() const { return m_num; }  ///< Copia del numerador
00053     INT den() const { return m_den; }  ///< Copia del denominador
00054 
00055 //  void num(long n) { m_num=n; Simplify(); }  // FEO
00056 //  void den(long d) { m_den= ( d!=0 ? d : m_den) ; Simplify(); }  // FEO
00057 
00058     rational& operator  = (const rational&);  // Asignación (copia)
00059     rational& operator  = (INT);
00060     rational& swap ( rational& );
00061 
00062     rational& operator += (const rational&);
00063     rational& operator -= (const rational&);
00064     rational& operator *= (const rational&);
00065     rational& operator /= (const rational&);
00066 
00067     rational operator  - () const;              // menos unario
00068         template <class T> friend rational<T> operator + (const rational<T>&, const rational<T>&);
00069         template <class T> friend rational<T> operator - (const rational<T>&, const rational<T>&);
00070     template <class T> friend rational<T> operator * (const rational<T>&, const rational<T>&);
00071     template <class T> friend rational<T> operator / (const rational<T>&, const rational<T>&);
00072 
00073     template <class T> friend bool operator == (const rational<T>&, const rational<T>&);
00074         template <class T> friend bool operator <  (const rational<T>&, const rational<T>&);
00075         template <class T> friend bool operator != (const rational<T>&, const rational<T>&);
00076         template <class T> friend bool operator <= (const rational<T>&, const rational<T>&);
00077         template <class T> friend bool operator >= (const rational<T>&, const rational<T>&);
00078         template <class T> friend bool operator >  (const rational<T>&, const rational<T>&);
00079 
00080         template <class T> friend ostream& operator << (ostream &, const rational<T>& );
00081         template <class T> friend istream& operator >> (istream &,       rational<T>& );
00082     rational& fromString (const char* nStr);
00083 
00084         template <class T> friend double real   (const rational<T>& );   // Conversión a real
00085         template <class T> friend INT   integer(const rational<T>& );   // Conversión a long
00086 
00087         template <class T> friend bool check_ok( const rational<T>& r ); // Ok()
00088 
00089 }; // rational
00090 
00091 template <class INT>
00092 INT mcd(INT x, INT y); // Calcula el Máximo Común Divisor
00093 
00094 /// Sinónimo de \c mcd(x,y) <code> [ inline ] </code>
00095 template <class INT>
00096 inline INT gcd(INT x, INT y) { return mcd(x,y); }
00097 
00098 /// Cambia el valor del número rational a \c "n/d"
00099 template <class INT>
00100 inline void rational<INT>::set(INT n, INT d) {
00101     m_num = n;
00102     m_den = d;
00103     Simplify();
00104 }
00105 
00106 /** Copia desde \c "o".
00107     - El valor anterior de \c "*this" se pierde.
00108     \par Complejidad:
00109          O( \c 1 )
00110     \returns *this
00111     \see http://www.di-mare.com/adolfo/binder/c04.htm#sc05
00112 */
00113 template <class INT>
00114 inline rational<INT>& rational<INT>::operator = (const rational<INT>& o) {
00115     m_num = o.m_num,
00116     m_den = o.m_den;
00117 
00118 //  sobra invocar a "Simplify()" pues "o" ya está simplificado
00119     return *this;
00120 }  // operator =
00121 
00122 /** Intercambia los valores de \c "*this" y \c "o".
00123       \par Complejidad:
00124          O( \c 1 )
00125 
00126     \returns *this
00127 
00128     \see http://www.di-mare.com/adolfo/binder/c04.htm#sc08
00129 */
00130 template <class INT>
00131 inline rational<INT>& rational<INT>::swap ( rational<INT>& o ) {
00132     #if 1
00133         rational tmp = o;
00134         o = *this;
00135         *this = tmp;
00136     #else
00137         // Esto NO funciona para objetos, métodos virtuales, etc.
00138         char tmp[ sizeof( *this ) ];
00139         memcpy( tmp,   o,     sizeof( *this ) ); // tmp = o;
00140         memcpy( o,    *this,  sizeof( *this ) ); // o = *this;
00141         memcpy( *this, tmp,   sizeof( *this ) ); // *this = tmp;
00142     #endif
00143     return *this;
00144 }
00145 
00146 /// Asignación desde un \c "long".
00147 template <class INT>
00148 inline rational<INT>& rational<INT>::operator = (INT entero) {
00149     m_num = entero;
00150     m_den = 1;
00151     return *this;
00152 }  // operator =
00153 
00154 /// Multiplica \c "*this" por \c "num".
00155 template <class INT>
00156 inline rational<INT>& rational<INT>::operator *= (const rational<INT>& num) {
00157     m_num *= num.m_num;
00158     m_den *= num.m_den;
00159     Simplify();
00160     return *this;
00161 }  // operator *=
00162 
00163 /**  Divide \c "*this" por el valor de \c "num".
00164     \pre
00165     - (num != 0)
00166 */
00167 template <class INT>
00168 inline rational<INT>& rational<INT>::operator /= (const rational<INT>& num) {
00169     m_num *= num.m_den;
00170     m_den *= num.m_num;
00171     Simplify();
00172     return *this;
00173 }  // operator /=
00174 
00175 /// \c "-x".
00176 /// - Menos unario
00177 /// - Calcula y retorna el valor \c "-x"
00178 template <class INT>
00179 inline rational<INT> rational<INT>::operator - () const {
00180     rational tmp = (*this);  // tmp.rational( *this );
00181     tmp.m_num = - tmp.m_num;
00182     return tmp;
00183 }  // operator -
00184 
00185 /// ¿ x == y ?
00186 template <class INT>
00187 inline bool operator == (const rational<INT> &x, const rational<INT> &y) {
00188     return (x.m_num == y.m_num) && (x.m_den == y.m_den);
00189 /*  Nota:
00190     Como los números racionales siempre están simplificados, no puede 
00191     ocurrir que [1/1] está almacenado como [3/3] y en consecuencia
00192     basta comparar los valores campo por campo para determinar si se
00193     da o no la igualdad.
00194 */
00195 }  // operator ==
00196 
00197 /// ¿ x < y ?
00198 template <class INT>
00199 inline bool operator < (const rational<INT> &x, const rational<INT> &y) {
00200     return (x.m_num * y.m_den) < (x.m_den * y.m_num);
00201 /*  Nota:
00202     Una desigualdad de fracciones se preserva siempre que se 
00203     multiplique a ambos lados por un número positivo. Por eso:
00204           [a/b] <       [c/d]   <==>
00205     [(b*d)*a/b] < [(b*d)*c/d]   <==>
00206           [d*a] < [b*c]
00207 
00208     [a/b] > [c/d] <==> [(b*d)*a/b] > [(b*d)*c/d] <==> [d*a] > [b*c]
00209 
00210     Debido a que el denominador siempre es un número positivo, el
00211     trabajo de comparar 2 racionales se puede lograr haciendo 2
00212     multiplicaciones de números enteros, en lugar de convertirlos
00213     a punto flotante para hacer la división, que es hasta un orden
00214     de magnitud más lento.
00215 */
00216 //  return double(x.m_num) / double(x.m_den) < double(y.m_num) / double(y.m_den);
00217 }  // operator <
00218 
00219 /// ¿ x > y ?
00220 template <class INT>
00221 inline bool operator > (const rational<INT> &x, const rational<INT> &y) {
00222     return (y < x);
00223 }  // operator >
00224 
00225 /// ¿ x != y ?
00226 template <class INT>
00227 inline bool operator != (const rational<INT>& x, const rational<INT>& y) {
00228     return !(x == y);
00229 }  // operator !=
00230 
00231 /// ¿ x <= y ?
00232 template <class INT>
00233 inline bool operator <= (const rational<INT>& x, const rational<INT>& y) {
00234     return !(y < x);
00235 }  // operator <=
00236 
00237 /// ¿ x >= y ?
00238 template <class INT>
00239 inline bool operator >= (const rational<INT>& x, const rational<INT>& y) {
00240     return !(x < y);
00241 }  // operator >=
00242 
00243 /// Convertidor a punto flotante.
00244 template <class INT>
00245 inline double real(const rational<INT>& num) {
00246     return double (num.m_num) / double (num.m_den);
00247 } // real()
00248 
00249 /// Convertidor a punto fijo.
00250 template <class INT>
00251 inline INT integer(const rational<INT>& num) {
00252     return INT   (num.m_num /          num.m_den);
00253 } // integer()
00254 
00255 #if 0
00256     /// Convertidor a punto fijo
00257     inline rational::operator INT() {
00258         return INT (m_num / m_den);
00259     }  // rational::operator long
00260 #endif
00261 template <class INT>
00262 bool check_ok_externo( const rational<INT>& r );
00263 
00264 
00265 #include  <cstdlib>
00266 #include  <cctype>     // isdigit()
00267 
00268 
00269 /** Verifica la invariante de la clase \c rational.
00270     \par <em>Rep</em> Modelo de la clase:
00271     \code
00272     +---+
00273     | 3 | <==  m_num == numerador del número racional
00274     +---+
00275     |134| <==  m_den == denominador del número racional
00276     +---+
00277     \endcode
00278     - http://www.di-mare.com/adolfo/binder/c03.htm#k1-Rep
00279 
00280     \remark
00281     Libera al programador de implementar el método \c Ok()
00282     - http://www.di-mare.com/adolfo/binder/c04.htm#sc11
00283 */
00284 template <class INT>
00285 bool check_ok( const rational<INT>& r ) {
00286     if (&r == 0) {
00287         /// - Invariante: ningún objeto puede estar almacenado en la posición nula.
00288         return false;
00289     }
00290 
00291     if ( ! (r.m_den > 0) )  {
00292         /// - Invariante: el denominador debe ser un número positivo.
00293         return false;
00294     }
00295     if (r.m_num == 0) {
00296         if ( r.m_den == 1 ) {
00297             /// - Invariante: el cero debe representarse con denominador igual a "1". 
00298             return true;
00299         }
00300         else {
00301             return false;
00302         }
00303     }
00304     if ( ! ( mcd(r.m_num, r.m_den) == 1 ) ) {
00305         /// - Invariante: el numerador y el denominador deben ser primos relativos.
00306         return false;
00307     }
00308     return true;
00309 } // check_ok()
00310 
00311 /** Verifica la invariante de la clase \c rational.
00312     \remark
00313     Esta implementación nos se le mete al <em>Rep</em>
00314     (casi siempre no es posible implementar una función como ésta).
00315       - http://www.di-mare.com/adolfo/binder/c03.htm#k1-Rep
00316     \remark
00317     Libera al programador de implementar el método \c Ok()
00318     - http://www.di-mare.com/adolfo/binder/c04.htm#sc11
00319 */
00320 template <class INT>
00321 bool check_ok_no_Rep( const rational<INT>& r ) {
00322     if (&r == 0) {
00323         /// - Invariante: ningún objeto puede estar almacenado en la posición nula.
00324         return false;
00325     }
00326 
00327     if ( ! (r.den() > 0) )  {
00328         /// - Invariante: el denominador debe ser un número positivo.
00329         return false;
00330     }
00331     if (r.num() == 0) {
00332         if ( r.den() == 1 ) {
00333             /// - Invariante: el cero debe representarse con denominador igual a "1".
00334             return true;
00335         }
00336         else {
00337             return false;
00338         }
00339     }
00340     if ( ! ( mcd(r.num(), r.den()) == 1 ) ) {
00341         /// - Invariante: el numerador y el denominador deben ser primos relativos.
00342         return false;
00343     }
00344     return true;
00345 } // check_ok_no_Rep()
00346 
00347 
00348 /** Calcula el Máximo Común Divisor de los números \c "x" y \c "y".
00349     - <code> mcd(x,y) >= 1 </code> siempre.
00350     - MCD <==> GCD: <em> Greatest Common Divisor </em>.
00351 
00352     \pre
00353     <code> (y != 0) </code>
00354 
00355     \remark
00356     Se usa el algoritmo de Euclides para hacer el cálculo.
00357 
00358     \par Ejemplo:
00359     \code
00360     2*3*5 == mcd( 2*2*2*2 * 3*3 * 5*5, 2*3*5 )
00361        30 == mcd( -3600, -30 )
00362     \endcode
00363 */
00364 template <class INT>
00365 INT mcd(INT x, INT y) {
00366     INT g = (x < 0 ? -x : x); // trabaja con valores positivos
00367     INT r = (y < 0 ? -y : y); // "r" es el resto
00368     INT temp;
00369 
00370     do {
00371         temp = r;
00372         r    = g % r;
00373         g    = temp;
00374     } while (0 != r);
00375 
00376     return g;
00377 }  // mcd()
00378 
00379 
00380 /** Simplifica el numerador y el denomidador.
00381     - Transforma el número rational de manera que el numerador y el
00382       denominador sean primos relativos, asegurando además que el
00383       denominador es siempre positivo.
00384     - Si <code>(m_num==0) ==> (m_den==1)</code>.
00385     - Simplifica la fracción para que \c m_num y \c m_den sean números
00386       primos relativos ie, <code>mcd(m_num,m_den) == 1</code>.
00387     - Asegura que \c m_den sea un número positivo.
00388     - Restaura la invariante de la clase \c rational.
00389 */
00390 template <class INT>
00391 void rational<INT>::Simplify() {
00392     if (m_num == 0) {
00393        m_den = 1;
00394     }
00395     INT divisor = mcd(m_num, m_den);
00396     if (divisor > 1) {   // ==> (divisor != 0)
00397         m_num /= divisor;
00398         m_den /= divisor;
00399     }
00400     if (m_den < 0) {
00401         m_num = -m_num;
00402         m_den = -m_den;
00403     }
00404 }  // rational::Simplify()
00405 
00406 /// Le suma a \c "*this" el valor de \c "otro".
00407 template <class INT>
00408 rational<INT>& rational<INT>::operator += (const rational<INT>& otro) {
00409     m_num  = m_num * otro.m_den + m_den * otro.m_num;
00410     m_den *= otro.m_den;
00411     Simplify();
00412 
00413     return *this;
00414 }  // operator +=
00415 
00416 
00417 /// Le resta a \c "*this" el valor de \c "otro".
00418 template <class INT>
00419 rational<INT>& rational<INT>::operator -= (const rational<INT>& otro) {
00420     INT oldm_den = m_den;
00421     INT oldm_num = m_num;
00422     INT d       = otro.m_den;
00423     INT n       = otro.m_num;
00424 
00425     m_den *= d;
00426     m_num = oldm_num * d - oldm_den * n;
00427     Simplify();
00428 
00429     return *this;
00430 }  // operator -=
00431 
00432 /// Establece el varlor de \c "*this" a partir de la hilera \c "nStr".
00433 /// \pre \c "nStr" debe estar escrita en el formato "[num/den]".
00434 template <class INT>
00435 rational<INT>& rational<INT>::fromString (const char* nStr) {
00436     char ch;  // valor obtenido, caracter por caracter, de "nStr"
00437 
00438     bool es_positivo = true;    // manejo de los signos + y -
00439 
00440     // se brinca todo hasta el primer dígito
00441     do {
00442         ch = *nStr; nStr++;
00443         if (ch == '-') {  // cambia de signo
00444             es_positivo = !es_positivo;
00445         }
00446     } while (!isdigit(ch));
00447 
00448     // se traga el numerador
00449     INT num = 0;
00450     while (isdigit(ch)) { // convierte a decimal: izq --> der
00451         num = 10 * num + (ch-'0');
00452         ch = *nStr; nStr++;
00453     }
00454 
00455     // se brinca los blancos después del numerador
00456     while (isspace(ch)) {
00457         ch = *nStr; nStr++;
00458     }
00459 
00460     INT den;
00461     if (ch ==']') { // es un número entero
00462         den = 1;
00463     }
00464     else {
00465         do {  // se brinca todo hasta el denominador
00466             ch = *nStr; nStr++;
00467             if (ch == '-') {
00468                 es_positivo = !es_positivo;
00469             }
00470         } while (!isdigit(ch));
00471 
00472         // se traga el denominador
00473         den = 0;
00474         while (isdigit(ch)) {
00475             den = 10 * den + (ch-'0');
00476             ch = *nStr; nStr++;
00477         }
00478         // Ya no importa si aparece o no el ']' del final del número
00479     }
00480 
00481 
00482     // le cambia el signo, si hace falta
00483     if (! es_positivo) {
00484         num = -num;
00485     }
00486     set( num, den );
00487     return *this;
00488 }
00489 
00490 /** Graba el valor de \c "r" en el flujo \c "COUT".
00491     - Graba el valor en el formato [num/den].
00492     - En particular, este es el operador que se invoca
00493       cuando se usa, por ejemplo, este tipo de instrucción:
00494      \code
00495           cout << r << q;
00496      \endcode
00497 */
00498 template <class INT>
00499 ostream& operator<< (ostream &COUT, const rational<INT>& r) {
00500     if ( r.m_den == 1 ) { // no hay parte fraccional
00501         return COUT << "[" << r.m_num << "]" ;
00502     } else {
00503         return COUT << "[" << r.m_num << "/" << r.m_den << "]" ;
00504     }
00505 }  // operator <<
00506 
00507 /** Lee del flujo de texto \c "CIN" el valor de \c "r".
00508     \pre
00509     El número rational debe haber sido escrito usando
00510     el formato "[r/den]", aunque es permisible usar
00511     algunos blancos.
00512     - Se termina de leer el valor sólo cuando encuentra \c "]".
00513     - <code> [ -+-+-+-+- 4 / -- -+ -- 32  ] </code> se lee como
00514       <code> [1/8] </code>
00515 */
00516 template <class INT>
00517 istream& operator >> (istream &CIN, rational<INT>& r) {
00518     char ch;  // valor leido, letra por letra, de "CIN"
00519 
00520     bool es_positivo = true;    // manejo de los signos + y -
00521 
00522     // se brinca todo hasta el primer dígito
00523     do {
00524         CIN >> ch;
00525         if (ch == '-') {  // cambia de signo
00526             es_positivo = !es_positivo;
00527         }
00528     } while (!isdigit(ch));
00529 
00530     // se traga el numerador
00531     r.m_num = 0;
00532     while (isdigit(ch)) { // convierte a decimal: izq --> der
00533         r.m_num = 10 * r.m_num + (ch-'0');
00534         CIN >> ch;
00535     }
00536 
00537     // se brinca los blancos después del numerador
00538     while (isspace(ch)) {
00539         CIN >> ch;
00540     }
00541 
00542     if (ch ==']') { // es un número entero
00543         r.m_den = 1;
00544     }
00545     else {
00546         do {  // se brinca todo hasta el denominador
00547             CIN >> ch;
00548             if (ch == '-') {
00549                 es_positivo = !es_positivo;
00550             }
00551         } while (!isdigit(ch));
00552 
00553         // se traga el denominador
00554         r.m_den = 0;
00555         while (isdigit(ch)) {
00556             r.m_den = 10 * r.m_den + (ch-'0');
00557             CIN >> ch;
00558         }
00559 
00560         // El programa se duerme si en el flujo de entrada
00561         // NO aparece el caracter delimitador final "]",
00562         // pues la lectura termina hasta encontrar el "]".
00563         while (ch != ']') {
00564             CIN >> ch;
00565         }
00566     }   // corrección: Andrés Arias <[email protected]>
00567 
00568 
00569     // le cambia el signo, si hace falta
00570     if (! es_positivo) {
00571         r.m_num = -r.m_num;
00572     }
00573 
00574     r.Simplify();
00575     return CIN;
00576 /*
00577     no detecta errores...
00578     [1/0] lo lee y no se queja
00579     [ !#!#!$#@! 3/ aaaa 4  jajaja ] lo lee como 3/4
00580     ... pero no se supone que el usuario cometa errores...
00581 */
00582 
00583 }  // operator >>
00584 
00585 /// \c "x+y".
00586 /// - Calcula y retorna la suma \c "x+y".
00587 template <class INT>
00588 rational<INT> operator + (const rational<INT> &x, const rational<INT> &y) {
00589     INT res_num, res_den;
00590     res_den = x.m_den * y.m_den;
00591     res_num = x.m_num * y.m_den + x.m_den * y.m_num;
00592 
00593     return rational<INT>(res_num, res_den);
00594 }  // operator + ()
00595 
00596 /// \c "x-y".
00597 /// - Calcula y retorna la resta \c "x-y".
00598 template <class INT>
00599 rational<INT> operator - (const rational<INT> &x, const rational<INT> &y) {
00600     INT res_num, res_den;
00601 
00602     res_den = x.m_den * y.m_den;
00603     res_num = x.m_num * y.m_den - x.m_den * y.m_num;
00604 
00605     return rational<INT>(res_num, res_den);
00606 }  // operator - ()
00607 
00608 /// \c "x*y".
00609 /// - Calcula y retorna la multiplicación \c "x*y".
00610 template <class INT>
00611 rational<INT> operator * (const rational<INT> &x, const rational<INT> &y) {
00612     INT res_num, res_den;
00613 
00614     res_num = x.m_num * y.m_num;
00615     res_den = x.m_den * y.m_den;
00616 
00617     return rational<INT>(res_num, res_den);
00618 }  // operator * ()
00619 
00620 /// \c "x/y".
00621 /// - Calcula y retorna la división \c "x/y".
00622 /// \pre <code> y != 0 </code>
00623 template <class INT>
00624 rational<INT> operator / (const rational<INT> &x, const rational<INT> &y) {
00625     INT res_num, res_den;
00626     if (0 != y.m_num) {
00627         res_num = x.m_num * y.m_den;
00628         res_den = x.m_den * y.m_num;
00629     }
00630     return rational<INT>(res_num, res_den);
00631 }  // operator / ()
00632 
00633 #endif // rational_h
00634 
00635 // EOF: rational.h

Generado el Thu Sep 20 17:24:00 2007 para Tarea Programada #4 por  doxygen 1.4.1
Hosted by www.Geocities.ws

1