Solution (source code)

= Solution

Place these methods inside the `LLBuffer` class. The two private insertion methods operate on physical endpoints; the public methods interpret logical endpoints using the reversal flag.
``
private def addFirst(x: Int): Unit = {
  val q = new Node(x)
  q.next = first
  if (first == null) last = q else first.prev = q
  first = q
  size += 1
}

private def addLast(x: Int): Unit = {
  val q = new Node(x)
  q.prev = last
  if (last == null) first = q else last.next = q
  last = q
  size += 1
}

def append(x: Int): Unit = {
  if (backwards) addFirst(x) else addLast(x)
}

def prepend(x: Int): Unit = {
  if (backwards) addLast(x) else addFirst(x)
}

def get(i: Int): Int = {
  require(0 <= i && i < size)
  val j = if (backwards) size - 1 - i else i
  var q: Node = null
  if (j <= size - 1 - j) {
    q = first
    var k = 0
    while (k < j) { q = q.next; k += 1 }
  } else {
    q = last
    var k = size - 1
    while (k > j) { q = q.prev; k -= 1 }
  }
  val x = q.value
  if (q.prev == null) first = q.next else q.prev.next = q.next
  if (q.next == null) last = q.prev else q.next.prev = q.prev
  q.prev = null
  q.next = null
  size -= 1
  x
}

def rev: Unit = { backwards = !backwards }
``
In the forward traversal, the <loop invariant> is that `q` is physical node $k$, with $0\leq k\leq j$; its <loop variant> is $j-k$. In the backward traversal, `q` is physical node $k$, with $j\leq k<size$; its <loop variant> is $k-j$. Thus both locate physical index $j$, which is exactly logical index $i$ under the <abstraction function>.

Deletion bypasses the chosen <linked list node> in both directions. Deleting the first or last <linked list node> updates the corresponding endpoint; deleting the only <linked list node> sets both endpoints to null. The surviving values keep their physical relative order, so the <abstraction function> gives precisely the required logical deletion. Each insertion preserves reciprocal links and adds its value at the physical end corresponding to the required logical end. Toggling the flag changes $s$ to its <list reversal> without moving any <linked list nodes>. Consequently
$$
\boxed{append,prepend,rev:O(1),\qquad get(i):O(1+\min(i,size_0-1-i)).}
$$
These are worst-case <time complexity> bounds in the constant-cost pointer model; total storage is $O(size)$ and each operation uses constant <auxiliary space>.