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/Deque.fs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Comment thread
gdziadkiewicz marked this conversation as resolved.
Comment thread
gdziadkiewicz marked this conversation as resolved.
Comment thread
gdziadkiewicz marked this conversation as resolved.

let inline tryUnconj(q: Deque<'T>) =
q.TryUnconj
23 changes: 23 additions & 0 deletions src/FSharpx.Collections/Deque.fsi
Original file line number Diff line number Diff line change
Expand Up @@ -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
53 changes: 53 additions & 0 deletions src/FSharpx.Collections/Queue.fs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Comment thread
gdziadkiewicz marked this conversation as resolved.

///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
Comment thread
gdziadkiewicz marked this conversation as resolved.

let inline uncons(q: Queue<'T>) = q.Uncons

let inline tryUncons(q: Queue<'T>) =
Expand Down
23 changes: 23 additions & 0 deletions src/FSharpx.Collections/Queue.fsi
Original file line number Diff line number Diff line change
Expand Up @@ -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>

Expand Down
83 changes: 82 additions & 1 deletion tests/FSharpx.Collections.Tests/DequeTest.fs
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}

Comment thread
gdziadkiewicz marked this conversation as resolved.
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)
}
Comment thread
gdziadkiewicz marked this conversation as resolved.

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)
}

Comment thread
gdziadkiewicz marked this conversation as resolved.
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<int>()
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)
}

Comment thread
gdziadkiewicz marked this conversation as resolved.
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)
Comment thread
gdziadkiewicz marked this conversation as resolved.
}

test "forall returns true for empty deque" {
let q = Deque.empty
Expect.isTrue "forall empty" (Deque.forall (fun x -> x % 2 = 0) q)
} ]
88 changes: 87 additions & 1 deletion tests/FSharpx.Collections.Tests/QueueTest.fs
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}

Comment thread
gdziadkiewicz marked this conversation as resolved.
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)
}
Comment thread
gdziadkiewicz marked this conversation as resolved.

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<int>()
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)
}

Comment thread
gdziadkiewicz marked this conversation as resolved.
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)
} ]
Comment on lines +392 to +405
Copy link

Copilot AI Apr 25, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The new Queue.forall tests cover all-true and some-false inputs, but don’t cover the empty-queue behavior. Other collection tests in this repo typically assert forall on empty returns true; adding that case here would align behavior guarantees across collections.

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot apply changes based on this feedback

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added in 5d66835Queue.forall now has a test asserting it returns true for an empty queue.