Prog. orientada a objetos

Práctica 1: Clase Rational

Objetivos

Durante esta práctica, los alumnos serán capaces de:

Esta actividad promueve las siguientes habilidades, valores y actitudes: análisis y síntesis, capacidad de resolver problemas, creatividad, y uso eficiente de la informática y las telecomunicaciones.

IMPORTANTE

Para esta actividad de programación, el uso de herramientas asistidas por IA, como GitHub Copilot, ChatGPT, Gemini o plataformas similares, para generar código automáticamente está estrictamente prohibido. El uso de herramientas de IA de esta manera socava el proceso de aprendizaje y viola las políticas de integridad académica. El propósito de esta tarea es evaluar su comprensión y aplicación de los conceptos cubiertos en el curso. El incumplimiento de estas pautas puede resultar en sanciones académicas, que incluyen, entre otras, una calificación más baja.

Si tienes alguna pregunta sobre la tarea o necesitas una aclaración sobre algún concepto, no dudes en visitar a tu profesor durante las horas de asesoría. Confía únicamente en tu conocimiento, los materiales del curso y cualquier recurso autorizado proporcionado por el profesor.

Descripción de la actividad

Esta práctica debe ser elaborada en los equipos de dos personas ya constituidos.

Implementa una clase Rational que incluya las funciones miembro (métodos) especificadas. Organiza el código en un directorio llamado rational, en donde deberás colocar los archivos main.cpp, rational.h y rational.cpp.

Función miembro Descripción

Rational(
    int numerator=0,
    int denominator=1,
)

Constructor: Inicializa un objeto Rational que representa una fracción con un numerador entero (por defecto 0) y un denominador entero (por defecto 1). Internamente, la fracción se debe simplificar automáticamente a su forma más reducida.

Si el denominador es 0, se debe lanzar una excepción de tipo std::invalid_argument con el mensaje:

Denominator cannot be zero!

std::string to_string()
    const

Este método devuelve un std::string que representa el objeto Rational en el formato "n/d", donde n es el numerador y d es el denominador. Pero, como caso especial, si el denominador es igual a 1, solo devuelve la cadena en el formato "n".

Si la fracción es negativa, el signo de menos (-) se debe incluir al inicio de la cadena.

Rational operator + (
    const Rational& other
) const

Suma (operador binario): regresa un nuevo objeto Rational que resulta de sumar el objeto receptor más other.

Fórmula:

$$ \frac{a}{b} + \frac{c}{d} = \frac{(a \times d) + (b \times c)}{b \times d} $$

Rational operator - () const

Negación (operador unario): regresa un nuevo objeto Rational que resulta de cambiar el signo del objeto receptor.

Fórmula:

$$ -\left ( \frac{a}{b} \right ) = \frac{-a}{b} $$

Rational operator - (
    const Rational& other
) const

Resta (operador binario): regresa un nuevo objeto Rational que resulta de restar el objeto receptor menos other.

Fórmula:

$$ \frac{a}{b} - \frac{c}{d} = \frac{a}{b} + -\left ( \frac{c}{d} \right ) $$

operator double() const;

Conversión a double (operador unario): regresa el resultado de convertir el objeto receptor a su valor de double correspondiente.

bool operator == (
    const Rational& other
) const

Igualdad (operador binario): regresa true si el objeto receptor es igual a other, o false en caso contrario.

Fórmula\(^*\):

$$ \left ( \frac{a}{b} = \frac{c}{d} \right ) = [(a = c) \land (b = d)] $$

\(^*\) La formula supone que ambas fracciones están reducidas a su forma más simple.

bool operator != (
    const Rational& other
) const

Desigualdad (operador binario): regresa true si el objeto receptor es diferente de other, o false en caso contrario.

Fórmula:

$$ \left ( \frac{a}{b} \ne \frac{c}{d} \right ) = \neg \left ( \frac{a}{b} = \frac{c}{d} \right ) $$

Rational operator * (
    const Rational& other
) const

Multiplicación (operador binario): regresa un nuevo objeto Rational que resulta de multiplicar el objeto receptor por other.

Fórmula:

$$ \frac{a}{b} \times \frac{c}{d} = \frac{a \times c}{b \times d} $$

Rational operator ~ () const

Recíproco (operador unario): regresa un nuevo objeto Rational que resulta de obtener el recíproco del objeto receptor.

El recíproco de un número racional es un número que, al multiplicarse por el original, da como resultado uno, obteniéndose al intercambiar el numerador y el denominador del número original.

Fórmula:

$$ \left ( \frac{a}{b} \right )^{-1} = \frac{b}{a} $$

Rational operator / (
    const Rational& other
) const

División (operador binario): regresa un nuevo objeto Rational que resulta de dividir el objeto receptor entre other.

Fórmula:

$$ \frac{a}{b} \div \frac{c}{d} = \frac{a}{b} \times \left ( \frac{c}{d} \right )^{-1} = \frac{a \times d}{b \times c} $$

bool operator < (
    const Rational& other
) const

Menor que (operador binario): regresa true si el objeto receptor es menor que other, o false en caso contrario.

Fórmula:

$$ \left ( \frac{a}{b} < \frac{c}{d} \right ) = [(a \times d) < (b \times c)] $$

bool operator <= (
    const Rational& other
) const

Menor o igual (operador binario): regresa true si el objeto receptor es menor o igual a other, o false en caso contrario.

Fórmula:

$$ \left ( \frac{a}{b} \le \frac{c}{d} \right ) = [(a \times d) \le (b \times c)] $$

bool operator > (
    const Rational& other
) const

Mayor que (operador binario): regresa true si el objeto receptor es mayor que other, o false en caso contrario.

Fórmula:

$$ \left ( \frac{a}{b} > \frac{c}{d} \right ) = [(a \times d) > (b \times c)] $$

bool operator >= (
    const Rational& other
) const

Mayor o igual (operador binario): regresa true si el objeto receptor es mayor o igual a other, o false en caso contrario.

Fórmula:

$$ \left ( \frac{a}{b} \ge \frac{c}{d} \right ) = [(a \times d) \ge (b \times c)] $$

También, debes incluir la sobrecarga del operador de inserción de flujo, operator<<. Esta función permitirá imprimir objetos Rational de forma conveniente en flujos de salida como std::cout. Para ello, en el archivo rational.h incluye la siguiente declaración fuera de la clase Rational:

std::ostream& operator<<(std::ostream& os, const Rational& r);

En el archivo rational.cpp coloca la implementación de dicha función:

std::ostream& operator<<(std::ostream& os, const Rational& r)
{
    return os << r.to_string();
}

Esta sobrecarga simplemente utiliza el método to_string() de la clase Rational para obtener la representación en cadena del objeto y la escribe en el flujo de salida, asegurando una visualización consistente y amigable de los números racionales.

Prueba tu código utilizando el siguiente archivo main.cpp:

// Archivo: main.cpp

// Para compilar y correr:
//               
//     g++ *.cpp -o main && ./main

#include <iostream>
#include <iomanip>
#include <stdexcept>
#include "rational.h"

int main()
{
    Rational a(1, 2);
    Rational b(6, 4);
    Rational c(4, 8);
    Rational d;
    Rational e(-3);

    // Para imprimir los booleanos como true o false
    // en lugar de 1 o 0.
    std::cout << std::boolalpha;

    std::cout << "a: " << a << "\n";
    std::cout << "b: " << b << "\n";
    std::cout << "c: " << c << "\n";
    std::cout << "d: " << d << "\n";
    std::cout << "e: " << e << "\n";
    std::cout << "a + b: " << a + b << "\n";
    std::cout << "c + d: " << c + d << "\n";
    std::cout << "a == c: " << (a == c) << "\n";
    std::cout << "a == b: " << (a == b) << "\n";
    std::cout << "a != b: " << (a != b) << "\n";
    std::cout << "a != c: " << (a != c) << "\n";
    std::cout << "-a: " << -a << "\n";
    std::cout << "-e: " << -e << "\n";
    std::cout << "a - b: " << a - b << "\n";
    std::cout << "c - e: " << c - e << "\n";
    std::cout << "static_cast<double>(a): "
        << static_cast<double>(a) << "\n";
    std::cout << "static_cast<double>(b): "
        << static_cast<double>(b) << "\n";
    std::cout << "a * b: " << a * b << "\n";
    std::cout << "c * e: " << c * e << "\n";
    std::cout << "~b: " << ~b << "\n";
    std::cout << "~e: " << ~e << "\n";
    std::cout << "b * ~b: " << b * ~b << "\n";
    std::cout << "e * ~e: " << e * ~e << "\n";
    std::cout << "a / b: " << a / b << "\n";
    std::cout << "c / e: " << c / e << "\n";
    try {
        Rational t = a / d;
        std::cout << "a / d: " << t << "\n";
    } catch (const std::invalid_argument& e) {
        std::cout << "a / d: " << e.what() << "\n";
    }
    std::cout << "a < b: " << (a < b) << "\n";
    std::cout << "a < c: " << (a < c) << "\n";
    std::cout << "a <= c: " << (a <= c) << "\n";
    std::cout << "a <= d: " << (a <= d) << "\n";
    std::cout << "b > c: " << (b > c) << "\n";
    std::cout << "a > c: " << (a > c) << "\n";
    std::cout << "a >= c: " << (a >= c) << "\n";
    std::cout << "e >= d: " << (e >= d) << "\n";
    return 0;
}

Asegúrate que la salida sea idéntica a lo siguiente:

a: 1/2
b: 3/2
c: 1/2
d: 0
e: -3
a + b: 2
c + d: 1/2
a == c: true
a == b: false
a != b: true
a != c: false
-a: -1/2
-e: 3
a - b: -1
c - e: 7/2
static_cast<double>(a): 0.5
static_cast<double>(b): 1.5
a * b: 3/4
c * e: -3/2
~b: 2/3
~e: -1/3
b * ~b: 1
e * ~e: 1
a / b: 1/3
c / e: -1/6
a / d: Denominator cannot be zero!
a < b: true
a < c: false
a <= c: true
a <= d: false
b > c: true
a > c: false
a >= c: true
e >= d: false

Consideraciones adicionales

¿Qué se debe entregar?

Crea un archivo comprimido TAR GZ o ZIP con el contenido completo del directorio rational. El archivo resultante se debe llamar rational.tar.gz o rational.zip.

Instrucciones para subir archivo

Para entregar el archivo rational.tar.gz/rational.zip, ingresa los siguientes datos:

Solicitar NIP

Solo es necesario que lo entregue un miembro del equipo.

La fecha límite es el lunes 2 de junio.