#ifndef TSINGLELINKEDLIST_HPP #define TSINGLELINKEDLIST_HPP #pragma once #include // Required for the Clone function, and prevent delete on non-pointer types #include "TDoublyLinkedListTemplate.hpp" #include "TCircularDoublyLinkedListTemplate.hpp" template class TDoublyLinkedList; // Forward declaration for friendship template class TCircularDoublyLinkedList; // Forward declaration for friendship // Callback type definitions template using FDataFactory = T(*)(TArgs); // <-- ADD THIS LINE template using FCheckNode = bool(*)(const T, const T); template using FVisitNode = void(*)(const T, int); template class TNode { private: T data; // Data of type T (e.g., TSong*) TNode* next; // Pointer to the next node TNode* prev; // Pointer to the previous node, prepared for future use void SwapNextPrev(); // Swaps the next and prev pointers of this node public: // Constructor TNode(const T& aData); // Destructor ~TNode(); // Getters T GetData() const { return data; } TNode* GetNext() const { return next; } TNode* GetPrev() const { return prev; } // Setters void SetNext(TNode* aNextNode) { next = aNextNode; } void SetPrev(TNode* aPrevNode) { prev = aPrevNode; } // Add friendships here if needed friend class TDoublyLinkedList; friend class TCircularDoublyLinkedList; }; // --- Method Implementations --- // Constructor: Initializes the node with data from the factory function template TNode::TNode(const T& aData) : data(aData), next(nullptr), prev(nullptr) {} // Destructor: Deletes the data pointer template TNode::~TNode() { //Do not delete data, data may exist outside the list data = nullptr; // Set data to nullptr to avoid dangling pointer next = nullptr; // Set next to nullptr to avoid dangling pointer prev = nullptr; // Set prev to nullptr to avoid dangling pointer } template void TNode::SwapNextPrev() { TNode* temp = next; next = prev; prev = temp; } // --- End of TNode class --- // Singly Linked List TSingleLinkedList using TNode and dummy node template class TSingleLinkedList { private: // Helper to reset the list to empty state void ResetList(); protected: TNode* head; // Pointer to the dummy head node TNode* tail; // Pointer to the tail node int size; // Current size of the list bool isDataOwner; // Indicates if the list owns the data // Internal implementations can be non-virtual TNode* InternAppend(const T&); TNode* InternPrepend(const T&); // Helper for RemoveAll to delete a node given its previous node void InternalRemoveNode(TNode*, TNode*); // Helper to get node at index (non-virtual, used internally) TNode* GetNodeAtIndex(int aIndex) const; public: // Constructor TSingleLinkedList(bool); // Virtual Destructor virtual ~TSingleLinkedList(); // --- Core Operations (Virtual) --- virtual void Append(const T&); virtual void Prepend(const T&); virtual T GetAtIndex(int); virtual void Remove(const T&); virtual void Reverse(); virtual void RemoveAll(const T&); virtual void ReverseSublist(int, int); virtual TNode* GetMiddle() const; virtual void Merge(TSingleLinkedList&); // --- Traversal functions that MUST now be virtual --- virtual bool Contains(const T&) const; virtual T Search(const T&, FCheckNode = nullptr) const; virtual void ForEach(FVisitNode) const; // --- Non-Virtual Methods --- template T Append(FDataFactory, TArgs); template T Prepend(FDataFactory, TArgs); int GetSize() const; bool IsEmpty() const; TSingleLinkedList Clone() const; TNode* StealNodes(); }; // Constructor: Initializes the dummy head node and list state template TSingleLinkedList::TSingleLinkedList(bool aIsDataOwner) : head(new TNode(nullptr)), tail(head), size(0), isDataOwner(aIsDataOwner) { // COMPILE-TIME SAFETY CHECK // If the user tries to create a list of non-pointer types with ownership, // stop compilation with a static_assert. static_assert(std::is_pointer::value || !aIsDataOwner, "isDataOwner can only be true if T is a pointer type (e.g., std::string*)."); } // Virtual Destructor: Deletes all nodes and, if owner, the data template TSingleLinkedList::~TSingleLinkedList() { TNode* current = head; while (current != nullptr) { TNode* nextNode = current->GetNext(); if (isDataOwner && current->GetData() != nullptr) { delete current->GetData(); // Delete data if the list owns it } delete current; // Free the node itself current = nextNode; } } template void TSingleLinkedList::InternalRemoveNode(TNode* aPrevNode, TNode* aNodeToDelete) { if (aPrevNode == nullptr || aNodeToDelete == nullptr) return; aPrevNode->SetNext(aNodeToDelete->GetNext()); if (aNodeToDelete == this->tail) { this->tail = aPrevNode; } if (this->isDataOwner) { delete aNodeToDelete->GetData(); } delete aNodeToDelete; this->size--; } // StealNodes: Detaches and returns the list's nodes, leaving it empty // Uses ResetList to clear the list state template TNode* TSingleLinkedList::StealNodes() { TNode* stolenHead = head->GetNext(); // First actual node this->ResetList(); // Clear the list to empty state return stolenHead; // Return the detached nodes } // InternAppend: Handles the logic of adding a node to the end template TNode* TSingleLinkedList::InternAppend(const T& aData) { TNode* newNode = new TNode(aData); tail->SetNext(newNode); // Link the old tail to the new node tail = newNode; // Update the tail pointer size++; return newNode; } // Public Append method template void TSingleLinkedList::Append(const T& aData) { InternAppend(aData); } // InternPrepend: Handles the logic of adding a node to the beginning template TNode* TSingleLinkedList::InternPrepend(const T& aData) { TNode* newNode = new TNode(aData); newNode->SetNext(head->GetNext()); head->SetNext(newNode); if (tail == head) { // If the list was empty, new node is also the tail tail = newNode; } size++; return newNode; } // Public Prepend method template void TSingleLinkedList::Prepend(const T& aData) { InternPrepend(aData); } // Append(): Creates a new node with the given factory and returns the new node's data. template template T TSingleLinkedList::Append(FDataFactory aDataFactory, TArgs aArgs) { if (aDataFactory == nullptr) { return nullptr; // Return nullptr if no factory is provided } T newData = aDataFactory(aArgs); this->Append(newData); // Call the existing virtual Append(T) method return newData; } // Prepend(): Creates a new node with the given factory and returns the new node's data. template template T TSingleLinkedList::Prepend(FDataFactory aDataFactory, TArgs aArgs) { if (aDataFactory == nullptr) { return nullptr; // Return nullptr if no factory is provided } T newData = aDataFactory(aArgs); this->Prepend(newData); // Call the existing virtual Prepend(T) method return newData; } template TNode* TSingleLinkedList::GetNodeAtIndex(int aIndex) const { TNode* current = head->GetNext(); // Start at the first actual node for (int i = 0; i < aIndex; ++i) { current = current->GetNext(); } return current; } // GetAtIndex: Returns the value at a specified index. // This version iterates from the beginning only, as it cannot go backward. template T TSingleLinkedList::GetAtIndex(int aIndex) { if (aIndex < 0 || aIndex >= size) { return nullptr; // Index out of bounds } return GetNodeAtIndex(aIndex)->GetData(); } // Remove: Removes the first node matching the given value. // Requires tracking the previous node to relink the list. template void TSingleLinkedList::Remove(const T& aData) { TNode* prev = head; TNode* current = head->GetNext(); while (current != nullptr) { if (current->GetData() == aData) { // Match found, remove the node InternalRemoveNode(prev, current); return; // Only remove the first occurrence } } prev = current; current = current->GetNext(); } // Reverse: Reverses the list using the classic iterative algorithm for singly-linked lists. template void TSingleLinkedList::Reverse() { if (size <= 1) { return; // Nothing to reverse } // The original first node will become the new tail tail = head->GetNext(); TNode* prevNode = nullptr; TNode* currentNode = head->GetNext(); TNode* nextNode = nullptr; while (currentNode != nullptr) { nextNode = currentNode->GetNext(); // Store next node currentNode->SetNext(prevNode); // Reverse the current node's pointer prevNode = currentNode; // Move pointers one position ahead currentNode = nextNode; } // After the loop, prevNode is the new first node head->SetNext(prevNode); } // Contains: Checks if the list contains the given value, // this is diffrent from Search as it only checks for existence // This using search with a nullptr as the check function template bool TSingleLinkedList::Contains(const T& aData) const { return Search(aData, nullptr) != nullptr; } // Search: Finds a value using an optional custom comparison function template T TSingleLinkedList::Search(const T& aData, FCheckNode aCheckNode) const { TNode* current = head->GetNext(); while (current != nullptr) { // Use the provided check function or default to direct comparison if (aCheckNode != nullptr) { if (aCheckNode(current->GetData(), aData)) { return current->GetData(); } } else { if (current->GetData() == aData) { return current->GetData(); } } current = current->GetNext(); } return nullptr; // Not found } // ForEach: Applies a function to each node in the list template void TSingleLinkedList::ForEach(FVisitNode aVisitNode) const { if (aVisitNode == nullptr) { return; } TNode* current = head->GetNext(); int index = 0; while (current != nullptr) { aVisitNode(current->GetData(), index); current = current->GetNext(); index++; } } // GetSize: Returns the current number of elements in the list template int TSingleLinkedList::GetSize() const { return size; } // IsEmpty: Checks if the list has any elements template bool TSingleLinkedList::IsEmpty() const { return size == 0; } // RemoveAll: Removes all occurrences of a given value from the list. template void TSingleLinkedList::RemoveAll(const T& aData) { TNode* prev = this->head; TNode* current = this->head->GetNext(); while (current != nullptr) { if (current->GetData() == aData) { // Match found, remove the node TNode* nodeToDelete = current; current = current->GetNext(); // Advance current before deletion InternalRemoveNode(prev, nodeToDelete); // prev remains the same, as we just removed current } else { // No match, advance both pointers prev = current; current = current->GetNext(); } } } // Clone: Creates a deep copy of the list. template TSingleLinkedList TSingleLinkedList::Clone() const { // Create a new list with the same ownership policy. TSingleLinkedList newList(this->isDataOwner); TNode* current = this->head->GetNext(); while (current != nullptr) { T dataToCopy = current->GetData(); // This is the core of the deep copy logic. if (this->isDataOwner && std::is_pointer::value && dataToCopy != nullptr) { // If the list owns its pointer data, we must create a NEW object. // This assumes the underlying type has a copy constructor. // `std::remove_pointer_t` gets the type T points to (e.g., TSong from TSong*). newList.Append(new std::remove_pointer_t(*dataToCopy)); } else { // For value types (int, double) or non-owned pointers, just copy the value. newList.Append(dataToCopy); } current = current->GetNext(); } return newList; } // ReverseSublist: Reverses a portion of the list between two indices (inclusive). template void TSingleLinkedList::ReverseSublist(int aStart, int aEnd) { // Validate indices if (aStart < 0 || aEnd >= this->size || aStart >= aEnd) return; // --- 1. Traverse to the nodes that define the sublist boundaries --- // Use the helper to find the node *before* the sublist starts TNode* startNodePrev = (aStart == 0) ? this->head : this->GetNodeAtIndex(aStart - 1); // Node before the start of the sublist TNode* sublistHead = startNodePrev->GetNext(); // First node of the sublist // --- 2. Perform standard reversal on the sublist part only --- TNode* prevNode = nullptr; TNode* currentNode = sublistHead; TNode* nextNode = nullptr; for (int i = 0; i <= (aEnd - aStart); ++i) { nextNode = currentNode->GetNext(); currentNode->SetNext(prevNode); prevNode = currentNode; currentNode = nextNode; } // --- 3. Stitch the reversed sublist back into the main list --- // 'prevNode' is now the new head of the reversed sublist. // 'sublistHead' is now the tail of the reversed sublist. startNodePrev->SetNext(prevNode); sublistHead->SetNext(currentNode); // Update the main tail pointer if the reversal included the original tail. if (aEnd == this->size - 1) { this->tail = sublistHead; } } // GetMiddle: Finds the middle node of the list using the fast/slow pointer algorithm. template TNode* TSingleLinkedList::GetMiddle() const { if (this->IsEmpty()) { return nullptr; } TNode* slow = this->head->GetNext(); TNode* fast = this->head->GetNext(); // The loop condition ensures 'fast' and 'fast->GetNext()' are valid. // When 'fast' reaches the end, 'slow' will be at the midpoint. while (fast != nullptr && fast->GetNext() != nullptr) { slow = slow->GetNext(); fast = fast->GetNext()->GetNext(); } return slow; } template void TSingleLinkedList::ResetList() { // Clear the list to an empty state this->head->SetNext(nullptr); this->tail = this->head; this->size = 0; } template void TSingleLinkedList::Merge(TSingleLinkedList& aOtherList) { // If the other list is empty, there's nothing to do. if (aOtherList.IsEmpty()) { return; } // If this list is empty, take ownership of the other list's nodes. if (this->IsEmpty()) { this->head->SetNext(aOtherList.head->GetNext()); this->tail = aOtherList.tail; this->size = aOtherList.size; // Clear the other list aOtherList.head->SetNext(nullptr); aOtherList.tail = aOtherList.head; aOtherList.size = 0; return; } // Pointers to the current nodes in each list TNode* p1 = this->head->GetNext(); TNode* p2 = aOtherList.head->GetNext(); // Use the dummy head of `this` list to start building the merged result. TNode* tail = this->head; // --- Main Loop: Traverse both lists and pick the smaller node --- while (p1 != nullptr && p2 != nullptr) { if (p1->GetData() <= p2->GetData()) { tail->SetNext(p1); p1 = p1->GetNext(); } else { tail->SetNext(p2); p2 = p2->GetNext(); } tail = tail->GetNext(); } // --- Append the Remainder --- tail->SetNext(p1 != nullptr ? p1 : p2); // --- Update the Tail Pointer --- while (tail->GetNext() != nullptr) { tail = tail->GetNext(); } this->tail = tail; // --- Update Size --- this->size += aOtherList.size; // --- Clear the Other List --- aOtherList.ResetList(); } // --- End of TSingleLinkedList class --- #endif // !TSINGLELINKEDLIST_HPP