diff --git a/src/fsharp/FSharp.Core/FSharp.Core.fsproj b/src/fsharp/FSharp.Core/FSharp.Core.fsproj index f7267fe0d84..305b1f30da2 100644 --- a/src/fsharp/FSharp.Core/FSharp.Core.fsproj +++ b/src/fsharp/FSharp.Core/FSharp.Core.fsproj @@ -108,6 +108,9 @@ Collections/array3.fs + + Collections/mapsetcmp.fs + Collections/map.fsi diff --git a/src/fsharp/FSharp.Core/map.fs b/src/fsharp/FSharp.Core/map.fs index c0f28b9715a..1345dcee9b8 100644 --- a/src/fsharp/FSharp.Core/map.fs +++ b/src/fsharp/FSharp.Core/map.fs @@ -1,45 +1,51 @@ // Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information. - namespace Microsoft.FSharp.Collections open System open System.Collections.Generic open System.Diagnostics +open System.Runtime.CompilerServices open System.Text open Microsoft.FSharp.Core open Microsoft.FSharp.Core.LanguagePrimitives.IntrinsicOperators [] [] -type internal MapTree<'Key, 'Value>(k: 'Key, v: 'Value) = +type internal MapTree<'Key, 'Value>(k: 'Key, v: 'Value, h: int) = + member _.Height = h member _.Key = k member _.Value = v - + new(k: 'Key, v: 'Value) = MapTree(k,v,1) + [] [] [] type internal MapTreeNode<'Key, 'Value>(k:'Key, v:'Value, left:MapTree<'Key, 'Value>, right: MapTree<'Key, 'Value>, h: int) = - inherit MapTree<'Key,'Value>(k, v) - + inherit MapTree<'Key,'Value>(k, v, h) member _.Left = left member _.Right = right - member _.Height = h - + [] module MapTree = + open MapSetDefaultComparison let empty = null - + let inline isEmpty (m:MapTree<'Key, 'Value>) = isNull m + let inline private asNode(value:MapTree<'Key,'Value>) : MapTreeNode<'Key,'Value> = + value :?> MapTreeNode<'Key,'Value> + let rec sizeAux acc (m:MapTree<'Key, 'Value>) = if isEmpty m then acc else - match m with - | :? MapTreeNode<'Key, 'Value> as mn -> sizeAux (sizeAux (acc+1) mn.Left) mn.Right - | _ -> acc + 1 - + if m.Height = 1 then + acc + 1 + else + let mn = asNode m + sizeAux (sizeAux (acc+1) mn.Left) mn.Right + let size x = sizeAux 0 x #if TRACE_SETS_AND_MAPS @@ -66,11 +72,11 @@ module MapTree = (totalSizeOnMapLookup / float numLookups)) System.Console.WriteLine("#largestMapSize = {0}, largestMapStackTrace = {1}", largestMapSize, largestMapStackTrace) - let MapTree n = + let MapTree (k,v) = report() numOnes <- numOnes + 1 totalSizeOnNodeCreation <- totalSizeOnNodeCreation + 1.0 - MapTree n + MapTree (k,v) let MapTreeNode (x, l, v, r, h) = report() @@ -82,10 +88,7 @@ module MapTree = let inline height (m: MapTree<'Key, 'Value>) = if isEmpty m then 0 - else - match m with - | :? MapTreeNode<'Key, 'Value> as mn -> mn.Height - | _ -> 1 + else m.Height [] let tolerance = 2 @@ -98,9 +101,6 @@ module MapTree = MapTree(k,v) else MapTreeNode(k,v,l,r,m+1) :> MapTree<'Key, 'Value> // new map is higher by 1 than the highest - - let inline private asNode(value:MapTree<'Key,'Value>) : MapTreeNode<'Key,'Value> = - value :?> MapTreeNode<'Key,'Value> let rebalance t1 (k: 'Key) (v: 'Value) t2 : MapTree<'Key, 'Value> = let t1h = height t1 @@ -125,127 +125,120 @@ module MapTree = mk t1'.Left t1'.Key t1'.Value (mk t1'.Right k v t2) else mk t1 k v t2 - let rec add (comparer: IComparer<'Key>) k (v: 'Value) (m: MapTree<'Key, 'Value>) : MapTree<'Key, 'Value> = + let rec add k (v: 'Value) (m: MapTree<'Key, 'Value>) : MapTree<'Key, 'Value> = if isEmpty m then MapTree(k,v) else - let c = comparer.Compare(k,m.Key) - match m with - | :? MapTreeNode<'Key, 'Value> as mn -> - if c < 0 then rebalance (add comparer k v mn.Left) mn.Key mn.Value mn.Right - elif c = 0 then MapTreeNode(k,v,mn.Left,mn.Right,mn.Height) :> MapTree<'Key, 'Value> - else rebalance mn.Left mn.Key mn.Value (add comparer k v mn.Right) - | _ -> + let c = cmp k m.Key + if m.Height = 1 then if c < 0 then MapTreeNode (k,v,empty,m,2) :> MapTree<'Key, 'Value> elif c = 0 then MapTree(k,v) else MapTreeNode (k,v,m,empty,2) :> MapTree<'Key, 'Value> - - let rec tryGetValue (comparer: IComparer<'Key>) k (v: byref<'Value>) (m: MapTree<'Key, 'Value>) = + else + let mn = asNode m + if c < 0 then rebalance (add k v mn.Left) mn.Key mn.Value mn.Right + elif c = 0 then MapTreeNode(k,v,mn.Left,mn.Right,mn.Height) :> MapTree<'Key, 'Value> + else rebalance mn.Left mn.Key mn.Value (add k v mn.Right) + + let rec tryGetValue k (v: byref<'Value>) (m: MapTree<'Key, 'Value>) = if isEmpty m then false else - let c = comparer.Compare(k, m.Key) + let c = cmp k m.Key if c = 0 then v <- m.Value; true else - match m with - | :? MapTreeNode<'Key, 'Value> as mn -> - tryGetValue comparer k &v (if c < 0 then mn.Left else mn.Right) - | _ -> false - - let find (comparer: IComparer<'Key>) k (m: MapTree<'Key, 'Value>) = + if m.Height = 1 then false + else + let mn = asNode m + tryGetValue k &v (if c < 0 then mn.Left else mn.Right) + + [] + let throwKeyNotFound() = raise (KeyNotFoundException()) + + [] + let find k (m: MapTree<'Key, 'Value>) = let mutable v = Unchecked.defaultof<'Value> - if tryGetValue comparer k &v m then + if tryGetValue k &v m then v else - raise (KeyNotFoundException()) + throwKeyNotFound() - let tryFind (comparer: IComparer<'Key>) k (m: MapTree<'Key, 'Value>) = + let tryFind k (m: MapTree<'Key, 'Value>) = let mutable v = Unchecked.defaultof<'Value> - if tryGetValue comparer k &v m then + if tryGetValue k &v m then Some v else None - let partition1 (comparer: IComparer<'Key>) (f: OptimizedClosures.FSharpFunc<_, _, _>) k v (acc1, acc2) = - if f.Invoke (k, v) then (add comparer k v acc1, acc2) else (acc1, add comparer k v acc2) + let partition1 (f: OptimizedClosures.FSharpFunc<_, _, _>) k v (acc1, acc2) = + if f.Invoke (k, v) then (add k v acc1, acc2) else (acc1, add k v acc2) - let rec partitionAux (comparer: IComparer<'Key>) (f: OptimizedClosures.FSharpFunc<_, _, _>) (m: MapTree<'Key, 'Value>) acc = + let rec partitionAux (f: OptimizedClosures.FSharpFunc<_, _, _>) (m: MapTree<'Key, 'Value>) acc = if isEmpty m then acc else - match m with - | :? MapTreeNode<'Key, 'Value> as mn -> - let acc = partitionAux comparer f mn.Right acc - let acc = partition1 comparer f mn.Key mn.Value acc - partitionAux comparer f mn.Left acc - | _ -> partition1 comparer f m.Key m.Value acc - - let partition (comparer: IComparer<'Key>) f m = - partitionAux comparer (OptimizedClosures.FSharpFunc<_, _, _>.Adapt f) m (empty, empty) + if m.Height = 1 then + partition1 f m.Key m.Value acc + else + let mn = asNode m + let acc = partitionAux f mn.Right acc + let acc = partition1 f mn.Key mn.Value acc + partitionAux f mn.Left acc + + let partition f m = + partitionAux (OptimizedClosures.FSharpFunc<_, _, _>.Adapt f) m (empty, empty) - let filter1 (comparer: IComparer<'Key>) (f: OptimizedClosures.FSharpFunc<_, _, _>) k v acc = - if f.Invoke (k, v) then add comparer k v acc else acc + let filter1 (f: OptimizedClosures.FSharpFunc<_, _, _>) k v acc = + if f.Invoke (k, v) then add k v acc else acc - let rec filterAux (comparer: IComparer<'Key>) (f: OptimizedClosures.FSharpFunc<_, _, _>) (m: MapTree<'Key, 'Value>) acc = + let rec filterAux (f: OptimizedClosures.FSharpFunc<_, _, _>) (m: MapTree<'Key, 'Value>) acc = if isEmpty m then acc else - match m with - | :? MapTreeNode<'Key, 'Value> as mn -> - let acc = filterAux comparer f mn.Left acc - let acc = filter1 comparer f mn.Key mn.Value acc - filterAux comparer f mn.Right acc - | _ -> filter1 comparer f m.Key m.Value acc + if m.Height = 1 then + filter1 f m.Key m.Value acc + else + let mn = asNode m + let acc = filterAux f mn.Left acc + let acc = filter1 f mn.Key mn.Value acc + filterAux f mn.Right acc + - let filter (comparer: IComparer<'Key>) f m = - filterAux comparer (OptimizedClosures.FSharpFunc<_, _, _>.Adapt f) m empty + let filter f m = + filterAux (OptimizedClosures.FSharpFunc<_, _, _>.Adapt f) m empty let rec spliceOutSuccessor (m: MapTree<'Key, 'Value>) = if isEmpty m then failwith "internal error: Map.spliceOutSuccessor" else - match m with - | :? MapTreeNode<'Key, 'Value> as mn -> + if m.Height = 1 then + m.Key, m.Value, empty + else + let mn = asNode m if isEmpty mn.Left then mn.Key, mn.Value, mn.Right else let k3, v3, l' = spliceOutSuccessor mn.Left in k3, v3, mk l' mn.Key mn.Value mn.Right - | _ -> m.Key, m.Value, empty - let rec remove (comparer: IComparer<'Key>) k (m: MapTree<'Key, 'Value>) = + let rec remove k (m: MapTree<'Key, 'Value>) = if isEmpty m then empty else - let c = comparer.Compare(k, m.Key) - match m with - | :? MapTreeNode<'Key, 'Value> as mn -> - if c < 0 then rebalance (remove comparer k mn.Left) mn.Key mn.Value mn.Right + let c = cmp k m.Key + if m.Height = 1 then + if c = 0 then empty else m + else + let mn = asNode m + if c < 0 then rebalance (remove k mn.Left) mn.Key mn.Value mn.Right elif c = 0 then if isEmpty mn.Left then mn.Right elif isEmpty mn.Right then mn.Left else let sk, sv, r' = spliceOutSuccessor mn.Right mk mn.Left sk sv r' - else rebalance mn.Left mn.Key mn.Value (remove comparer k mn.Right) - | _ -> - if c = 0 then empty else m + else rebalance mn.Left mn.Key mn.Value (remove k mn.Right) + - let rec change (comparer: IComparer<'Key>) k (u: 'Value option -> 'Value option) (m: MapTree<'Key, 'Value>) : MapTree<'Key,'Value> = + let rec change k (u: 'Value option -> 'Value option) (m: MapTree<'Key, 'Value>) : MapTree<'Key,'Value> = if isEmpty m then match u None with | None -> m | Some v -> MapTree (k, v) else - match m with - | :? MapTreeNode<'Key, 'Value> as mn -> - let c = comparer.Compare(k, mn.Key) - if c < 0 then - rebalance (change comparer k u mn.Left) mn.Key mn.Value mn.Right - elif c = 0 then - match u (Some mn.Value) with - | None -> - if isEmpty mn.Left then mn.Right - elif isEmpty mn.Right then mn.Left - else - let sk, sv, r' = spliceOutSuccessor mn.Right - mk mn.Left sk sv r' - | Some v -> MapTreeNode (k, v, mn.Left, mn.Right, mn.Height) :> MapTree<'Key,'Value> - else - rebalance mn.Left mn.Key mn.Value (change comparer k u mn.Right) - | _ -> - let c = comparer.Compare(k, m.Key) + if m.Height = 1 then + let c = cmp k m.Key if c < 0 then match u None with | None -> m @@ -258,23 +251,44 @@ module MapTree = match u None with | None -> m | Some v -> MapTreeNode (k, v, m, empty, 2) :> MapTree<'Key,'Value> + else + let mn = asNode m + let c = cmp k mn.Key + if c < 0 then + rebalance (change k u mn.Left) mn.Key mn.Value mn.Right + elif c = 0 then + match u (Some mn.Value) with + | None -> + if isEmpty mn.Left then mn.Right + elif isEmpty mn.Right then mn.Left + else + let sk, sv, r' = spliceOutSuccessor mn.Right + mk mn.Left sk sv r' + | Some v -> MapTreeNode (k, v, mn.Left, mn.Right, mn.Height) :> MapTree<'Key,'Value> + else + rebalance mn.Left mn.Key mn.Value (change k u mn.Right) - let rec mem (comparer: IComparer<'Key>) k (m: MapTree<'Key, 'Value>) = + let rec mem k (m: MapTree<'Key, 'Value>) = if isEmpty m then false else - let c = comparer.Compare(k, m.Key) - match m with - | :? MapTreeNode<'Key, 'Value> as mn -> - if c < 0 then mem comparer k mn.Left - else (c = 0 || mem comparer k mn.Right) - | _ -> c = 0 + let c = cmp k m.Key + if m.Height = 1 then + c = 0 + else + let mn = asNode m + if c < 0 then mem k mn.Left + else (c = 0 || mem k mn.Right) + let rec iterOpt (f: OptimizedClosures.FSharpFunc<_, _, _>) (m: MapTree<'Key, 'Value>) = if isEmpty m then () else - match m with - | :? MapTreeNode<'Key, 'Value> as mn -> iterOpt f mn.Left; f.Invoke (mn.Key, mn.Value); iterOpt f mn.Right - | _ -> f.Invoke (m.Key, m.Value) + if m.Height = 1 then + f.Invoke (m.Key, m.Value) + else + let mn = asNode m + iterOpt f mn.Left; f.Invoke (mn.Key, mn.Value); iterOpt f mn.Right + let iter f m = iterOpt (OptimizedClosures.FSharpFunc<_, _, _>.Adapt f) m @@ -282,8 +296,10 @@ module MapTree = let rec tryPickOpt (f: OptimizedClosures.FSharpFunc<_, _, _>) (m: MapTree<'Key, 'Value>) = if isEmpty m then None else - match m with - | :? MapTreeNode<'Key, 'Value> as mn -> + if m.Height = 1 then + f.Invoke (m.Key, m.Value) + else + let mn = asNode m match tryPickOpt f mn.Left with | Some _ as res -> res | None -> @@ -291,7 +307,7 @@ module MapTree = | Some _ as res -> res | None -> tryPickOpt f mn.Right - | _ -> f.Invoke (m.Key, m.Value) + let tryPick f m = tryPickOpt (OptimizedClosures.FSharpFunc<_, _, _>.Adapt f) m @@ -299,9 +315,12 @@ module MapTree = let rec existsOpt (f: OptimizedClosures.FSharpFunc<_, _, _>) (m: MapTree<'Key, 'Value>) = if isEmpty m then false else - match m with - | :? MapTreeNode<'Key, 'Value> as mn -> existsOpt f mn.Left || f.Invoke (mn.Key, mn.Value) || existsOpt f mn.Right - | _ -> f.Invoke (m.Key, m.Value) + if m.Height = 1 then + f.Invoke (m.Key, m.Value) + else + let mn = asNode m + existsOpt f mn.Left || f.Invoke (mn.Key, mn.Value) || existsOpt f mn.Right + let exists f m = existsOpt (OptimizedClosures.FSharpFunc<_, _, _>.Adapt f) m @@ -309,9 +328,12 @@ module MapTree = let rec forallOpt (f: OptimizedClosures.FSharpFunc<_, _, _>) (m: MapTree<'Key, 'Value>) = if isEmpty m then true else - match m with - | :? MapTreeNode<'Key, 'Value> as mn -> forallOpt f mn.Left && f.Invoke (mn.Key, mn.Value) && forallOpt f mn.Right - | _ -> f.Invoke (m.Key, m.Value) + if m.Height = 1 then + f.Invoke (m.Key, m.Value) + else + let mn = asNode m + forallOpt f mn.Left && f.Invoke (mn.Key, mn.Value) && forallOpt f mn.Right + let forall f m = @@ -320,24 +342,27 @@ module MapTree = let rec map (f:'Value -> 'Result) (m: MapTree<'Key, 'Value>) : MapTree<'Key, 'Result> = if isEmpty m then empty else - match m with - | :? MapTreeNode<'Key, 'Value> as mn -> + if m.Height = 1 then + MapTree (m.Key, f m.Value) + else + let mn = asNode m let l2 = map f mn.Left let v2 = f mn.Value let r2 = map f mn.Right MapTreeNode (mn.Key, v2, l2, r2, mn.Height) :> MapTree<'Key, 'Result> - | _ -> MapTree (m.Key, f m.Value) let rec mapiOpt (f: OptimizedClosures.FSharpFunc<'Key, 'Value, 'Result>) (m: MapTree<'Key, 'Value>) = if isEmpty m then empty else - match m with - | :? MapTreeNode<'Key, 'Value> as mn -> + if m.Height = 1 then + MapTree (m.Key, f.Invoke (m.Key, m.Value)) + else + let mn = asNode m let l2 = mapiOpt f mn.Left let v2 = f.Invoke (mn.Key, mn.Value) let r2 = mapiOpt f mn.Right MapTreeNode (mn.Key, v2, l2, r2, mn.Height) :> MapTree<'Key, 'Result> - | _ -> MapTree (m.Key, f.Invoke (m.Key, m.Value)) + let mapi f m = mapiOpt (OptimizedClosures.FSharpFunc<_, _, _>.Adapt f) m @@ -345,12 +370,14 @@ module MapTree = let rec foldBackOpt (f: OptimizedClosures.FSharpFunc<_, _, _, _>) (m: MapTree<'Key, 'Value>) x = if isEmpty m then x else - match m with - | :? MapTreeNode<'Key, 'Value> as mn -> + if m.Height = 1 then + f.Invoke (m.Key, m.Value, x) + else + let mn = asNode m let x = foldBackOpt f mn.Right x let x = f.Invoke (mn.Key, mn.Value, x) foldBackOpt f mn.Left x - | _ -> f.Invoke (m.Key, m.Value, x) + let foldBack f m x = foldBackOpt (OptimizedClosures.FSharpFunc<_, _, _, _>.Adapt f) m x @@ -358,73 +385,77 @@ module MapTree = let rec foldOpt (f: OptimizedClosures.FSharpFunc<_, _, _, _>) x (m: MapTree<'Key, 'Value>) = if isEmpty m then x else - match m with - | :? MapTreeNode<'Key, 'Value> as mn -> + if m.Height = 1 then + f.Invoke (x, m.Key, m.Value) + else + let mn = asNode m let x = foldOpt f x mn.Left let x = f.Invoke (x, mn.Key, mn.Value) foldOpt f x mn.Right - | _ -> f.Invoke (x, m.Key, m.Value) let fold f x m = foldOpt (OptimizedClosures.FSharpFunc<_, _, _, _>.Adapt f) x m - let foldSectionOpt (comparer: IComparer<'Key>) lo hi (f: OptimizedClosures.FSharpFunc<_, _, _, _>) (m: MapTree<'Key, 'Value>) x = + let foldSectionOpt lo hi (f: OptimizedClosures.FSharpFunc<_, _, _, _>) (m: MapTree<'Key, 'Value>) x = let rec foldFromTo (f: OptimizedClosures.FSharpFunc<_, _, _, _>) (m: MapTree<'Key, 'Value>) x = if isEmpty m then x else - match m with - | :? MapTreeNode<'Key, 'Value> as mn -> - let cLoKey = comparer.Compare(lo, mn.Key) - let cKeyHi = comparer.Compare(mn.Key, hi) + if m.Height = 1 then + let cLoKey = cmp lo m.Key + let cKeyHi = cmp m.Key hi + let x = if cLoKey <= 0 && cKeyHi <= 0 then f.Invoke (m.Key, m.Value, x) else x + x + else + let mn = asNode m + let cLoKey = cmp lo mn.Key + let cKeyHi = cmp mn.Key hi let x = if cLoKey < 0 then foldFromTo f mn.Left x else x let x = if cLoKey <= 0 && cKeyHi <= 0 then f.Invoke (mn.Key, mn.Value, x) else x let x = if cKeyHi < 0 then foldFromTo f mn.Right x else x x - | _ -> - let cLoKey = comparer.Compare(lo, m.Key) - let cKeyHi = comparer.Compare(m.Key, hi) - let x = if cLoKey <= 0 && cKeyHi <= 0 then f.Invoke (m.Key, m.Value, x) else x - x - if comparer.Compare(lo, hi) = 1 then x else foldFromTo f m x + if cmp lo hi = 1 then x else foldFromTo f m x - let foldSection (comparer: IComparer<'Key>) lo hi f m x = - foldSectionOpt comparer lo hi (OptimizedClosures.FSharpFunc<_, _, _, _>.Adapt f) m x + let foldSection lo hi f m x = + foldSectionOpt lo hi (OptimizedClosures.FSharpFunc<_, _, _, _>.Adapt f) m x let toList (m: MapTree<'Key, 'Value>) = let rec loop (m: MapTree<'Key, 'Value>) acc = if isEmpty m then acc else - match m with - | :? MapTreeNode<'Key, 'Value> as mn -> loop mn.Left ((mn.Key, mn.Value) :: loop mn.Right acc) - | _ -> (m.Key, m.Value) :: acc + if m.Height = 1 then + (m.Key, m.Value) :: acc + else + let mn = asNode m + loop mn.Left ((mn.Key, mn.Value) :: loop mn.Right acc) + loop m [] let toArray m = m |> toList |> Array.ofList - let ofList comparer l = - List.fold (fun acc (k, v) -> add comparer k v acc) empty l + let ofList l = + List.fold (fun acc (k, v) -> add k v acc) empty l - let rec mkFromEnumerator comparer acc (e : IEnumerator<_>) = + let rec mkFromEnumerator acc (e : IEnumerator<_>) = if e.MoveNext() then let (x, y) = e.Current - mkFromEnumerator comparer (add comparer x y acc) e + mkFromEnumerator (add x y acc) e else acc - let ofArray comparer (arr : array<'Key * 'Value>) = + let ofArray (arr : array<'Key * 'Value>) = let mutable res = empty for (x, y) in arr do - res <- add comparer x y res + res <- add x y res res - let ofSeq comparer (c : seq<'Key * 'T>) = + let ofSeq (c : seq<'Key * 'T>) = match c with - | :? array<'Key * 'T> as xs -> ofArray comparer xs - | :? list<'Key * 'T> as xs -> ofList comparer xs + | :? array<'Key * 'T> as xs -> ofArray xs + | :? list<'Key * 'T> as xs -> ofList xs | _ -> use ie = c.GetEnumerator() - mkFromEnumerator comparer empty ie + mkFromEnumerator empty ie let copyToArray m (arr: _[]) i = let mutable j = i @@ -448,9 +479,11 @@ module MapTree = | m :: rest -> if isEmpty m then collapseLHS rest else - match m with - | :? MapTreeNode<'Key, 'Value> as mn -> collapseLHS (mn.Left :: MapTree (mn.Key, mn.Value) :: mn.Right :: rest) - | _ -> stack + if m.Height = 1 then + stack + else + let mn = asNode m + collapseLHS (mn.Left :: MapTree (mn.Key, mn.Value) :: mn.Right :: rest) let mkIterator m = { stack = collapseLHS [m]; started = false } @@ -460,15 +493,20 @@ module MapTree = let alreadyFinished() = raise (InvalidOperationException(SR.GetString(SR.enumerationAlreadyFinished))) + + let unexpectedStackForCurrent() = + failwith "Please report error: Map iterator, unexpected stack for current" + + let unexpectedStackForMoveNext() = + failwith "Please report error: Map iterator, unexpected stack for moveNext" let current i = if i.started then match i.stack with | [] -> alreadyFinished() | m :: _ -> - match m with - | :? MapTreeNode<'Key, 'Value> -> failwith "Please report error: Map iterator, unexpected stack for current" - | _ -> new KeyValuePair<_, _>(m.Key, m.Value) + if m.Height = 1 then KeyValuePair<_, _>(m.Key, m.Value) + else unexpectedStackForCurrent() else notStarted() @@ -477,11 +515,10 @@ module MapTree = match i.stack with | [] -> false | m :: rest -> - match m with - | :? MapTreeNode<'Key, 'Value> -> failwith "Please report error: Map iterator, unexpected stack for moveNext" - | _ -> + if m.Height = 1 then i.stack <- collapseLHS rest not i.stack.IsEmpty + else unexpectedStackForMoveNext() else i.started <- true (* The first call to MoveNext "starts" the enumeration. *) not i.stack.IsEmpty @@ -504,12 +541,8 @@ module MapTree = [] [] [] -type Map<[]'Key, []'Value when 'Key : comparison >(comparer: IComparer<'Key>, tree: MapTree<'Key, 'Value>) = +type Map<[]'Key, []'Value when 'Key : comparison >(tree: MapTree<'Key, 'Value>) = - [] - // This type is logically immutable. This field is only mutated during deserialization. - let mutable comparer = comparer - [] // This type is logically immutable. This field is only mutated during deserialization. let mutable tree = tree @@ -523,8 +556,7 @@ type Map<[]'Key, [ - new Map<'Key, 'Value>(comparer, MapTree.empty) + new Map<'Key, 'Value>(MapTree.empty : MapTree<'Key, 'Value>) [] member _.OnSerializing(context: System.Runtime.Serialization.StreamingContext) = @@ -539,23 +571,20 @@ type Map<[]'Key, [] member _.OnDeserialized(context: System.Runtime.Serialization.StreamingContext) = ignore context - comparer <- LanguagePrimitives.FastGenericComparer<'Key> - tree <- serializedData |> Array.map (fun kvp -> kvp.Key, kvp.Value) |> MapTree.ofArray comparer + tree <- serializedData |> Array.map (fun kvp -> kvp.Key, kvp.Value) |> MapTree.ofArray serializedData <- null static member Empty : Map<'Key, 'Value> = empty static member Create(ie : IEnumerable<_>) : Map<'Key, 'Value> = - let comparer = LanguagePrimitives.FastGenericComparer<'Key> - new Map<_, _>(comparer, MapTree.ofSeq comparer ie) + Map<_, _>(MapTree.ofSeq ie) new (elements : seq<_>) = - let comparer = LanguagePrimitives.FastGenericComparer<'Key> - new Map<_, _>(comparer, MapTree.ofSeq comparer elements) + Map<_, _>(MapTree.ofSeq elements) [] - member internal m.Comparer = comparer + member internal m.Comparer = LanguagePrimitives.FastGenericComparer //[] member internal m.Tree = tree @@ -570,10 +599,10 @@ type Map<[]'Key, [(comparer, MapTree.add comparer key value tree) + new Map<'Key, 'Value>(MapTree.add key value tree) member m.Change(key, f) : Map<'Key, 'Value> = - new Map<'Key, 'Value>(comparer, MapTree.change comparer key f tree) + new Map<'Key, 'Value>(MapTree.change key f tree) [] member m.IsEmpty = MapTree.isEmpty tree @@ -585,7 +614,7 @@ type Map<[]'Key, []'Key, [(comparer, MapTree.filter comparer predicate tree) + new Map<'Key, 'Value>(MapTree.filter predicate tree) member m.ForAll predicate = MapTree.forall predicate tree @@ -603,20 +632,20 @@ type Map<[]'Key, ['Result) = - new Map<'Key, 'Result>(comparer, MapTree.map f tree) + new Map<'Key, 'Result>(MapTree.map f tree) member m.Map f = - new Map<'Key, 'b>(comparer, MapTree.mapi f tree) + new Map<'Key, 'b>(MapTree.mapi f tree) member m.Partition predicate : Map<'Key, 'Value> * Map<'Key, 'Value> = - let r1, r2 = MapTree.partition comparer predicate tree - new Map<'Key, 'Value>(comparer, r1), new Map<'Key, 'Value>(comparer, r2) + let r1, r2 = MapTree.partition predicate tree + new Map<'Key, 'Value>(r1), new Map<'Key, 'Value>(r2) member m.Count = MapTree.size tree @@ -627,13 +656,13 @@ type Map<[]'Key, [(comparer, MapTree.remove comparer key tree) + new Map<'Key, 'Value>(MapTree.remove key tree) member m.TryGetValue(key, [] value: byref<'Value>) = - MapTree.tryGetValue comparer key &value tree + MapTree.tryGetValue key &value tree member m.TryFind key = #if TRACE_SETS_AND_MAPS @@ -641,7 +670,7 @@ type Map<[]'Key, []'Key, [ = - let comparer = LanguagePrimitives.FastGenericComparer<'Key> - new Map<_, _>(comparer, MapTree.ofList comparer l) + Map<_, _>(MapTree.ofList l) member this.ComputeHashCode() = let combineHash x y = (x <<< 1) + y + 631 @@ -724,7 +752,7 @@ type Map<[]'Key, [ as m2-> Seq.compareWith (fun (kvp1 : KeyValuePair<_, _>) (kvp2 : KeyValuePair<_, _>)-> - let c = comparer.Compare(kvp1.Key, kvp2.Key) in + let c = MapSetDefaultComparison.cmp kvp1.Key kvp2.Key in if c <> 0 then c else Unchecked.compare kvp1.Value kvp2.Value) m m2 | _ -> @@ -877,8 +905,7 @@ module Map = [] let ofArray (elements: ('Key * 'Value) array) = - let comparer = LanguagePrimitives.FastGenericComparer<'Key> - new Map<_, _>(comparer, MapTree.ofArray comparer elements) + Map<_, _>(MapTree.ofArray elements) [] let toList (table: Map<_, _>) = diff --git a/src/fsharp/FSharp.Core/mapsetcmp.fs b/src/fsharp/FSharp.Core/mapsetcmp.fs new file mode 100644 index 00000000000..d05f73ab337 --- /dev/null +++ b/src/fsharp/FSharp.Core/mapsetcmp.fs @@ -0,0 +1,122 @@ +// Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information. +namespace Microsoft.FSharp.Collections + +open System +open System.Collections +open System.Numerics +open System.Reflection +open System.Runtime.CompilerServices +open Microsoft.FSharp.Core +open Microsoft.FSharp.Core.LanguagePrimitives.IntrinsicOperators + +module internal MapSetDefaultComparison = + type CompareHelper<'T when 'T : comparison>() = + static let c = LanguagePrimitives.FastGenericComparer + + // A constrained call to IComparable<'T>.CompareTo + static member private CompareCG<'U when 'U :> IComparable<'U>>(l:'U, r:'U):int = l.CompareTo(r) + + // A call to IComparable.CompareTo + static member private CompareC<'U when 'U :> IComparable>(l:'U, r:'U):int = l.CompareTo(r) + + static member val CompareToDlg : Func<'T,'T,int> = + let dlg = + let ty = typeof<'T> + try + let normalCmp = + not (typeof.IsAssignableFrom(ty)) + && isNull (Attribute.GetCustomAttribute(ty, typeof)) + && isNull (Attribute.GetCustomAttribute(ty, typeof)) + && not (ty.IsArray) + + // See #816, IComparable<'T> actually does not satisfy comparison constraint, but it should be preferred + if typeof>.IsAssignableFrom(ty) then + let m = + typeof>.GetMethod("CompareCG", BindingFlags.NonPublic ||| BindingFlags.Static) + .MakeGenericMethod([|ty|]) + Delegate.CreateDelegate(typeof>, m) :?> Func<'T,'T,int> + elif typeof.IsAssignableFrom(ty) && normalCmp then + let m = + typeof>.GetMethod("CompareC", BindingFlags.NonPublic ||| BindingFlags.Static) + .MakeGenericMethod([|typeof<'T>|]) + Delegate.CreateDelegate(typeof>, m) :?> Func<'T,'T,int> + else null + with _ -> null + dlg + with get + + // If backed by static readonly field that will be JIT-time constant + static member val IsIComparable = not(isNull CompareHelper<'T>.CompareToDlg) with get + + [] + static member Compare(l:'T, r:'T):int = + // Should use IsIComparable when it's backed by static readonly field + if isNull CompareHelper<'T>.CompareToDlg then + c.Compare(l, r) + else + CompareHelper<'T>.CompareToDlg.Invoke(l,r) + + // Constructors are not inlined by F#, but JIT could inline them. + // This is what we need here, because LanguagePrimitives.FastGenericComparer.Compare + // has a .tail prefix that breaks the typeof(T)==typeof(...) JIT optimization in cmp + // A struct with a single int field should be lowered by JIT. + [] + [] + type Comparison<'T when 'T : comparison> = + struct + val Value: int + [] + new(l:'T,r:'T) = { Value = CompareHelper<'T>.Compare(l, r) } + end + + [] + let cmp<'T when 'T : comparison> (l:'T) (r:'T) : int = + // See the pattern explanation: https://github.com/dotnet/runtime/blob/4b8d10154c39b1f56424d4ba2068a3150d90d475/src/libraries/System.Private.CoreLib/src/System/Numerics/Vector_1.cs#L14 + // All types that implement IComparable<'T> and are accessible here without additional dependencies should be in the list + if Type.op_Equality(typeof<'T>, typeof) then unbox(box(l)).CompareTo(unbox(box(r))) + else if Type.op_Equality(typeof<'T>, typeof) then unbox(box(l)).CompareTo(unbox(box(r))) + else if Type.op_Equality(typeof<'T>, typeof) then unbox(box(l)).CompareTo(unbox(box(r))) + else if Type.op_Equality(typeof<'T>, typeof) then unbox(box(l)).CompareTo(unbox(box(r))) + else if Type.op_Equality(typeof<'T>, typeof) then unbox(box(l)).CompareTo(unbox(box(r))) + else if Type.op_Equality(typeof<'T>, typeof) then unbox(box(l)).CompareTo(unbox(box(r))) + else if Type.op_Equality(typeof<'T>, typeof) then unbox(box(l)).CompareTo(unbox(box(r))) + else if Type.op_Equality(typeof<'T>, typeof) then unbox(box(l)).CompareTo(unbox(box(r))) + else if Type.op_Equality(typeof<'T>, typeof) then + unbox(box(l)).ToInt64().CompareTo( (unbox(box(r))).ToInt64()) + else if Type.op_Equality(typeof<'T>, typeof) then + unbox(box(l)).ToUInt64().CompareTo( (unbox(box(r))).ToUInt64()) + else if Type.op_Equality(typeof<'T>, typeof) then unbox(box(l)).CompareTo(unbox(box(r))) + else if Type.op_Equality(typeof<'T>, typeof) then unbox(box(l)).CompareTo(unbox(box(r))) + + // F# rules for floats + else if Type.op_Equality(typeof<'T>, typeof) then + let l = unbox(box(l)) + let r = unbox(box(r)) + if l < r then (-1) + elif l > r then (1) + elif l = r then (0) + elif r = r then (-1) + elif l = l then (1) + else 0 + else if Type.op_Equality(typeof<'T>, typeof) then + let l = unbox(box(l)) + let r = unbox(box(r)) + if l < r then (-1) + elif l > r then (1) + elif l = r then (0) + elif r = r then (-1) + elif l = l then (1) + else 0 + else if Type.op_Equality(typeof<'T>, typeof) then unbox(box(l)).CompareTo(unbox(box(r))) + else if Type.op_Equality(typeof<'T>, typeof) then unbox(box(l)).CompareTo(unbox(box(r))) + else if Type.op_Equality(typeof<'T>, typeof) then unbox(box(l)).CompareTo(unbox(box(r))) + else if Type.op_Equality(typeof<'T>, typeof) then unbox(box(l)).CompareTo(unbox(box(r))) + + else if Type.op_Equality(typeof<'T>, typeof) then unbox(box(l)).CompareTo(unbox(box(r))) + else if Type.op_Equality(typeof<'T>, typeof) then unbox(box(l)).CompareTo(unbox(box(r))) + + else if Type.op_Equality(typeof<'T>, typeof) then + // same as in GenericComparisonFast + String.CompareOrdinal(unbox(box(l)),(unbox(box(r)))) + + else Comparison(l,r).Value \ No newline at end of file diff --git a/src/fsharp/FSharp.Core/set.fs b/src/fsharp/FSharp.Core/set.fs index 23455cb1d44..29f6af81673 100644 --- a/src/fsharp/FSharp.Core/set.fs +++ b/src/fsharp/FSharp.Core/set.fs @@ -16,19 +16,19 @@ open Microsoft.FSharp.Collections [] [] -type internal SetTree<'T>(k: 'T) = +type internal SetTree<'T>(k: 'T, h: int) = + member _.Height = h member _.Key = k - + new(k: 'T) = SetTree(k,1) + [] [] [] type internal SetTreeNode<'T>(v:'T, left:SetTree<'T>, right: SetTree<'T>, h: int) = - inherit SetTree<'T>(v) - + inherit SetTree<'T>(v,h) member _.Left = left member _.Right = right - member _.Height = h - + [] module internal SetTree = @@ -36,13 +36,18 @@ module internal SetTree = let inline isEmpty (t:SetTree<'T>) = isNull t + let inline private asNode(value:SetTree<'T>) : SetTreeNode<'T> = + value :?> SetTreeNode<'T> + let rec countAux (t:SetTree<'T>) acc = if isEmpty t then acc else - match t with - | :? SetTreeNode<'T> as tn -> countAux tn.Left (countAux tn.Right (acc+1)) - | _ -> acc+1 + if t.Height = 1 then + acc + 1 + else + let tn = asNode t + countAux tn.Left (countAux tn.Right (acc+1)) let count s = countAux s 0 @@ -84,22 +89,19 @@ module internal SetTree = let inline height (t:SetTree<'T>) = if isEmpty t then 0 - else - match t with - | :? SetTreeNode<'T> as tn -> tn.Height - | _ -> 1 + else t.Height #if CHECKED let rec checkInvariant (t:SetTree<'T>) = // A good sanity check, loss of balance can hit perf if isEmpty t then true else - match t with - | :? SetTreeNode<'T> as tn -> + if t.Height = 1 then true + else + let tn = asNode t let h1 = height tn.Left let h2 = height tn.Right (-2 <= (h1 - h2) && (h1 - h2) <= 2) && checkInvariant tn.Left && checkInvariant tn.Right - | _ -> true #endif [] @@ -114,9 +116,6 @@ module internal SetTree = else SetTreeNode (k, l, r, m+1) :> SetTree<'T> - let inline private asNode(value:SetTree<'T>) : SetTreeNode<'T> = - value :?> SetTreeNode<'T> - let rebalance t1 v t2 = let t1h = height t1 let t2h = height t2 @@ -144,17 +143,16 @@ module internal SetTree = if isEmpty t then SetTree k else let c = comparer.Compare(k, t.Key) - match t with - | :? SetTreeNode<'T> as tn -> - if c < 0 then rebalance (add comparer k tn.Left) tn.Key tn.Right - elif c = 0 then t - else rebalance tn.Left tn.Key (add comparer k tn.Right) - | _ -> + if t.Height = 1 then // nb. no check for rebalance needed for small trees, also be sure to reuse node already allocated - let c = comparer.Compare(k, t.Key) if c < 0 then SetTreeNode (k, empty, t, 2) :> SetTree<'T> elif c = 0 then t else SetTreeNode (k, t, empty, 2) :> SetTree<'T> + else + let tn = asNode t + if c < 0 then rebalance (add comparer k tn.Left) tn.Key tn.Right + elif c = 0 then t + else rebalance tn.Left tn.Key (add comparer k tn.Right) let rec balance comparer (t1:SetTree<'T>) k (t2:SetTree<'T>) = // Given t1 < k < t2 where t1 and t2 are "balanced", @@ -163,10 +161,12 @@ module internal SetTree = if isEmpty t1 then add comparer k t2 // drop t1 = empty elif isEmpty t2 then add comparer k t1 // drop t2 = empty else - match t1 with - | :? SetTreeNode<'T> as t1n -> - match t2 with - | :? SetTreeNode<'T> as t2n -> + if t1.Height = 1 then add comparer k (add comparer t1.Key t2) + else + let t1n = asNode t1 + if t2.Height = 1 then add comparer k (add comparer t2.Key t1) + else + let t2n = asNode t2 // Have: (t1l < k1 < t1r) < k < (t2l < k2 < t2r) // Either (a) h1, h2 differ by at most 2 - no rebalance needed. // (b) h1 too small, i.e. h1+2 < h2 @@ -182,16 +182,19 @@ module internal SetTree = else // case: a, h1 and h2 meet balance requirement mk t1 k t2 - | _ -> add comparer k (add comparer t2.Key t1) - | _ -> add comparer k (add comparer t1.Key t2) let rec split (comparer: IComparer<'T>) pivot (t:SetTree<'T>) = // Given a pivot and a set t // Return { x in t s.t. x < pivot }, pivot in t?, { x in t s.t. x > pivot } if isEmpty t then empty, false, empty else - match t with - | :? SetTreeNode<'T> as tn -> + if t.Height = 1 then + let c = comparer.Compare(t.Key, pivot) + if c < 0 then t, false, empty // singleton under pivot + elif c = 0 then empty, true, empty // singleton is pivot + else empty, false, t // singleton over pivot + else + let tn = asNode t let c = comparer.Compare(pivot, tn.Key) if c < 0 then // pivot t1 let t11Lo, havePivot, t11Hi = split comparer pivot tn.Left @@ -201,27 +204,24 @@ module internal SetTree = else // pivot t2 let t12Lo, havePivot, t12Hi = split comparer pivot tn.Right balance comparer tn.Left tn.Key t12Lo, havePivot, t12Hi - | _ -> - let c = comparer.Compare(t.Key, pivot) - if c < 0 then t, false, empty // singleton under pivot - elif c = 0 then empty, true, empty // singleton is pivot - else empty, false, t // singleton over pivot let rec spliceOutSuccessor (t:SetTree<'T>) = if isEmpty t then failwith "internal error: Set.spliceOutSuccessor" else - match t with - | :? SetTreeNode<'T> as tn -> + if t.Height = 1 then t.Key, empty + else + let tn = asNode t if isEmpty tn.Left then tn.Key, tn.Right else let k3, l' = spliceOutSuccessor tn.Left in k3, mk l' tn.Key tn.Right - | _ -> t.Key, empty let rec remove (comparer: IComparer<'T>) k (t:SetTree<'T>) = if isEmpty t then t else let c = comparer.Compare(k, t.Key) - match t with - | :? SetTreeNode<'T> as tn -> + if t.Height = 1 then + if c = 0 then empty else t + else + let tn = asNode t if c < 0 then rebalance (remove comparer k tn.Left) tn.Key tn.Right elif c = 0 then if isEmpty tn.Left then tn.Right @@ -229,63 +229,64 @@ module internal SetTree = else let sk, r' = spliceOutSuccessor tn.Right mk tn.Left sk r' - else rebalance tn.Left tn.Key (remove comparer k tn.Right) - | _ -> - if c = 0 then empty - else t + else rebalance tn.Left tn.Key (remove comparer k tn.Right) let rec mem (comparer: IComparer<'T>) k (t:SetTree<'T>) = if isEmpty t then false else let c = comparer.Compare(k, t.Key) - match t with - | :? SetTreeNode<'T> as tn -> + if t.Height = 1 then (c = 0) + else + let tn = asNode t if c < 0 then mem comparer k tn.Left elif c = 0 then true else mem comparer k tn.Right - | _ -> (c = 0) let rec iter f (t:SetTree<'T>) = if isEmpty t then () else - match t with - | :? SetTreeNode<'T> as tn -> iter f tn.Left; f tn.Key; iter f tn.Right - | _ -> f t.Key + if t.Height = 1 then f t.Key + else + let tn = asNode t + iter f tn.Left; f tn.Key; iter f tn.Right let rec foldBackOpt (f:OptimizedClosures.FSharpFunc<_, _, _>) (t:SetTree<'T>) x = if isEmpty t then x else - match t with - | :? SetTreeNode<'T> as tn -> foldBackOpt f tn.Left (f.Invoke(tn.Key, (foldBackOpt f tn.Right x))) - | _ -> f.Invoke(t.Key, x) + if t.Height = 1 then f.Invoke(t.Key, x) + else + let tn = asNode t + foldBackOpt f tn.Left (f.Invoke(tn.Key, (foldBackOpt f tn.Right x))) let foldBack f m x = foldBackOpt (OptimizedClosures.FSharpFunc<_, _, _>.Adapt f) m x let rec foldOpt (f:OptimizedClosures.FSharpFunc<_, _, _>) x (t:SetTree<'T>) = if isEmpty t then x else - match t with - | :? SetTreeNode<'T> as tn -> + if t.Height = 1 then f.Invoke(x, t.Key) + else + let tn = asNode t let x = foldOpt f x tn.Left in let x = f.Invoke(x, tn.Key) foldOpt f x tn.Right - | _ -> f.Invoke(x, t.Key) let fold f x m = foldOpt (OptimizedClosures.FSharpFunc<_, _, _>.Adapt f) x m let rec forall f (t:SetTree<'T>) = if isEmpty t then true else - match t with - | :? SetTreeNode<'T> as tn -> f tn.Key && forall f tn.Left && forall f tn.Right - | _ -> f t.Key + if t.Height = 1 then f t.Key + else + let tn = asNode t + f tn.Key && forall f tn.Left && forall f tn.Right let rec exists f (t:SetTree<'T>) = if isEmpty t then false else - match t with - | :? SetTreeNode<'T> as tn -> f tn.Key || exists f tn.Left || exists f tn.Right - | _ -> f t.Key + if t.Height = 1 then f t.Key + else + let tn = asNode t + f tn.Key || exists f tn.Left || exists f tn.Right let subset comparer a b = forall (fun x -> mem comparer x b) a @@ -296,11 +297,12 @@ module internal SetTree = let rec filterAux comparer f (t:SetTree<'T>) acc = if isEmpty t then acc else - match t with - | :? SetTreeNode<'T> as tn -> + if t.Height = 1 then + if f t.Key then add comparer t.Key acc else acc + else + let tn = asNode t let acc = if f tn.Key then add comparer tn.Key acc else acc filterAux comparer f tn.Left (filterAux comparer f tn.Right acc) - | _ -> if f t.Key then add comparer t.Key acc else acc let filter comparer f s = filterAux comparer f s empty @@ -309,9 +311,10 @@ module internal SetTree = else if isEmpty t then acc else - match t with - | :? SetTreeNode<'T> as tn -> diffAux comparer tn.Left (diffAux comparer tn.Right (remove comparer tn.Key acc)) - | _ -> remove comparer t.Key acc + if t.Height = 1 then remove comparer t.Key acc + else + let tn = asNode t + diffAux comparer tn.Left (diffAux comparer tn.Right (remove comparer tn.Key acc)) let diff comparer a b = diffAux comparer b a @@ -320,10 +323,12 @@ module internal SetTree = if isEmpty t1 then t2 elif isEmpty t2 then t1 else - match t1 with - | :? SetTreeNode<'T> as t1n -> - match t2 with - | :? SetTreeNode<'T> as t2n -> // (t1l < k < t1r) AND (t2l < k2 < t2r) + if t1.Height = 1 then add comparer t1.Key t2 + else + if t2.Height = 1 then add comparer t2.Key t1 + else + let t1n = asNode t1 + let t2n = asNode t2 // (t1l < k < t1r) AND (t2l < k2 < t2r) // Divide and Conquer: // Suppose t1 is largest. // Split t2 using pivot k1 into lo and hi. @@ -334,19 +339,17 @@ module internal SetTree = else let lo, _, hi = split comparer t2n.Key t1 in balance comparer (union comparer t2n.Left lo) t2n.Key (union comparer t2n.Right hi) - | _ -> add comparer t2.Key t1 - | _ -> add comparer t1.Key t2 let rec intersectionAux comparer b (t:SetTree<'T>) acc = if isEmpty t then acc else - match t with - | :? SetTreeNode<'T> as tn -> + if t.Height = 1 then + if mem comparer t.Key b then add comparer t.Key acc else acc + else + let tn = asNode t let acc = intersectionAux comparer b tn.Right acc let acc = if mem comparer tn.Key b then add comparer tn.Key acc else acc intersectionAux comparer b tn.Left acc - | _ -> - if mem comparer t.Key b then add comparer t.Key acc else acc let intersection comparer a b = intersectionAux comparer b a empty @@ -355,42 +358,46 @@ module internal SetTree = let rec partitionAux comparer f (t:SetTree<'T>) acc = if isEmpty t then acc else - match t with - | :? SetTreeNode<'T> as tn -> + if t.Height = 1 then partition1 comparer f t.Key acc + else + let tn = asNode t let acc = partitionAux comparer f tn.Right acc let acc = partition1 comparer f tn.Key acc partitionAux comparer f tn.Left acc - | _ -> partition1 comparer f t.Key acc let partition comparer f s = partitionAux comparer f s (empty, empty) let rec minimumElementAux (t:SetTree<'T>) n = if isEmpty t then n else - match t with - | :? SetTreeNode<'T> as tn -> minimumElementAux tn.Left tn.Key - | _ -> t.Key + if t.Height = 1 then t.Key + else + let tn = asNode t + minimumElementAux tn.Left tn.Key and minimumElementOpt (t:SetTree<'T>) = if isEmpty t then None else - match t with - | :? SetTreeNode<'T> as tn -> Some(minimumElementAux tn.Left tn.Key) - | _ -> Some t.Key + if t.Height = 1 then Some t.Key + else + let tn = asNode t + Some(minimumElementAux tn.Left tn.Key) and maximumElementAux (t:SetTree<'T>) n = if isEmpty t then n else - match t with - | :? SetTreeNode<'T> as tn -> maximumElementAux tn.Right tn.Key - | _ -> t.Key + if t.Height = 1 then t.Key + else + let tn = asNode t + maximumElementAux tn.Right tn.Key and maximumElementOpt (t:SetTree<'T>) = if isEmpty t then None else - match t with - | :? SetTreeNode<'T> as tn -> Some(maximumElementAux tn.Right tn.Key) - | _ -> Some t.Key + if t.Height = 1 then Some t.Key + else + let tn = asNode t + Some(maximumElementAux tn.Right tn.Key) let minimumElement s = match minimumElementOpt s with @@ -418,9 +425,10 @@ module internal SetTree = | x :: rest -> if isEmpty x then collapseLHS rest else - match x with - | :? SetTreeNode<'T> as xn-> collapseLHS (xn.Left :: SetTree xn.Key :: xn.Right :: rest) - | _ -> stack + if x.Height = 1 then stack + else + let xn = asNode x + collapseLHS (xn.Left :: SetTree xn.Key :: xn.Right :: rest) let mkIterator s = { stack = collapseLHS [s]; started = false } @@ -436,16 +444,19 @@ module internal SetTree = else notStarted() + let unexpectedStackForMoveNext() = failwith "Please report error: Set iterator, unexpected stack for moveNext" + let unexpectedstateInSetTreeCompareStacks() = failwith "unexpected state in SetTree.compareStacks" + let rec moveNext i = if i.started then match i.stack with | [] -> false | t :: rest -> - match t with - | :? SetTreeNode<'T> -> failwith "Please report error: Set iterator, unexpected stack for moveNext" - | _ -> + if t.Height = 1 then i.stack <- collapseLHS rest - not i.stack.IsEmpty + not i.stack.IsEmpty + else + unexpectedStackForMoveNext() else i.started <- true; // The first call to MoveNext "starts" the enumeration. not i.stack.IsEmpty @@ -466,16 +477,18 @@ module internal SetTree = let cont() = match l1, l2 with | (x1 :: t1), _ when not (isEmpty x1) -> - match x1 with - | :? SetTreeNode<'T> as x1n -> + if x1.Height = 1 then + compareStacks comparer (empty :: SetTree x1.Key :: t1) l2 + else + let x1n = asNode x1 compareStacks comparer (x1n.Left :: (SetTreeNode (x1n.Key, empty, x1n.Right, 0) :> SetTree<'T>) :: t1) l2 - | _ -> compareStacks comparer (empty :: SetTree x1.Key :: t1) l2 | _, (x2 :: t2) when not (isEmpty x2) -> - match x2 with - | :? SetTreeNode<'T> as x2n -> + if x2.Height = 1 then + compareStacks comparer l1 (empty :: SetTree x2.Key :: t2) + else + let x2n = asNode x2 compareStacks comparer l1 (x2n.Left :: (SetTreeNode (x2n.Key, empty, x2n.Right, 0) :> SetTree<'T> ) :: t2) - | _ -> compareStacks comparer l1 (empty :: SetTree x2.Key :: t2) - | _ -> failwith "unexpected state in SetTree.compareStacks" + | _ -> unexpectedstateInSetTreeCompareStacks() match l1, l2 with | [], [] -> 0 @@ -487,30 +500,30 @@ module internal SetTree = else cont() elif isEmpty x2 then cont() else - match x1 with - | :? SetTreeNode<'T> as x1n -> + if x1.Height = 1 then + if x2.Height = 1 then + let c = comparer.Compare(x1.Key, x2.Key) + if c <> 0 then c else compareStacks comparer t1 t2 + else + let x2n = asNode x2 + if isEmpty x2n.Left then + let c = comparer.Compare(x1.Key, x2n.Key) + if c <> 0 then c else compareStacks comparer (empty :: t1) (x2n.Right :: t2) + else cont() + else + let x1n = asNode x1 if isEmpty x1n.Left then - match x2 with - | :? SetTreeNode<'T> as x2n -> + if x2.Height = 1 then + let c = comparer.Compare(x1n.Key, x2.Key) + if c <> 0 then c else compareStacks comparer (x1n.Right :: t1) (empty :: t2) + else + let x2n = asNode x2 if isEmpty x2n.Left then let c = comparer.Compare(x1n.Key, x2n.Key) if c <> 0 then c else compareStacks comparer (x1n.Right :: t1) (x2n.Right :: t2) else cont() - | _ -> - let c = comparer.Compare(x1n.Key, x2.Key) - if c <> 0 then c else compareStacks comparer (x1n.Right :: t1) (empty :: t2) else cont() - | _ -> - match x2 with - | :? SetTreeNode<'T> as x2n -> - if isEmpty x2n.Left then - let c = comparer.Compare(x1.Key, x2n.Key) - if c <> 0 then c else compareStacks comparer (empty :: t1) (x2n.Right :: t2) - else cont() - | _ -> - let c = comparer.Compare(x1.Key, x2.Key) - if c <> 0 then c else compareStacks comparer t1 t2 - + let compare comparer (t1:SetTree<'T>) (t2:SetTree<'T>) = if isEmpty t1 then if isEmpty t2 then 0 @@ -526,9 +539,10 @@ module internal SetTree = let rec loop (t':SetTree<'T>) acc = if isEmpty t' then acc else - match t' with - | :? SetTreeNode<'T> as tn -> loop tn.Left (tn.Key :: loop tn.Right acc) - | _ -> t'.Key :: acc + if t'.Height = 1 then t'.Key :: acc + else + let tn = asNode t' + loop tn.Left (tn.Key :: loop tn.Right acc) loop t [] let copyToArray s (arr: _[]) i =