diff --git a/ex4/ex4.c b/ex4/ex4.c
index 97016cc686ec4794f73e8854d71525288fc720f2..1d1275987fff5b445760768402d19f523da922b8 100644
--- a/ex4/ex4.c
+++ b/ex4/ex4.c
@@ -9,37 +9,264 @@
  *
  */
 
+#include <assert.h>
 #include <math.h>
 #include <stdbool.h>
 #include <stdint.h>
 #include <stdio.h>
 #include <stdlib.h>
-#include <string.h>
+
+// Matrix.h
+
+typedef struct Matrix {
+    int32_t m;
+    int32_t n;
+    int32_t **data;
+} Matrix;
+
+Matrix *matrix_init(int32_t m, int32_t n);
+void matrix_destroy(Matrix **matrix);
+int32_t matrix_get(Matrix *matrix, int32_t y, int32_t x);
+void matrix_set(Matrix *matrix, int32_t y, int32_t x, int32_t value);
+
+// Matrix.c
+
+Matrix *matrix_init(int32_t m, int32_t n) {
+    Matrix *matrix = (Matrix *)malloc(sizeof(Matrix));
+    matrix->m = m;
+    matrix->n = n;
+    matrix->data = (int32_t **)malloc(sizeof(int32_t *) * m);
+
+    for (int32_t i = 0; i < matrix->m; i += 1) {
+        matrix->data[i] = (int32_t *)calloc(n, sizeof(int32_t));
+    }
+
+    return matrix;
+}
+
+void matrix_destroy(Matrix **matrix) {
+    for (int32_t i = 0; i < (*matrix)->m; i += 1) {
+        free((*matrix)->data[i]);
+    }
+
+    free((*matrix)->data);
+    free(*matrix);
+    *matrix = NULL;
+}
+
+int32_t matrix_get(Matrix *matrix, int32_t y, int32_t x) {
+    return matrix->data[y][x];
+}
+
+void matrix_set(Matrix *matrix, int32_t y, int32_t x, int32_t value) {
+    matrix->data[y][x] = value;
+}
+
+void matrix_print(Matrix *matrix) {
+    for (int i = 0; i < matrix->m; i += 1) {
+        for (int j = 0; j < matrix->n; j += 1) {
+            printf("%2d ", matrix->data[i][j]);
+        }
+
+        printf("\n");
+    }
+}
+
+// Quadtree.h
+
+#define CHILDREN_COUNT 4
+
+typedef struct Node {
+    int data;
+    struct Node *children[CHILDREN_COUNT];
+} Node;
+
+int32_t _max(int32_t a, int32_t b);
+Node *node_init();
+Node *quadtree_init(int32_t depth);
+void quadtree_destroy(Node **root);
+int32_t quadtree_depth(Node *root);
+bool quadtree_is_leaf(Node *root);
+Node *quadtree_search(Node *root, int32_t depth, int32_t y, int32_t x);
+Node *matrix_to_quadtree(Matrix *matrix);
+Matrix *quadtree_to_matrix(Node *root);
+
+// Quadtree.c
+
+int32_t _max(int32_t a, int32_t b) {
+    return a > b ? a : b;
+}
+
+Node *node_init() {
+    Node *node = (Node *)malloc(sizeof(Node));
+    node->data = 0;
+
+    for (int32_t i = 0; i < CHILDREN_COUNT; i += 1) {
+        node->children[i] = NULL;
+    }
+
+    return node;
+}
+
+Node *quadtree_init(int32_t depth) {
+    Node *root = node_init();
+
+    if (depth == 0) {
+        return root;
+    }
+
+    for (int32_t i = 0; i < CHILDREN_COUNT; i += 1) {
+        root->children[i] = quadtree_init(depth - 1);
+    }
+
+    return root;
+}
+
+void quadtree_destroy(Node **root) {
+    if (!quadtree_is_leaf(*root)) {
+        for (int32_t i = 0; i < CHILDREN_COUNT; i += 1) {
+            quadtree_destroy(&(*root)->children[i]);
+        }
+    }
+
+    free(*root);
+    *root = NULL;
+}
+
+int32_t quadtree_depth(Node *root) {
+    if (root == NULL) {
+        return -1;
+    }
+
+    if (quadtree_is_leaf(root)) {
+        return 0;
+    }
+
+    int32_t biggest_depth = 0;
+
+    for (int32_t i = 0; i < CHILDREN_COUNT; i += 1) {
+        biggest_depth = _max(biggest_depth, quadtree_depth(root->children[i]));
+    }
+
+    return biggest_depth + 1;
+}
+
+bool quadtree_is_leaf(Node *root) {
+    return root->children[0] == NULL;
+}
+
+Node *quadtree_search(Node *root, int32_t depth, int32_t y, int32_t x) {
+    for (int32_t d = depth - 1; d >= 0; d -= 1) {
+        if (quadtree_is_leaf(root)) {
+            return root;
+        }
+
+        int32_t i = (y >> d) & 1;
+        int32_t j = (x >> d) & 1;
+        root = root->children[2 * i + j];
+    }
+
+    return root;
+}
+
+Node *matrix_to_quadtree(Matrix *matrix) {
+    int32_t depth = (int32_t)log2(matrix->m);
+    Node *root = quadtree_init(depth);
+
+    for (int32_t i = 0; i < matrix->m; i += 1) {
+        for (int32_t j = 0; j < matrix->n; j += 1) {
+            Node *node = quadtree_search(root, depth, i, j);
+            node->data = matrix_get(matrix, i, j);
+        }
+    }
+
+    return root;
+}
+
+Matrix *quadtree_to_matrix(Node *root) {
+    int32_t depth = quadtree_depth(root);
+    int32_t size = (int32_t)pow(2, depth);
+    Matrix *matrix = matrix_init(size, size);
+
+    for (int32_t i = 0; i < matrix->m; i++) {
+        for (int32_t j = 0; j < matrix->n; j++) {
+            Node *node = quadtree_search(root, depth, i, j);
+            int32_t value = node->data;
+            matrix_set(matrix, i, j, value);
+        }
+    }
+
+    return matrix;
+}
+
+// ---
+
+Node *clone(Node *root) {
+    if (root == NULL) {
+        return NULL;
+    }
+
+    Node *output = node_init();
+    output->data = root->data;
+
+    for (int i = 0; i < 4; i += 1) {
+        output->children[i] = clone(root->children[i]);
+    }
+
+    return output;
+}
 
 int main() {
-    // int32_t values_length = 5;
-    // double values[values_length];
-
-    // for (int32_t i = 0; i < values_length; i += 1) {
-    //     double value;
-    //     scanf("%lf", &value);
-    //     values[i] = value;
-    // }
-
-    // int32_t values_length = 5;
-    // int32_t values[values_length];
-
-    // for (int32_t i = 0; i < values_length; i += 1) {
-    //     int32_t value;
-    //     scanf("%d", &value);
-    //     values[i] = value;
-    // }
-
-    // char a[100];
-    // int32_t b;
-    // scanf("%s %d", a, &b);
-    // printf("%s %d\n", a, b);
-
-    printf("ex1\n");
+    int n = 16;
+    int32_t input[16][16] = {
+        {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+        {0, 3, 3, 3, 3, 0, 0, 7, 7, 7, 7, 0, 0, 11, 11, 11},
+        {0, 3, 0, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 11, 0, 0},
+        {0, 3, 3, 3, 0, 0, 0, 7, 7, 7, 0, 0, 0, 11, 11, 11},
+        {0, 3, 0, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 11, 0, 0},
+        {0, 3, 0, 0, 0, 0, 0, 7, 7, 7, 7, 0, 0, 11, 11, 11},
+        {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+        {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+        {0, 3, 3, 3, 3, 0, 0, 7, 7, 7, 7, 0, 0, 11, 11, 11},
+        {0, 3, 0, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 11, 0, 0},
+        {0, 3, 3, 3, 0, 0, 0, 7, 7, 7, 0, 0, 0, 11, 11, 11},
+        {0, 3, 0, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 11, 0, 0},
+        {0, 3, 0, 0, 0, 0, 0, 7, 7, 7, 7, 0, 0, 11, 11, 11},
+        {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+        {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
+        {0, 3, 3, 3, 3, 0, 0, 7, 7, 7, 7, 0, 0, 11, 11, 11},
+    };
+
+    Matrix *matrix_input = matrix_init(n, n);
+
+    for (int i = 0; i < n; i += 1) {
+        for (int j = 0; j < n; j += 1) {
+            matrix_set(matrix_input, i, j, input[i][j]);
+        }
+    }
+
+    Node *tree = matrix_to_quadtree(matrix_input);
+    Node *tree_cloned = clone(tree);
+    quadtree_destroy(&tree);
+    Matrix *matrix_output = quadtree_to_matrix(tree_cloned);
+    quadtree_destroy(&tree_cloned);
+
+    // matrix_print(matrix_input);
+    matrix_print(matrix_output);
+
+    // Assert that the two matrices are not the same in memory and that they have the same data.
+    assert(matrix_input != matrix_output);
+    assert(matrix_input->data != matrix_output->data);
+    for (int i = 0; i < matrix_input->m; i += 1) {
+        assert(matrix_input->data[i] != matrix_output->data[i]);
+
+        for (int j = 0; j < matrix_input->n; j += 1) {
+            assert(matrix_input->data[i][j] == matrix_output->data[i][j]);
+            assert(matrix_input->data[i][j] == matrix_output->data[i][j]);
+        }
+    }
+
+    matrix_destroy(&matrix_input);
+    matrix_destroy(&matrix_output);
     return EXIT_SUCCESS;
 }