       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

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

  getLength: ->
    return @end - @start

  # 2. Retains in deltaC become retains in deltaB, which reference the original
  #    retain in deltaA.
  decompose: (deltaA, deltaC) ->
    console.assert(JetDelta.isDelta(deltaA), "Decompose2 called when deltaA is not a JetDelta, type: " + typeof deltaA)
    console.assert(JetDelta.isDelta(deltaC), "Decompose2 called when deltaC is not a JetDelta, type: " + typeof deltaC)
    console.assert(deltaA.startLength == deltaC.startLength, "startLength #{deltaA.startLength} / startLength #{deltaC.startLength} mismatch")

    # Temporary lcs solution, lifted from
    # http://en.wikibooks.org/wiki/Algorithm_Implementation/Strings/Longest_common_substring
    # This is also not the most optimal solution. This dp approach is O(n*m), but
    # using a suffix tree is O(n + m).
    # Returns the lcs, its length, and the offset into _str1_ that the lcs can
    # be found.
    longestCommonSubstring = `function (str1, str2) {
      if (!str1 || !str2) {
        return {
          length: 0,
          sequence: "",
          offset: 0
        };
      }

      var sequence = "",
          str1Length = str1.length,
          str2Length = str2.length,
          num = new Array(str1Length),
          maxlen = 0,
          lastSubsBegin = 0;

      for (var i = 0; i < str1Length; i++) {
              var subArray = new Array(str2Length);
              for (var j = 0; j < str2Length; j++)
                      subArray[j] = 0;
              num[i] = subArray;
      }

      for (var i = 0; i < str1Length; i++)
      {
              for (var j = 0; j < str2Length; j++)
              {
                      if (str1[i] !== str2[j])
                              num[i][j] = 0;
                      else
                      {
                              if ((i === 0) || (j === 0))
                                      num[i][j] = 1;
                              else
                                      num[i][j] = 1 + num[i - 1][j - 1];

                              if (num[i][j] > maxlen)
                              {
                                      maxlen = num[i][j];
                                      var thisSubsBegin = i - num[i][j] + 1;
                                      if (lastSubsBegin === thisSubsBegin)
                                      {//if the current LCS is the same as the last time this block ran
                                              sequence += str1[i];
                                      }
                                      else //this block resets the string builder if a different LCS is found
                                      {
                                              lastSubsBegin = thisSubsBegin;
                                              sequence= ""; //clear it
                                              sequence += str1.substr(lastSubsBegin, (i + 1) - lastSubsBegin);
                                      }
                              }
                      }
              }
      }
      return {
              length: maxlen,
              sequence: sequence,
              offset: thisSubsBegin
      };
      }`

    # Returns hash with keys _offsetA_, _offsetC_, _sequence_, _length_
    getBestMatch = (strA, strC) ->
      meta = longestCommonSubstring(strA, strC)
      # Clean up the output of longestCommonSubstring for our purposes
      meta['offsetA'] = meta.offset
      meta['offsetC'] = strC.indexOf(meta.sequence)
      delete meta.offset
      return meta

    deltaA = JetDelta.copy(deltaA)
    deltaC = JetDelta.copy(deltaC)
    deltaA.normalizeChanges()
    deltaC.normalizeChanges()

    decomposeAttributes = (elemA, elemC) ->
      decomposedAttributes = {}
      attributes = elemA.attributes
