Solution (source code)

= Solution

Use a fixed-size count <array> and protected hooks defining a stable character-to-slot mapping. The default hooks count the ASCII lowercase interval. The document must start empty, and all subsequent edits must pass through the <mixin> methods, as they do for `new DocImp with CharCountPlugin`.

``
trait CharCountPlugin extends Doc {
  protected def firstCounted: Char = 'a'
  protected def numberCounted: Int = 26
  protected def slot(ch: Char): Int = {
    val k = ch.toInt - firstCounted.toInt
    if (0 <= k && k < numberCounted) k else -1
  }
  private lazy val totals = Array.fill(numberCounted)(0)

  abstract override def insert(pos: Int, ch: Char): Unit = {
    val k = slot(ch)
    super.insert(pos, ch)
    if (k >= 0) totals(k) += 1
  }

  abstract override def delete(pos: Int): Char = {
    val removed = super.delete(pos)
    val k = slot(removed)
    if (k >= 0) totals(k) -= 1
    removed
  }

  def count(ch: Char): Int = {
    val k = slot(ch)
    require(k >= 0, "character is outside the counted alphabet")
    totals(k)
  }
}
``

The additional <representation invariant> is that each <array> slot equals the number of corresponding characters in the current document. Initially all counts are zero. Each successful underlying edit changes exactly one relevant count, while a failed position check throws before any count is changed. Returning the actual deleted character lets deletion adjust the correct slot.

`count` takes \b[$O(1)$ time for the fixed alphabet], as do the <mixin>'s additions to edit cost. The lazy <array> avoids reading subclass-overridden size hooks during <trait> construction; the hooks must remain stable. An extension can override the interval hooks, or override `slot` with another fixed finite mapping. This is <stackable modification by a trait>: `abstract override` permits the method to call the next implementation of `super`, supplied here by `DocImp`.<Scala 2.13 classes and objects specification>[Abstract override and <trait> linearization]{ref}