Ejercicio obligatorio 3

Fecha de entrega: Domingo 7 de mayo

Nota

Nunca es buena idea empezar por un ejercicio integrador antes de tener practicados los temas que el trabajo integra.

Se sugiere antes de desarrollar este trabajo resolver, al menos, los siguientes ejercicios de la guía de Memoria dinámica:

  • Ejercicio 12 (combinar vectores).

  • Ejercicio 14.a (matriz identidad).

  • Ejercicio 16 con el 1.b y 1.c de la guía de estructuras (dirección y persona).

Introducción

Queremos modelar una matriz, para ello definimos la estructura:

typedef struct {
    double **m;
    size_t filas, columnas;
} matriz_t;

que permite almacenar en memoria una matriz según el siguiente esquema:

../_images/20231_ej3.png

Trabajo

Creación y destrucción

Se pide implementar la función auxiliar matriz_t *_matriz_crear(size_t n, size_t m); que genere la memoria necesaria para almacenar una matriz de n x m valores. Los valores no deben ser inicializados. La función debe devolver la matriz creada o NULL en caso de falla.

Se pide implementar la función void matriz_destruir(matriz_t *matriz); que libere la memoria asociada a la matriz previamente creada.

Acceso a los datos

Se pide implementar una función size_t matriz_filas(const matriz_t *matriz); que devuelve el número de filas de la matriz.

Se pide implementar una función size_t matriz_columnas(const matriz_t *matriz); que devuelve el número de columnas de la matriz.

Se pide implementar una función void matriz_dimensiones(const matriz_t *matriz, size_t *filas, size_t *columnas); que devuelva la cantidad de filas y columnas de la matriz.

Se pide implementar una función double matriz_obtener(const matriz_t *matriz, size_t fila, size_t columna); que devuelva el valor almacenado en la posición fila, columna de la matriz.

Generación de matrices

Implementar una función matriz_t *matriz_crear(size_t n, size_t m, double a[n][m]); que cree una matriz a partir del arreglo bidimensional a. La función debe retornar la matriz creada o NULL en caso de falla.

Implementar una función matriz_t *matriz_clonar(const matriz_t *matriz); que devuelva una copia de matriz en memoria nueva o NULL en caso de falla.

Implementar una función matriz_t *matriz_identidad(size_t n); que devuelva la matriz identidad de \(\mathbb R^{n\times n}\) o NULL en caso de falla.

Alguna operación suelta del EJ2

Implementar una función matriz_t *matriz_multiplicar(const matriz_t *a, const matriz_t *b); que devuelva \(A \times B\). En caso de falla o de no ser compatibles las matrices recibidas debe devolver NULL.

Aplicación y validación

Se provee el código fuente de la función main() que realiza llamadas a cada una de estas funciones. Dicha función debe ser la que se entregue como parte del trabajo sin efectuarle modificaciones de ningún tipo

#include <stddef.h>
#include <assert.h>

typedef struct {
    double **m;
    size_t filas, columnas;
} matriz_t;

int main(void) {
    double k[3][2] = {{21, -11},
        {-11, 23},
        {0, -12}};

    matriz_t *m1 = matriz_identidad(3);
    assert(m1 != NULL);
    assert(matriz_filas(m1) == 3 && matriz_columnas(m1) == 3);
    assert(matriz_obtener(m1, 0, 0) == 1);
    assert(matriz_obtener(m1, 1, 0) == 0);
    assert(matriz_obtener(m1, 2, 2) == 1);
    assert(matriz_obtener(m1, 2, 0) == 0);

    matriz_t *m2 = matriz_crear(3, 2, k);
    assert(m2 != NULL);
    assert(matriz_filas(m2) == 3 && matriz_columnas(m2) == 2);
    size_t f, c;
    matriz_dimensiones(m2, &f, &c);
    assert(f == 3 && c == 2);
    assert(matriz_obtener(m2, 0, 0) == 21);
    assert(matriz_obtener(m2, 1, 0) == -11);
    assert(matriz_obtener(m2, 2, 1) == -12);

    assert(matriz_multiplicar(m2, m1) == NULL);

    matriz_t *m3 = matriz_multiplicar(m1, m2);
    assert(m3 != NULL);

    matriz_destruir(m1);

    assert(matriz_filas(m3) == 3 && matriz_columnas(m3) == 2);

    matriz_t *m4 = matriz_clonar(m3);
    assert(m4 != NULL);

    matriz_destruir(m3);

    assert(matriz_filas(m4) == 3 && matriz_columnas(m4) == 2);

    assert(matriz_obtener(m2, 0, 0) == matriz_obtener(m4, 0, 0));
    assert(matriz_obtener(m2, 1, 0) == matriz_obtener(m4, 1, 0));
    assert(matriz_obtener(m2, 0, 1) == matriz_obtener(m4, 0, 1));
    assert(matriz_obtener(m2, 2, 1) == matriz_obtener(m4, 2, 1));

    matriz_destruir(m2);
    matriz_destruir(m4);

    assert(_matriz_crear(1000, 1UL << 59) == NULL);
    assert(_matriz_crear(1UL << 59, 1000) == NULL);

    return 0;
}

El programa al ser invocado deberá pasar los asserts dados.

Entrega

Deberá entregarse el código fuente del programa desarrollado.

El programa debe:

  1. Compilar correctamente con los flags:

    -Wall -Werror -std=c99 -pedantic
    
  2. pasar Valgrind correctamente,

  3. validar los asserts.

La entrega se realiza a través del sistema de entregas.

El ejercicio es de entrega individual.