diff --git a/.DS_Store b/.DS_Store index 6308708..13aadb5 100644 Binary files a/.DS_Store and b/.DS_Store differ diff --git a/.rope.sml.swp b/.rope.sml.swp new file mode 100644 index 0000000..38d7d93 Binary files /dev/null and b/.rope.sml.swp differ diff --git a/mlton b/mlton new file mode 160000 index 0000000..fd6d870 --- /dev/null +++ b/mlton @@ -0,0 +1 @@ +Subproject commit fd6d8705780ea6e223c91a6b33553be1633c4eab diff --git a/rope.sml b/rope.sml index 218c9eb..fec75bb 100644 --- a/rope.sml +++ b/rope.sml @@ -1,398 +1,277 @@ -datatype rope = - N0 of string - | N1 of rope - | N2 of rope * int * int * rope - | L2 of string * string - | N3 of rope * rope * rope +signature ROPE = sig + type t + val empty : t + val of_string : string -> t + val size : t -> int + val insert : int * string * t -> t +end -val target_length = 1024 -val empty = N0 "" -fun of_string string = N0 string +structure Rope :> ROPE = struct + datatype t + = N0 of string + | N1 of t + | N2 of t * int * t + | L2 of string * string + | N3 of t * t * t -exception Size -exception Ins -exception Substring -exception Delete -exception To_string + datatype balance + = AddedNode + | DeletedNode + | NoAction -fun size rope = - case rope of - N0 s => String.size s - | N1 t => size t - | N2(_, lm, rm, _) => lm + rm - | N3(t1, t2, t3) => - let - val t1_size = size t1 - val t2_size = size t2 - val t3_size = size t3 - in - t1_size + t2_size + t3_size - end - | _ => raise Size + val target_length = 1024 + val empty = N0 "" + fun of_string string = N0 string -fun root rope = - case rope of - L2(s1, s2) => N2(N0 s1, String.size s1, String.size s2, N0 s2) - | N3(t1, t2, t3) => - let - val t1_size = size t1 - val t2_size = size t2 - val left = N2(t1, t1_size, t2_size, t2) - val left_size = t1_size + t2_size - val t3_size = size t3 - in - N2(left, left_size, t3_size, N1 t3) - end - | t => t + fun is_less_than_target(str1, str2) = + String.size str1 + String.size str2 <= target_length -fun n1 rope = - case rope of - L2(s1, s2) => - N2(N0 s1, String.size s1, String.size s2, N0 s2) - | N3(t1, t2, t3) => - let - val t1_size = size t1 - val t2_size = size t2 - val left = N2(t1, t1_size, t2_size, t2) - val left_size = t1_size + t2_size - val t3_size = size t3 - in - N2(left, left_size, t3_size, N1 t3) - end - | t => N1 t + exception AuxConstructor -fun ins_n2_left left right = - case (left, right) of - (L2(s1, s2), t3) => N3(N0 s1, N0 s2, t3) - | (N3(t1, t2, t3), N1 t4) => - let - val t1_size = size t1 - val t2_size = size t2 - val left = N2(t1, t1_size, t2_size, t2) - val left_size = t1_size + t2_size - val t3_size = size t3 - val t4_size = size t4 - val right = N2(t3, t3_size, t4_size, t4) - val right_size = t3_size + t4_size - in - N2(left, left_size, right_size, right) - end - | (N3(t1, t2, t3), t4 as N2 _) => - let - val left = N2(t1, size t1, size t2, t2) - in - N3(left, N1 t3, t4) - end - | (N3(t1, t2, t3), t4) => - let - val t1_size = size t1 - val t2_size = size t2 - val left = N2(t1, t1_size, t2_size, t2) - val t3_size = size t3 - val t4_size = size t4 - val right = N2(t3, t3_size, t4_size, t4) - val left_size = t1_size + t2_size - val right_size = t3_size + t4_size - in - N2(left, left_size, right_size, right) - end - | (l, r) => - N2(l, size l, size r, r) + local + fun help_size(acc, rope) = + case rope of + N0 s => + acc + String.size s + | N1 t => + help_size(acc, t) + | N2(_, lm, r) => + help_size(acc + lm, r) + | _ => raise AuxConstructor + in + fun size rope = help_size(0, rope) + end -fun ins_n2_right left right = - case (left, right) of - (t1, L2(s1, s2)) => N3(t1, N0 s1, N0 s2) - | (N1 t1, N3(t2, t3, t4)) => - let - val t1_size = size t1 - val t2_size = size t2 - val left = N2(t1, t1_size, t2_size, t2) - val t3_size = size t3 - val t4_size = size t4 - val right = N2(t3, t3_size, t4_size, t4) - val right_size = t3_size + t4_size - val left_size = t1_size + t2_size - in - N2(left, left_size, right_size, right) - end - | (t1 as N2 _, N3(t2, t3, t4)) => - let - val right = N2(t3, size t3, size t4, t4) - in - N3(t1, N1 t2, right) - end - | (t1, N3(t2, t3, t4)) => - let - val t1_size = size t1 - val t2_size = size t2 - val left = N2(t1, t1_size, t2_size, t2) - val t3_size = size t3 - val t4_size = size t4 - val right = N2(t3, t3_size, t4_size, t4) - val right_size = t3_size + t4_size - val left_size = t1_size + t2_size - in - N2(left, left_size, right_size, right) - end - | (l, r) => - N2(l, size l, size r, r) - -fun ins cur_index string rope = - case rope of - N0 str => - let - val old_str_size = String.size str - val ins_str_size = String.size string - val total_str_size = old_str_size + ins_str_size - val left_size = ins_str_size + cur_index - val prefer_left = left_size <= target_length - val right_size = ins_str_size - cur_index - val prefer_right = right_size <= target_length - in - if cur_index <= 0 then - if total_str_size <= target_length then - N0(string ^ str) - else L2(string, str) - else if cur_index >= old_str_size then - if total_str_size <= target_length then - N0(str ^ string) - else L2(str, string) - else if total_str_size <= target_length then - let - val str1 = String.substring(str, 0, cur_index) - val str3 = String.substring(str, cur_index, old_str_size - cur_index) - in - N0(String.concat [str1, string, str3]) - end - else - let - val str1 = String.substring(str, 0, cur_index) - val str3 = String.substring(str, cur_index, old_str_size - cur_index) - in - if prefer_left then - L2(str1 ^ string, str3) - else if prefer_right then - L2(str1, string ^ str3) - else - N3(N0 str1, N0 string, N0 str3) - end - end - | N1 t => + fun ins_root rope = + case rope of + L2(s1, s2) => + N2(N0 s1, String.size s1, N0 s2) + | N3(t1, t2, t3) => let - val t = ins cur_index string t + val t1_size = size t1 + val left = N2(t1, t1_size, t2) + val t2_size = size t2 in - n1 t + N2(left, t1_size + t2_size, N1 t3) end - | N2(l, lm, _, r) => - if cur_index < lm then - let - val l = ins cur_index string l - in - ins_n2_left l r - end - else + | t => + N1 t + + fun del_root rope = + case rope of + N1 t => t + | t => t + + fun ins_n1 rope = + case rope of + L2 (s1, s2) => + N2(N0 s1, String.size s1, N0 s2) + | N3(t1, t2, t3) => let - val next_index = cur_index - lm - val r = ins next_index string r + val left = N2(t1, size t1, t2) in - ins_n2_right l r + N2(left, size left, t2) end - | _ => raise Ins + | t => + N1 t -fun insert index string rope = - let - val rope = ins index string rope - in - root rope - end + fun ins_n2_left (left, right) = + case (left, right) of + (L2(s1, s2), t3) => + N3(N0 s1, N0 s2, t3) + | (N3(t1, t2, t3), N1 t4) => + let + val t1_size = size t1 + val left = N2(t1, t1_size, t2) + val t3_size = size t3 + val right = N2(t3, t3_size, t4) + val t2_size = size t2 + val left_size = t1_size + t2_size + in + N2(left, left_size, right) + end + | (N3(t1, t2, t3), t4) => + let + val left = N2(t1, size t2, t2) + in + N3(left, N1 t3, t4) + end + | (l, r) => + N2(l, size l, r) -fun sub start_idx end_idx acc rope = - case rope of - N0 str => - let - val str_size = String.size str - val before_start = start_idx <= 0 - val after_start = start_idx >= 0 - val after_end = end_idx >= str_size - val before_end = end_idx <= str_size - in - if before_start andalso after_end then - (str :: acc) - else if after_start andalso before_start then - let - val len = end_idx - start_idx - val str = String.substring(str, start_idx, len) - in - (str :: acc) - end - else if after_start andalso after_end then - let - val len = str_size - start_idx - val str = String.substring(str, start_idx, len) - in - (str :: acc) - end + fun del_n2_left(left, right) = + case (left, right) of + (N1 t1, N1 t2) => + N1(N2(t1, size t1, t2)) + | (N1 (N1 t1), N2(N1 t2, _, (t3 as N2 _))) => + let + val left = N2(t1, size t1, t2) + val inner = N2(left, size left, t3) + in + N1 (inner) + end + | (N1 (N1 t1), N2(N2(t2, _, t3), _, N1 t4)) => + let + val left = N2(t1, size t1, t2) + val right = N2(t3, size t3, t4) + val inner = N2(left, size left, right) + in + N1 inner + end + | (N1 (t1 as N1 _), N2((t2 as N2 _), _, (t3 as N2 _))) => + let + val left = N2(t1, size t1, t2) + val right = N1 t3 + in + N2(left, size left, right) + end + | (l, r) => + N2(l, size l, r) + + fun ins_n2_right (left, right) = + case (left, right) of + (t1, L2(s1, s2)) => + N3(t1, N0 s1, N0 s2) + | (N1 t1, N3(t2, t3, t4)) => + let + val left = N2(t1, size t1, t2) + val t3_size = size t3 + val right = N2(t3, t3_size, t4) + in + N2(left, size left, right) + end + | (t1, N3(t2, t3, t4)) => + let + val right = N2(t3, size t3, t4) + in + N3(t1, N1 t2, right) + end + | (l, r) => + N2(l, size l, r) + + fun del_n2_right(left, right) = + case (left, right) of + (N2(N1 t1, _, N2(t2, _, t3)), N1 t4) => + let + val left = N2(t1, size t1, t2) + val right = N2(t3, size t3, t4) + val inner = N2(left, size left, right) + in + N1 inner + end + | (N2((t1 as N2 _), lm, N1 t2), N1 (N1 t3)) => + let + val right = N2(t2, size t2, t3) + val inner = N2(t1, lm, right) + in + N1 inner + end + | (N2( (t1 as N2 _), _, (t2 as N2 _)), N1 (N1 t3)) => + let + val left_size = size t1 + val left = N1 t1 + val right = N2(t2, size t2, t3) + in + N2(left, left_size, right) + end + | (l, r) => + N2(l, size l, r) + + local + fun ins_leaf(cur_index, new_str, rope, old_str) = + if cur_index <= 0 then + if is_less_than_target(old_str, new_str) then + (N0(new_str ^ old_str), NoAction) else - let - val str = String.substring(str, 0, end_idx) - in - (str::acc) - end - end - | N1 t => - sub start_idx end_idx acc t - | N2(l, lm, _, r) => + (L2(new_str, old_str), AddedNode) + else if cur_index >= String.size old_str then + if is_less_than_target(old_str, new_str) then + (N0(old_str ^ new_str), NoAction) + else + (L2(old_str, new_str), AddedNode) + else + (* Need to split in middle of string. *) let - val starts_before = lm > start_idx - val ends_before = lm > end_idx - val starts_after = lm < start_idx - val ends_after = lm < end_idx + val sub1 = String.substring(old_str, 0, cur_index) + val sub2_len = String.size old_str - cur_index + val sub2 = String.substring(old_str, cur_index, sub2_len) in - if starts_before andalso ends_before then - sub start_idx end_idx acc l - else if starts_after andalso ends_after then - let - val next_start = start_idx - lm - val next_end = end_idx - lm - in - sub next_start next_end acc r - end + if is_less_than_target(old_str, new_str) then + (N0(sub1 ^ new_str ^ sub2), NoAction) + else if cur_index + String.size new_str <= target_length then + (L2(sub1 ^ new_str, sub2), AddedNode) + else if (String.size old_str - cur_index) + String.size new_str <= target_length then + (L2(sub1, new_str ^ sub2), AddedNode) else - let - val next_start = start_idx - lm - val next_end = end_idx - lm - val sub_acc = sub next_start next_end acc r - in - sub start_idx end_idx sub_acc l - end + (N3(N0 sub1, N0 new_str, N0 sub2), AddedNode) end - | _ => raise Substring - -fun substring start length rope = - let - val finish = start + length - val lst = sub start finish [] rope - in - String.concat lst - end - -fun del start_idx end_idx rope = - case rope of - N0 str => - let - val str_size = String.size str - val before_start = start_idx <= 0 - val after_end = end_idx >= str_size - val after_start = start_idx >= 0 - val before_end = end_idx <= str_size - in - if before_start andalso after_end then - (N0 "", false) - else if after_start andalso before_end then - let - val str1 = String.substring(str, 0, start_idx) - val del_len = str_size - end_idx - val str2 = String.substring(str, start_idx, del_len) - val new_len = str_size - del_len - in - if new_len <= target_length then - (N0(str1 ^ str2), false) - else - (L2(str1, str2), true) - end - else if after_start andalso after_end then - let - val str = String.substring(str, 0, start_idx) - in - (N0 str, false) - end - else + fun ins(cur_index, new_str, rope) = + case rope of + N2(l, lm, r) => + if cur_index < lm then let - val len = str_size - end_idx - val str = String.substring(str, end_idx, len) + val (l, action) = ins(cur_index, new_str, l) in - (N0 str, false) - end - end - | N1 t => - let - val (t, did_ins) = del start_idx end_idx t - val t = if did_ins then n1 t else t - in - (t, did_ins) - end - | N2(l, lm, rm, r) => - let - val start_is_less = lm > start_idx - val end_is_less = lm > end_idx - val start_is_more = lm < start_idx - val end_is_more = lm < end_idx - in - if start_is_less andalso end_is_less then - let - val (l, did_ins) = del start_idx end_idx l - in - case did_ins of - false => - (N2(l, size l, rm, r), false) - | true => - (ins_n2_left l r, true) - end - else if start_is_more andalso end_is_more then - let - val next_start = start_idx - lm - val next_end = end_idx - lm - val (r, did_ins) = del next_start next_end r - in - case did_ins of - false => - (N2(l, lm, size r, r), false) - | true => - (ins_n2_right l r, true) + (case action of + NoAction => + (case (l, r) of + (N0 s1, N0 s2) => + if is_less_than_target(s1, s2) then + (N0 (s1 ^ s2), DeletedNode) + else + (N2(l, lm + String.size new_str, r), action) + | _ => + (N2(l, lm + String.size new_str, r), action)) + | AddedNode => + (ins_n2_left(l, r), action) + | DeletedNode => + (del_n2_left(l, r), action)) end else let - val (l, did_ins_l) = del start_idx end_idx l - val r_start = start_idx - lm - val r_end = end_idx - lm - val (r, did_ins_r) = del r_start r_end r + val (r, action) = ins(cur_index - lm, new_str, r) in - if did_ins_l then - (ins_n2_left l r, true) - else if did_ins_r then - (ins_n2_right l r, true) - else - (N2(l, size l, size r, r), false) + (case action of + NoAction => + (case (l, r) of + (N0 s1, N0 s2) => + if is_less_than_target(s1, s2) then + (N0 (s1 ^ s2), DeletedNode) + else + (N2(l, lm, r), action) + | _ => + (N2(l, lm, r), action)) + | AddedNode => + (ins_n2_right(l, r), action) + | DeletedNode => + (del_n2_right(l, r), action)) end - end - | _ => raise Delete - -fun delete start length rope = - let - val finish = start + length - val (t, did_ins) = del start finish rope - val t = if did_ins then root rope else rope - in - t - end - -fun to_str acc rope = - case rope of - N0 str => - (str::acc) - | N1 t => - to_str acc t - | N2 (l, _, _, r) => + | N1 t => + let + val (t, action) = ins(cur_index, new_str, t) + in + (case action of + AddedNode => + (ins_n1 t, action) + | _ => + (N1 t, action)) + end + | N0 old_str => + ins_leaf(cur_index, new_str, rope, old_str) + | _ => + raise AuxConstructor + in + fun insert (index, str, rope) = let - val acc = to_str acc r + val (rope, action) = ins(index, str, rope) in - to_str acc l + (case action of + NoAction => + rope + | AddedNode => + ins_root rope + | DeletedNode => + del_root rope) end - | _ => raise To_string - -fun to_string rope = - let - val lst = to_str [] rope - in - String.concat lst end +end diff --git a/utils.sml b/utils.sml index 726869b..a1cf381 100644 --- a/utils.sml +++ b/utils.sml @@ -50,9 +50,15 @@ fun run_to_string_time title rope = time_func title f end -fun run_txns_1000_times counter arr acc = +fun run_txns_1000_times (counter, arr, total) = if counter = 1000 then - acc + let + val divisor = Int.toLarge 1000 + val total = total div divisor + val str = LargeInt.toString total + in + print str + end else let val start_time = Time.now() @@ -63,9 +69,10 @@ fun run_txns_1000_times counter arr acc = val end_time = Time.now() val end_time = Time.toMilliseconds end_time val time_diff = end_time - start_time - val time_diff = LargeInt.toString time_diff + val counter = counter + 1 + val total = time_diff + total in - run_txns_1000_times (counter + 1) arr (time_diff::acc) + run_txns_1000_times (counter, arr, total) end fun write_file filename acc = @@ -80,17 +87,14 @@ fun write_file filename acc = val _ = let - val svelte = run_txns_1000_times 0 svelte_arr [] - val _ = write_file "svelte_edit_traces.csv" svelte + val start_time = LargeInt.fromInt 0 + val svelte = run_txns_1000_times (999 ,svelte_arr, start_time ) - val rust = run_txns_1000_times 0 rust_arr [] - val _ = write_file "rust_edit_traces.csv" rust + val rust = run_txns_1000_times (999 ,rust_arr, start_time ) - val seph = run_txns_1000_times 0 seph_arr [] - val _ = write_file "seph_edit_traces.csv" seph + val seph = run_txns_1000_times (999, seph_arr, start_time) - val automerge = run_txns_1000_times 0 automerge_arr [] - val _ = write_file "automerge_edit_traces.csv" automerge + val automerge = run_txns_1000_times (999, automerge_arr , start_time) in () end