docs: function(oldLength, newXs, perm) {
    let i;
    if (!newXs.length) {
      return null; // just do a full splice if we're emptying the array
    }
    let refs = perm.filter(i => i >= 0);
    let end = refs.length -1;
    let asc = 0 <= end;
    let giveUp = [];
    for (i = 0; asc ? i < end : i > end; asc ? i++ : i--) {
      giveUp.push((refs[i + 1] - refs[i]) <= 0);
    }
    if (giveUp.some(_.identity)) { return null; }
  
    let splices = [];
    let last = -1;
    i = 0;
    while (i < perm.length) {
      // skip over any good consecutive runs
      while (i < perm.length && perm[i] === last + 1) {
        last += 1;
        i += 1;
      }
      // lump any additions into this splice
      let splice = {index: i, count: 0, additions: []};
      while (i < perm.length && perm[i] === -1) {
        splice.additions.push(newXs[i]);
        i += 1;
      }
      // Find the step difference to find how many from old were removed/skipped;
      // if no step (perm[i] == last + 1) then count should be 0.  If we see no
      // more references to old elements, then we need oldLength to determine how
      // many remaining old elements were logically removed.
      let cur = i === perm.length ? oldLength : perm[i];
      splice.count = cur - (last + 1);
      if (splice.count > 0 || splice.additions.length > 0) {
        splices.push([splice.index, splice.count, splice.additions]);
      }
      last = cur;
      i += 1;
    }
    return splices;
  }