Untitled

 avatar
unknown
plain_text
9 months ago
16 kB
17
Indexable
#include <iostream>
#include <vector>
#include <string>
#include <algorithm>
#include <stdexcept>

class BookShelfManager {
private:
    // Represents the infinitely long bookshelf.
    // The index in the vector is the book's position.
    std::vector<std::string> shelf;
    // The index of the book where the bookmark is attached.
    // -1 can represent "no bookmark" or an empty shelf state.
    int bookmarkIndex; 

    /**
     * @brief Helper function to validate an index against the current shelf size.
     * @param index The index to validate.
     * @param inclusiveMax Whether the index can be equal to the size (e.g., for insertion).
     */
    void validateIndex(int index, bool inclusiveMax = false) const {
        if (index < 0) {
            throw std::out_of_range("Index cannot be negative.");
        }
        int max_val = inclusiveMax ? shelf.size() : shelf.size() - 1;
        if (index > max_val) {
             throw std::out_of_range("Index out of bounds. Max valid index is " + std::to_string(max_val) + 
                                     " for an inclusive-max context, or " + std::to_string(shelf.size()) + 
                                     " for an exclusive-max context (like insertion at the end).");
        }
    }

public:
    /**
     * @brief Constructor: Initializes an empty bookshelf and sets the bookmark to -1 (no bookmark).
     */
    BookShelfManager() : bookmarkIndex(-1) {}

    // ----------------------------------------------------------------------
    // Book Management Methods
    // ----------------------------------------------------------------------

    /**
     * @brief Inserts a list of books starting at toIndex.
     * @param toIndex The starting index for insertion.
     * @param list A vector of book titles (strings) to insert.
     */
    void addBooks(int toIndex, const std::vector<std::string>& list) {
        // toIndex can be from 0 to shelf.size() (inclusive)
        if (toIndex < 0 || toIndex > shelf.size()) {
            throw std::out_of_range("Invalid insertion index.");
        }
        
        // Use std::vector::insert
        shelf.insert(shelf.begin() + toIndex, list.begin(), list.end());

        // Update bookmark: If the bookmark was before or at toIndex, it moves right.
        if (bookmarkIndex != -1 && bookmarkIndex >= toIndex) {
            bookmarkIndex += list.size();
        }
        // If the shelf was empty and books were added at index 0, the bookmark should probably remain -1 
        // until explicitly set, or could be set to the first book (index 0). 
        // We'll keep it at -1 unless explicitly set, as per the initial state.
    }

    /**
     * @brief Removes all books from fromIndex to toIndex (toIndex is inclusive).
     * @param fromIndex The starting index for removal (inclusive).
     * @param toIndex The ending index for removal (inclusive).
     */
    void removeBooks(int fromIndex, int toIndex) {
        if (fromIndex < 0 || toIndex >= shelf.size() || fromIndex > toIndex) {
            throw std::out_of_range("Invalid removal indices or range.");
        }

        // Use std::vector::erase
        shelf.erase(shelf.begin() + fromIndex, shelf.begin() + toIndex + 1);

        // Update bookmark:
        int numRemoved = toIndex - fromIndex + 1;

        if (bookmarkIndex != -1) {
            if (bookmarkIndex < fromIndex) {
                // Bookmark is before the removed segment; no change.
            } else if (bookmarkIndex > toIndex) {
                // Bookmark is after the removed segment; it shifts left.
                bookmarkIndex -= numRemoved;
            } else {
                // Bookmark was inside the removed segment.
                // Reset to -1 or, if the shelf is not empty, set to the book now at fromIndex.
                // Let's reset it to -1 as it's the safest assumption.
                bookmarkIndex = -1;
            }
        }
    }

    /**
     * @brief Moves size number of books from fromIndex to toIndex.
     * @param fromIndex The starting index of the books to move.
     * @param toIndex The destination index where the books will be inserted.
     * @param size The number of books to move.
     * * Note: toIndex is the *new* starting index of the moved block.
     */
    void moveBooks(int fromIndex, int toIndex, int size) {
        if (size <= 0) return; // Nothing to move

        // Validate basic indices
        if (fromIndex < 0 || fromIndex + size > shelf.size() || toIndex < 0 || toIndex > shelf.size()) {
            throw std::out_of_range("Invalid move indices or size.");
        }
        // Check for self-move (no-op)
        if (fromIndex == toIndex) return;

        // 1. Extract the books to move
        std::vector<std::string> booksToMove;
        booksToMove.reserve(size);
        std::copy(shelf.begin() + fromIndex, shelf.begin() + fromIndex + size, 
                  std::back_inserter(booksToMove));

        // 2. Remove the books from the original location
        // The bookmark logic is complex here, so we will handle the bookmark *after* the move is complete.
        shelf.erase(shelf.begin() + fromIndex, shelf.begin() + fromIndex + size);

        // Adjust toIndex because the shelf has shrunk by 'size' books.
        // If fromIndex < toIndex, the original toIndex is now smaller by 'size'.
        int adjustedToIndex = toIndex;
        if (fromIndex < toIndex) {
            adjustedToIndex -= size;
        }

        // 3. Insert the books at the new location
        shelf.insert(shelf.begin() + adjustedToIndex, booksToMove.begin(), booksToMove.end());
        
        // 4. Update bookmark:
        if (bookmarkIndex != -1) {
            int oldBookmarkIndex = bookmarkIndex;
            int newBookmarkIndex = -1;
            bool wasInMovedBlock = (oldBookmarkIndex >= fromIndex && oldBookmarkIndex < fromIndex + size);
            
            if (wasInMovedBlock) {
                // The book with the bookmark moved. Calculate its new position.
                int offsetInBlock = oldBookmarkIndex - fromIndex;
                newBookmarkIndex = adjustedToIndex + offsetInBlock;
            } else {
                // The book with the bookmark did not move.
                // It was at oldBookmarkIndex. Now we need to find its new location.
                
                // Case 1: Bookmark was *before* both the 'from' and 'to' ranges (or toIndex if it's earlier).
                if (oldBookmarkIndex < std::min(fromIndex, toIndex)) {
                    // No change to its index.
                    newBookmarkIndex = oldBookmarkIndex;
                } 
                // Case 2: Bookmark was in the 'gap' created by the move.
                // The book at oldBookmarkIndex shifted *back* when the block was removed.
                else if (oldBookmarkIndex >= fromIndex && oldBookmarkIndex >= fromIndex + size) {
                    // This case is actually impossible because if oldBookmarkIndex >= fromIndex + size,
                    // the index will be adjusted.
                }

                // A simpler approach for non-moved books:
                // If the bookmark was before the 'from' block, its index stays the same *unless* // the 'to' block is inserted before it.
                if (oldBookmarkIndex < fromIndex) {
                    if (toIndex <= oldBookmarkIndex) {
                        // The moved block was inserted *before* the bookmark's original location.
                        newBookmarkIndex = oldBookmarkIndex + size;
                    } else {
                        // The moved block was inserted *after* the bookmark's original location.
                        newBookmarkIndex = oldBookmarkIndex;
                    }
                } 
                // If the bookmark was after the 'from' block, its index is reduced by 'size' 
                // *unless* the 'to' block is inserted after it.
                else { // oldBookmarkIndex >= fromIndex + size
                    // The old index is effectively reduced by 'size' when the block is removed.
                    newBookmarkIndex = oldBookmarkIndex - size;
                    if (toIndex <= oldBookmarkIndex) { // toIndex is the original toIndex
                        // The moved block was inserted at or before the bookmark's new location.
                        // The reduction is cancelled out by the insertion shift.
                        // However, we need to consider the adjustedToIndex which is what matters.
                        
                        // Let's use the actual indices in the final vector:
                        // The book was at oldBookmarkIndex. When the 'from' block (fromIndex to fromIndex+size-1) 
                        // was removed, its index became oldBookmarkIndex - size.
                        int tempIndex = oldBookmarkIndex - size; 
                        
                        // If the 'to' block (size elements) was inserted at adjustedToIndex, 
                        // and tempIndex is >= adjustedToIndex, the index shifts by 'size'.
                        if (tempIndex >= adjustedToIndex) {
                            newBookmarkIndex = tempIndex + size; // Back to original index, which is wrong.
                        } else {
                            newBookmarkIndex = tempIndex;
                        }
                    } 
                    // Simpler logic for non-moved:
                    int shift = 0;
                    // If the book was *after* the original 'from' block (and didn't move)
                    if (oldBookmarkIndex >= fromIndex + size) {
                        // It shifts left by 'size' when the block is removed.
                        shift -= size;
                    }
                    // If the book is *at or after* the new insertion point (adjustedToIndex)
                    if (oldBookmarkIndex + shift >= adjustedToIndex) {
                        // It shifts right by 'size' when the block is inserted.
                        shift += size;
                    }
                    newBookmarkIndex = oldBookmarkIndex + shift;
                }
            }
            bookmarkIndex = newBookmarkIndex;
        }
    }


    // ----------------------------------------------------------------------
    // Query Methods
    // ----------------------------------------------------------------------

    /**
     * @brief Returns a list of all books.
     * @return A const reference to the internal bookshelf vector.
     */
    const std::vector<std::string>& getBooks() const {
        return shelf;
    }

    /**
     * @brief Returns the index of the book where the bookmark is attached.
     * @return The bookmark index, or -1 if no bookmark is set.
     */
    int getBookmarkIndex() const {
        return bookmarkIndex;
    }

    /**
     * @brief Set the bookmark index at newBookmarkIndex.
     * @param newBookmarkIndex The new index for the bookmark.
     */
    void setBookmarkIndex(int newBookmarkIndex) {
        if (newBookmarkIndex == -1) {
            bookmarkIndex = -1;
            return;
        }
        
        // Check for valid index (it must point to an existing book)
        if (shelf.empty() || newBookmarkIndex < 0 || newBookmarkIndex >= shelf.size()) {
            throw std::out_of_range("Invalid index for setting bookmark. Shelf is empty or index is out of bounds.");
        }
        bookmarkIndex = newBookmarkIndex;
    }
};

// ----------------------------------------------------------------------
// Example Usage and Testing
// ----------------------------------------------------------------------

void printShelf(const BookShelfManager& manager) {
    const auto& books = manager.getBooks();
    std::cout << "Shelf: [";
    for (size_t i = 0; i < books.size(); ++i) {
        std::cout << "\"" << books[i] << "\"" << (i < books.size() - 1 ? ", " : "");
    }
    std::cout << "]\n";
    std::cout << "Bookmark Index: " << manager.getBookmarkIndex() << "\n";
    if (manager.getBookmarkIndex() != -1 && !books.empty()) {
        std::cout << "Book at Bookmark: \"" << books[manager.getBookmarkIndex()] << "\"\n";
    }
    std::cout << std::string(30, '-') << "\n";
}

int main() {
    BookShelfManager shelfManager;
    std::cout << "--- Initial State ---\n";
    printShelf(shelfManager);

    // 1. Add Books
    std::cout << "--- addBooks: Insert 'A', 'B', 'C' at index 0 ---\n";
    shelfManager.addBooks(0, {"A", "B", "C"});
    printShelf(shelfManager); // Shelf: ["A", "B", "C"], Bookmark: -1

    // 2. Set Bookmark
    std::cout << "--- setBookmarkIndex: Set at index 1 ('B') ---\n";
    shelfManager.setBookmarkIndex(1);
    printShelf(shelfManager); // Shelf: ["A", "B", "C"], Bookmark: 1

    std::cout << "--- addBooks: Insert 'X', 'Y' at index 1 ---\n";
    shelfManager.addBooks(1, {"X", "Y"}); 
    // New Shelf: ["A", "X", "Y", "B", "C"]
    // Bookmark was at 1 ('B'). 'B' is now at index 3. Bookmark should be 3.
    printShelf(shelfManager); 

    // 3. Remove Books
    std::cout << "--- removeBooks: Remove from index 0 to 2 ('A', 'X', 'Y') ---\n";
    shelfManager.removeBooks(0, 2); 
    // New Shelf: ["B", "C"]
    // Bookmark was at 3 ('B'). 'B' is now at index 0. Bookmark should be 0.
    printShelf(shelfManager); 

    std::cout << "--- setBookmarkIndex: Set at index 1 ('C') ---\n";
    shelfManager.setBookmarkIndex(1);
    printShelf(shelfManager); // Bookmark: 1

    std::cout << "--- addBooks: Insert 'P', 'Q', 'R' at index 1 ---\n";
    shelfManager.addBooks(1, {"P", "Q", "R"}); 
    // New Shelf: ["B", "P", "Q", "R", "C"]
    // Bookmark was at 1 ('C'). 'C' is now at index 4. Bookmark should be 4.
    printShelf(shelfManager); 

    std::cout << "--- removeBooks: Remove from index 1 to 3 ('P', 'Q', 'R'). Bookmark ('C') is at 4 ---\n";
    shelfManager.removeBooks(1, 3);
    // New Shelf: ["B", "C"]
    // Bookmark was at 4. Now shifts left by 3. Bookmark should be 1.
    printShelf(shelfManager);

    // 4. Move Books
    std::cout << "--- addBooks: Add books for move test ---\n";
    shelfManager.addBooks(2, {"D", "E", "F", "G", "H"});
    // Current Shelf: ["B", "C", "D", "E", "F", "G", "H"]
    // set bookmark to 'D' at index 2
    shelfManager.setBookmarkIndex(2);
    printShelf(shelfManager); 

    std::cout << "--- moveBooks: Move 3 books ('D', 'E', 'F') from index 2 to index 6 ---\n";
    shelfManager.moveBooks(2, 6, 3);
    // New Shelf: ["B", "C", "G", "H", "D", "E", "F"]
    // Bookmark was at 'D' (index 2 in old shelf). 'D' is now at index 4. Bookmark should be 4.
    printShelf(shelfManager); 
    
    std::cout << "--- setBookmarkIndex: Set to 'G' at index 2 ---\n";
    shelfManager.setBookmarkIndex(2);
    printShelf(shelfManager);

    std::cout << "--- moveBooks: Move 3 books ('D', 'E', 'F') from index 4 to index 0 ---\n";
    shelfManager.moveBooks(4, 0, 3);
    // Old Shelf: ["B", "C", "G", "H", "D", "E", "F"]
    // New Shelf: ["D", "E", "F", "B", "C", "G", "H"]
    // Bookmark was at 'G' (index 2 in old shelf). It shifts right by 3. Bookmark should be 5.
    printShelf(shelfManager);

    return 0;
}
Editor is loading...
Leave a Comment