diff --git a/fcore/bin-search.sml b/fcore/bin-search.sml index 6c878c3..3b576e7 100644 --- a/fcore/bin-search.sml +++ b/fcore/bin-search.sml @@ -69,4 +69,28 @@ struct fun equalOrMore (findNum, vec) = helpBinSearch (findNum, vec, 0, Vector.length vec - 1) end + + local + fun helpExists (findNum, vec, low, high) = + let + val mid = low + ((high - low) div 2) + in + if high >= low then + let + val midVal = Vector.sub (vec, mid) + in + if midVal = findNum then + true + else if midVal < findNum then + helpExists (findNum, vec, mid + 1, high) + else + helpExists (findNum, vec, low, mid - 1) + end + else + false + end + in + fun exists (findNum, vec) = + helpExists (findNum, vec, 0, Vector.length vec - 1) + end end diff --git a/fcore/build-search-list.sml b/fcore/build-search-list.sml index 7debfd1..fef9892 100644 --- a/fcore/build-search-list.sml +++ b/fcore/build-search-list.sml @@ -5,8 +5,7 @@ struct case tl of tlhd :: tltl => helpNextMatch (0, tlhd, tltl, absIdx, searchString, matchedChrs) - | [] => - NONE + | [] => NONE else let val hdChr = String.sub (hd, idx) @@ -14,13 +13,11 @@ struct in if hdChr = searchChr then if matchedChrs + 1 = String.size searchString then - let - val matchedIdx = absIdx - String.size searchString + 1 - in - SOME matchedIdx + let val matchedIdx = absIdx - String.size searchString + 1 + in SOME matchedIdx end else - helpNextMatch + helpNextMatch (idx + 1, hd, tl, absIdx + 1, searchString, matchedChrs + 1) else helpNextMatch (idx + 1, hd, tl, absIdx + 1, searchString, 0) @@ -29,20 +26,18 @@ struct fun nextMatch (bufferIdx, absIdx, rightStrings, searchString) = case rightStrings of hd :: tl => - let + let val strIdx = absIdx - bufferIdx in if strIdx < String.size hd then helpNextMatch (strIdx, hd, tl, absIdx, searchString, 0) else (case tl of - tlhd :: tltl => - let - val strIdx = strIdx - String.size hd - in - helpNextMatch (strIdx, tlhd, tltl, absIdx, searchString, 0) - end - | [] => NONE) + tlhd :: tltl => + let val strIdx = strIdx - String.size hd + in helpNextMatch (strIdx, tlhd, tltl, absIdx, searchString, 0) + end + | [] => NONE) end | [] => NONE @@ -53,12 +48,26 @@ struct in case nextMatch (bufferIdx, absIdx, rightStrings, searchString) of SOME matchedIdx => - let - val searchList = SearchList.append (matchedIdx, searchList) - in + (* Edge case: we may be searching for a string like "a" + * when the buffer represents "aaa aaa aaa". + * In this case, there will be continual matches that are consecutive + * and we need to check every char in the buffer which is absIdx + 1. + * However, we can skip to matchedIdx + 1 if matchedIdx already exists + * in the searchList because we know the string between + * [absIdx ... matchedIdx - 1] contains no matches. + * This check is important to preserve the set-like semaantics + * of the searchList too: SearchList.append does not check for this. + * *) + if SearchList.exists (matchedIdx, searchList) then helpFromStart (app, origIdx, matchedIdx + 1, buffer, searchString, searchList) - end + else + let + val searchList = SearchList.append (matchedIdx, searchList) + in + helpFromStart + (app, origIdx, absIdx + 1, buffer, searchString, searchList) + end | NONE => let val buffer = LineGap.goToIdx (origIdx, buffer) @@ -80,38 +89,46 @@ struct app (* searches for matchedIdx within a range from the buffer instead of from start *) - fun helpFromRange + fun helpFromRange (origIdx, curIdx, finishIdx, buffer, searchString, searchList) = - let - val buffer = LineGap.goToIdx (curIdx, buffer) - val {idx = bufferIdx, rightStrings, ...} = buffer - in - case nextMatch (bufferIdx, curIdx, rightStrings, searchString) of - SOME matchedIdx => - if matchedIdx > finishIdx then - let - val buffer = LineGap.goToIdx (origIdx, buffer) - val searchList = SearchList.goToNum (origIdx, searchList) - in - (buffer, searchList) - end - else - let - val searchList = SearchList.insert (matchedIdx, searchList) - in - helpFromRange - ( origIdx, matchedIdx + 1, finishIdx - , buffer, searchString, searchList - ) - end - | NONE => + let + val buffer = LineGap.goToIdx (curIdx, buffer) + val {idx = bufferIdx, rightStrings, ...} = buffer + in + case nextMatch (bufferIdx, curIdx, rightStrings, searchString) of + SOME matchedIdx => + if matchedIdx > finishIdx then let val buffer = LineGap.goToIdx (origIdx, buffer) val searchList = SearchList.goToNum (origIdx, searchList) in (buffer, searchList) end - end + else + let + val searchList = + if SearchList.exists (matchedIdx, searchList) then + searchList + else + SearchList.insert (matchedIdx, searchList) + in + helpFromRange + ( origIdx + , curIdx + 1 + , finishIdx + , buffer + , searchString + , searchList + ) + end + | NONE => + let + val buffer = LineGap.goToIdx (origIdx, buffer) + val searchList = SearchList.goToNum (origIdx, searchList) + in + (buffer, searchList) + end + end fun fromRange (startIdx, length, buffer, searchString, searchList) = let diff --git a/fcore/search-list.sml b/fcore/search-list.sml index 420ba3c..8111ccb 100644 --- a/fcore/search-list.sml +++ b/fcore/search-list.sml @@ -3,6 +3,7 @@ sig type t = {left: int vector list, right: int vector list} val empty: t + val exists: int * t -> bool val insert: int * t -> t val append: int * t -> t val delete: int * int * string * t -> t @@ -587,4 +588,38 @@ struct in moveRightAndMap (num, mapBy, left, right) end + + fun helpExistsRight (num, right) = + case right of + hd :: tl => + let + val rlast = Vector.sub (hd, Vector.length hd - 1) + in + if num > rlast then helpExistsRight (num, tl) + else BinSearch.exists (num, hd) + end + | [] => false + + fun helpExistsLeft (num, left) = + case left of + hd :: tl => + let + val lfirst = Vector.sub (hd, 0) + in + if num < lfirst then helpExistsLeft (num, tl) + else BinSearch.exists (num, hd) + end + | [] => false + + fun exists (num, {left, right}) = + case right of + rhd :: rtl => + let + val rfirst = Vector.sub (rhd, 0) + in + if num = rfirst then true + else if num > rfirst then helpExistsRight (num, right) + else helpExistsLeft (num, left) + end + | [] => helpExistsLeft (num, left) end