From 96006ff1708a68730d46031ee1856728eadd03e8 Mon Sep 17 00:00:00 2001 From: LaEntropiaa Date: Sat, 21 Mar 2026 22:13:19 -0600 Subject: [PATCH] Got everything, just need to change comments and do the menu --- .gitignore | 149 +++++++++++++++++ CMakeLists.txt | 33 ++++ include/linkedlist.h | 352 +++++++++++++++++++++++++++++++++++++++ include/utils.h | 14 ++ src/main.cpp | 8 + src/utils.cpp | 129 ++++++++++++++ test/CMakeLists.txt | 38 +++++ test/test_linkedlist.cpp | 249 +++++++++++++++++++++++++++ 8 files changed, 972 insertions(+) create mode 100644 .gitignore create mode 100644 CMakeLists.txt create mode 100644 include/linkedlist.h create mode 100644 include/utils.h create mode 100644 src/main.cpp create mode 100644 src/utils.cpp create mode 100644 test/CMakeLists.txt create mode 100644 test/test_linkedlist.cpp diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..6129d0a --- /dev/null +++ b/.gitignore @@ -0,0 +1,149 @@ +##Doxygen +docs +Doxyfile +images + +# Prerequisites +*.d + +# Compiled Object files +*.slo +*.lo +*.o +*.obj + +# Precompiled Headers +*.gch +*.pch + +# Linker files +*.ilk + +# Debugger Files +*.pdb + +# Compiled Dynamic libraries +*.so +*.dylib +*.dll +*.so.* + + +# Fortran module files +*.mod +*.smod + +# Compiled Static libraries +*.lai +*.la +*.a +*.lib + +# Executables +*.exe +*.out +*.app + +# Build directories +build/ +Build/ +build-*/ +out +out/Debug + +# CMake generated files +CMakeFiles/ +CMakeCache.txt +cmake_install.cmake +Makefile +install_manifest.txt +compile_commands.json + +# Temporary files +*.tmp +*.log +*.bak +*.swp + +# vcpkg +vcpkg_installed/ + +# debug information files +*.dwo + +# test output & cache +Testing/ +.cache/Doxygen +docs +Doxyfile +images + +# Prerequisites +*.d + +# Compiled Object files +*.slo +*.lo +*.o +*.obj + +# Precompiled Headers +*.gch +*.pch + +# Linker files +*.ilk + +# Debugger Files +*.pdb + +# Compiled Dynamic libraries +*.so +*.dylib +*.dll +*.so.* + + +# Fortran module files +*.mod +*.smod + +# Compiled Static libraries +*.lai +*.la +*.a +*.lib + +# Executables +*.exe +*.out +*.app + +# Build directories +build/ +Build/ +build-*/ + +# CMake generated files +CMakeFiles/ +CMakeCache.txt +cmake_install.cmake +Makefile +install_manifest.txt +compile_commands.json + +# Temporary files +*.tmp +*.log +*.bak +*.swp + +# vcpkg +vcpkg_installed/ + +# debug information files +*.dwo + +# test output & cache +Testing/ +.cache/ diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..a58f0c2 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,33 @@ +cmake_minimum_required(VERSION 3.16) + +project(LinkedList + VERSION 1.0 + LANGUAGES CXX +) + +set(CMAKE_CXX_STANDARD 23) +set(CMAKE_CXX_STANDARD_REQUIRED ON) +set(CMAKE_CXX_EXTENSIONS OFF) + +set(CMAKE_EXPORT_COMPILE_COMMANDS ON) + +add_compile_options( + -Wall + -Wextra + -Wpedantic +) + + +add_executable(main_exec + src/main.cpp + src/utils.cpp +) + +target_include_directories(main_exec + PRIVATE + ${PROJECT_SOURCE_DIR}/include +) + +enable_testing() + +add_subdirectory(test) diff --git a/include/linkedlist.h b/include/linkedlist.h new file mode 100644 index 0000000..d436734 --- /dev/null +++ b/include/linkedlist.h @@ -0,0 +1,352 @@ +#ifndef LINKEDLIST_H +#define LINKEDLIST_H + +#include +#include +#include +#include + +/** + * @brief Codigos de error para operaciones de LinkedList + */ +enum class LinkedListErr { + LINKEDLIST_OK, /**< Operacin exitosa */ + LINKEDLIST_BAD_ALLOC, /**< Error al asignar memoria */ + LINKEDLIST_OUT_OF_BOUNDS, /**< Índice fuera de rango */ + LINKESLIST_INVALID_SIZE, /**< Tamaño inválido */ + LINKEDLIST_NOT_FOUND, /**< Elemento no encontrado */ + LINKEDLIST_EMPTY, /**< La lista está vacia */ +}; + +/** + * @brief Nodo de una lista enlazada + * + * @tparam T Tipo de dato almacenado en el nodo + */ +template +class Node { +public: + T data; /**< Dato almacenado en el nodo */ + Node *next; /**< Puntero al siguiente nodo */ +}; + +/** + * @brief Implementación de una lista enlazada simple + * + * Estructura de datos dinamica que almacena elementos de tipo T + * utilizando nodos enlazados mediante punteros. + * + * @tparam T Tipo de dato almacenado en la lista + */ +template +class LinkedList { +private: + size_t size; /**< Numero de elementos en la lista */ + Node *head; /**< Primer nodo de la lista */ + Node *tail; /**< Último nodo de la lista */ + +public: + + /** + * @brief Constructor de la lista enlazada + */ + LinkedList(); + + /** + * @brief Destructor de la lista + * + * Libera toda la memoria de los nodos. + */ + ~LinkedList(); + + /** + * @brief Obtiene el elemento en una posicion específica + * + * @param index indice del elemento + * @return std::expected Valor encontrado o error + */ + std::expected get(size_t index); + + /** + * @brief Inserta un elemento al final de la lista + * + * @param value Valor a insertar + * @return LinkedListErr Cdigo de estado de la operacion + */ + LinkedListErr append(const T& value); + + /** + * @brief Inserta un elemento al inicio de la lista + * + * @param value Valor a insertar + * @return LinkedListErr Codigo de estado + */ + LinkedListErr prepend(const T& value); + + /** + * @brief Elimina y devuelve el ultimo elemento de la lista + * + * @return std::expected Elemento eliminado o error + */ + std::expected pop(); + + /** + * @brief Elimina el elemento en una posicion especifica + * + * @param index Indice del elemento a eliminar + * @return LinkedListErr Codigo de estado + */ + LinkedListErr remove(size_t index); + + /** + * @brief Modifica el valor de un elemento en un indice + * + * @param index Índice del elemento + * @param value Nuevo valor + * @return LinkedListErr Código de estado + */ + LinkedListErr set(size_t index, const T& value); + + /** + * @brief Busca un valor dentro de la lista + * + * @param value Valor a buscar + * @return std::expected Índice del elemento o error + */ + std::expected find(const T& value); + + /** + * @brief Imprime los elementos de la lista + * + * @return LinkedListErr Código de estado + */ + LinkedListErr print(); + + /** + * @brief Verifica si la lista esta vacia + * + * @return true si la lista no contiene elementos + */ + bool is_empty(); + + /** + * @brief Obtiene el numero de elementos de la lista + * + * @return size_t Tamaño de la lista + */ + size_t len(); +}; + +template +LinkedList::LinkedList() { + size = 0; + head = nullptr; + tail = nullptr; +} + +template +LinkedList::~LinkedList() { + Node *current = this->head; + + while (current != nullptr) { + Node *temp = current; + current = current->next; + delete temp; + } +} + +template +std::expected LinkedList::get(size_t index) { + if (index >= this->size) { + return std::unexpected(LinkedListErr::LINKEDLIST_OUT_OF_BOUNDS); + } + + if (index == this->size - 1) { + return tail->data; + } + + Node *current = this->head; + for (size_t i = 0; i < index ; i++) { + current = current->next; + } + + return current->data; +} + +template +LinkedListErr LinkedList::set(size_t index, const T& val) { + if (index >= this->size) { + return LinkedListErr::LINKEDLIST_OUT_OF_BOUNDS; + } + + if (index == this->size - 1) { + this->tail->data = val; + } + + Node *current = this->head; + for (size_t i = 0; i < index; i++) { + current = current->next; + } + + current->data = val; + return LinkedListErr::LINKEDLIST_OK; +} + +template +LinkedListErr LinkedList::append(const T& value) { + Node *new_node = nullptr; + + try { + new_node = new Node; + } catch(std::bad_alloc&) { + return LinkedListErr::LINKEDLIST_BAD_ALLOC; + } + + new_node->data = value; + new_node->next = nullptr; + + if (this->head == nullptr && this->tail == nullptr) { + this->head = new_node; + this->tail = this->head; + } else { + this->tail->next = new_node; + this->tail = this->tail->next; + } + + this->size += 1; + + return LinkedListErr::LINKEDLIST_OK; +} + +template +LinkedListErr LinkedList::prepend(const T& value) { + Node *new_node = nullptr; + + try { + new_node = new Node; + } catch(std::bad_alloc&) { + return LinkedListErr::LINKEDLIST_BAD_ALLOC; + } + + new_node->data = value; + new_node->next = nullptr; + + if (this->head == nullptr && this->tail == nullptr) { + this->head = new_node; + this->tail = this->head; + } else { + new_node->next = this->head; + this->head = new_node; + } + + this->size += 1; + + return LinkedListErr::LINKEDLIST_OK; +} + +template +std::expected LinkedList::pop() { + if (this->size == 0) { + return std::unexpected(LinkedListErr::LINKEDLIST_EMPTY); + } + + if (this->head->next == nullptr) { + T return_val = this->head->data; + delete this->head; + this->head = nullptr; + this->size--; + return return_val; + } + + Node* current = this->head; + + while (current->next->next != nullptr) { + current = current->next; + } + + T return_val = current->next->data; + delete current->next; + current->next = nullptr; + + this->size--; + + return return_val; +} + +template +LinkedListErr LinkedList::remove(size_t index) { + if (index >= this->size) { + return LinkedListErr::LINKEDLIST_OUT_OF_BOUNDS; + } + + if (index == 0) { + Node* tmp = this->head; + this->head = this->head->next; + delete tmp; + this->size--; + return LinkedListErr::LINKEDLIST_OK; + } + + Node* current = this->head; + + for (size_t i = 1; i < index; ++i) { + current = current->next; + } + + Node* tmp = current->next; + current->next = tmp->next; + + if(tmp == this->tail) { + this->tail = current; + } + + delete tmp; + this->size--; + + return LinkedListErr::LINKEDLIST_OK; +} + + +template +std::expected LinkedList::find(const T& value) { + Node *current = this->head; + size_t count = 0; + while (current != nullptr) { + if (current->data == value) { + return count; + } + + count++; + current = current->next; + } + + return std::unexpected(LinkedListErr::LINKEDLIST_NOT_FOUND); +} + +template +LinkedListErr LinkedList::print() { + if (this->size == 0) { + return LinkedListErr::LINKEDLIST_OK; + } + + std::println("[LL]"); + std::println(" |\n v"); + Node *current = this->head; + while (current != nullptr) { + std::println("Node({})", current->data); + std::println(" |"); + std::println(" v"); + current = current->next; + } + + std::println("NULL"); + return LinkedListErr::LINKEDLIST_OK; +} + +template +size_t LinkedList::len() { + return this->size; +} + + +#endif // ! diff --git a/include/utils.h b/include/utils.h new file mode 100644 index 0000000..f7af976 --- /dev/null +++ b/include/utils.h @@ -0,0 +1,14 @@ +#ifndef UTILS_H +#define UTILS_H + +#include +#include + +void clear_screen(); +void wait_enter(); +void sleep_seconds(size_t s); +void read_string(const char *message, char **dest); +int read_int(const char *message, int *dest); +int read_double(const char *message, double *dest); + +#endif // ! diff --git a/src/main.cpp b/src/main.cpp new file mode 100644 index 0000000..dcf36c6 --- /dev/null +++ b/src/main.cpp @@ -0,0 +1,8 @@ +#include "linkedlist.h" +#include +#include + +int main(void) { + std::print("hi"); + return EXIT_SUCCESS; +} diff --git a/src/utils.cpp b/src/utils.cpp new file mode 100644 index 0000000..3c1c82e --- /dev/null +++ b/src/utils.cpp @@ -0,0 +1,129 @@ +#include "utils.h" +#include +#include +#include +#include +#include +#include +#include +#include + +void clear_screen() { + printf("\033[2J\033[H"); + fflush(stdout); +} + +void wait_enter() { + int c; + printf("Press Enter to continue..."); + fflush(stdout); + + while ((c = getchar()) != '\n' && c != EOF) { + ; + } +} + +void sleep_seconds(size_t s) { + #ifdef _WIN32 + #include + Sleep(s * 1000); + #else + // #include + // sleep(s); + #endif /* ifdef _WIN32 + */ +} + +void read_string(const char *message, char **dest) { + if (!dest) { + return; + } + + printf("%s", message); + + char buffer[256]; // I mean, should be enough + if (!fgets(buffer, sizeof(buffer), stdin)) + return; + + buffer[strcspn(buffer, "\n")] = '\0'; + + char *tmp = static_cast(malloc(strlen(buffer) + 1)); + if (!tmp) { + return; + } + + strcpy(tmp, buffer); + + free(*dest); + *dest = tmp; +} + +int read_int(const char *message, int *dest) { + char buffer[64]; + char *end; + long value; + + printf("%s", message); + fflush(stdout); + + if (!fgets(buffer, sizeof(buffer), stdin)) { + return 0; + } + + errno = 0; + value = strtol(buffer, &end, 10); + + if (errno != 0) { + return 0; + } + + if (end == buffer) { + return 0; + } + + if (*end != '\n' && *end != '\0') { + return 0; + } + + if (value < INT_MIN || value > INT_MAX) { + return 0; + } + + *dest = (int)value; + return 1; +} + +int read_double(const char *message, double *dest) { + char buffer[64]; + char *end; + double value; + + printf("%s", message); + fflush(stdout); + + if (!fgets(buffer, sizeof(buffer), stdin)) { + return 0; + } + + errno = 0; + value = strtod(buffer, &end); + + if (errno != 0) { + return 0; + } + + if (end == buffer) { + return 0; + } + + if (*end != '\n' && *end != '\0') { + return 0; + } + + if (!isfinite(value)) { + return 0; + } + + *dest = value; + return 1; +} diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt new file mode 100644 index 0000000..0a46e74 --- /dev/null +++ b/test/CMakeLists.txt @@ -0,0 +1,38 @@ +include(FetchContent) + +FetchContent_Declare( + Catch2 + GIT_REPOSITORY https://github.com/catchorg/Catch2.git + GIT_TAG v3.5.3 +) + +FetchContent_MakeAvailable(Catch2) + +add_executable(test_linkedlist + test_linkedlist.cpp +) + +target_include_directories(test_linkedlist + PRIVATE + ${PROJECT_SOURCE_DIR}/include +) + +target_link_libraries(test_linkedlist + PRIVATE + Catch2::Catch2WithMain +) + +target_compile_options(test_linkedlist PRIVATE + -fsanitize=address + -fno-omit-frame-pointer + -g +) + +target_link_options(test_linkedlist PRIVATE + -fsanitize=address +) + +include(CTest) +include(Catch) + +catch_discover_tests(test_linkedlist) diff --git a/test/test_linkedlist.cpp b/test/test_linkedlist.cpp new file mode 100644 index 0000000..db16829 --- /dev/null +++ b/test/test_linkedlist.cpp @@ -0,0 +1,249 @@ +#include "linkedlist.h" +#include + +TEST_CASE("Append 3 items to linkedlist", "[linkedlist]") { + LinkedList list = {}; + + REQUIRE(list.len() == 0); + + list.append(8); + REQUIRE(list.len() == 1); + REQUIRE(list.get(0) == 8); + + list.append(9); + REQUIRE(list.len() == 2); + REQUIRE(list.get(1) == 9); + + list.append(10); + REQUIRE(list.len() == 3); + REQUIRE(list.get(2) == 10); + +} + +TEST_CASE("Prepend 3 items to linkedlist", "[linkedlist]") { + LinkedList list = {}; + + REQUIRE(list.len() == 0); + + list.prepend(8); + REQUIRE(list.len() == 1); + REQUIRE(list.get(0) == 8); + + list.prepend(9); + REQUIRE(list.len() == 2); + REQUIRE(list.get(0) == 9); + + list.prepend(10); + REQUIRE(list.len() == 3); + REQUIRE(list.get(0) == 10); + +} + +TEST_CASE("Get out ouf bounds item", "[linkedlist]") { + LinkedList list = {}; + + REQUIRE(list.len() == 0); + + list.append(8); + REQUIRE(list.len() == 1); + REQUIRE(list.get(0) == 8); + + list.append(9); + REQUIRE(list.len() == 2); + REQUIRE(list.get(1) == 9); + + list.append(10); + REQUIRE(list.len() == 3); + REQUIRE(list.get(2) == 10); + + auto result = list.get(50); + REQUIRE(!result.has_value()); + REQUIRE(result.error() == LinkedListErr::LINKEDLIST_OUT_OF_BOUNDS); +} + +TEST_CASE("Set head to val", "[linkedlist]") { + LinkedList list = {}; + + REQUIRE(list.len() == 0); + + list.append(8); + REQUIRE(list.len() == 1); + REQUIRE(list.get(0) == 8); + + list.append(9); + REQUIRE(list.len() == 2); + REQUIRE(list.get(1) == 9); + + list.append(10); + REQUIRE(list.len() == 3); + REQUIRE(list.get(2) == 10); + + auto result = list.set(0, 90); + REQUIRE(list.len() == 3); + REQUIRE(result == LinkedListErr::LINKEDLIST_OK); + REQUIRE(list.get(0) == 90); +} + +TEST_CASE("Set tail to val", "[linkedlist]") { + LinkedList list = {}; + + REQUIRE(list.len() == 0); + + list.append(8); + REQUIRE(list.len() == 1); + REQUIRE(list.get(0) == 8); + + list.append(9); + REQUIRE(list.len() == 2); + REQUIRE(list.get(1) == 9); + + list.append(10); + REQUIRE(list.len() == 3); + REQUIRE(list.get(2) == 10); + + auto result = list.set(2, 90); + REQUIRE(list.len() == 3); + REQUIRE(result == LinkedListErr::LINKEDLIST_OK); + REQUIRE(list.get(2) == 90); +} + +TEST_CASE("Set to out of bounds", "[linkedlist]") { + LinkedList list = {}; + + REQUIRE(list.len() == 0); + + list.append(8); + REQUIRE(list.len() == 1); + REQUIRE(list.get(0) == 8); + + list.append(9); + REQUIRE(list.len() == 2); + REQUIRE(list.get(1) == 9); + + list.append(10); + REQUIRE(list.len() == 3); + REQUIRE(list.get(2) == 10); + + auto result = list.set(50, 90); + REQUIRE(list.len() == 3); + REQUIRE(result == LinkedListErr::LINKEDLIST_OUT_OF_BOUNDS); +} + +TEST_CASE("Pop element", "[linkedlist]") { + LinkedList list = {}; + + REQUIRE(list.len() == 0); + + list.append(8); + REQUIRE(list.len() == 1); + REQUIRE(list.get(0) == 8); + + list.append(9); + REQUIRE(list.len() == 2); + REQUIRE(list.get(1) == 9); + + list.append(10); + REQUIRE(list.len() == 3); + REQUIRE(list.get(2) == 10); + + REQUIRE(list.pop() == 10); + REQUIRE(list.len() == 2); +} + +TEST_CASE("Pop empty", "[linkedlist]") { + LinkedList list = {}; + + REQUIRE(list.len() == 0); + + auto result = list.pop(); + REQUIRE(result.error() == LinkedListErr::LINKEDLIST_EMPTY); + REQUIRE(list.len() == 0); +} + +TEST_CASE("Remove head", "[linkedlist]") { + LinkedList list = {}; + + REQUIRE(list.len() == 0); + + list.append(8); + REQUIRE(list.len() == 1); + REQUIRE(list.get(0) == 8); + + list.append(9); + REQUIRE(list.len() == 2); + REQUIRE(list.get(1) == 9); + + list.append(10); + REQUIRE(list.len() == 3); + REQUIRE(list.get(2) == 10); + + auto result = list.remove(0); + REQUIRE(result == LinkedListErr::LINKEDLIST_OK); + REQUIRE(list.len() == 2); + REQUIRE(list.get(0) == 9); +} + +TEST_CASE("Remove tail", "[linkedlist]") { + LinkedList list = {}; + + REQUIRE(list.len() == 0); + + list.append(8); + REQUIRE(list.len() == 1); + REQUIRE(list.get(0) == 8); + + list.append(9); + REQUIRE(list.len() == 2); + REQUIRE(list.get(1) == 9); + + list.append(10); + REQUIRE(list.len() == 3); + REQUIRE(list.get(2) == 10); + + auto result = list.remove(2); + REQUIRE(result == LinkedListErr::LINKEDLIST_OK); + REQUIRE(list.len() == 2); + REQUIRE(list.get(1) == 9); +} + +TEST_CASE("Find 1 value present", "[linkedlist]") { + LinkedList list = {}; + + REQUIRE(list.len() == 0); + + list.append(8); + REQUIRE(list.len() == 1); + REQUIRE(list.get(0) == 8); + + list.append(9); + REQUIRE(list.len() == 2); + REQUIRE(list.get(1) == 9); + + list.append(10); + REQUIRE(list.len() == 3); + REQUIRE(list.get(2) == 10); + + auto result = list.find(9); + REQUIRE(result == 1); +} + +TEST_CASE("Find 1 value not present", "[linkedlist]") { + LinkedList list = {}; + + REQUIRE(list.len() == 0); + + list.append(8); + REQUIRE(list.len() == 1); + REQUIRE(list.get(0) == 8); + + list.append(9); + REQUIRE(list.len() == 2); + REQUIRE(list.get(1) == 9); + + list.append(10); + REQUIRE(list.len() == 3); + REQUIRE(list.get(2) == 10); + + auto result = list.find(80); + REQUIRE(result.error() == LinkedListErr::LINKEDLIST_NOT_FOUND); +}