       index += length
     return changes
 
+  getDocIndex: (elemIndex) ->
+    return _.reduce(@deltas.slice(0, elemIndex), (length, elem) ->
+      return length + elem.getLength()
+    , 0)
+
+  getElemIndexAndOffset: (docIndex) ->
+    console.assert docIndex < @endLength, "docIndex of length #{docIndex} is
+      greater than or equal to delta length #{@endLength}"
+    count = 0
+    elemIndex = 0
+    for elem in @deltas
+      if count + elem.getLength() > docIndex
+        offset = docIndex - count
+        return [elemIndex, offset]
+      elemIndex += 1
+      count += elem.getLength()
+
   @copy: (subject) ->
     changes = []
     for delta in subject.deltas

          decomposedAttributes[key] = null
      return decomposedAttributes

    decomposed = []
    docIndex = 0 # Character based index into deltaA
    while deltaC.deltas.length > 0 and deltaA.deltas.length > 0
      elemA = _.first(deltaA.deltas)
      elemC = _.first(deltaC.deltas)
      if JetDelta.isInsert(elemC)
        commonPrefixLength = 0
        lookAhead = 0
        if JetDelta.isInsert(elemA)
          while lookAhead < elemA.getLength()
            commonPrefixLength = getCommonPrefixLength(elemC.text, elemA.text.substring(lookAhead))
            if commonPrefixLength != 0 then break
            lookAhead += 1
        if commonPrefixLength > 0
          docIndex += lookAhead
          decomposed = decomposed.concat(new JetRetain(docIndex, docIndex + commonPrefixLength, decomposeAttributes(elemA, elemC)))
          docIndex += commonPrefixLength
          advance(elemA, lookAhead + commonPrefixLength, "A")
          advance(elemC, commonPrefixLength, "C")
        else
          take = 1
          if JetDelta.isInsert(elemA)
            index = 1
            while index < elemC.text.length
              commonPrefixLength = 0
              lookAhead = 0
              while lookAhead < elemA.getLength()
                commonPrefixLength = getCommonPrefixLength(elemA.text.substring(lookAhead), elemC.text.substring(index))
                if commonPrefixLength != 0 then break
                lookAhead += 1
              if commonPrefixLength == 0
                take++
                index++
              else
                break
          else
            # Take as much as we can while still leaving enough to cover the
            # remainder of deltaA
            deltaARemaining = deltaA.endLength - docIndex
            if deltaARemaining >= elemC.getLength()
              take = elemC.getLength()
            else
              take = elemC.getLength() - deltaARemaining
          decomposed = decomposed.concat(new JetInsert(elemC.text.substr(0, take), _.clone(elemC.attributes)))
          advance(elemC, take, "C")
      else
        # Search for the retain in deltaA that matches the retain elemC
        deltasSeen = 0
        for elem in deltaA.deltas
          if JetInsert.isInsert(elem)
            docIndex += elem.getLength()
          else
            if elem.start <= elemC.start && elem.end > elemC.start
              take = Math.min(elemC.getLength(), elem.getLength())
              docIndex += elemC.start - elem.start
              decomposed = decomposed.concat(new JetRetain(docIndex, docIndex + take, decomposeAttributes(elem, elemC)))
              docIndex += take
              deltaA.deltas.splice(0, deltasSeen)
              advance(elem, elemC.start - elem.start + take, "A")
              advance(elemC, take, "C")
              break
            else
              docIndex += elem.getLength()
          deltasSeen++
    while (deltaC.deltas.length > 0)
      elemC = _.first(deltaC.deltas)
      console.assert(JetDelta.isInsert(elemC), "Received Retain when expecting insert: #{elemC}")
      decomposed = decomposed.concat(elemC)
      deltaC.deltas.shift()
    deltaB = new JetDelta(deltaA.endLength, deltaC.endLength, decomposed)
    deltaB.compact()
    console.assert(JetDelta.isDelta(deltaB), "Decomposed returning invalid JetDelta", deltaB)

    console.assert(JetRetain.isRetain(subject), "Copy called on non-retain", subject)
    attributes = _.clone(subject.attributes)
    return new JetRetain(subject.start, subject.end, attributes)

  # True if this retain contains part or all of other.
  contains: (other) ->
    return @start <= other.start and @end >= other.start

  getLength: ->
    return @end - @start
