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

Generado el Thu Aug 30 17:50:35 2007 para A41369- A55609 Tarea Programada 2 por  doxygen 1.4.1
Hosted by www.Geocities.ws

1