81. Design a Library Management System

Design a Library Management System
Design and implement an in-memory Library Management system that caters to its registered members by cataloging and housing books that can be borrowed.

The system must support:
  • Adding books to the catalog.
  • Registering and unregistering users.
  • Reservation management to borrow books using book ids (with FIFO waitlist).
  • Fine calculation on late returns (20 rupees per delayed day after 14 days).
  • (Bonus) Limiting a user to reserve only one copy of the same book.
  • (Bonus) Auditing APIs:
    • Given a bookId, list users currently having that book.
    • Given a userId, list books currently issued to that user.

Important: Use of a DB is not allowed. The solution must use in-memory data structures only. CLI, Web application, REST API, and UI are not expected.

Terminology

  • Book: Identified by bookId, and has title, author, and copies.
  • User: A registered library member identified by userId.
  • Issued / Borrowed: A copy of a book is currently with a user.
  • Waitlist: A FIFO queue of users waiting to borrow a book when no copy is immediately available.
  • Held Copy: A returned copy that is reserved exclusively for a specific user (HELD_FOR,<userId>) and is not generally available.
  • Day: Integer day value provided by the caller for borrow/return operations.

Book Id Generation Rules

  • For each distinct book (unique by (title, author)), the system must generate a unique bookId.
  • bookId format: <PREFIX><NUMBER>
    • PREFIX = first three letters of the author’s last name, uppercased (if last name has fewer than 3 letters, use the full last name uppercased).
    • NUMBER = a 4-digit sequence (zero-padded) maintained per PREFIX, starting from 1000.
  • Example: author last name RowlingROW1000, next ROW1001, etc.
  • If the same (title, author) is added again, the system must increase its copy count and return the same bookId.

Borrowing, Reservation, and Waitlist Rules

  • Users borrow books by bookId (assume users already know book ids).
  • If at least one copy is available (not issued and not held), the book is issued to the requesting user immediately.
  • If all copies are already issued (or all non-held copies are issued), the user is added to a FIFO waitlist for that bookId.
  • Held copy behavior (clarified): When a copy is returned and the waitlist is non-empty, the system must:
    • dequeue the first user from the FIFO waitlist, and
    • mark the returned copy as HELD_FOR,<userId> for that dequeued user.
    The held copy is not considered generally available; only that userId may successfully borrow it next.
  • If the requesting user is the user for whom a copy is currently held (HELD_FOR), the book is issued to them.
  • (Bonus) One user should only be allowed to reserve/borrow one copy of the same bookId at a time.

Fine Rules

  • A user is allowed to borrow a book for 14 days.
  • If returned late, fine = 20 rupees per delayed day:
    • borrowDuration = returnDay - borrowDay
    • No fine if borrowDuration <= 14
    • delayDays = borrowDuration - 14 if borrowDuration > 14
    • fine = delayDays * 20

Methods

1) addBook

String addBook(String title, String author, int copies)
  • Adds a book entry (or increases copies for an existing (title, author)).
  • copies will be between 1 and 10000
  • Returns:
    • BOOK_ID,<bookId> on success
    • INVALID_COPIES if copies <= 0
    • INVALID_INPUT if title or author is empty

2) registerUser

String registerUser(String userId, String name)
  • Registers a new user in the library.
  • Returns:
    • SUCCESS on success
    • USER_ALREADY_EXISTS if userId is already registered
    • INVALID_INPUT if userId or name is empty

3) unregisterUser

String unregisterUser(String userId)
  • Unregisters an existing user.
  • Returns:
    • SUCCESS on success
    • USER_NOT_FOUND if user does not exist
    • USER_HAS_ISSUED_BOOKS if user currently has any issued books
    • USER_IN_WAITLIST if user is in any waitlist

4) requestBorrow

String requestBorrow(String userId, String bookId, int requestDay)
  • Requests to borrow a book by bookId on requestDay.
  • requestDay will be between 1 and 100,000
  • Returns:
    • ISSUED if a copy is issued immediately to the user (including if a copy is HELD_FOR this user)
    • WAITLISTED,<position> if no copy is available for the user; position is 1-based in FIFO
    • USER_NOT_FOUND if user is not registered
    • BOOK_NOT_FOUND if bookId does not exist
    • INVALID_DAY if requestDay < 0
    • ALREADY_ISSUED_TO_USER if user already has this bookId issued (bonus behavior; if not implemented, you may omit this check)
    • ALREADY_WAITLISTED if user is already in the waitlist for this bookId
  • Note: Since the FIFO head is dequeued when a copy becomes HELD_FOR them, they will not be considered “already waitlisted” at that time.

5) returnBook

String returnBook(String userId, String bookId, int returnDay)
  • Returns an issued book copy and calculates fine if returned late.
  • returnDay will be between 1 and 100,000
  • Returns:
    • RETURNED,<fine> on success (fine can be 0)
    • USER_NOT_FOUND if user is not registered
    • BOOK_NOT_FOUND if bookId does not exist
    • NOT_ISSUED_TO_USER if that user does not currently have this book issued
    • INVALID_DAY if returnDay < 0 or returnDay < borrowDay
  • On a successful return, if a waitlist exists, the system must dequeue the FIFO head and mark the returned copy as HELD_FOR,<userId> for that dequeued user.

6) usersHavingBook

List<String> usersHavingBook(String bookId)
  • Auditing API: given a bookId, returns a list of userIds currently issued that book.
  • Output rules:
    • Return [] if bookId does not exist or no one currently has it.
    • Sort userIds in ascending lexicographic order for deterministic output.

7) booksIssuedToUser

List<String> booksIssuedToUser(String userId)
  • Auditing API: given a userId, returns a list of bookIds currently issued to the user.
  • Output rules:
    • Return [] if userId does not exist or user has no issued books.
    • Sort bookIds in ascending lexicographic order for deterministic output.

Constraints

  • 1 <= title.length() <= 200
  • 1 <= author.length() <= 200 (author includes first/last name; last token is treated as last name for id prefix)
  • 1 <= userId.length() <= 50
  • 1 <= name.length() <= 200
  • 1 <= copies <= 100000
  • 0 <= requestDay, returnDay <= 1000000000
  • All operations must be supported using in-memory data structures (no database).
  • Waitlist ordering must be FIFO.

Examples

  • Note: Each example below assumes a fresh empty system (no data carries over between examples).

Example 1: Add books (including more copies), register users, and issue immediately

  • addBook(title="Harry Potter and the Sorcerer's Stone", author="J K Rowling", copies=2)BOOK_ID,ROW1000
  • addBook(title="Harry Potter and the Sorcerer's Stone", author="J K Rowling", copies=1)BOOK_ID,ROW1000 (same (title, author); copies increase)
  • registerUser(userId="U1", name="Alice")SUCCESS
  • registerUser(userId="U2", name="Bob")SUCCESS
  • requestBorrow(userId="U1", bookId="ROW1000", requestDay=1)ISSUED
  • requestBorrow(userId="U2", bookId="ROW1000", requestDay=1)ISSUED (another copy is available)
  • usersHavingBook(bookId="ROW1000")["U1","U2"]
  • booksIssuedToUser(userId="U1")["ROW1000"]

Example 2: Waitlist behavior, dequeue + HELD_FOR for FIFO head, and late fine on return

  • registerUser(userId="U1", name="Alice")SUCCESS
  • registerUser(userId="U2", name="Bob")SUCCESS
  • registerUser(userId="U3", name="Charlie")SUCCESS
  • addBook(title="Clean Code", author="Robert C Martin", copies=1)BOOK_ID,MAR1000
  • requestBorrow(userId="U1", bookId="MAR1000", requestDay=5)ISSUED
  • requestBorrow(userId="U2", bookId="MAR1000", requestDay=6)WAITLISTED,1 (no copy available)
  • requestBorrow(userId="U3", bookId="MAR1000", requestDay=6)WAITLISTED,2
  • returnBook(userId="U1", bookId="MAR1000", returnDay=10)RETURNED,0 (borrowDuration = 10 - 5 = 5 ≤ 14; FIFO head U2 is dequeued and copy becomes HELD_FOR,U2)
  • requestBorrow(userId="U3", bookId="MAR1000", requestDay=10)ALREADY_WAITLISTED (still waiting; cannot borrow a copy held for U2)
  • requestBorrow(userId="U2", bookId="MAR1000", requestDay=10)ISSUED (copy is HELD_FOR,U2)
  • returnBook(userId="U2", bookId="MAR1000", returnDay=30)RETURNED,120 (borrowDuration = 30 - 10 = 20; delayDays = 20 - 14 = 6; fine = 6 * 20 = 120; FIFO head U3 is dequeued and copy becomes HELD_FOR,U3)
  • requestBorrow(userId="U3", bookId="MAR1000", requestDay=30)ISSUED (copy is HELD_FOR,U3)
  • usersHavingBook(bookId="MAR1000")["U3"]
  • booksIssuedToUser(userId="U2")[] (book returned)

Example 3: Fine calculation on late return

  • registerUser(userId="U1", name="Alice")SUCCESS
  • addBook(title="The Hobbit", author="J R R Tolkien", copies=1)BOOK_ID,TOL1000
  • requestBorrow(userId="U1", bookId="TOL1000", requestDay=1)ISSUED
  • returnBook(userId="U1", bookId="TOL1000", returnDay=20)RETURNED,100 (borrowDuration = 20 - 1 = 19; delayDays = 19 - 14 = 5; fine = 5 * 20 = 100)
  • usersHavingBook(bookId="TOL1000")[] (no active issue)
  • booksIssuedToUser(userId="U1")[] (book returned)




Please use Laptop/Desktop or any other large screen to add/edit code.