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

Generado el Thu Aug 30 17:46:14 2007 para 952809 Tarea Programada #2 por  doxygen 1.4.1
Hosted by www.Geocities.ws

1