Skip to content

Commit a11eaa7

Browse files
committed
* added AList/IndexList.pairwise(Cyclic) with tests (#53)
* fixed bug in `MapExt.tryRemove(Min|Max)`
1 parent 7f7f6e4 commit a11eaa7

6 files changed

Lines changed: 221 additions & 10 deletions

File tree

src/FSharp.Data.Adaptive/AdaptiveIndexList/AdaptiveIndexList.fs

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1018,6 +1018,81 @@ module internal AdaptiveIndexListImplementation =
10181018
let ops = IndexList.computeDelta last v
10191019
last <- v
10201020
ops
1021+
1022+
/// Reader for pairwise operations
1023+
type PairwiseReader<'a>(input : alist<'a>, cyclic : bool) =
1024+
inherit AbstractReader<IndexList<'a * 'a>, IndexListDelta<'a * 'a>>(IndexList.trace)
1025+
1026+
let reader = input.GetReader()
1027+
1028+
override x.Compute(token: AdaptiveToken) =
1029+
let o = reader.State
1030+
let ops = reader.GetChanges token
1031+
let s = reader.State
1032+
1033+
let inline neighbours (i : Index) =
1034+
let (l, _, r) = IndexList.neighbours i s
1035+
1036+
let r =
1037+
match r with
1038+
| None when cyclic -> s.Content.TryMin
1039+
| _ -> r
1040+
1041+
let l =
1042+
match l with
1043+
| None when cyclic -> s.Content.TryMax
1044+
| _ -> l
1045+
1046+
l, r
1047+
1048+
let mutable delta = IndexListDelta.empty
1049+
for i, op in IndexListDelta.toSeq ops do
1050+
match op with
1051+
| Remove ->
1052+
match IndexList.tryGet i o with
1053+
| Some _ov ->
1054+
let (l, r) = neighbours i
1055+
delta <- IndexListDelta.add i Remove delta
1056+
match l with
1057+
| Some (li, lv) ->
1058+
match r with
1059+
| Some (_ri, rv) ->
1060+
delta <- IndexListDelta.add li (Set(lv, rv)) delta
1061+
| None ->
1062+
delta <- IndexListDelta.add li Remove delta
1063+
| None ->
1064+
()
1065+
| None ->
1066+
()
1067+
| Set v ->
1068+
let (l, r) = neighbours i
1069+
match IndexList.tryGet i o with
1070+
| Some ov when CheapEquality.cheapEqual ov v ->
1071+
()
1072+
| _ ->
1073+
match r with
1074+
| Some (_ri, rv) ->
1075+
delta <- IndexListDelta.add i (Set(v, rv)) delta
1076+
| None ->
1077+
()
1078+
1079+
match l with
1080+
| Some(li, lv) ->
1081+
delta <- IndexListDelta.add li (Set(lv, v)) delta
1082+
| None ->
1083+
()
1084+
1085+
//let ref =
1086+
// if cyclic then IndexList.pairwiseCyclic s
1087+
// else IndexList.pairwise s
1088+
//let t, _ = IndexList.applyDelta x.State delta
1089+
1090+
//if not (Unchecked.equals ref t) then
1091+
// printfn "%A %A" ref t
1092+
1093+
1094+
delta
1095+
10211096

10221097
/// Gets the current content of the alist as IndexList.
10231098
let inline force (list : alist<'T>) =
@@ -1334,6 +1409,22 @@ module AList =
13341409
let inline cmp a b = compare b a
13351410
sortWith cmp list
13361411

1412+
/// Returns a list containing all elements tupled with their successor.
1413+
let pairwise (list: alist<'T>) =
1414+
if list.IsConstant then
1415+
constant (fun () -> force list |> IndexList.pairwise)
1416+
else
1417+
ofReader (fun () -> PairwiseReader(list, false))
1418+
1419+
/// Returns a list of each element tupled with its successor and the last element tupled with the first.
1420+
let pairwiseCyclic (list: alist<'T>) =
1421+
if list.IsConstant then
1422+
constant (fun () -> force list |> IndexList.pairwiseCyclic)
1423+
else
1424+
ofReader (fun () -> PairwiseReader(list, true))
1425+
1426+
1427+
13371428
/// Adaptively reverses the list
13381429
let rev (list: alist<'T>) =
13391430
// TODO: more efficient implementation possible?

src/FSharp.Data.Adaptive/AdaptiveIndexList/AdaptiveIndexList.fsi

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,12 @@ module AList =
184184
val inline sortDescending : list: alist<'T> -> alist<'T>
185185
when 'T : comparison
186186

187+
/// Returns a list containing all elements tupled with their successor.
188+
val pairwise : list: alist<'T> -> alist<'T * 'T>
189+
190+
/// Returns a list of each element tupled with its successor and the last element tupled with the first.
191+
val pairwiseCyclic : list: alist<'T> -> alist<'T * 'T>
192+
187193
/// Tries to get the element associated to a specific Index from the list.
188194
/// Note that this operation should not be used extensively since its resulting
189195
/// aval will be re-evaluated upon every change of the list.

src/FSharp.Data.Adaptive/Datastructures/IndexList.fs

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -335,6 +335,53 @@ type IndexList< [<EqualityConditionalOn>] 'T> internal(l : Index, h : Index, con
335335
let (p,_,_) = MapExt.neighbours index content
336336
p
337337

338+
/// Returns a list of each element tupled with its successor.
339+
member x.Pairwise() =
340+
match MapExt.tryRemoveMin content with
341+
| Some (i0, v0, rest) ->
342+
let mutable res = MapExt.empty
343+
let mutable rest = rest
344+
let mutable lastIndex = l
345+
let mutable i0 = i0
346+
let mutable v0 = v0
347+
while not (MapExt.isEmpty rest) do
348+
match MapExt.tryRemoveMin rest with
349+
| Some (i1, v1, r) ->
350+
res <- MapExt.add i0 (v0, v1) res
351+
lastIndex <- i0
352+
i0 <- i1
353+
v0 <- v1
354+
rest <- r
355+
| None ->
356+
()
357+
358+
IndexList(l, lastIndex, res)
359+
| None ->
360+
IndexList.Empty
361+
362+
/// Returns a list of each element tupled with its successor and the last element tupled with the first.
363+
member x.PairwiseCyclic() =
364+
match MapExt.tryRemoveMin content with
365+
| Some (i0, initial, rest) ->
366+
let mutable res = MapExt.empty
367+
let mutable rest = rest
368+
let mutable i0 = i0
369+
let mutable v0 = initial
370+
while not (MapExt.isEmpty rest) do
371+
match MapExt.tryRemoveMin rest with
372+
| Some (i1, v1, r) ->
373+
res <- MapExt.add i0 (v0, v1) res
374+
i0 <- i1
375+
v0 <- v1
376+
rest <- r
377+
| None ->
378+
()
379+
380+
res <- MapExt.add i0 (v0, initial) res
381+
IndexList(l, i0, res)
382+
| None ->
383+
IndexList.Empty
384+
338385
override x.ToString() =
339386
let suffix =
340387
if x.Count > 5 then "; ..."
@@ -660,6 +707,15 @@ module IndexList =
660707
let tryPickBack (mapping : Index -> 'T1 -> option<'T2>) (list : IndexList<'T1>) =
661708
list.Content |> MapExt.tryPickBack mapping
662709

710+
711+
/// Returns a list of each element tupled with its successor.
712+
let inline pairwise (l : IndexList<'T>) =
713+
l.Pairwise()
714+
715+
/// Returns a list of each element tupled with its successor and the last element tupled with the first.
716+
let inline pairwiseCyclic (l : IndexList<'T>) =
717+
l.PairwiseCyclic()
718+
663719
/// concats the given lists.
664720
let append (l : IndexList<'T>) (r : IndexList<'T>) =
665721
if l.Count = 0 then r
@@ -712,6 +768,15 @@ module IndexList =
712768
/// all elements from the list with their respective Index.
713769
let toArrayIndexed (list: IndexList<'T>) = list.Content |> MapExt.toArray
714770

771+
/// all elements from the list with their respective Index in reveresed order.
772+
let toSeqIndexedBack (list: IndexList<'T>) = list.Content |> MapExt.toSeqBack
773+
774+
/// all elements from the list with their respective Index in reveresed order.
775+
let toListIndexedBack (list: IndexList<'T>) = list.Content |> MapExt.toSeqBack |> Seq.toList
776+
777+
/// all elements from the list with their respective Index in reveresed order.
778+
let toArrayIndexedBack (list: IndexList<'T>) = list.Content |> MapExt.toSeqBack |> Seq.toArray
779+
715780
/// creates a new IndexList containing all the given elements at their respective Index.
716781
let ofSeqIndexed (elements: seq<Index * 'T>) = MapExt.ofSeq elements |> ofMap
717782

src/FSharp.Data.Adaptive/Datastructures/MapExt.fs

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -225,7 +225,7 @@ module internal MapExtImplementation =
225225
None
226226

227227

228-
let rec tryRemoveMin m =
228+
let rec tryRemoveMin cmp m =
229229
match m with
230230
| MapEmpty ->
231231
None
@@ -234,15 +234,15 @@ module internal MapExtImplementation =
234234
Some (k2, v, MapEmpty)
235235

236236
| MapNode(k2,v2,l,r,_,_) ->
237-
match tryRemoveMin l with
237+
match tryRemoveMin cmp l with
238238
| Some (k,v,rest) ->
239239
match rest with
240-
| MapEmpty -> Some (k,v,r)
241-
| _ -> Some (k,v, rebalance rest k2 v2 r)
240+
| MapEmpty -> Some (k, v, add cmp k2 v2 r)
241+
| _ -> Some (k, v, rebalance rest k2 v2 r)
242242
| None ->
243243
Some(k2, v2, r)
244244

245-
let rec tryRemoveMax m =
245+
let rec tryRemoveMax cmp m =
246246
match m with
247247
| MapEmpty ->
248248
None
@@ -251,11 +251,11 @@ module internal MapExtImplementation =
251251
Some (k2, v, MapEmpty)
252252

253253
| MapNode(k2,v2,l,r,_,_) ->
254-
match tryRemoveMax r with
254+
match tryRemoveMax cmp r with
255255
| Some (k,v,rest) ->
256256
match rest with
257-
| MapEmpty -> Some (k,v,l)
258-
| _ -> Some (k,v, rebalance l k2 v2 rest)
257+
| MapEmpty -> Some (k, v, add cmp k2 v2 l)
258+
| _ -> Some (k, v, rebalance l k2 v2 rest)
259259
| None ->
260260
Some(k2, v2, l)
261261

@@ -1583,12 +1583,12 @@ type internal MapExt<[<EqualityConditionalOn>]'Key,[<EqualityConditionalOn;Compa
15831583
| _ -> None
15841584

15851585
member x.TryRemoveMin() =
1586-
match MapTree.tryRemoveMin tree with
1586+
match MapTree.tryRemoveMin comparer tree with
15871587
| Some (k,v,t) -> Some(k,v, MapExt(comparer, t))
15881588
| None -> None
15891589

15901590
member x.TryRemoveMax() =
1591-
match MapTree.tryRemoveMax tree with
1591+
match MapTree.tryRemoveMax comparer tree with
15921592
| Some (k,v,t) -> Some(k,v, MapExt(comparer, t))
15931593
| None -> None
15941594

@@ -1628,6 +1628,9 @@ type internal MapExt<[<EqualityConditionalOn>]'Key,[<EqualityConditionalOn;Compa
16281628
new MapExt<'Key,'Value>(comparer,r1), new MapExt<'Key,'Value>(comparer,r2)
16291629

16301630
member m.Count = MapTree.size tree
1631+
1632+
member x.TryMin = MapTree.tryMin tree
1633+
member x.TryMax = MapTree.tryMax tree
16311634

16321635
member x.TryMinKey = MapTree.tryMin tree |> Option.map fst
16331636
member x.TryMaxKey = MapTree.tryMax tree |> Option.map fst

src/Test/FSharp.Data.Adaptive.Reference/AdaptiveIndexList.fs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,3 +122,9 @@ module AList =
122122

123123
let sortWith (compare: 'T -> 'T -> int) (list : alist<'T>) =
124124
list.Content |> AVal.map (IndexList.sortWith compare) |> ofRef
125+
126+
let pairwise (list : alist<'T>) =
127+
list.Content |> AVal.map IndexList.pairwise |> ofRef
128+
129+
let pairwiseCyclic (list : alist<'T>) =
130+
list.Content |> AVal.map IndexList.pairwiseCyclic |> ofRef

src/Test/FSharp.Data.Adaptive.Tests/Utilities/Generators.fs

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1519,6 +1519,40 @@ module Generators =
15191519
getChanges
15201520
}
15211521

1522+
1523+
let pairwise<'a>() =
1524+
gen {
1525+
let! list = Arb.generate<VList<'a>> |> Gen.scaleSize (fun v -> v - 1)
1526+
let fc, f = randomFunction2<'a, 'a, 'a>()
1527+
1528+
return
1529+
create
1530+
(Adaptive.AList.pairwise list.lreal |> Adaptive.AList.mapi (fun i (l,r) -> f l r))
1531+
(Reference.AList.pairwise list.lref |> Reference.AList.mapi (fun i (l,r) -> f l r))
1532+
(fun verbose ->
1533+
let ma, a = list.lexpression verbose
1534+
ma, sprintf "pairwise\r\n%s" (indent a)
1535+
)
1536+
list.lchanges
1537+
}
1538+
1539+
let pairwiseCyclic<'a>() =
1540+
gen {
1541+
let! list = Arb.generate<VList<'a>> |> Gen.scaleSize (fun v -> v - 1)
1542+
let fc, f = randomFunction2<'a, 'a, 'a>()
1543+
1544+
return
1545+
create
1546+
(Adaptive.AList.pairwiseCyclic list.lreal |> Adaptive.AList.mapi (fun i (l,r) -> f l r))
1547+
(Reference.AList.pairwiseCyclic list.lref |> Reference.AList.mapi (fun i (l,r) -> f l r))
1548+
(fun verbose ->
1549+
let ma, a = list.lexpression verbose
1550+
ma, sprintf "pairwiseCyclic\r\n%s" (indent a)
1551+
)
1552+
list.lchanges
1553+
}
1554+
1555+
15221556
[<Struct; CustomEquality; NoComparison>]
15231557
type StupidHash(v : int) =
15241558
member x.Value = v
@@ -1784,6 +1818,8 @@ type AdaptiveGenerators() =
17841818
yield 3, Gen.constant "sortBy"
17851819
yield 3, Gen.constant "sortWith"
17861820
yield 3, Gen.constant "ofAVal"
1821+
yield 2, Gen.constant "pairwise"
1822+
yield 2, Gen.constant "pairwiseCyclic"
17871823
if typeof<IComparable>.IsAssignableFrom typeof<'a> then
17881824
yield 3, Gen.constant "setSortBy"
17891825
yield 3, Gen.constant "setSortWith"
@@ -1799,6 +1835,10 @@ type AdaptiveGenerators() =
17991835
return! Generators.List.append<'a>()
18001836
| "ofAVal" ->
18011837
return! Generators.List.ofAVal<'a>()
1838+
| "pairwise" ->
1839+
return! Generators.List.pairwise<'a>()
1840+
| "pairwiseCyclic" ->
1841+
return! Generators.List.pairwiseCyclic<'a>()
18021842
| "sortBy" ->
18031843
return! Generators.List.sortBy<'a>()
18041844
| "sortWith" ->

0 commit comments

Comments
 (0)