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

Generado el Thu Sep 20 12:33:06 2007 para Clase decimal: por  doxygen 1.4.1
Hosted by www.Geocities.ws

1