Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 44 additions & 0 deletions src/FSharpx.Collections/PriorityQueue.fs
Original file line number Diff line number Diff line change
Expand Up @@ -335,6 +335,50 @@ module Heap =
let inline tryUncons(xs: Heap<'T>) =
xs.TryUncons()

///O(n). Returns a sorted list of all elements.
let inline toList(xs: Heap<'T>) =
xs |> toSeq |> Seq.toList

///O(n). Returns a sorted array of all elements.
let inline toArray(xs: Heap<'T>) =
xs |> toSeq |> Seq.toArray

///O(n log n). Returns heap from a list.
let inline ofList isDescending (xs: 'T list) =
ofSeq isDescending xs

///O(n log n). Returns heap from an array.
let inline ofArray isDescending (xs: 'T array) =
ofSeq isDescending xs

///O(n). Applies a function to each element in sorted order, threading an accumulator.
let inline fold (f: 'State -> 'T -> 'State) (state: 'State) (xs: Heap<'T>) =
xs |> toSeq |> Seq.fold f state

///O(n). Applies a function to each element in sorted order.
let inline iter (f: 'T -> unit) (xs: Heap<'T>) =
xs |> toSeq |> Seq.iter f

///O(n). Returns true if any element satisfies the predicate.
let inline exists (f: 'T -> bool) (xs: Heap<'T>) =
xs |> toSeq |> Seq.exists f

///O(n). Returns true if all elements satisfy the predicate.
let inline forall (f: 'T -> bool) (xs: Heap<'T>) =
xs |> toSeq |> Seq.forall f

///O(n log n). Returns a new heap with elements mapped by the function, preserving sort direction.
let inline map (f: 'T -> 'U) (xs: Heap<'T>) : Heap<'U> =
xs |> toSeq |> Seq.map f |> ofSeq(isDescending xs)

///O(n log n). Returns a new heap containing only elements satisfying the predicate.
let inline filter (f: 'T -> bool) (xs: Heap<'T>) =
xs |> toSeq |> Seq.filter f |> ofSeq(isDescending xs)

///O(n log n). Applies an option-returning function to each element; returns a new heap of the Some values.
let inline choose (f: 'T -> 'U option) (xs: Heap<'T>) : Heap<'U> =
xs |> toSeq |> Seq.choose f |> ofSeq(isDescending xs)

[<RequireQualifiedAccess>]
module PriorityQueue =
///O(1). Returns a empty queue, with indicated ordering.
Expand Down
98 changes: 97 additions & 1 deletion tests/FSharpx.Collections.Tests/HeapTest.fs
Original file line number Diff line number Diff line change
Expand Up @@ -482,4 +482,100 @@ module HeapTests =
(Prop.forAll(Arb.fromGen intGensStart2.[5])
<| fun (h, l) ->
let x, tl = h.Uncons()
x = l.Head && tl.Length = (l.Length - 1)) ]
x = l.Head && tl.Length = (l.Length - 1))

test "toList ascending returns sorted list" {
let h = Heap.ofSeq false [ 3; 1; 4; 1; 5; 9; 2; 6 ]
Expect.equal "toList asc" [ 1; 1; 2; 3; 4; 5; 6; 9 ] (Heap.toList h)
}

test "toList descending returns reverse-sorted list" {
let h = Heap.ofSeq true [ 3; 1; 4; 1; 5; 9; 2; 6 ]
Expect.equal "toList desc" [ 9; 6; 5; 4; 3; 2; 1; 1 ] (Heap.toList h)
}

test "toArray ascending returns sorted array" {
let h = Heap.ofSeq false [ 5; 3; 1; 4; 2 ]
Expect.equal "toArray asc" [| 1; 2; 3; 4; 5 |] (Heap.toArray h)
}

test "ofList round-trips through toList" {
let xs = [ 7; 3; 5; 1; 9; 2; 4; 8; 6 ]
let h = Heap.ofList false xs
Expect.equal "ofList round-trip" (List.sort xs) (Heap.toList h)
}

test "ofArray round-trips through toArray" {
let xs = [| 7; 3; 5; 1; 9; 2; 4; 8; 6 |]
let h = Heap.ofArray false xs
Expect.equal "ofArray round-trip" (Array.sort xs) (Heap.toArray h)
}

test "fold accumulates in sorted order" {
let h = Heap.ofSeq false [ 3; 1; 2 ]
let result = Heap.fold (fun acc x -> acc @ [ x ]) [] h
Expect.equal "fold ascending" [ 1; 2; 3 ] result
}

test "iter visits elements in sorted order" {
let h = Heap.ofSeq false [ 3; 1; 2 ]
let mutable result = []
Heap.iter (fun x -> result <- result @ [ x ]) h
Expect.equal "iter ascending" [ 1; 2; 3 ] result
}

test "exists returns true when predicate matches" {
let h = Heap.ofSeq false [ 1; 2; 3; 4; 5 ]
Expect.isTrue "exists" (Heap.exists (fun x -> x = 3) h)
}

test "exists returns false when predicate does not match" {
let h = Heap.ofSeq false [ 1; 2; 3; 4; 5 ]
Expect.isFalse "exists" (Heap.exists (fun x -> x = 6) h)
}

test "forall returns true when all match" {
let h = Heap.ofSeq false [ 2; 4; 6; 8 ]
Expect.isTrue "forall" (Heap.forall (fun x -> x % 2 = 0) h)
}

test "forall returns false when some do not match" {
let h = Heap.ofSeq false [ 2; 3; 4 ]
Expect.isFalse "forall" (Heap.forall (fun x -> x % 2 = 0) h)
}

test "map transforms elements" {
let h = Heap.ofSeq false [ 1; 2; 3; 4; 5 ]
let mapped = Heap.map (fun x -> x * 2) h
Expect.equal "map" [ 2; 4; 6; 8; 10 ] (Heap.toList mapped)
}

test "map preserves sort direction" {
let h = Heap.ofSeq true [ 1; 2; 3 ]
let mapped = Heap.map (fun x -> x * 10) h
Expect.equal "map desc" [ 30; 20; 10 ] (Heap.toList mapped)
}

test "filter keeps only matching elements" {
let h = Heap.ofSeq false [ 1; 2; 3; 4; 5; 6 ]
let filtered = Heap.filter (fun x -> x % 2 = 0) h
Expect.equal "filter" [ 2; 4; 6 ] (Heap.toList filtered)
}

test "filter empty heap returns empty heap" {
let h = Heap.empty false
let filtered = Heap.filter (fun x -> x > 0) h
Expect.isTrue "filter empty" (Heap.isEmpty filtered)
}

test "choose keeps Some values" {
let h = Heap.ofSeq false [ 1; 2; 3; 4; 5 ]
let chosen = Heap.choose (fun x -> if x % 2 = 0 then Some(x * 10) else None) h
Expect.equal "choose" [ 20; 40 ] (Heap.toList chosen)
}

test "choose all None returns empty heap" {
let h = Heap.ofSeq false [ 1; 3; 5 ]
let chosen = Heap.choose (fun _ -> None) h
Expect.isTrue "choose none" (Heap.isEmpty chosen)
} ]
Loading