// My assumption: i, j are 0-indexed and m, n are not // My assumption: -1234 is a sentinel value that is used as an error code for two_d_int. #include #include #include typedef struct Node { int payload; struct Node *next; } node; // Function to allocate a 2D array of given dimensions and element size char* two_d_alloc(size_t N, size_t M, size_t sz) { if (sz <= 0) return NULL; // Check for invalid element size char *buff = malloc((N * M) * sz); // Allocate memory for the array return buff; } // Function to deallocate memory allocated for the 2D array void two_d_dealloc(char* array) { free(array); } // Function to store an integer in the 2D array at the specified position int two_d_store_int(int arg, char* array, size_t i, size_t j, size_t M, size_t N) { if (array == NULL || i >= N || j >= M) return -1; // Check for invalid arguments array[i * M + j] = arg; // Store the integer in the array return 1; } // Function to fetch an integer from the 2D array at the specified position int two_d_fetch_int(char* array, size_t i, size_t j, size_t M, size_t N) { if (array == NULL || i >= N || j >= M) { printf("\nInvalid fetch :("); return -1234; // Return sentinel value for invalid fetch } return array[i * M + j]; // Return the fetched integer } // Function to store data of arbitrary type in the 2D array at the specified position int two_d_store(void* arg, char* array, size_t i, size_t j, size_t M, size_t N, size_t sz) { if (array == NULL || i >= N || j >= M) return -1; // Check for invalid arguments memcpy(&array[(i * M + j) * sz], arg, sz); // Copy the data to the array return 1; } // Function to fetch data of arbitrary type from the 2D array at the specified position void* two_d_fetch(char* array, size_t i, size_t j, size_t M, size_t N, size_t sz) { if (array == NULL || i >= N || j >= M) { printf("\nInvalid fetch with garbage values"); } return &array[(i * M + j) * sz]; // Return a pointer to the fetched data } // Function to print a 2D array of nodes, displaying their addresses and hexadecimal values void print_node_arr(char* array, size_t M, size_t N, size_t sz) { for (size_t i = 0; i < N; ++i) { for (size_t j = 0; j < M; ++j) { // Print the address of the current element printf("%p\t", (void*)&array[(i * M + j) * sz]); // Print the hexadecimal values of each byte in the element for (size_t k = 0; k < sz; ++k) { printf("%02X ", array[(i * M + j) * sz + k]); } printf("\t"); } printf("\n"); } printf("\n"); } int main() { // Allocate a 2D array for integers char* bf = two_d_alloc(2, 3, sizeof(int)); if (bf == NULL) printf("Failed due to invalid size"); // Test cases for two_d_store_int printf("\n-------------------------------------------------------------------------------------------"); printf("\ntwo_d_store_int Test Cases:\n"); int result1 = two_d_store_int(42, bf, 0, 0, 3, 2); printf("\nTest Case 1 -> Result: %s, Expected: 1", (result1 == 1) ? "Passed :)" : "Fail :("); int result2 = two_d_store_int(99, bf, 1, 2, 3, 2); printf("\nTest Case 2 -> Result: %s, Expected: 1", (result2 == 1) ? "Passed :)" : "Fail :("); int result3 = two_d_store_int(77, bf, 0, 1, 3, 2); printf("\nTest Case 3 -> Result: %s, Expected: 1", (result3 == 1) ? "Passed :)" : "Fail :("); int result4 = two_d_store_int(123, bf, 4, 7, 3, 2); printf("\nTest Case 4 -> Result: %s, Expected: -1", (result4 == -1) ? "Passed :)" : "Fail :("); // Test cases for two_d_fetch_int printf("\n-------------------------------------------------------------------------------------------"); printf("\ntwo_d_fetch_int Test Cases:\n"); int result5 = two_d_fetch_int(bf, 0, 0, 3, 2); printf("\nTest Case 1 -> Result: %s, Expected: 42", (result5 == 42) ? "Passed :)" : "Fail :("); int result6 = two_d_fetch_int(bf, 1, 2, 3, 2); printf("\nTest Case 2 -> Result: %s, Expected: 99", (result6 == 99) ? "Passed :)" : "Fail :("); int result7 = two_d_fetch_int(bf, 0, 1, 3, 2); printf("\nTest Case 3 -> Result: %s, Expected: 77", (result7 == 77) ? "Passed :)" : "Fail :("); int result8 = two_d_fetch_int(bf, 4, 7, 3, 2); printf("\nTest Case 4 -> Result: %s, Expected: -1234", (result8 == -1234) ? "Passed :)" : "Fail :("); two_d_dealloc(bf); // Allocate a 2D array for floats char* bf2 = two_d_alloc(3, 3, sizeof(float)); if (bf2 == NULL) printf("Failed Allocation"); // Test cases for two_d_store printf("\n-------------------------------------------------------------------------------------------"); printf("\ntwo_d_store Test Cases:\n"); float floatValue1 = 1.111; int result9 = two_d_store((void*)&floatValue1, bf2, 1, 1, 3, 2, sizeof(float)); printf("\nTest Case 1 -> Result: %s, Expected: 1", (result9 == 1) ? "Passed :)" : "Fail :("); float floatValue2 = 2.222; int result10 = two_d_store((void*)&floatValue2, bf2, 0, 0, 3, 2, sizeof(float)); printf("\nTest Case 2 -> Result: %s, Expected: 1", (result10 == 1) ? "Passed :)" : "Fail :("); float floatValue3 = 3.333; int result11 = two_d_store((void*)&floatValue3, bf2, 2, 2, 3, 2, sizeof(float)); printf("\nTest Case 3 -> Result: %s, Expected: -1", (result11 == -1) ? "Passed :)" : "Fail :("); float floatValue4 = 4.444; int result12 = two_d_store((void*)&floatValue4, bf2, 6, 6, 3, 2, sizeof(float)); printf("\nTest Case 4 -> Result: %s, Expected: -1", (result12 == -1) ? "Passed :)" : "Fail :("); // Test cases for two_d_fetch printf("\n-------------------------------------------------------------------------------------------"); printf("\ntwo_d_fetch Test Cases:\n"); float result13 = *(float*)(two_d_fetch(bf2, 1, 1, 3, 2, sizeof(float))); printf("\nTest Case 1 -> Result: %s, Expected: 1.111", (result13 == (float)1.111) ? "Passed :)" : "Fail :("); float result14 = *(float*)(two_d_fetch(bf2, 0, 0, 3, 2, sizeof(float))); printf("\nTest Case 2 -> Result: %s, Expected: 2.222", (result14 == (float)2.222) ? "Passed :)" : "Fail :("); float result15 = *(float*)(two_d_fetch(bf2, 2, 2, 3, 2, sizeof(float))); printf("\nTest Case 3 -> Result: %s, Expected: 0.0", (result15 == 0.0) ? "Passed :)" : "Fail :("); float result16 = *(float*)(two_d_fetch(bf2, 6, 6, 3, 2, sizeof(float))); printf("\nTest Case 4 -> Result: %s, Expected: 0.0", (result16 == 0.0) ? "Passed :)" : "Fail :("); two_d_dealloc(bf2); printf("\n-------------------------------------------------------------------------------------------"); printf("\nAll test cases complete ;)"); // Bonus // Test two_d_alloc // Set the size parameters for the 2D array and the size of each element size_t M = 4, N = 4, sz = sizeof(int); char* bf3 = two_d_alloc(3, 3, sizeof(float)); // Test Node storage and fetch // Display the state of the 2D array before storing nodes printf("Before:\n"); print_node_arr(bf3, M, N, sz); // Create and store a node with payload -1 and NULL next pointer at position (0, 0) node last; last.payload = -1; last.next = NULL; two_d_store((void *)&last, bf3, 0, 0, M, N, sz); // Fetch the stored node at position (0, 0) node *lst = (node *)two_d_fetch(bf3, 0, 0, M, N, sz); // Create and store a node with payload 100 and a next pointer pointing to the previously stored node at (0, 0) node d1; d1.payload = 100; d1.next = lst; two_d_store((void *)&d1, bf3, 0, 1, M, N, sz); // Fetch the stored node at position (0, 1) lst = (node *)two_d_fetch(bf3, 0, 1, M, N, sz); // Create and store a node with payload 200 and a next pointer pointing to the previously stored node at (0, 1) d1.payload = 200; d1.next = lst; two_d_store((void *)&d1, bf3, 1, 1, M, N, sz); // Display the state of the 2D array after storing nodes printf("After:\n"); print_node_arr(bf3, M, N, sz); // Deallocate memory for the 2D array two_d_dealloc(bf3); return 0; }