text: namespace Imms.Tests.Integrity
  open System.Collections
  open System.Collections.Generic
  open System.Collections.Immutable
  open Imms.FSharp.Implementation.Compatibility
  open System
  open Imms.Abstract
  open Imms
  open Imms.FSharp.Implementation
  open Imms.FSharp
  open ExtraFunctional
  open System.Diagnostics
  #nowarn"0044"
  type CollisionFunc<'k, 'v> = ('k -> 'v -> 'v -> 'v)
  type SubtractionFunc<'k, 'v> = ('k -> 'v -> 'v -> 'v option)
  [<AbstractClass>]
  type MapWrapper<'k when 'k : comparison>(name : string) = 
      inherit ColWrapper<KeyValuePair<'k,'k>>(name)
      interface IReadOnlyDictionary<'k,'k> with
          member x.GetEnumerator() = x.GetEnumerator()
          member x.GetEnumerator() : IEnumerator = x.GetEnumerator() :>_
          member x.Count = x.Length
          member x.get_Item k = x.Get k
          member x.ContainsKey k= x.ContainsKey k
          member x.Keys = x.Keys
          member x.Values = x.Values
          member x.TryGetValue(k, v) = 
              if x.ContainsKey k then
                  v <- x.Get k
                  true
              else
                  false
      abstract Add : 'k * 'k -> MapWrapper<'k>
      abstract Get : 'k -> 'k
      abstract EmptySet : SetWrapper<'k>
      abstract TryGet : 'k -> 'k option
      abstract MapEquals : (Kvp<'k,'k> seq) -> bool
      abstract AddRange : (Kvp<'k,'k>) seq -> MapWrapper<'k>
      abstract SetRange : (Kvp<'k,'k>) seq -> MapWrapper<'k>
      abstract Set : 'k * 'k -> MapWrapper<'k>
      abstract Remove : 'k -> MapWrapper<'k>
      abstract ByOrder : int -> (Kvp<'k,'k>)
      abstract ContainsKey : 'k -> bool
      abstract MaxItem : 'k * 'k
      abstract MinItem : 'k * 'k
      abstract ByArbitraryOrder : int -> Kvp<'k,'k>
      abstract IsOrdered : bool
      abstract IsEmpty : bool
      abstract RemoveRange : seq<'k> -> MapWrapper<'k>
      abstract Merge : (Kvp<'k,'k> seq) * CollisionFunc<'k,'k> option -> MapWrapper<'k>
      abstract Join : (Kvp<'k,'k> seq) * CollisionFunc<'k,'k> option -> MapWrapper<'k>
      abstract Except : (Kvp<'k,'k> seq) * f:SubtractionFunc<'k,'k> option -> MapWrapper<'k>
      abstract Empty : MapWrapper<'k>
      abstract Difference : (Kvp<'k,'k> seq) -> MapWrapper<'k>
      member x.Keys = x |> Seq.map (Kvp.ToTuple >> fst)
      member x.Values = x |> Seq.map (Kvp.ToTuple >> snd)
      default x.ByArbitraryOrder i = x.ByOrder i
      default x.ByOrder _ = raise <|  OperationNotImplemented("ByOrder")
      default x.MaxItem = raise <|  OperationNotImplemented("MaxItem")
      default x.MinItem = raise <|  OperationNotImplemented("MinItem")
      default x.TryGet k = if x.ContainsKey k then Some <| x.Get k else None
      default x.Difference _ = raise <|  OperationNotImplemented("Difference")
      default x.RemoveRange _ = raise <|  OperationNotImplemented("RemoveRange")
      default x.Merge (other, f) =
          let mutable working = x :> MapWrapper<_>
          let f = f.Or(fun k v1 v2 -> v2)
          for Kvp(k,v) in other do
              let mutable v = v
              let myV = working.TryGet k
              if myV.IsSome then
                  v <- f k (myV.Value) v
              working <- working.Set(k, v)
          working
      default x.Except(other,f) =
          let f = f.Or(fun k v1 v2 -> None)
          let mutable working = x
          for Kvp(k,v) in other do
              let myVal = working.TryGet(k)
              if myVal.IsSome then
                  let newVal = f k (myVal.Value) v
                  if newVal.IsSome then
                      working <-  working.Set(k,newVal.Value)
                  else
                      working <- working.Remove k
          working
      default x.Join (other, f) =
          let mutable tally = x.Empty
          let f = f.Or (fun k v1 v2 -> v2)
          for Kvp(k,v) in other do
              let mutable v = v
              let mutable add = false
              if tally.ContainsKey k then
                  v <- f k (tally.Get k) v
                  add <- true
              elif x.ContainsKey k then
                  v <- f k (x.Get k) v
                  add <- true
              if add then
                  tally <- tally.Set(k,v)
          tally
      override x.Equals other = 
          match other with
          | :? MapWrapper<'k> as other -> 
              let l1,l2 = x.Length,other.Length
              if l1 <> l2 then
                  Debugger.Break()
              let eq1 = x.MapEquals other
              let eq2 = other.MapEquals x
              assert_eq(eq1, eq2)
  
              if x.IsOrdered && other.IsOrdered then
                  eq1 && Seq.equalsWith (fun (a : Kvp<_,_>) (b : Kvp<_,_>) -> obj.Equals(a.Key,b.Key) && obj.Equals(a.Value,b.Value)) x other
              else eq1
          | _ -> false
  
      override x.GetHashCode() = failwith "Do not call this method"
  
  type MapReferenceWrapper<'k when 'k : comparison>(Inner : Map<'k, 'k>, Ordering : ImmSortedMap<'k, 'k>) = 
      inherit MapWrapper<'k>("FSharpMap")
      static let wrap ord x  = MapReferenceWrapper<'k>(x, ord) :> MapWrapper<'k>
      static let wrapL (x : Map<'k,'k>) = x |> wrap (x |> ImmSortedMap.ofKvpSeq)
      let min = lazy (Inner |> Seq.head |> Kvp.ToTuple)
      let max = lazy (Inner |> Seq.last |> Kvp.ToTuple)
      let len = lazy (Inner.Count)
      member val Inner = Inner
      override x.GetEnumerator() = (Inner :>_ seq).GetEnumerator()
      override x.Add(k,v) = 
          if x.ContainsKey k then raise <| Errors.Key_exists(k) else Inner.Add(k,v) |> wrap (Ordering.Add(k,v))
      override x.Remove k = 
          Inner.Remove(k) |> wrap (Ordering.Remove k)
      override x.ContainsKey k = Inner.ContainsKey(k)
      override x.ByArbitraryOrder i = Ordering.ByOrder(i)
      override x.EmptySet = ReferenceSetWrapper<'k>.FromSeq [] 
      override x.SetRange kvps =  
          let mutable mp = x.Inner
          for item in kvps do
              if mp.ContainsKey(item.Key) then
                  mp <- mp.Remove(item.Key).Add(item.Key, item.Value)
              else
                  mp <- mp.Add(item.Key, item.Value)
          mp |> wrap (Ordering.SetRange (kvps |> Seq.disableIterateOnce))
      override x.MinItem = min.Value
      override x.MapEquals other = 
          let other = other |> x.Empty.AddRange
          if other.Length <> x.Length then false
          else 
              x |> Seq.forall (fun (Kvp(k,v)) -> other.ContainsKey(k) && obj.Equals(other.Get(k), v))
                  
      override x.SelfTest() = true
      override x.IsOrdered = true
      override x.IsReference = true
      override x.MaxItem = max.Value
      override x.AddRange vs = 
          let mutable mp = x :> MapWrapper<_>
          for Kvp(k,v) in vs do
              mp <- mp.Add(k,v)
          mp
      override x.Empty = Map.empty |> wrap (ImmSortedMap.empty)
      override x.Get k = Inner.[k]
      override x.Set(k,v) = 
          if Inner.ContainsKey k then
              Inner.Remove(k).Add(k, v) |> wrap (Ordering.Set(k, v))
          else
              Inner.Add(k, v) |> wrap (Ordering.Set(k, v))
  
      static member FromSeq s = s |> Seq.map (Kvp.ToTuple) |> Map.ofSeq |> wrapL
      override x.Difference other = x.Except(other, None).AddRange(MapReferenceWrapper.FromSeq(other).Except(x, None))
      override x.RemoveRange ks = 
          let mutable x = x.Inner
          for item in ks do
              x <- x.Remove item
          x |> wrapL
      override x.IsEmpty = Inner.IsEmpty
      override x.Length = len.Value
      override x.ByOrder i = x |> Seq.nth i
      override x.All f = Inner |> Seq.forall f
      override x.Any f = Inner |> Seq.exists f
      override x.Count f = Inner |> Seq.fold (fun st cur -> if f cur then st + 1 else st) 0
      override x.Pick f = Inner |> Seq.tryPick f
      override x.Find f = Inner |> Seq.tryFind f
      override x.Fold initial f = Inner |> Seq.fold f initial
      override x.Reduce f = Inner |> Seq.reduce f
     
      
  type ImmSortedMapWrapper<'k when 'k : comparison>(Inner : ImmSortedMap<'k, 'k>) =
      inherit MapWrapper<'k>("ImmSortedMap")
      static let wrap x = ImmSortedMapWrapper<'k>(x) :> MapWrapper<'k>
      let unwrap (y : Kvp<'k,'k> seq) = 
          match y with
          | :? ImmSortedMapWrapper<'k> as map -> map.Inner :> _ seq
          | _ -> y
      let unwrapSet (y : 'k seq) =
          match y with
          | :? ImmSortedSetWrapper<'k> as set -> set.Inner :> _ seq
          | _ -> y
      member val public Inner =
           Inner
      override x.GetEnumerator() = (Inner :>_ seq).GetEnumerator()
      override x.Add(k,v) = Inner.Add(k,v) |> wrap 
      override x.Remove k = Inner.Remove k |> wrap
      override x.ContainsKey k = Inner.ContainsKey(k)
      override x.AddRange vs = Inner.AddRange (vs |> unwrap) |> wrap
      override x.IsOrdered = true
      override x.SetRange vs= Inner.SetRange (vs |> unwrap) |> wrap
      override x.Empty = ImmSortedMap.emptyWith (Cmp.Default) |> wrap
      override x.EmptySet = ImmSortedSetWrapper<'k>.FromSeq []
      override x.MapEquals other = Inner.MapEquals(other)
      override x.Get k = Inner.[k]
      override x.SelfTest() = 
          let mutable i = 0
          let mutable okay = true
          let distinctKeyCount = x |> Seq.distinctBy (fun y -> y.Key) |> Seq.smartLength
          if distinctKeyCount <> x.Length then false
          else
              for item in x do
                  let (Kvp(k,v)) = x.ByOrder i
                  okay <- okay && obj.Equals(item.Key, k) && obj.Equals(item.Value, v)
                  i <- i + 1
              okay
      override x.Set(k,v) = Inner.Set(k,v) |> wrap
      override x.Merge(other, f) =
          Inner.Merge(other |> unwrap, f.Map toValSelector |> Option.asNull) |> wrap
      override x.Join(other, f) = Inner.Join(other |> unwrap, f.Map toValSelector |> Option.asNull) |> wrap
      override x.Except(other,f) = Inner.Subtract(other |> unwrap, f.Map(Fun.compose3 toOption >> toValSelector).Or(null)) |> wrap
      override x.Difference other = Inner.Difference(other |> unwrap) |> wrap
      override x.RemoveRange vs = Inner.RemoveRange (vs |> unwrapSet) |> wrap
      override x.Length = Inner.Length
      override x.IsEmpty = Inner.IsEmpty
      override x.MinItem = Inner.MinItem.Key,Inner.MinItem.Value
      override x.MaxItem = Inner.MaxItem.Key, Inner.MaxItem.Value
      override x.ByOrder i = Inner.ByOrder(i)
      override x.All f = Inner.All f
      override x.Any f = Inner.Any f
      override x.Count f = Inner.Count f
      override x.Pick f = Inner |> ImmSortedMap.pick f
      override x.Find f = Inner.Find f |> fromOption
      override x.Fold initial f = Inner |> ImmSortedMap.fold initial f
      override x.Reduce f = Inner |> ImmSortedMap.reduce f
      static member FromSeq s = 
          ImmSortedMapWrapper(ImmSortedMap.ofKvpSeqWith Cmp.Default s) :> MapWrapper<_>
  
  type ImmMapWrapper<'k when 'k : comparison>(Inner : ImmMap<'k,'k>, Ordering : ImmSortedMap<'k, 'k>) =
      inherit MapWrapper<'k>("ImmMap")
      static let wrap ord x  = ImmMapWrapper<'k>(x, ord) :> MapWrapper<'k>
      static let unwrap (xs : Kvp<'k,'k> seq) =
          match xs with
          | :? ImmMapWrapper<'k> as wrapped -> wrapped.Inner :> _ seq
          | _ -> xs
      static let unwrapSet (xs : 'k seq) = 
          match xs with
          | :? ImmSetWrapper<'k> as wrapped -> wrapped.Inner :> _ seq
          | _ -> xs
      member val public Inner = Inner
      override x.GetEnumerator() = (Inner :> _ seq).GetEnumerator()
      override x.Add(k,v) = Inner.Add(k,v) |> wrap (Ordering.Add(k,v))
      override x.Remove k = Inner.Remove k |> wrap (Ordering.Remove k)
      override x.IsOrdered = false
      override x.EmptySet = ImmSetWrapper<'k>.FromSeq []
      override x.MapEquals other = Inner.MapEquals(other)
      override x.SetRange vs = Inner.SetRange (vs |> unwrap) |> wrap (Ordering.SetRange(vs |> Seq.disableIterateOnce))
      override x.AddRange vs = Inner.AddRange (vs |> unwrap) |> wrap (Ordering.AddRange(vs |> Seq.disableIterateOnce))
      override x.SelfTest() = 
          x |> Seq.distinctBy (fun y -> y.Key) |> Seq.smartLength = x.Length
      override x.ContainsKey k = Inner.ContainsKey(k)
      override x.Empty = ImmMap.empty |> wrap (ImmSortedMap.empty)
      override x.Get k = Inner.[k]
      override x.Set(k,v) = Inner.Set(k,v) |> wrap (Ordering.Set(k,v))
      override x.ByArbitraryOrder i = Ordering.ByOrder(i)
      override x.Merge(other, f : _ option) = 
          let f = f.Map toValSelector |> Option.asNull
          Inner.Merge(other |> unwrap, f) |> wrap (Ordering.Merge(other, null))
      override x.Join(other, f) = 
          let f = f.Map toValSelector |> Option.asNull
          let f2 = (fun k v1 v2 -> v2) |> toValSelector
          Inner.Join(other |> unwrap, f) |> wrap (Ordering.Join(other |> Seq.disableIterateOnce, f2))
      override x.Except(other,f) = 
          let f = f.Map(Fun.compose3 toOption >> toValSelector) |> Option.asNull
          Inner.Subtract(other |> unwrap, f)|> wrap (Ordering.Subtract(other |> Seq.disableIterateOnce, null))
      override x.Difference other = Inner.Difference(other |> unwrap) |> wrap (Ordering.Difference(other))
      override x.RemoveRange vs = 
          Inner.RemoveRange (vs |> unwrapSet) |> wrap (Ordering.RemoveRange(vs |> Seq.disableIterateOnce))
      override x.Length = Inner.Length
      override x.IsEmpty = Inner.IsEmpty
      override x.All f = Inner.All f
      override x.Any f = Inner.Any f
      override x.Count f = Inner.Count f
      override x.Pick f = Inner |> ImmMap.pick f
      override x.Find f = Inner.Find f |> fromOption
      override x.Fold initial f = Inner.Aggregate(initial, toFunc2 f)
      override x.Reduce f = Inner |> ImmMap.reduce f
      static member FromSeq s = ImmMapWrapper(ImmMap.ofKvpSeq(s), ImmSortedMap.ofKvpSeq(s))  :> MapWrapper<_>
  
  type MapWrapper<'k when 'k : comparison> with
      member x.assert_keyExists k  =
          if not <| x.IsReference then
              assert_true(x.ContainsKey k)
              //assert_ex_arg(fun () -> x.Add(k,k))
              is_some(x.TryGet k)
  
      member x.assert_keyDoesntExist k  =
          if not <| x.IsReference then
              assert_false(x.ContainsKey k)
              is_none(x.TryGet k)
              assert_no_ex(fun () -> x.Add(k,k))
            //  assert_ex_key_not_found(fun () -> x.Get k)
  
      member x.U_get k  =
          is_some(x.TryGet k)
          assert_true(x.ContainsKey k)
          let v = x.Get k
          assert_eq(x.TryGet(k).Value, v)
          v
  
      member x.U_add k v  =
          let mutable next = x
          if x.ContainsKey k then
              //set |> assert_keyExists k
              next <- x.Set(k, v)
              assert_eq(x.Length, next.Length)
          else
             // set |> assert_keyDoesntExist k
              next <- x.Add(k, v)
              assert_eq(next.Length, x.Length + 1)
          assert_eq(next.U_get k, v)
          next
  
      member x.U_remove k  =
          let mutable next = x.Remove k
          if x.ContainsKey k then
              x.assert_keyExists k
              assert_eq(x.Length - 1, next.Length)
          else
              x.assert_keyDoesntExist k
              assert_eq(x.Length, next.Length)
          next.assert_keyDoesntExist k
          next
  
      member x.U_addRange kvps  =
          let shared = kvps |> Seq.countWhere (fun (Kvp(k,_)) -> x.ContainsKey k)
          let total = kvps |> Seq.smartLength
          let kvps = kvps |> Seq.canEnsureIterateOnce
          let next = 
              if shared > 0 then
                  x.SetRange kvps
              else
                  x.AddRange kvps
  
          assert_eq(next.Length, x.Length + total - shared)
          for Kvp(k,v) in kvps do
              assert_eq(next.U_get k, v)
          next
  
      member x.U_removeRange keys  =
          let shared = keys |> Seq.countWhere (fun k -> x.ContainsKey k)
          let next = x.RemoveRange(keys |> Seq.canEnsureIterateOnce)
          assert_eq(next.Length, x.Length - shared)
          next
  
      member x.U_except (f : _ option) other  =
          
          let shared = other |> Seq.keys |> Seq.countWhere (x.ContainsKey)
          let result =  x.Except(other |> Seq.canEnsureIterateOnce, f)
          result
  
      member x.U_merge (f : _ option) other  =
          let dif = other |> Seq.countWhere (fun (Kvp(k,v)) -> not <| x.ContainsKey k)
          let result = x.Merge(other |> Seq.canEnsureIterateOnce,f)
          assert_eq(result.Length, dif + x.Length)
          result
  
      member x.U_join (f : _ option) other  =
          let shared = other |> Seq.keys |> Seq.countWhere (x.ContainsKey)
          let result = x.Join(other |> Seq.canEnsureIterateOnce,f)
          assert_eq(result.Length, shared)
          result
  
      member x.U_difference other =
          let result = x.Difference(other |> Seq.canEnsureIterateOnce)
          result
  
      member x.toSet  =
          let set = x.EmptySet
          set.Union (x.Keys)
  