diff --git a/bench_rope b/bench_rope new file mode 100755 index 0000000..f70b11e Binary files /dev/null and b/bench_rope differ diff --git a/gap_buffer.sml b/gap_buffer.sml index 04fb115..bf8c401 100644 --- a/gap_buffer.sml +++ b/gap_buffer.sml @@ -36,15 +36,6 @@ struct fun isThreeLessThanTarget (s1, s2, s3) = String.size s1 + String.size s2 + String.size s3 <= targetLength - local - fun helpCalcIndex (left, total) = - case left of - [] => total - | hd :: tail => helpCalcIndex (tail, total + String.size hd) - in - fun calcIndex left = helpCalcIndex (left, 0) - end - fun consLeft (curIdx, newString, left, right) = { idx = curIdx + String.size newString , left = newString :: left @@ -69,6 +60,20 @@ struct | [] => consLeft (curIdx, newString, left, right)) | [] => consLeft (curIdx, newString, left, right) + fun joinEndOfLeft (newString, left) = + case left of + hd :: tail => + if isLessThanTarget (newString, hd) then (hd ^ newString) :: tail + else newString :: left + | [] => newString :: left + + fun joinStartOfRight (newString, right) = + case right of + hd :: tail => + if isLessThanTarget (newString, hd) then (newString ^ hd) :: tail + else newString :: right + | [] => newString :: right + fun ins (idx, newString, curIdx, left, right) : t = if curIdx = idx then preferInsertLeft (curIdx, newString, left, right) @@ -87,7 +92,7 @@ struct then (* The requested index is prior to the string on the left, * so move leftward one string. *) - ins (idx, newString, prevIdx, tail, hd :: right) + ins (idx, newString, prevIdx, tail, joinStartOfRight (hd, right)) else (* The requested index is either: * - At the start of the left string @@ -96,13 +101,27 @@ struct idx = prevIdx then (* At start of string. *) - if isLessThanTarget (newString, hd) then - { idx = curIdx + String.size newString - , left = (newString ^ hd) :: tail - , right = right - } - else - {idx = prevIdx, left = tail, right = newString :: hd :: right} + { idx = curIdx + String.size newString + , right = right + , left = + (* These two meant to look reversed, + * with respect to newString and hd. + * + * The line + * `newString ^ hd` + * places the contents of newString before hd, + * and the line + * `hd :: newString` + * in a zipper also places newString before hd. + * + * Using `newString ^ hd` with `newString :: hd` gives + * different contents in the case of a zipper. + * *) + if isLessThanTarget (newString, hd) then + (newString ^ hd) :: tail + else + hd :: newString :: tail + } else (* In middle of string. *) let @@ -119,7 +138,7 @@ struct else if isLessThanTarget (sub1, newString) then { idx = prevIdx + String.size sub1 + String.size newString , left = (sub1 ^ newString) :: tail - , right = sub2 :: right + , right = joinStartOfRight (sub2, right) } else if isLessThanTarget (newString, sub2) then { idx = prevIdx + String.size sub1 @@ -142,13 +161,16 @@ struct val nextIdx = String.size hd + curIdx in if idx > nextIdx then - ins (idx, newString, nextIdx, hd :: left, tail) + ins (idx, newString, nextIdx, joinEndOfLeft (hd, left), tail) else if idx = nextIdx then (* At end of next string. *) if isLessThanTarget (newString, hd) then {idx = curIdx, left = left, right = (hd ^ newString) :: tail} else - {idx = curIdx, left = left, right = hd :: newString :: tail} + { idx = curIdx + , left = left + , right = hd :: (joinStartOfRight (newString, tail)) + } else let val length = idx - curIdx @@ -166,7 +188,7 @@ struct else if isLessThanTarget (sub1, newString) then { idx = curIdx + String.size sub1 + String.size newString , left = (sub1 ^ newString) :: left - , right = sub2 :: tail + , right = joinStartOfRight (sub2, tail) } else if isLessThanTarget (newString, sub2) then { idx = curIdx + String.size sub1 @@ -176,7 +198,7 @@ struct else { idx = curIdx + String.size sub1 + String.size newString , left = newString :: sub1 :: left - , right = sub2 :: tail + , right = joinStartOfRight (sub2, tail) } end end diff --git a/utils.sml b/utils.sml index 759a566..dac78e2 100644 --- a/utils.sml +++ b/utils.sml @@ -1,20 +1,3 @@ -fun timeFun title f = - let - val title = String.concat ["Starting ", title, "..."] - val _ = (print title) - val startTime = Time.now () - val startTime = Time.toNanoseconds startTime - val x = f () - val endTime = Time.now () - val endTime = Time.toNanoseconds endTime - val timeDiff = endTime - startTime - val timeDiff = LargeInt.toString timeDiff - val timeTook = String.concat ["took ", timeDiff, " nanoseconds\n"] - val _ = (print timeTook) - in - x - end - fun runTxns arr = Vector.foldl (fn ((pos, delNum, insStr), rope) => @@ -26,6 +9,23 @@ fun runTxns arr = rope end) GapBuffer.empty arr +fun runTxnsTime arr = + let + val startTime = Time.now () + val startTime = Time.toMilliseconds startTime + + val x = runTxns arr + + val endTime = Time.now () + val endTime = Time.toMilliseconds endTime + val timeDiff = endTime - startTime + val timeDiff = LargeInt.toString timeDiff + val timeTook = String.concat ["took ", timeDiff, " ms\n"] + val _ = (print timeTook) + in + x + end + fun compareTxns arr = Vector.foldli (fn (idx, (pos, delNum, insStr), (rope, gapBuffer)) => let @@ -55,43 +55,8 @@ fun compareTxns arr = end ) (TinyRope.empty, GapBuffer.empty) arr -fun runTxnsTime title arr = - let val f = (fn () => runTxns arr) - in timeFun title f - end - fun runToString rope = GapBuffer.toString rope -fun runToStringTime title rope = - let val f = (fn () => runToString rope) - in timeFun title f - end - -fun runTxns1000Times (counter, arr, total) = - if counter = 1000 then - let - val divisor = Int.toLarge 1000 - val total = total div divisor - val str = LargeInt.toString total - in - print (str ^ "\n") - end - else - let - val startTime = Time.now () - val startTime = Time.toNanoseconds startTime - - val _ = runTxns arr - - val endTime = Time.now () - val endTime = Time.toNanoseconds endTime - val timeDiff = endTime - startTime - val counter = counter + 1 - val total = timeDiff + total - in - runTxns1000Times (counter, arr, total) - end - fun writeFile filename acc = let val str = String.concatWith "," acc @@ -117,20 +82,24 @@ fun loop () = loop() fun main () = let (* Timing benchmarks. *) - val _ = compareTxns SvelteComponent.txns + val _ = runTxnsTime SvelteComponent.txns + val _ = runTxnsTime rust_arr + val _ = runTxnsTime seph_arr + val _ = runTxnsTime automerge_arr - val startTime = LargeInt.fromInt 0 - val _ = runTxns1000Times (999, SvelteComponent.txns, startTime) - val _ = runTxns1000Times (999, rust_arr, startTime) - val _ = runTxns1000Times (999, seph_arr, startTime) - val _ = runTxns1000Times (999, automerge_arr, startTime) - - (* Tests that line metadata is correct; will fail if incorrect. *) + (* Tests for correctness; will fail if incorrect. *) val svelte = runTxns SvelteComponent.txns val rust = runTxns rust_arr val seph = runTxns seph_arr val automerge = runTxns automerge_arr + (* Tests for insertion correctness (compare against rope). *) + val _ = compareTxns SvelteComponent.txns + val _ = compareTxns rust_arr + val _ = compareTxns seph_arr + val _ = compareTxns automerge_arr + + (* Tests for line metadata. *) (* val _ = Rope.verifyLines svelte val _ = Rope.verifyLines rust