diff --git a/fcore/persistent-vector.sml b/fcore/persistent-vector.sml index c36a4d8..d5e1524 100644 --- a/fcore/persistent-vector.sml +++ b/fcore/persistent-vector.sml @@ -32,8 +32,10 @@ struct else if searchIdx = 0 then isInRange (checkIdx, Vector.sub (nodes, searchIdx)) else - let val nextCheckIdx = checkIdx - Vector.sub (sizes, searchIdx - 1) - in isInRange (nextCheckIdx, Vector.sub (nodes, searchIdx)) + let + val nextCheckIdx = checkIdx - Vector.sub (sizes, searchIdx - 1) + in + isInRange (nextCheckIdx, Vector.sub (nodes, searchIdx)) end end | LEAF (values, sizes) => @@ -43,8 +45,10 @@ struct if searchIdx = ~1 then false else - let val {start, finish} = Vector.sub (values, searchIdx) - in checkIdx >= start andalso checkIdx <= finish + let + val {start, finish} = Vector.sub (values, searchIdx) + in + checkIdx >= start andalso checkIdx <= finish end end @@ -76,13 +80,12 @@ struct end | APPEND newVec => if Vector.length nodes = maxSize then - let + let (* adjust "finish" so that it does not consider * offset for "lower" vector *) - val finish = - finish - Vector.sub (sizes, Vector.length sizes - 1) + val finish = finish - Vector.sub (sizes, Vector.length sizes - 1) val newNode = BRANCH (#[newVec], #[finish]) - in + in APPEND newNode end else @@ -100,12 +103,12 @@ struct * we want to adjust the start and finish parameters * so that they don't contain the offset relevant to the * "lower" vector, which was split from *) - let + let val prevFinish = Vector.sub (sizes, Vector.length sizes - 1) val start = start - prevFinish val finish = finish - prevFinish val newNode = LEAF (#[{start = start, finish = finish}], #[finish]) - in + in APPEND newNode end else @@ -121,43 +124,36 @@ struct fun append (start, finish, tree) = case helpAppend (start, finish, tree) of UPDATE t => t - | APPEND newNode => - let val maxSize = getFinishIdx tree - in BRANCH (#[tree, newNode], #[maxSize, finish]) + | APPEND newNode => + let + val maxSize = getFinishIdx tree + in + BRANCH (#[tree, newNode], #[maxSize, finish]) end + (* todo: modify below functions so that they also + * use rope-like metadata *) + fun getStart tree = case tree of LEAF (values, _) => Vector.sub (values, 0) | BRANCH (nodes, _) => getStart (Vector.sub (nodes, 0)) - fun helpNextMatch (cursorIdx, tree, acc) = + fun helpNextMatch (cursorIdx, tree) = case tree of LEAF (values, sizes) => let val idx = BinSearch.equalOrMore (cursorIdx, sizes) in - if idx = ~1 then - {start = ~1, finish = ~1} - else - let val {start, finish} = Vector.sub (values, idx) - in {start = start + acc, finish = finish + acc} - end + if idx = ~1 then {start = ~1, finish = ~1} + else Vector.sub (values, idx) end | BRANCH (nodes, sizes) => let val idx = BinSearch.equalOrMore (cursorIdx, sizes) in - if idx = ~1 then - {start = ~1, finish = ~1} - else - let - val prevSize = if idx = 0 then 0 else Vector.sub (sizes, idx - 1) - val acc = acc + prevSize - val cursorIdx = cursorIdx - prevSize - in - helpNextMatch (cursorIdx, Vector.sub (nodes, idx), acc) - end + if idx = ~1 then {start = ~1, finish = ~1} + else helpNextMatch (cursorIdx, Vector.sub (nodes, idx)) end fun startNextMatch (cursorIdx, tree) = @@ -176,15 +172,8 @@ struct let val idx = BinSearch.equalOrMore (cursorIdx, sizes) in - if idx = ~1 then - {start = ~1, finish = ~1} - else - let - val prevSize = if idx = 0 then 0 else Vector.sub (sizes, idx - 1) - val cursorIdx = cursorIdx - prevSize - in - helpNextMatch (cursorIdx, Vector.sub (nodes, idx), prevSize) - end + if idx = ~1 then {start = ~1, finish = ~1} + else helpNextMatch (cursorIdx, Vector.sub (nodes, idx)) end fun loopNextMatch (prevStart, prevFinish, tree, count) = @@ -213,31 +202,16 @@ struct let val {start, finish} = getStart tree in loopNextMatch (start, finish, tree, count - 1) end + else if cursorIdx >= start andalso cursorIdx <= finish then + loopNextMatch (start, finish, tree, count) else - let in - if cursorIdx >= start andalso cursorIdx <= finish then - loopNextMatch (start, finish, tree, count) - else - loopNextMatch (start, finish, tree, count - 1) - end + loopNextMatch (start, finish, tree, count - 1) end - fun getLast (tree, acc) = + fun getLast tree = case tree of - LEAF (values, _) => - let val {start, finish} = Vector.sub (values, Vector.length values - 1) - in {start = start + acc, finish = finish + acc} - end - | BRANCH (nodes, sizes) => - let - val acc = - if Vector.length sizes > 1 then - acc + Vector.sub (sizes, Vector.length sizes - 1) - else - acc - in - getLast (Vector.sub (nodes, Vector.length nodes - 1), acc) - end + LEAF (values, _) => Vector.sub (values, Vector.length values - 1) + | BRANCH (nodes, _) => getLast (Vector.sub (nodes, Vector.length nodes - 1)) (* slightly tricky. * The `sizes` vector contains the last/finish position of the item @@ -254,7 +228,7 @@ struct * There might not be a previous index because the current index is 0. * In this case, either the call stack will handle it, * or the caller to `helpPrevMatch` will. *) - fun helpPrevMatch (cursorIdx, tree, acc) = + fun helpPrevMatch (cursorIdx, tree) = case tree of LEAF (values, sizes) => let @@ -266,19 +240,15 @@ struct let val result = Vector.sub (values, 0) in - if #start result < cursorIdx then - {start = #start result + acc, finish = #finish result + acc} - else - {start = ~1, finish = ~1} + if #start result < cursorIdx then result + else {start = ~1, finish = ~1} end else let val current = Vector.sub (values, idx) - val {start, finish} = - if cursorIdx > #start current then current - else Vector.sub (values, idx - 1) in - {start = start + acc, finish = finish + acc} + if cursorIdx > #start current then current + else Vector.sub (values, idx - 1) end end | BRANCH (nodes, sizes) => @@ -288,23 +258,14 @@ struct if idx < 0 then {start = ~1, finish = ~1} else if idx = 0 then - helpPrevMatch (cursorIdx, Vector.sub (nodes, idx), acc) + helpPrevMatch (cursorIdx, Vector.sub (nodes, idx)) else let val node = Vector.sub (nodes, idx) - val prevSize = Vector.sub (sizes, idx - 1) - val result = - helpPrevMatch (cursorIdx - prevSize, node, acc + prevSize) + val result = helpPrevMatch (cursorIdx, node) in - if #start result = ~1 then - let - val prevPrevSize = - if idx - 2 < 0 then 0 else Vector.sub (sizes, idx - 2) - in - getLast (Vector.sub (nodes, idx - 1), acc + prevPrevSize) - end - else - result + if #start result = ~1 then getLast (Vector.sub (nodes, idx - 1)) + else result end end @@ -313,10 +274,10 @@ struct prevStart else let - val {start, finish} = helpPrevMatch (prevFinish - 1, tree, 0) + val {start, finish} = helpPrevMatch (prevFinish - 1, tree) in if start = ~1 then - let val {start, finish} = getLast (tree, ~1) + let val {start, finish} = getLast tree in loopPrevMatch (start, finish, tree, count - 1) end else @@ -328,10 +289,10 @@ struct ~1 else let - val {start, finish} = helpPrevMatch (cursorIdx, tree, 0) + val {start, finish} = helpPrevMatch (cursorIdx, tree) in if start = ~1 then - let val {start, finish} = getLast (tree, ~1) + let val {start, finish} = getLast tree in loopPrevMatch (start, finish, tree, count - 1) end else if cursorIdx >= start andalso cursorIdx <= finish then @@ -340,668 +301,275 @@ struct loopPrevMatch (start, finish, tree, count - 1) end - fun getFirstItem tree = - case tree of - BRANCH (nodes, _) => getFirstItem (Vector.sub (nodes, 0)) - | LEAF (items, _) => - if Vector.length items = 0 then {start = ~1, finish = ~1} - else Vector.sub (items, 0) + datatype insert_result = INSERT_UPDATE of t | INSERT_SPLIT of t * t fun getMaxSize tree = case tree of - BRANCH (_, sizes) => - if Vector.length sizes = 0 then - 0 - else - Vector.sub (sizes, Vector.length sizes - 1) - | LEAF (_, sizes) => - if Vector.length sizes = 0 then - 0 - else - Vector.sub (sizes, Vector.length sizes - 1) + LEAF (_, sizes) => Vector.sub (sizes, Vector.length sizes - 1) + | BRANCH (_, sizes) => Vector.sub (sizes, Vector.length sizes - 1) - fun splitLeft (offset, tree) = + fun helpInsert (start, finish, tree) = case tree of BRANCH (nodes, sizes) => - if offset <= Vector.sub (sizes, 0) then - splitLeft (offset, Vector.sub (nodes, 0)) - else if offset >= Vector.sub (sizes, Vector.length sizes - 1) then - let - val prevSize = - if Vector.length sizes > 1 then - Vector.sub (sizes, Vector.length sizes - 1) - else - 0 - val result = splitLeft (offset - prevSize, Vector.sub - (nodes, Vector.length nodes - 1)) - in - if isEmpty result then + if finish >= Vector.sub (sizes, Vector.length sizes - 1) then + (* if we want to append *) + case + helpAppend (start, finish, Vector.sub + (nodes, Vector.length sizes - 1)) + of + UPDATE newLast => let - val len = SOME (Vector.length sizes - 1) - val sizeSlice = VectorSlice.slice (sizes, 0, len) - val sizes = VectorSlice.vector sizeSlice - val nodeSlice = VectorSlice.slice (nodes, 0, len) - val nodes = VectorSlice.vector nodeSlice - in - BRANCH (nodes, sizes) - end - else - let - val newChildSize = getMaxSize result + prevSize val sizes = Vector.update - (sizes, Vector.length sizes - 1, newChildSize) + (sizes, Vector.length sizes - 1, finish) val nodes = Vector.update - (nodes, Vector.length nodes - 1, result) + (nodes, Vector.length nodes - 1, newLast) in - BRANCH (nodes, sizes) + INSERT_UPDATE (BRANCH (nodes, sizes)) end - end - else - let - val idx = BinSearch.equalOrMore (offset, sizes) - val prevSize = if idx > 1 then Vector.sub (sizes, idx - 1) else 0 - val result = splitLeft (offset - prevSize, Vector.sub (nodes, idx)) - in - if isEmpty result then - let - val len = SOME idx - val sizeSlice = VectorSlice.slice (sizes, 0, len) - val sizes = VectorSlice.vector sizeSlice - val nodeSlice = VectorSlice.slice (nodes, 0, len) - val nodes = VectorSlice.vector nodeSlice - in - BRANCH (nodes, sizes) - end - else - let - val len = idx + 1 - val sizes = Vector.tabulate (len, fn i => - if i = idx then getMaxSize result + prevSize - else Vector.sub (sizes, i)) - val nodes = Vector.tabulate (len, fn i => - if i = idx then result else Vector.sub (nodes, i)) - in - BRANCH (nodes, sizes) - end - end - | LEAF (items, sizes) => - if Vector.length sizes > 0 then - if offset > Vector.sub (sizes, Vector.length sizes - 1) then - tree - else if offset <= Vector.sub (sizes, 0) then - LEAF (#[], #[]) - else - let - val len = BinSearch.equalOrMore (offset, sizes) - val sizes = VectorSlice.slice (sizes, 0, SOME len) - val sizes = VectorSlice.vector sizes - val items = VectorSlice.slice (items, 0, SOME len) - val items = VectorSlice.vector items - in - LEAF (items, sizes) - end - else - tree - - (* Unlike 'splitLeft' which leaves the size metadata alone - * (except for splitting it), - * we want splitRight to decrement the size metadata - * by the largest entry in the size table that was split. - * This is so that we can maintain relative indexing metadata. *) - fun splitRight (offset, tree) = - case tree of - BRANCH (nodes, sizes) => - if offset <= Vector.sub (sizes, 0) then - (* we want to split first node *) - let - val firstSizeBefore = Vector.sub (sizes, 0) - val result = splitRight (offset, Vector.sub (nodes, 0)) - in - if isEmpty result then - let - val len = Vector.length sizes - 1 - - val sizeSlice = VectorSlice.slice (sizes, 1, SOME len) - val sizes = - VectorSlice.map (fn el => el - firstSizeBefore) sizeSlice - - val nodeSlice = VectorSlice.slice (nodes, 1, SOME len) - val nodes = VectorSlice.vector nodeSlice - in - BRANCH (nodes, sizes) - end - else - let - val firstSizeAfter = getMaxSize result - val sizeDiff = firstSizeBefore - firstSizeAfter - - val sizes = - Vector.mapi - (fn (idx, el) => - if idx = 0 then firstSizeAfter else el - sizeDiff) sizes - - val nodes = Vector.update (nodes, 0, result) - in - BRANCH (nodes, sizes) - end - end - else if offset >= Vector.sub (sizes, Vector.length sizes - 1) then - (* we want to split after last node, leaving empty *) - BRANCH (#[], #[]) - else - let - val idx = BinSearch.equalOrMore (offset, sizes) - val prevSize = Vector.sub (sizes, idx - 1) - val curSize = Vector.sub (sizes, idx) - val result = splitRight (offset - prevSize, Vector.sub (nodes, idx)) - in - if isEmpty result then - let - val startIdx = idx + 1 - val len = Vector.length sizes - startIdx - - val sizeSlice = VectorSlice.slice (sizes, startIdx, SOME len) - val sizes = VectorSlice.map (fn el => el - curSize) sizeSlice - - val nodeSlice = VectorSlice.slice (nodes, startIdx, SOME len) - val nodes = VectorSlice.vector nodeSlice - in - BRANCH (nodes, sizes) - end - else - let - val newCurSize = getMaxSize result - val sizeDiff = curSize - newCurSize - - val len = Vector.length sizes - idx - val sizeSlice = VectorSlice.slice (sizes, idx, SOME len) - val sizes = VectorSlice.map (fn el => el - sizeDiff) sizeSlice - - val nodeSlice = VectorSlice.slice (nodes, idx, SOME len) - val nodes = VectorSlice.vector nodeSlice - in - BRANCH (nodes, sizes) - end - end - | LEAF (items, sizes) => - if offset > Vector.sub (sizes, Vector.length sizes - 1) then - LEAF (#[], #[]) - else if offset <= Vector.sub (sizes, 0) then - tree - else - let - val idx = BinSearch.equalOrMore (offset, sizes) - val len = Vector.length sizes - idx - val len = SOME len - - val prevSize = if idx < 1 then 0 else Vector.sub (sizes, idx - 1) - val sizes = VectorSlice.slice (sizes, idx, len) - val sizes = VectorSlice.map (fn i => i - prevSize) sizes - - val items = VectorSlice.slice (items, idx, len) - val items = - VectorSlice.map - (fn {start, finish} => - {start = start - prevSize, finish = finish - prevSize}) items - in - LEAF (items, sizes) - end - - fun getDepth (tree, acc) = - case tree of - BRANCH (nodes, _) => getDepth (Vector.sub (nodes, 0), acc + 1) - | LEAF (_, _) => acc - - fun getRootVecLength tree = - case tree of - BRANCH (_, sizes) => Vector.length sizes - | LEAF (_, sizes) => Vector.length sizes - - fun joinSameDepth (left, right) = - case (left, right) of - (BRANCH (leftNodes, leftSizes), BRANCH (rightNodes, rightSizes)) => - let - val nodes = Vector.concat [leftNodes, rightNodes] - val maxLeftSize = Vector.sub (leftSizes, Vector.length leftSizes - 1) - val sizes = - Vector.tabulate - ( Vector.length leftSizes + Vector.length rightSizes - , fn i => - if i < Vector.length leftSizes then - Vector.sub (leftSizes, i) - else - Vector.sub (rightSizes, i - Vector.length leftSizes) - + maxLeftSize - ) - in - BRANCH (nodes, sizes) - end - | (LEAF (leftItems, leftSizes), LEAF (rightItems, rightSizes)) => - let - val maxLeftSize = Vector.sub (leftSizes, Vector.length leftSizes - 1) - val len = Vector.length leftItems + Vector.length rightItems - val items = Vector.tabulate (len, fn i => - if i < Vector.length leftItems then - Vector.sub (leftItems, i) - else - let - val {start, finish} = Vector.sub - (rightItems, i - Vector.length leftItems) - in - {start = start + maxLeftSize, finish = finish + maxLeftSize} - end) - val sizes = Vector.tabulate (len, fn i => - if i < Vector.length leftSizes then - Vector.sub (leftSizes, i) - else - Vector.sub (rightSizes, i - Vector.length leftSizes) + maxLeftSize) - in - LEAF (items, sizes) - end - | _ => - raise Fail - "PersistentVector.joinSameDepth: one is BRANCH and other is LEAF" - - datatype append_join_result = - APPEND_JOIN_APPEND of t - | APPEND_JOIN_UPDATE of t - - fun appendJoin (left, right, joinDepth, rightLength) = - case left of - BRANCH (nodes, sizes) => - if joinDepth = 0 then - (* base case: should join at this depth *) - if Vector.length nodes + rightLength > maxSize then - APPEND_JOIN_APPEND right - else - (case right of - BRANCH (rightNodes, rightSizes) => - let - val nodes = Vector.concat [nodes, rightNodes] - - val lastLeftSize = Vector.sub - (sizes, Vector.length sizes - 1) - val sizes = Vector.tabulate (Vector.length nodes, fn i => - if i < Vector.length sizes then - Vector.sub (sizes, i) - else - Vector.sub (rightSizes, i - Vector.length sizes) - + lastLeftSize) - in - APPEND_JOIN_UPDATE (BRANCH (nodes, sizes)) - end - | LEAF _ => - raise Fail - "persistent-vector.sml appendJoin: \ - \expected to join when left and right are both BRANCH \ - \but left is BRANCH and right is LEAF") - else - (* recursion case: join below *) - let - val lastIdx = Vector.length nodes - 1 - val lastNode = Vector.sub (nodes, lastIdx) - in - case appendJoin (lastNode, right, joinDepth - 1, rightLength) of - APPEND_JOIN_UPDATE newLast => + | APPEND newLast => + if Vector.length nodes = maxSize then + (* have to split *) let - val prevSize = - if lastIdx > 0 then Vector.sub (sizes, lastIdx - 1) else 0 - val newLastSize = getMaxSize newLast + prevSize - val sizes = Vector.update (sizes, lastIdx, newLastSize) - val nodes = Vector.update (nodes, lastIdx, newLast) - in - APPEND_JOIN_UPDATE (BRANCH (nodes, sizes)) - end - | APPEND_JOIN_APPEND newNode => - if Vector.length nodes = maxSize then - (* parent has to append insead as this node - * would exceed capacity if appended here *) - APPEND_JOIN_APPEND (BRANCH - (#[newNode], #[getMaxSize newNode])) - else - let - val prevSize = Vector.sub (sizes, Vector.length sizes - 1) - val newNodeSize = #[getMaxSize newNode + prevSize] - val sizes = Vector.concat [sizes, newNodeSize] + val leftLen = SOME halfSize + val rightLen = SOME (Vector.length nodes - halfSize) - val newNode = #[newNode] - val nodes = Vector.concat [nodes, newNode] + val leftNodeSlice = VectorSlice.slice (nodes, 0, leftLen) + val rightNodeSlice = + VectorSlice.slice (nodes, halfSize, rightLen) + + val leftSizeSlice = VectorSlice.slice(sizes, 0, leftLen) + val rightSizeSlice = + VectorSlice.slice (sizes, halfSize, rightLen) + + val leftNodes = VectorSlice.vector leftNodeSlice + val leftSizes = VectorSlice.vector leftSizeSlice + + val newLast = VectorSlice.full (#[newLast]) + val finish = VectorSlice.full (#[finish]) + val rightNodes = VectorSlice.concat [rightNodeSlice, newLast] + val rightSizes = VectorSlice.concat [rightSizeSlice, finish] + + val left = BRANCH (leftNodes, leftSizes) + val right = BRANCH (rightNodes, rightSizes) + in + INSERT_SPLIT (left, right) + end + else + (* append newLast to current node *) + let + val newLast = #[newLast] + val finish = #[finish] + + val nodes = Vector.concat [nodes, newLast] + val sizes = Vector.concat [sizes, finish] + in + INSERT_UPDATE (BRANCH (nodes, sizes)) + end + else + let + val idx = BinSearch.equalOrMore (finish, sizes) + val idx = if idx = ~1 then 0 else idx + in + case helpInsert (start, finish, tree) of + INSERT_UPDATE newNode => + let + val sizes = + if finish > Vector.sub (sizes, idx) then + Vector.update (sizes, idx, finish) + else + sizes + val nodes = Vector.update (nodes, idx, newNode) + in + INSERT_UPDATE (BRANCH (nodes, sizes)) + end + | INSERT_SPLIT (left, right) => + if Vector.length nodes = maxSize then + (* have to split this node too *) + let + (* slice sizes *) + val leftSize = VectorSlice.full #[getMaxSize left] + val rightSize = VectorSlice.full #[getMaxSize right] + + val leftLen = SOME idx + val rightLen = SOME (Vector.length nodes - idx - 1) + + val leftSizeSlice = VectorSlice.slice (sizes, 0, leftLen) + val rightSizeSlice = VectorSlice.slice (sizes, idx + 1, rightLen) + + val leftSizes = VectorSlice.concat [leftSizeSlice, leftSize] + val rightSizes = VectorSlice.concat [rightSizeSlice, rightSize] + + (* slice nodes *) + val left = VectorSlice.full #[left] + val right = VectorSlice.full #[right] + + val leftNodesSlice = VectorSlice.slice (nodes, 0, leftLen) + val rightNodesSlice = VectorSlice.slice (nodes, idx + 1, rightLen) + + val leftNodes = VectorSlice.concat [leftNodesSlice, left] + val rightNodes = VectorSlice.concat [right, rightNodesSlice] + + (* join sizes and nodes *) + val left = BRANCH (leftNodes, leftSizes) + val right = BRANCH (rightNodes, rightSizes) in - APPEND_JOIN_UPDATE (BRANCH (nodes, sizes)) + INSERT_SPLIT (left, right) + end + else + (* can join children into parent *) + let + val midSizes = #[getMaxSize left, getMaxSize right] + val midSizes = VectorSlice.full midSizes + val midNodes = #[left, right] + val midNodes = VectorSlice.full midNodes + + val leftLen = SOME idx + val rightLen = SOME (Vector.length sizes - idx) + + val leftSizes = VectorSlice.slice (sizes, 0, leftLen) + val rightSizes = VectorSlice.slice (sizes, idx, rightLen) + + val leftNodes = VectorSlice.slice (nodes, 0, leftLen) + val rightNodes = VectorSlice.slice (nodes, idx, rightLen) + + val sizes = + VectorSlice.concat [leftSizes, midSizes, rightSizes] + val nodes = + VectorSlice.concat [leftNodes, midNodes, rightNodes] + in + INSERT_UPDATE (BRANCH (nodes, sizes)) end end | LEAF (items, sizes) => - (* joinDepth should = 0, and we assume it is *) - if Vector.length items + rightLength > maxSize then - APPEND_JOIN_APPEND right - else - (case right of - LEAF (rightItems, rightSizes) => - let - val leftMaxSize = Vector.sub (sizes, Vector.length sizes - 1) - - val newLen = Vector.length items + Vector.length rightItems - val items = Vector.tabulate (newLen, fn i => - if i < Vector.length items then - Vector.sub (items, i) - else - let - val {start, finish} = Vector.sub - (rightItems, i - Vector.length items) - in - { start = start + leftMaxSize - , finish = finish + leftMaxSize - } - end) - val sizes = Vector.tabulate (newLen, fn i => - if i < Vector.length sizes then - Vector.sub (sizes, i) - else - Vector.sub (rightSizes, i - Vector.length sizes) - + leftMaxSize) - in - APPEND_JOIN_UPDATE (LEAF (items, sizes)) - end - | BRANCH _ => - raise Fail - "persistent-vector.sml appendJoin: \ - \left is LEAF and expected right to also be LEAF \ - \but right is BRANCH") - - datatype prepend_join_result = - PREPEND_JOIN_PREPEND of t - | PREPEND_JOIN_UPDATE of t - - fun prependJoin (left, right, joinDepth, leftLength) = - case right of - BRANCH (rightNodes, rightSizes) => - if joinDepth = 0 then - (* base case: join *) - if Vector.length rightNodes + leftLength > maxSize then - PREPEND_JOIN_PREPEND left - else - (case left of - BRANCH (leftNodes, leftSizes) => - let - val nodes = Vector.concat [leftNodes, rightNodes] - val maxLeftSize = Vector.sub - (leftSizes, Vector.length leftSizes - 1) - val sizes = Vector.tabulate (Vector.length nodes, fn i => - if i < Vector.length leftSizes then - Vector.sub (leftSizes, i) - else - Vector.sub (rightSizes, i - Vector.length leftSizes) - + maxLeftSize) - in - PREPEND_JOIN_UPDATE (BRANCH (nodes, sizes)) - end - | LEAF _ => - raise Fail - "persistent-vector.sml prependJoin: \ - \expected left and right to be BRANCH \ - \but right is BRANCH while left is LEAF") - else - (* recursive case *) + if Vector.length items = 0 then + (* leaf is empty, so return leaf containing one item *) let - val firstRightNode = Vector.sub (rightNodes, 0) - val firstRightSize = getMaxSize firstRightNode + val item = #[{start = start, finish = finish}] + val size = #[finish] in - (case prependJoin (left, firstRightNode, joinDepth - 1, leftLength) of - PREPEND_JOIN_UPDATE newFirst => - let - val newFirstSize = getMaxSize newFirst - val sizeDiff = newFirstSize - firstRightSize - val sizes = Vector.map (fn el => el + sizeDiff) rightSizes - val nodes = Vector.update (rightNodes, 0, newFirst) - in - PREPEND_JOIN_UPDATE (BRANCH (nodes, sizes)) - end - | PREPEND_JOIN_PREPEND newFirst => - if Vector.length rightSizes = maxSize then - PREPEND_JOIN_PREPEND (BRANCH - (#[newFirst], #[getMaxSize newFirst])) - else - let - val nodes = Vector.concat [#[newFirst], rightNodes] - val newFirstSize = getMaxSize newFirst - - val sizes = Vector.tabulate (Vector.length nodes, fn i => - if i = 0 then newFirstSize - else Vector.sub (rightSizes, i - 1) + newFirstSize) - in - PREPEND_JOIN_UPDATE (BRANCH (nodes, sizes)) - end) - end - | LEAF (rightItems, rightSizes) => - (* joinDepth should = 0, and we assume it is *) - if Vector.length rightItems + leftLength > maxSize then - PREPEND_JOIN_PREPEND left - else - (case left of - LEAF (leftItems, leftSizes) => - let - val maxLeftSize = Vector.sub - (leftSizes, Vector.length leftSizes - 1) - val len = Vector.length leftSizes + Vector.length rightItems - val sizes = Vector.tabulate (len, fn i => - if i < Vector.length leftSizes then - Vector.sub (leftSizes, i) - else - Vector.sub (rightSizes, i - Vector.length leftSizes) - + maxLeftSize) - val items = Vector.tabulate (len, fn i => - if i < Vector.length leftItems then - Vector.sub (leftItems, i) - else - let - val {start, finish} = Vector.sub - (rightItems, i - Vector.length leftItems) - in - { start = start + maxLeftSize - , finish = finish + maxLeftSize - } - end) - in - PREPEND_JOIN_UPDATE (LEAF (items, sizes)) - end - | BRANCH _ => - raise Fail - "persistent-vector.sml prependJoin: \ - \expected left and right to be LEAF \ - \but right is LEAF while left is BRANCH") - - fun join (left, right) = - if isEmpty left then - right - else if isEmpty right then - left - else - let - val leftDepth = getDepth (left, 0) - val rightDepth = getDepth (right, 0) - in - if leftDepth = rightDepth then - if getRootVecLength left + getRootVecLength right <= maxSize then - joinSameDepth (left, right) - else - let - val ls = getMaxSize left - val rs = getMaxSize right + ls - val sizes = #[ls, rs] - val nodes = #[left, right] - in - BRANCH (nodes, sizes) - end - else if leftDepth > rightDepth then - let - val joinDepth = leftDepth - rightDepth - val rightLength = getRootVecLength right - in - case appendJoin (left, right, joinDepth, rightLength) of - APPEND_JOIN_UPDATE t => t - | APPEND_JOIN_APPEND newRight => - let - val ls = getMaxSize left - val rs = getMaxSize right + ls - val sizes = #[ls, rs] - val nodes = #[left, newRight] - in - BRANCH (nodes, sizes) - end + INSERT_UPDATE (LEAF (item, size)) end else + if finish > Vector.sub (sizes, Vector.length sizes - 1) then + if Vector.length sizes = maxSize then + (* have to split *) + let + val startLen = SOME halfSize + val midLen = SOME (Vector.length items - halfSize) + + val leftSizes = VectorSlice.slice (sizes, 0, startLen) + val leftItems = VectorSlice.slice (items, 0, startLen) + + val midSizes = VectorSlice.slice (sizes, halfSize, midLen) + val midItems = VectorSlice.slice (items, halfSize, midLen) + + val rightSizes = VectorSlice.full #[finish] + val rightItems = VectorSlice.full #[{start = start, finish = finish}] + + val rightItems = VectorSlice.concat [midItems, rightItems] + val leftItems = VectorSlice.vector leftItems + + val rightSizes = VectorSlice.concat [midSizes, rightSizes] + val leftSizes = VectorSlice.vector leftSizes + + val left = LEAF (leftItems, leftSizes) + val right = LEAF (rightItems, rightSizes) + in + INSERT_SPLIT (left, right) + end + else + (* can just append *) + let + val sizes = Vector.concat [sizes, #[finish]] + val item = #[{start = start, finish = finish}] + val items = Vector.concat [items, item] + in + INSERT_UPDATE (LEAF (items, sizes)) + end + else if finish < #start (Vector.sub (items, 0)) then + (* prepend *) + if Vector.length sizes = maxSize then + (* have to split *) + let + val leftSizes = VectorSlice.full #[finish] + val leftItems = VectorSlice.full #[{start = start, finish = finish}] + + val midLen = SOME halfSize + val rightLen = SOME (Vector.length items - halfSize) + + val midSizes = VectorSlice.slice (sizes, 0, midLen) + val midItems = VectorSlice.slice (items, 0, midLen) + + val rightSizes = VectorSlice.slice (sizes, halfSize, rightLen) + val rightItems = VectorSlice.slice (items, halfSize, rightLen) + + val leftSizes = VectorSlice.concat [leftSizes, midSizes] + val rightSizes = VectorSlice.vector rightSizes + + val leftItems = VectorSlice.concat [leftItems, midItems] + val rightItems = VectorSlice.vector rightItems + + val left = LEAF (leftItems, leftSizes) + val right = LEAF (rightItems, rightSizes) + in + INSERT_SPLIT (left, right) + end + else + (* just prepend *) + let + val sizes = Vector.concat [#[finish], sizes] + val item = {start = start, finish = finish} + val items = Vector.concat [#[item], items] + in + INSERT_UPDATE (LEAF (items, sizes)) + end + else + (* insert into middle *) let - val joinDepth = rightDepth - leftDepth - val leftLength = getRootVecLength left + val idx = BinSearch.equalOrMore (finish, sizes) + val leftLen = SOME idx + val rightLen = SOME (Vector.length sizes - idx) + + val leftSizes = VectorSlice.slice (sizes, 0, leftLen) + val rightSizes = VectorSlice.slice (sizes, idx, rightLen) + + val leftItems = VectorSlice.slice (items, 0, leftLen) + val rightItems = VectorSlice.slice (items, idx, rightLen) + val midSize = VectorSlice.full #[finish] + val midItem = VectorSlice.full #[{start = start, finish = finish}] in - case prependJoin (left, right, joinDepth, leftLength) of - PREPEND_JOIN_UPDATE t => t - | PREPEND_JOIN_PREPEND newLeft => - let - val ls = getMaxSize newLeft - val rs = getMaxSize right + ls - val sizes = #[ls, rs] - val nodes = #[newLeft, right] - in - BRANCH (nodes, sizes) - end + if Vector.length items = maxSize then + (* have to return split *) + let + val leftSizes = VectorSlice.concat [leftSizes, midSize] + val rightSizes = VectorSlice.vector rightSizes + + val leftItems = VectorSlice.concat [leftItems, midItem] + val rightItems = VectorSlice.vector rightItems + + val left = LEAF (leftItems, leftSizes) + val right = LEAF (rightItems, rightSizes) + in + INSERT_SPLIT (left, right) + end + else + (* have to return update *) + let + val sizes = VectorSlice.concat [leftSizes, midSize, rightSizes] + val items = VectorSlice.concat [leftItems, midItem, rightItems] + in + INSERT_UPDATE (LEAF (items, sizes)) + end end - end - fun root tree = - case tree of - BRANCH (nodes, sizes) => - if Vector.length nodes = 1 then - Vector.sub (nodes, 0) - else - tree - | LEAF _ => tree - - fun decrement (minusBy, tree) = - case tree of - BRANCH (nodes, sizes) => + fun insert (start, finish, tree) = + case helpInsert (start, finish, tree) of + INSERT_UPDATE tree => tree + | INSERT_SPLIT (left, right) => let - val sizes = Vector.map (fn el => el - minusBy) sizes - val newFirstNode = decrement (minusBy, Vector.sub (nodes, 0)) - val nodes = Vector.update (nodes, 0, newFirstNode) + val sizes = #[getMaxSize left, getMaxSize right] + val nodes = #[left, right] in BRANCH (nodes, sizes) end - | LEAF (items, sizes) => - let - val sizes = Vector.map (fn el => el - minusBy) sizes - val items = Vector.map - (fn {start, finish} => - {start = start - minusBy, finish = finish - minusBy}) - items - in - LEAF (items, sizes) - end - - fun delete (start, length, tree) = - let - val finishIdx = start + length + 1 - val {start, finish} = startNextMatch (start, tree) - in - if start = ~1 then - (* nothing to delete *) - tree - else - let - (* split left and right side *) - val left = splitLeft (start, tree) - val left = root left - val right = splitRight (finishIdx, tree) - val right = root right - in - if isEmpty right then - left - else - let - (* calculate what the new index should be - * for the first match in the right tree, - * and decrement the right tree to reach this index - * if necessary. - * - * We calculate the index the right node should start at - * using this method: - * 1. We get the first match after finishIdx from the original * tree - * 2. We subtract this by the length provided to us, - * which tells us where the right node should start at. - * 3. We get the first item in the right node, - * and add the last item in the left node - * to get an absolute value. - * 4. We subtract the match from the original tree - * by the first item in the right node. - * This step gives us the difference: how much - * we need to decrement the right node by. - * *) - val {start = originalStart, ...} = startNextMatch (finishIdx, tree) - val originalStart = originalStart - length - - val leftSize = getMaxSize left - val {start = rightStart, ...} = getFirstItem right - val rightStart = rightStart + leftSize - val difference = rightStart - originalStart - val right = - if difference = 0 then - right - else - decrement (difference, right) - in - join (left, right) - end - end - end - - fun insert (start, length, tree) = - let - val left = splitLeft (start, tree) - val left = append (start, start + length, tree) - val right = splitRight (start + 1, tree) - in - join (left, right) - end - - (* conversion functions for testing *) - fun helpFromList ([], acc) = acc - | helpFromList ((start, finish) :: tl, acc) = - let - val acc = append (start, finish, acc) - in - helpFromList (tl, acc) - end - - fun fromList lst = helpFromList (lst, empty) - - fun helpToList (tree, acc): (int * int) list = - case tree of - BRANCH (nodes, _) => - helpToListNodes (Vector.length nodes - 1, nodes, acc) - | LEAF (items, _) => - helpToListItems (Vector.length items - 1, items, acc) - - and helpToListItems (pos, items, acc) = - if pos < 0 then - acc - else - let - val {start, finish} = Vector.sub (items, pos) - val acc = (start, finish) :: acc - in - helpToListItems (pos - 1, items, acc) - end - - and helpToListNodes (pos, nodes, acc) = - if pos < 0 then - acc - else - let - val node = Vector.sub (nodes, pos) - val acc = helpToList (node, acc) - in - helpToListNodes (pos - 1, nodes, acc) - end - - fun toList tree = helpToList (tree, []) end diff --git a/shf-tests.mlb b/shf-tests.mlb index 6fb2427..e6f423b 100644 --- a/shf-tests.mlb +++ b/shf-tests.mlb @@ -71,5 +71,4 @@ test/test-utils.sml test/normal-move.sml test/normal-delete.sml test/regression.sml -test/persistent-vector-tests.sml test/test.sml diff --git a/test/normal-delete.sml b/test/normal-delete.sml index c2d6b98..05c2492 100644 --- a/test/normal-delete.sml +++ b/test/normal-delete.sml @@ -4001,72 +4001,92 @@ struct ] val diwDelete = describe "delete motion 'diw' (delete inside word)" - [test - "deletes middle word when middle word is \ - \an alphanumeric word surrounded on both sides by spaces" - (fn _ => - let - (* arrange *) - val originalString = "hello abc_123 world\n" - val app = TestUtils.init originalString + [ test + "deletes middle word when middle word is \ + \an alphanumeric word surrounded on both sides by spaces" + (fn _ => + let + (* arrange *) + val originalString = "hello abc_123 world\n" + val app = TestUtils.init originalString - val app1 = AppWith.idx (app, 6) - val app2 = AppWith.idx (app, 7) - val app3 = AppWith.idx (app, 8) - val app4 = AppWith.idx (app, 9) - val app5 = AppWith.idx (app, 10) - val app6 = AppWith.idx (app, 11) - val app7 = AppWith.idx (app, 12) + val app1 = AppWith.idx (app, 6) + val app2 = AppWith.idx (app, 7) + val app3 = AppWith.idx (app, 8) + val app4 = AppWith.idx (app, 9) + val app5 = AppWith.idx (app, 10) + val app6 = AppWith.idx (app, 11) + val app7 = AppWith.idx (app, 12) - (* act *) - val app1 = TestUtils.updateMany (app1, "diw") - val app2 = TestUtils.updateMany (app2, "diw") - val app3 = TestUtils.updateMany (app3, "diw") - val app4 = TestUtils.updateMany (app4, "diw") - val app5 = TestUtils.updateMany (app5, "diw") - val app6 = TestUtils.updateMany (app6, "diw") - val app7 = TestUtils.updateMany (app7, "diw") + (* act *) + val app1 = TestUtils.updateMany (app1, "diw") + val app2 = TestUtils.updateMany (app2, "diw") + val app3 = TestUtils.updateMany (app3, "diw") + val app4 = TestUtils.updateMany (app4, "diw") + val app5 = TestUtils.updateMany (app5, "diw") + val app6 = TestUtils.updateMany (app6, "diw") + val app7 = TestUtils.updateMany (app7, "diw") - (* assert *) - val expectedString = "hello world\n" - val expectedCursorIdx = 6 + (* assert *) + val expectedString = "hello world\n" + val expectedCursorIdx = 6 - val actualString1 = LineGap.toString (#buffer app1) - val actualString2 = LineGap.toString (#buffer app2) - val actualString3 = LineGap.toString (#buffer app3) - val actualString4 = LineGap.toString (#buffer app4) - val actualString5 = LineGap.toString (#buffer app5) - val actualString6 = LineGap.toString (#buffer app6) - val actualString7 = LineGap.toString (#buffer app7) + val actualString1 = LineGap.toString (#buffer app1) + val actualString2 = LineGap.toString (#buffer app2) + val actualString3 = LineGap.toString (#buffer app3) + val actualString4 = LineGap.toString (#buffer app4) + val actualString5 = LineGap.toString (#buffer app5) + val actualString6 = LineGap.toString (#buffer app6) + val actualString7 = LineGap.toString (#buffer app7) - val stringsAreExpected = - actualString1 = expectedString - andalso actualString2 = expectedString - andalso actualString3 = expectedString - andalso actualString4 = expectedString - andalso actualString5 = expectedString - andalso actualString6 = expectedString - andalso actualString7 = expectedString + val stringsAreExpected = + actualString1 = expectedString + andalso actualString2 = expectedString + andalso actualString3 = expectedString + andalso actualString4 = expectedString + andalso actualString5 = expectedString + andalso actualString6 = expectedString + andalso actualString7 = expectedString - val actualCursor1 = #cursorIdx app1 - val actualCursor2 = #cursorIdx app2 - val actualCursor3 = #cursorIdx app3 - val actualCursor4 = #cursorIdx app4 - val actualCursor5 = #cursorIdx app5 - val actualCursor6 = #cursorIdx app6 - val actualCursor7 = #cursorIdx app7 + val actualCursor1 = #cursorIdx app1 + val actualCursor2 = #cursorIdx app2 + val actualCursor3 = #cursorIdx app3 + val actualCursor4 = #cursorIdx app4 + val actualCursor5 = #cursorIdx app5 + val actualCursor6 = #cursorIdx app6 + val actualCursor7 = #cursorIdx app7 - val cursorsAreExpected = - actualCursor1 = expectedCursorIdx - andalso actualCursor2 = expectedCursorIdx - andalso actualCursor3 = expectedCursorIdx - andalso actualCursor4 = expectedCursorIdx - andalso actualCursor5 = expectedCursorIdx - andalso actualCursor6 = expectedCursorIdx - andalso actualCursor7 = expectedCursorIdx - in - Expect.isTrue (stringsAreExpected andalso cursorsAreExpected) - end)] + val cursorsAreExpected = + actualCursor1 = expectedCursorIdx + andalso actualCursor2 = expectedCursorIdx + andalso actualCursor3 = expectedCursorIdx + andalso actualCursor4 = expectedCursorIdx + andalso actualCursor5 = expectedCursorIdx + andalso actualCursor6 = expectedCursorIdx + andalso actualCursor7 = expectedCursorIdx + in + Expect.isTrue (stringsAreExpected andalso cursorsAreExpected) + end) + , test + "deletes middle word if word is a punctuation word \ + \surrounded by spaces" + (fn _ => + let + (* arrange *) + val originalString = "hello !#%&( world\n" + val app = TestUtils.init originalString + val app = AppWith.idx (app, 9) + + (* act *) + val {buffer, cursorIdx, ...} = TestUtils.updateMany (app, "diw") + + (* assert *) + val actualString = LineGap.toString buffer + val expectedString = "hello world\n" + in + Expect.isTrue (actualString = expectedString) + end) + ] val tests = [ dhDelete diff --git a/test/persistent-vector-tests.sml b/test/persistent-vector-tests.sml deleted file mode 100644 index 17fd50e..0000000 --- a/test/persistent-vector-tests.sml +++ /dev/null @@ -1,107 +0,0 @@ -structure PersistentVectorTests = -struct - open Railroad - open Railroad.Test - - structure Pv = PersistentVector - - val appendTests = describe "PersistentVector.append" - [test "appends new values to end" (fn _ => - let - (* arrange *) - val pv = Pv.fromList [(1, 1), (3, 3)] - - (* act *) - val pv = Pv.append (5, 7, pv) - - (* assert *) - val outputList = Pv.toList pv - val expectedList = [(1, 1), (3, 3), (5, 7)] - in - Expect.isTrue (outputList = expectedList) - end)] - - fun printList lst = - let - val str = - List.map - (fn (start, finish) => - "(" ^ Int.toString start ^ ", " ^ Int.toString finish ^ ")") lst - val str = "[" ^ String.concatWith ", " str ^ "]\n" - in - print str - end - - val deleteTests = describe "PersistentVector.delete" - [ test - "deletes last value correctly \ - \when only last value is in deletion range" - (fn _ => - let - (* arrange *) - val pv = Pv.fromList [(0, 0), (3, 3), (5, 5)] - - (* act *) - val pv = Pv.delete (5, 1, pv) - - (* assert *) - val outputList = Pv.toList pv - val expectedList = [(0, 0), (3, 3)] - in - Expect.isTrue (outputList = expectedList) - end) - , test - "deletes second-last value correctly \ - \and adjusts last value as expected" - (fn _ => - let - (* arrange *) - val pv = Pv.fromList [(0, 0), (3, 3), (5, 5), (7, 7)] - - (* act *) - val pv = Pv.delete (4, 1, pv) - - (* assert *) - val outputList = Pv.toList pv - val expectedList = [(0, 0), (3, 3), (6, 6)] - in - Expect.isTrue (outputList = expectedList) - end) - , test - "deletes middle value correctly \ - \and adjusts values-after-middle as well" - (fn _ => - let - (* arrange *) - val pv = Pv.fromList [(0, 0), (3, 3), (5, 5)] - - (* act *) - val pv = Pv.delete (3, 1, pv) - - (* assert *) - val outputList = Pv.toList pv - val expectedList = [(0, 0), (4, 4)] - in - Expect.isTrue (outputList = expectedList) - end) - , test - "deletes first value correctly \ - \and adjusts values-after-first as well" - (fn _ => - let - (* arrange *) - val pv = Pv.fromList [(0, 0), (3, 3), (5, 5)] - - (* act *) - val pv = Pv.delete (0, 1, pv) - - (* assert *) - val outputList = Pv.toList pv - val expectedList = [(2, 2), (4, 4)] - in - Expect.isTrue (outputList = expectedList) - end) - ] - - val tests = [appendTests, deleteTests] -end diff --git a/test/test.sml b/test/test.sml index 2d5976d..416569a 100644 --- a/test/test.sml +++ b/test/test.sml @@ -5,13 +5,13 @@ struct fun main () = let - val tests = List.concat - [ NormalMove.tests - , NormalDelete.tests - , Regression.tests - , RegexTests.tests - , PersistentVectorTests.tests - ] + val tests = + List.concat + [ NormalMove.tests + , NormalDelete.tests + , Regression.tests + , RegexTests.tests + ] val tests = concat tests in runWithConfig [Configuration.PrintPassed false] tests