A playlist can be explored in two directions. One listener may move from the first song to the last while another moves backwards from the final song, and neither walk should disturb the playlist—or the other listener's place.
The Playlist class is already implemented for you. Your task is to implement only the Iterator pattern participants:
SongCursor, the common cursor contract with hasNext() and next().ForwardCursor, which starts at index 0 and moves toward the end.ReverseCursor, which starts at the final index and moves toward the beginning.Both concrete cursors receive the playlist's song collection when they are created. Each cursor must keep its own index. Return "END" from next() once that cursor is exhausted; do not modify or reverse the underlying collection.
The pre-implemented Playlist exposes the following API to the tests:
Playlist() creates a playlist holding no songs and no cursors.boolean addSong(String title) appends a song and returns true, or returns false once 20 songs are stored.int songCount() returns how many songs are stored.int openForward() opens a cursor that reads first to last and returns its id. Ids start at 1 and count up across both kinds of cursor.int openReverse() opens a cursor that reads last to first and returns its id.boolean hasNext(int cursorId) returns whether that cursor has a song left. An id that was never handed out returns false.String next(int cursorId) returns that cursor's next song and moves it along. A cursor with nothing left returns "END". An id that was never handed out returns "NONE".int cursorCount() returns how many cursors have been opened.All songs are added before any cursor is opened. Playlist creates the appropriate cursor and delegates hasNext and next to it. Unknown cursor ids are handled by the provided code, so stay focused on the iterator contract, traversal direction, and per-cursor state.
Input:
Output:
Explanation: The reverse cursor starts at the last song and works back to the first. Once all three have come back, hasNext is false and next reports the sentinel.
Input:
Output:
Explanation: Two cursors read the same three songs alternately. The forward one moves first to last while the reverse one moves last to first, and neither disturbs the other.
1 <= title.length <= 4020 songs are stored in one playlist.10 cursors are opened on one playlist.-100 <= cursorId <= 100100 calls in total are made across all methods.Full marks when each way of walking the songs is its own class behind one contract, and the playlist answers `hasNext` and `next` by delegating to a cursor it looks up by id. Lose points heavily when the position is a field on `Playlist`, or when `next` branches on which direction the caller asked for.
Full marks when two cursors over the same playlist keep separate positions, so advancing one leaves the other exactly where it was, and a cursor opened later starts from its own end of the list. Lose points when opening a second cursor resets the first, or when both cursors share one index.
Full marks when the reverse walk returns the first song before it finishes, an exhausted cursor returns `END` instead of failing, an id that was never handed out returns `NONE` and `false`, and reading leaves the stored order untouched. Lose points for printing to stdout.
Passing every test is not enough on its own. A submission is accepted only when the design also clears the bar.
| Call | Returns |
|---|---|
| new Playlist() | null |
| addSong("Shape of You") | true |
| addSong("Bohemian Rhapsody") | true |
| addSong("Blinding Lights") | true |
| songCount() | 3 |
| openReverse() | 1 |
| next(1) | "Blinding Lights" |
| next(1) | "Bohemian Rhapsody" |
| next(1) | "Shape of You" |
| hasNext(1) | false |
| next(1) | "END" |
The reverse cursor starts at the last song and works back to the first. Once all three have come back, `hasNext` is false and `next` reports the sentinel.
Run checks these cases. Submit also runs a larger hidden set.

