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 /**  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 
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(INT n) { m_num=n; Simplify(); }  // FEO
00054 //  void den(INT 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 
00067     template <class T> 
00068         friend rational<T> operator + (const rational<T>&, const rational<T>&);
00069     template <class T> 
00070         friend rational<T> operator - (const rational<T>&, const rational<T>&);
00071     template <class T> 
00072         friend rational<T> operator * (const rational<T>&, const rational<T>&);
00073     template <class T> 
00074         friend rational<T> operator / (const rational<T>&, const rational<T>&);
00075 
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     template <class T> 
00087         friend bool operator >  (const rational<T>&, const rational<T>&);
00088 
00089     template <class T> friend ostream& operator << (ostream &, const rational& );
00090     template <class T> friend istream& operator >> (istream &,       rational& );
00091     rational& fromString (const char* nStr);
00092 
00093     template <class T> 
00094         friend double real   (const rational& );   // Conversi�n a real
00095     template <class T> 
00096         friend INT   integer(const rational& );   // Conversi�n a INT
00097 
00098     template <class T> 
00099         friend bool check_ok( const rational& 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<INT>& 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<INT>& 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<INT>& 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 template <class INT>
00272 inline rational<INT>::operator INT() {
00273         return INT (m_num / m_den);
00274     }  // rational::operator INT
00275 #endif
00276 
00277 template <class INT>
00278 bool check_ok_externo( const rational<INT>& r );
00279 
00280 
00281 
00282 // rational.cpp        (c) 2005 [email protected]
00283 
00284 /** \file  rational.cpp
00285     \brief Implementaciones para la clase \c "rational"
00286 
00287     \author Adolfo Di Mare <[email protected]>
00288     \date   2005
00289 
00290     - Why English names??? ==> http://www.di-mare.com/adolfo/binder/c01.htm#sc04
00291 */
00292 
00293 #include  <cstdlib>
00294 #include  <cctype>     // isdigit()
00295 
00296 /** Verifica la invariante de la clase \c rational.
00297     \par <em>Rep</em> Modelo de la clase:
00298     \code
00299     +---+
00300     | 3 | <==  m_num == numerador del n�mero racional
00301     +---+
00302     |134| <==  m_den == denominador del n�mero racional
00303     +---+
00304     \endcode
00305     - http://www.di-mare.com/adolfo/binder/c03.htm#k1-Rep
00306 
00307     \remark
00308     Libera al programador de implementar el m�todo \c Ok()
00309     - http://www.di-mare.com/adolfo/binder/c04.htm#sc11
00310 */
00311 template <class INT>
00312 bool check_ok( const rational<INT>& r ) {
00313     if (&r == 0) {
00314         /// - Invariante: ning�n objeto puede estar almacenado en la posici�n nula.
00315         return false;
00316     }
00317 
00318     if ( ! (r.m_den > 0) )  {
00319         /// - Invariante: el denominador debe ser un n�mero positivo.
00320         return false;
00321     }
00322     if (r.m_num == 0) {
00323         if ( r.m_den == 1 ) {
00324             /// - Invariante: el cero debe representarse con denominador igual a "1". 
00325             return true;
00326         }
00327         else {
00328             return false;
00329         }
00330     }
00331     if ( ! ( mcd(r.m_num, r.m_den) == 1 ) ) {
00332         /// - Invariante: el numerador y el denominador deben ser primos relativos.
00333         return false;
00334     }
00335     return true;
00336 } // check_ok()
00337 
00338 /** Verifica la invariante de la clase \c rational.
00339     \remark
00340     Esta implementaci�n nos se le mete al <em>Rep</em>
00341     (casi siempre no es posible implementar una funci�n como �sta).
00342       - http://www.di-mare.com/adolfo/binder/c03.htm#k1-Rep
00343     \remark
00344     Libera al programador de implementar el m�todo \c Ok()
00345     - http://www.di-mare.com/adolfo/binder/c04.htm#sc11
00346 */
00347 template <class INT>
00348 bool check_ok_no_Rep( const rational<INT>& r ) {
00349     if (&r == 0) {
00350         /// - Invariante: ning�n objeto puede estar almacenado en la posici�n nula.
00351         return false;
00352     }
00353 
00354     if ( ! (r.den() > 0) )  {
00355         /// - Invariante: el denominador debe ser un n�mero positivo.
00356         return false;
00357     }
00358     if (r.num() == 0) {
00359         if ( r.den() == 1 ) {
00360             /// - Invariante: el cero debe representarse con denominador igual a "1".
00361             return true;
00362         }
00363         else {
00364             return false;
00365         }
00366     }
00367     if ( ! ( mcd(r.num(), r.den()) == 1 ) ) {
00368         /// - Invariante: el numerador y el denominador deben ser primos relativos.
00369         return false;
00370     }
00371     return true;
00372 } // check_ok_no_Rep()
00373 
00374 
00375 /** Calcula el M�ximo Com�n Divisor de los n�meros \c "x" y \c "y".
00376     - <code> mcd(x,y) >= 1 </code> siempre.
00377     - MCD <==> GCD: <em> Greatest Common Divisor </em>.
00378 
00379     \pre
00380     <code> (y != 0) </code>
00381 
00382     \remark
00383     Se usa el algoritmo de Euclides para hacer el c�lculo.
00384 
00385     \par Ejemplo:
00386     \code
00387     2*3*5 == mcd( 2*2*2*2 * 3*3 * 5*5, 2*3*5 )
00388        30 == mcd( -3600, -30 )
00389     \endcode
00390 */
00391 template <class INT>
00392 INT mcd(INT x, INT y) {
00393     INT g = (x < 0 ? -x : x); // trabaja con valores positivos
00394     INT r = (y < 0 ? -y : y); // "r" es el resto
00395     INT temp;
00396 
00397     do {
00398         temp = r;
00399         r    = g % r;
00400         g    = temp;
00401     } while (0 != r);
00402 
00403     return g;
00404 }  // mcd()
00405 
00406 
00407 /** Simplifica el numerador y el denomidador.
00408     - Transforma el n�mero rational de manera que el numerador y el
00409       denominador sean primos relativos, asegurando adem�s que el
00410       denominador es siempre positivo.
00411     - Si <code>(m_num==0) ==> (m_den==1)</code>.
00412     - Simplifica la fracci�n para que \c m_num y \c m_den sean n�meros
00413       primos relativos ie, <code>mcd(m_num,m_den) == 1</code>.
00414     - Asegura que \c m_den sea un n�mero positivo.
00415     - Restaura la invariante de la clase \c rational.
00416 */
00417 template <class INT>
00418 void rational<INT>::Simplify() {
00419     if (m_num == 0) {
00420        m_den = 1;
00421     }
00422     INT divisor = mcd(m_num, m_den);
00423     if (divisor > 1) {   // ==> (divisor != 0)
00424         m_num /= divisor;
00425         m_den /= divisor;
00426     }
00427     if (m_den < 0) {
00428         m_num = -m_num;
00429         m_den = -m_den;
00430     }
00431 }  // rational::Simplify()
00432 
00433 /// Le suma a \c "*this" el valor de \c "otro".
00434 template <class INT>
00435 rational<INT>& rational<INT>::operator += (const rational<INT>& otro) {
00436     m_num  = m_num * otro.m_den + m_den * otro.m_num;
00437     m_den *= otro.m_den;
00438     Simplify();
00439 
00440     return *this;
00441 }  // operator +=
00442 
00443 
00444 /// Le resta a \c "*this" el valor de \c "otro".
00445 template <class INT>
00446 rational<INT>& rational<INT>::operator -= (const rational<INT>& otro) {
00447     INT oldm_den = m_den;
00448     INT oldm_num = m_num;
00449     INT d       = otro.m_den;
00450     INT n       = otro.m_num;
00451 
00452     m_den *= d;
00453     m_num = oldm_num * d - oldm_den * n;
00454     Simplify();
00455 
00456     return *this;
00457 }  // operator -=
00458 
00459 /// Establece el varlor de \c "*this" a partir de la hilera \c "nStr".
00460 /// \pre \c "nStr" debe estar escrita en el formato "[num/den]".
00461 template <class INT>
00462 rational<INT>& rational<INT>::fromString (const char* nStr) {
00463     char ch;  // valor obtenido, caracter por caracter, de "nStr"
00464 
00465     bool es_positivo = true;    // manejo de los signos + y -
00466 
00467     // se brinca todo hasta el primer d�gito
00468     do {
00469         ch = *nStr; nStr++;
00470         if (ch == '-') {  // cambia de signo
00471             es_positivo = !es_positivo;
00472         }
00473     } while (!isdigit(ch));
00474 
00475     // se traga el numerador
00476     INT num = 0;
00477     while (isdigit(ch)) { // convierte a decimal: izq --> der
00478         num = 10 * num + (ch-'0');
00479         ch = *nStr; nStr++;
00480     }
00481 
00482     // se brinca los blancos despu�s del numerador
00483     while (isspace(ch)) {
00484         ch = *nStr; nStr++;
00485     }
00486 
00487     INT den;
00488     if (ch ==']') { // es un n�mero entero
00489         den = 1;
00490     }
00491     else {
00492         do {  // se brinca todo hasta el denominador
00493             ch = *nStr; nStr++;
00494             if (ch == '-') {
00495                 es_positivo = !es_positivo;
00496             }
00497         } while (!isdigit(ch));
00498 
00499         // se traga el denominador
00500         den = 0;
00501         while (isdigit(ch)) {
00502             den = 10 * den + (ch-'0');
00503             ch = *nStr; nStr++;
00504         }
00505         // Ya no importa si aparece o no el ']' del final del n�mero
00506     }
00507 
00508 
00509     // le cambia el signo, si hace falta
00510     if (! es_positivo) {
00511         num = -num;
00512     }
00513     set( num, den );
00514     return *this;
00515 }
00516 
00517 /** Graba el valor de \c "r" en el flujo \c "COUT".
00518     - Graba el valor en el formato [num/den].
00519     - En particular, este es el operador que se invoca
00520       cuando se usa, por ejemplo, este tipo de instrucci�n:
00521      \code
00522           cout << r << q;
00523      \endcode
00524 */
00525 template <class INT>
00526 ostream& operator<< (ostream &COUT, const rational<INT>& r) {
00527     if ( r.m_den == 1 ) { // no hay parte fraccional
00528         return COUT << "[" << r.m_num << "]" ;
00529     } else {
00530         return COUT << "[" << r.m_num << "/" << r.m_den << "]" ;
00531     }
00532 }  // operator <<
00533 
00534 /** Lee del flujo de texto \c "CIN" el valor de \c "r".
00535     \pre
00536     El n�mero rational debe haber sido escrito usando
00537     el formato "[r/den]", aunque es permisible usar
00538     algunos blancos.
00539     - Se termina de leer el valor s�lo cuando encuentra \c "]".
00540     - <code> [ -+-+-+-+- 4 / -- -+ -- 32  ] </code> se lee como
00541       <code> [1/8] </code>
00542 */
00543 template <class INT>
00544 istream& operator >> (istream &CIN, rational<INT>& r) {
00545     char ch;  // valor leido, letra por letra, de "CIN"
00546 
00547     bool es_positivo = true;    // manejo de los signos + y -
00548 
00549     // se brinca todo hasta el primer d�gito
00550     do {
00551         CIN >> ch;
00552         if (ch == '-') {  // cambia de signo
00553             es_positivo = !es_positivo;
00554         }
00555     } while (!isdigit(ch));
00556 
00557     // se traga el numerador
00558     r.m_num = 0;
00559     while (isdigit(ch)) { // convierte a decimal: izq --> der
00560         r.m_num = 10 * r.m_num + (ch-'0');
00561         CIN >> ch;
00562     }
00563 
00564     // se brinca los blancos despu�s del numerador
00565     while (isspace(ch)) {
00566         CIN >> ch;
00567     }
00568 
00569     if (ch ==']') { // es un n�mero entero
00570         r.m_den = 1;
00571     }
00572     else {
00573         do {  // se brinca todo hasta el denominador
00574             CIN >> ch;
00575             if (ch == '-') {
00576                 es_positivo = !es_positivo;
00577             }
00578         } while (!isdigit(ch));
00579 
00580         // se traga el denominador
00581         r.m_den = 0;
00582         while (isdigit(ch)) {
00583             r.m_den = 10 * r.m_den + (ch-'0');
00584             CIN >> ch;
00585         }
00586 
00587         // El programa se duerme si en el flujo de entrada
00588         // NO aparece el caracter delimitador final "]",
00589         // pues la lectura termina hasta encontrar el "]".
00590         while (ch != ']') {
00591             CIN >> ch;
00592         }
00593     }   // correcci�n: Andr�s Arias <[email protected]>
00594 
00595 
00596     // le cambia el signo, si hace falta
00597     if (! es_positivo) {
00598         r.m_num = -r.m_num;
00599     }
00600 
00601     r.Simplify();
00602     return CIN;
00603 /*
00604     no detecta errores...
00605     [1/0] lo lee y no se queja
00606     [ !#!#!$#@! 3/ aaaa 4  jajaja ] lo lee como 3/4
00607     ... pero no se supone que el usuario cometa errores...
00608 */
00609 
00610 }  // operator >>
00611 
00612 /// \c "x+y".
00613 /// - Calcula y retorna la suma \c "x+y".
00614 template <class INT>
00615 rational<INT> operator + (const rational<INT> &x, const rational<INT> &y) {
00616     INT res_num, res_den;
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 resta \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_den = x.m_den * y.m_den;
00630     res_num = x.m_num * y.m_den - x.m_den * y.m_num;
00631 
00632     return rational<INT>(res_num, res_den);
00633 }  // operator - ()
00634 
00635 /// \c "x*y".
00636 /// - Calcula y retorna la multiplicaci�n \c "x*y".
00637 template <class INT>
00638 rational<INT> operator * (const rational<INT> &x, const rational<INT> &y) {
00639     INT res_num, res_den;
00640 
00641     res_num = x.m_num * y.m_num;
00642     res_den = x.m_den * y.m_den;
00643 
00644     return rational<INT>(res_num, res_den);
00645 }  // operator * ()
00646 
00647 /// \c "x/y".
00648 /// - Calcula y retorna la divisi�n \c "x/y".
00649 /// \pre <code> y != 0 </code>
00650 template <class INT>
00651 rational<INT> operator / (const rational<INT> &x, const rational<INT> &y) {
00652     INT res_num, res_den;
00653     if (0 != y.m_num) {
00654         res_num = x.m_num * y.m_den;
00655         res_den = x.m_den * y.m_num;
00656     }
00657     return rational<INT>(res_num, res_den);
00658 }  // operator / ()
00659 
00660 
00661 // EOF: rational.cpp
00662 
00663  #endif// rational_h
00664 
00665 // EOF: rational.h

Generado el Thu Sep 6 23:18:36 2007 para Prueba de la clase rational: por  doxygen 1.5.3
Hosted by www.Geocities.ws

1