diff --git a/src/FSharpx.Collections/Deque.fs b/src/FSharpx.Collections/Deque.fs index 2a056164..edb3ac96 100644 --- a/src/FSharpx.Collections/Deque.fs +++ b/src/FSharpx.Collections/Deque.fs @@ -244,5 +244,49 @@ module Deque = let inline toSeq(q: Deque<'T>) = q :> seq<'T> + ///O(n). Returns a list of the deque elements in front-to-back (head-to-last) order. + let toList(q: Deque<'T>) : 'T list = + q.front @ List.rev q.rBack + + ///O(n). Returns an array of the deque elements in front-to-back (head-to-last) order. + let toArray(q: Deque<'T>) : 'T[] = + let result = Array.zeroCreate q.Length + let mutable i = 0 + + for x in q.front do + result.[i] <- x + i <- i + 1 + + let mutable j = q.Length - 1 + + for x in q.rBack do + result.[j] <- x + j <- j - 1 + + result + + ///O(n). Returns a new deque whose elements are the results of applying the given function to each element. + let map (f: 'T -> 'U) (q: Deque<'T>) : Deque<'U> = + Deque<'U>(List.map f q.front, List.map f q.rBack) + + ///O(n). Returns a new deque containing only the elements of the original for which the given predicate returns true. + let filter (predicate: 'T -> bool) (q: Deque<'T>) : Deque<'T> = + Deque<'T>(List.filter predicate q.front, List.filter predicate q.rBack) + + ///O(n). Applies the given function to each element of the deque. + let iter (f: 'T -> unit) (q: Deque<'T>) = + List.iter f q.front + List.iter f (List.rev q.rBack) + + ///O(n). Returns true if any element of the deque satisfies the given predicate. + ///Note: elements are not necessarily checked in front-to-back (enumeration) order; the internal rear list is checked in reverse order. + let exists (predicate: 'T -> bool) (q: Deque<'T>) : bool = + List.exists predicate q.front || List.exists predicate q.rBack + + ///O(n). Returns true if all elements of the deque satisfy the given predicate. + ///Note: elements are not necessarily checked in front-to-back (enumeration) order; the internal rear list is checked in reverse order. + let forall (predicate: 'T -> bool) (q: Deque<'T>) : bool = + List.forall predicate q.front && List.forall predicate q.rBack + let inline tryUnconj(q: Deque<'T>) = q.TryUnconj diff --git a/src/FSharpx.Collections/Deque.fsi b/src/FSharpx.Collections/Deque.fsi index e7b0fe14..2b67848f 100644 --- a/src/FSharpx.Collections/Deque.fsi +++ b/src/FSharpx.Collections/Deque.fsi @@ -143,5 +143,28 @@ module Deque = ///O(n). Views the given deque as a sequence. val inline toSeq: Deque<'T> -> seq<'T> + ///O(n). Returns a list of the deque elements in front-to-back (head-to-last) order. + val toList: Deque<'T> -> 'T list + + ///O(n). Returns an array of the deque elements in front-to-back (head-to-last) order. + val toArray: Deque<'T> -> 'T[] + + ///O(n). Returns a new deque whose elements are the results of applying the given function to each element. + val map: ('T -> 'U) -> Deque<'T> -> Deque<'U> + + ///O(n). Returns a new deque containing only the elements for which the given predicate returns true. + val filter: ('T -> bool) -> Deque<'T> -> Deque<'T> + + ///O(n). Applies the given function to each element of the deque. + val iter: ('T -> unit) -> Deque<'T> -> unit + + ///O(n). Returns true if any element of the deque satisfies the given predicate. + ///Note: elements are not necessarily checked in front-to-back (enumeration) order; the internal rear list is checked in reverse order. + val exists: ('T -> bool) -> Deque<'T> -> bool + + ///O(n). Returns true if all elements of the deque satisfy the given predicate. + ///Note: elements are not necessarily checked in front-to-back (enumeration) order; the internal rear list is checked in reverse order. + val forall: ('T -> bool) -> Deque<'T> -> bool + ///O(1) amortized, O(n), worst case. Returns option first element and tail. val inline tryUncons: Deque<'T> -> ('T * Deque<'T>) option diff --git a/src/FSharpx.Collections/Queue.fs b/src/FSharpx.Collections/Queue.fs index c4a5b218..b3ba2e23 100644 --- a/src/FSharpx.Collections/Queue.fs +++ b/src/FSharpx.Collections/Queue.fs @@ -148,6 +148,59 @@ module Queue = let inline toSeq(q: Queue<'T>) = q :> seq<'T> + ///O(n). Returns a list of the queue elements in FIFO order. + let toList(q: Queue<'T>) : 'T list = + q.front @ List.rev q.rBack + + ///O(n). Returns an array of the queue elements in FIFO order. + let toArray(q: Queue<'T>) : 'T[] = + let arr = Array.zeroCreate q.Length + let mutable i = 0 + + List.iter + (fun x -> + arr.[i] <- x + i <- i + 1) + q.front + + let mutable j = q.Length - 1 + + List.iter + (fun x -> + arr.[j] <- x + j <- j - 1) + q.rBack + + arr + + ///O(n). Returns a new queue whose elements are the results of applying the given function to each element. + let map (f: 'T -> 'U) (q: Queue<'T>) : Queue<'U> = + Queue<'U>(List.map f q.front, List.map f q.rBack) + + ///O(n). Returns a new queue containing only the elements of the original for which the given predicate returns true. + let filter (predicate: 'T -> bool) (q: Queue<'T>) : Queue<'T> = + let f = List.filter predicate q.front + let r = List.filter predicate q.rBack + + match f with + | [] -> Queue<'T>(List.rev r, []) + | _ -> Queue<'T>(f, r) + + ///O(n). Applies the given function to each element of the queue. + let iter (f: 'T -> unit) (q: Queue<'T>) = + List.iter f q.front + List.iter f (List.rev q.rBack) + + ///O(n). Returns true if any element of the queue satisfies the given predicate. + ///Note: elements are not necessarily checked in FIFO order; the internal rear list is checked in reverse (LIFO) order. + let exists (predicate: 'T -> bool) (q: Queue<'T>) : bool = + List.exists predicate q.front || List.exists predicate q.rBack + + ///O(n). Returns true if all elements of the queue satisfy the given predicate. + ///Note: elements are not necessarily checked in FIFO order; the internal rear list is checked in reverse (LIFO) order. + let forall (predicate: 'T -> bool) (q: Queue<'T>) : bool = + List.forall predicate q.front && List.forall predicate q.rBack + let inline uncons(q: Queue<'T>) = q.Uncons let inline tryUncons(q: Queue<'T>) = diff --git a/src/FSharpx.Collections/Queue.fsi b/src/FSharpx.Collections/Queue.fsi index c6736574..be7c6959 100644 --- a/src/FSharpx.Collections/Queue.fsi +++ b/src/FSharpx.Collections/Queue.fsi @@ -91,6 +91,29 @@ module Queue = ///O(n). Views the given queue as a sequence. val inline toSeq: Queue<'T> -> seq<'T> + ///O(n). Returns a list of the queue elements in FIFO order. + val toList: Queue<'T> -> 'T list + + ///O(n). Returns an array of the queue elements in FIFO order. + val toArray: Queue<'T> -> 'T[] + + ///O(n). Returns a new queue whose elements are the results of applying the given function to each element. + val map: ('T -> 'U) -> Queue<'T> -> Queue<'U> + + ///O(n). Returns a new queue containing only the elements for which the given predicate returns true. + val filter: ('T -> bool) -> Queue<'T> -> Queue<'T> + + ///O(n). Applies the given function to each element of the queue. + val iter: ('T -> unit) -> Queue<'T> -> unit + + ///O(n). Returns true if any element of the queue satisfies the given predicate. + ///Note: elements are not necessarily checked in FIFO order; the internal rear list is checked in reverse (LIFO) order. + val exists: ('T -> bool) -> Queue<'T> -> bool + + ///O(n). Returns true if all elements of the queue satisfy the given predicate. + ///Note: elements are not necessarily checked in FIFO order; the internal rear list is checked in reverse (LIFO) order. + val forall: ('T -> bool) -> Queue<'T> -> bool + ///O(1) amortized, O(n) worst-case. Returns the first element and tail. val inline uncons: Queue<'T> -> 'T * Queue<'T> diff --git a/tests/FSharpx.Collections.Tests/DequeTest.fs b/tests/FSharpx.Collections.Tests/DequeTest.fs index 153366db..c4857b24 100644 --- a/tests/FSharpx.Collections.Tests/DequeTest.fs +++ b/tests/FSharpx.Collections.Tests/DequeTest.fs @@ -1221,4 +1221,85 @@ module DequeTests = config10k "get Deque.initial from deque safely 2" (Prop.forAll(Arb.fromGen intGensStart2.[2]) - <| fun (q, l) -> List.ofSeq q.TryInitial.Value = (List.rev l |> List.tail |> List.rev)) ] + <| fun (q, l) -> List.ofSeq q.TryInitial.Value = (List.rev l |> List.tail |> List.rev)) + + test "toList preserves front-to-back order" { + let q = Deque.ofSeq [ 1; 2; 3; 4; 5 ] + Expect.equal "toList preserves front-to-back order" [ 1; 2; 3; 4; 5 ] (Deque.toList q) + } + + test "toList empty deque" { Expect.equal "toList empty" [] (Deque.toList Deque.empty) } + + test "toArray preserves front-to-back order" { + let q = Deque.ofSeq [ 1; 2; 3 ] |> Deque.conj 4 |> Deque.conj 5 + Expect.equal "toArray" [| 1; 2; 3; 4; 5 |] (Deque.toArray q) + } + + test "toArray empty deque" { Expect.equal "toArray empty" [||] (Deque.toArray Deque.empty) } + + test "map transforms elements" { + let q = Deque.ofSeq [ 1; 2; 3 ] + Expect.equal "map" [ 2; 4; 6 ] (Deque.map ((*) 2) q |> Deque.toList) + } + + test "map preserves front-to-back order" { + let q = Deque.ofSeq [ "a"; "b"; "c" ] |> Deque.conj "d" |> Deque.conj "e" + + Expect.equal "map order" [ "A"; "B"; "C"; "D"; "E" ] (Deque.map (fun (s: string) -> s.ToUpper()) q |> Deque.toList) + } + + test "filter keeps matching elements" { + let q = Deque.ofSeq [ 1; 2; 3; 4; 5; 6 ] + Expect.equal "filter even" [ 2; 4; 6 ] (Deque.filter (fun x -> x % 2 = 0) q |> Deque.toList) + } + + test "filter preserves order" { + let q = Deque.ofSeq [ 1; 2; 3; 4; 5 ] + Expect.equal "filter order" [ 1; 3; 5 ] (Deque.filter (fun x -> x % 2 <> 0) q |> Deque.toList) + } + + test "filter preserves front-to-back order across front/rBack" { + let q = Deque.ofSeq [ 1; 2; 3 ] |> Deque.conj 4 |> Deque.conj 5 + Expect.equal "filter front/rBack" [ 2; 4 ] (Deque.filter (fun x -> x % 2 = 0) q |> Deque.toList) + } + + test "filter returns empty deque when no elements match" { + let q = Deque.ofSeq [ 1; 2; 3 ] + Expect.isTrue "filter empty" (Deque.filter (fun _ -> false) q |> Deque.isEmpty) + } + + test "iter visits each element in front-to-back order" { + let q = Deque.ofSeq [ 1; 2; 3 ] + let result = System.Collections.Generic.List() + Deque.iter result.Add q + Expect.equal "iter order" [ 1; 2; 3 ] (List.ofSeq result) + } + + test "exists returns true when element satisfies predicate" { + let q = Deque.ofSeq [ 1; 2; 3 ] + Expect.isTrue "exists" (Deque.exists ((=) 2) q) + } + + test "exists returns false when no element satisfies predicate" { + let q = Deque.ofSeq [ 1; 2; 3 ] + Expect.isFalse "exists false" (Deque.exists ((=) 99) q) + } + + test "exists returns false on empty deque" { + let q = Deque.empty + Expect.isFalse "exists empty" (Deque.exists ((=) 1) q) + } + test "forall returns true when all elements satisfy predicate" { + let q = Deque.ofSeq [ 2; 4; 6 ] + Expect.isTrue "forall" (Deque.forall (fun x -> x % 2 = 0) q) + } + + test "forall returns false when some elements do not" { + let q = Deque.ofSeq [ 2; 3; 6 ] + Expect.isFalse "forall false" (Deque.forall (fun x -> x % 2 = 0) q) + } + + test "forall returns true for empty deque" { + let q = Deque.empty + Expect.isTrue "forall empty" (Deque.forall (fun x -> x % 2 = 0) q) + } ] diff --git a/tests/FSharpx.Collections.Tests/QueueTest.fs b/tests/FSharpx.Collections.Tests/QueueTest.fs index b942dfd3..265ff9a5 100644 --- a/tests/FSharpx.Collections.Tests/QueueTest.fs +++ b/tests/FSharpx.Collections.Tests/QueueTest.fs @@ -316,4 +316,90 @@ module QueueTests = config10k "ofList build and serialize" (Prop.forAll(Arb.fromGen queueOfListGen) - <| fun (q, l) -> q |> Seq.toList = l) ] + <| fun (q, l) -> q |> Seq.toList = l) + + test "toList preserves FIFO order" { + let q = Queue.ofSeq [ 1; 2; 3; 4; 5 ] + Expect.equal "toList" [ 1; 2; 3; 4; 5 ] (Queue.toList q) + } + + test "toList empty queue" { Expect.equal "toList empty" [] (Queue.toList Queue.empty) } + + test "toArray preserves FIFO order" { + let q = Queue.ofSeq [ 1; 2; 3 ] + Expect.equal "toArray" [| 1; 2; 3 |] (Queue.toArray q) + } + + test "toArray preserves FIFO order across front/rBack boundary" { + let q = Queue.ofSeq [ 1; 2; 3 ] |> Queue.conj 4 |> Queue.conj 5 + Expect.equal "toArray front/rBack" [| 1; 2; 3; 4; 5 |] (Queue.toArray q) + } + + test "toArray empty queue" { Expect.equal "toArray empty" [||] (Queue.toArray Queue.empty) } + test "map transforms elements" { + let q = Queue.ofSeq [ 1; 2; 3 ] + Expect.equal "map" [ 2; 4; 6 ] (Queue.map ((*) 2) q |> Queue.toList) + } + + test "map preserves FIFO order across front/rBack boundary" { + let q = Queue.ofSeq [ "a"; "b"; "c" ] |> Queue.conj "d" |> Queue.conj "e" + + Expect.equal "map order" [ "A"; "B"; "C"; "D"; "E" ] (Queue.map (fun (s: string) -> s.ToUpper()) q |> Queue.toList) + } + + test "filter keeps matching elements" { + let q = Queue.ofSeq [ 1; 2; 3; 4; 5; 6 ] + Expect.equal "filter even" [ 2; 4; 6 ] (Queue.filter (fun x -> x % 2 = 0) q |> Queue.toList) + } + + test "filter preserves order" { + let q = Queue.ofSeq [ 1; 2; 3; 4; 5 ] + Expect.equal "filter preserves order" [ 1; 3; 5 ] (Queue.filter (fun x -> x % 2 <> 0) q |> Queue.toList) + } + + test "filter all out gives empty" { + let q = Queue.ofSeq [ 1; 2; 3 ] + Expect.isTrue "filter all out" (Queue.filter (fun _ -> false) q |> Queue.isEmpty) + } + + test "filter rebuilds front when all front elements are filtered out" { + // front=[1;3], rBack=[4;2] — filtering for evens empties front, so List.rev rBack becomes new front + let q = Queue.ofSeq [ 1; 3 ] |> Queue.conj 2 |> Queue.conj 4 + Expect.equal "filter front rebuild" [ 2; 4 ] (Queue.filter (fun x -> x % 2 = 0) q |> Queue.toList) + } + + test "iter visits each element in FIFO order" { + let q = Queue.ofSeq [ 1; 2; 3 ] + let result = System.Collections.Generic.List() + Queue.iter result.Add q + Expect.equal "iter order" [ 1; 2; 3 ] (List.ofSeq result) + } + + test "exists returns true when element satisfies predicate" { + let q = Queue.ofSeq [ 1; 2; 3 ] + Expect.isTrue "exists" (Queue.exists ((=) 2) q) + } + + test "exists returns false when no element satisfies predicate" { + let q = Queue.ofSeq [ 1; 2; 3 ] + Expect.isFalse "exists false" (Queue.exists ((=) 99) q) + } + + test "exists returns false on empty queue" { + let q = Queue.empty + Expect.isFalse "exists empty" (Queue.exists ((=) 1) q) + } + test "forall returns true when all elements satisfy predicate" { + let q = Queue.ofSeq [ 2; 4; 6 ] + Expect.isTrue "forall" (Queue.forall (fun x -> x % 2 = 0) q) + } + + test "forall returns false when some elements do not satisfy predicate" { + let q = Queue.ofSeq [ 2; 3; 6 ] + Expect.isFalse "forall false" (Queue.forall (fun x -> x % 2 = 0) q) + } + + test "forall returns true for empty queue" { + let q = Queue.empty + Expect.isTrue "forall empty" (Queue.forall (fun x -> x % 2 = 0) q) + } ]